Sendmail not working for local GAE local development server - python

I've enabled sendmail using the --enable_sendmail=yes option as described in the GAE docs and am still not getting any emails (though the emails do appear in the logging messages). Simplified code looks like:
from google.appengine.api import mail
SENDER_EMAIL_ADDRESS = "admin#APPID.appspot.com"
msg = "Test message"
subject = "Test subject"
recipient = "{} <{}>".format('username', 'username#gmail.com')
mail.send_mail(sender=SENDER_EMAIL_ADDRESS,
to=recipient,
subject=subject,
body=msg)
What am I doing wrong?

If you specify a From address it must correspond with your local machine. The sendmail handling function in the Mail Stub that google provides for its development server looks like:
...
try:
child.stdin.write(mime_message.as_string())
child.stdin.close()
...
The problem is that the From address passed to the send_mail method is in mime_message headers. Adding the following line as the first line in the try block removes that header from the mime message. This allows sendmail to use a default From address:
mime_message._headers = [x for x in mime_message._headers if x[0] != 'From']
The file with this function can be found at /path/to/google_cloud_sdk/platform/google_appengine/google/appengine/api/mail_stub.py

Related

Python aiosmtpd - what is missing for an Mail-Transfer-Agent (MTA)?

I want to write my own small mailserver application in python with aiosmtpd
a) for educational purpose to better understand mailservers
b) to realize my own features
So my question is, what is missing (besides aiosmtpd) for an Mail-Transfer-Agent, that can send and receive emails to/from other full MTAs (gmail.com, yahoo.com ...)?
I'm guessing:
1.) Of course a domain and static ip
2.) Valid certificate for this domain
...should be doable with Lets Encrypt
3.) Encryption
...should be doable with SSL/Context/Starttls... with aiosmtpd itself
4.) Resolving MX DNS entries for outgoing emails!?
...should be doable with python library dnspython
5.) Error handling for SMTP communication errors, error replies from other MTAs, bouncing!?
6.) Queue for handling inbound and pending outbund emails!?
Are there any other "essential" features missing?
Of course i know, there are a lot more "advanced" features for a mailserver like spam checking, malware checking, certificate validation, blacklisting, rules, mailboxes and more...
Thanks for all hints!
EDIT:
Let me clarify what is in my mind:
I want to write a mailserver for a club. Its main purpose will be a mailing-list-server. There will be different lists for different groups of the club.
Lets say my domain is myclub.org then there will be for example youth#myclub.org, trainer#myclub.org and so on.
Only members will be allowed to use this mailserver and only the members will receive emails from this mailserver. No one else will be allowed to send emails to this mailserver nor will receive emails from it. The members email-addresses and their group(s) are stored in a database.
In the future i want to integrate some other useful features, for example:
Auto-reminders
A chatbot, where members can control services and request informations by email
What i don't need:
User Mailboxes
POP/IMAP access
Webinterface
Open relay issue:
I want to reject any [FROM] email address that is not in the members database during SMTP negotiation.
I want to check the sending mailservers for a valid certificate.
The number of emails/member/day will be limited.
I'm not sure, if i really need spam detection for the incoming emails?
Losing emails issue:
I think i will need a "lightweight" retry mechanism. However if an outgoing email can't be delivered after some retries, it will be dropped and only the administrator will be notified, not the sender. The members should not be bothered by email delivery issues. Is there any Python Library that can generate RFC3464 compliant error reply emails?
Reboot issue:
I'm not sure if i really need persistent storage for emails, that are not yet sent? In my use case, all the outgoing emails should be delivered usually within a few seconds (if no delivery problem occurs). Before a (planned) reboot i can check for an empty send queue.
aiosmtpd is an excellent tool for writing custom routing and header rewriting rules for email. However, aiosmtpd is not an MTA, since it does not do message queuing or DSN generation. One popular choice of MTA is postfix, and since postfix can be configured to relay all emails for a domain to another local SMTP server (such as aiosmtpd), a natural choice is to use postfix as the internet-facing frontend and aiosmtpd as the business-logic backend.
Advantages of using postfix as the middle-man instead of letting aiosmtpd face the public internet:
No need to handle DNS MX lookups in aiosmtpd -- just relay through postfix (localhost:25)
No worry about non-compliant SMTP clients in aiosmtpd
No worry about STARTTLS in aiosmtpd -- configure this in postfix instead (simpler and more battle-hardened)
No worry about retrying failed email deliveries and sending delivery status notifications
aiosmtpd can be configured to respond with "transient failure" (SMTP 4xx code) upon programming errors, so no email is lost as long as the programming error is fixed within 4 days
Here's how you might configure postfix to work with a local SMTP server powered by e.g. aiosmtpd.
We're going to run postfix on port 25 and aiosmtpd on port 20381.
To specify that postfix should relay emails for example.com to an SMTP server running on port 20381, add the following to /etc/postfix/main.cf:
transport_maps = hash:/etc/postfix/smtp_transport
relay_domains = example.com
And create /etc/postfix/smtp_transport with the contents:
# Table of special transport method for domains in
# virtual_mailbox_domains. See postmap(5), virtual(5) and
# transport(5).
#
# Remember to run
# postmap /etc/postfix/smtp_transport
# and update relay_domains in main.cf after changing this file!
example.com smtp:127.0.0.1:20381
Run postmap /etc/postfix/smtp_transport after creating that file (and every time you modify it).
On the aiosmtpd side, there are a few things to consider.
The most important is how you handle bounce emails. The short story is that you should set the envelope sender to an email address you control that is dedicated to receiving bounces, e.g. bounce#example.com. When email arrives at this address, it should be stored somewhere so you can process bounces, e.g. by removing member email addresses from your database.
Another important thing to consider is how you tell your members' email providers that you are doing mailing list forwarding. You might want to add the following headers when forwarding emails to GROUP#example.com:
Sender: bounce#example.com
List-Name: GROUP
List-Id: GROUP.example.com
List-Unsubscribe: <mailto:postmaster#example.com?subject=unsubscribe%20GROUP>
List-Help: <mailto:postmaster#example.com?subject=list-help>
List-Subscribe: <mailto:postmaster#example.com?subject=subscribe%20GROUP>
Precedence: bulk
X-Auto-Response-Suppress: OOF
Here, I used postmaster#example.com as the recipient for list unsubscribe requests. This should be an address that forwards to the email administrator (that is, you).
Below is a skeleton (untested) that does the above. It stores bounce emails in a directory named bounces and forwards emails with a valid From:-header (one that appears in MEMBERS) according to the list of groups (in GROUPS).
import os
import email
import email.utils
import mailbox
import smtplib
import aiosmtpd.controller
LISTEN_HOST = '127.0.0.1'
LISTEN_PORT = 20381
DOMAIN = 'example.com'
BOUNCE_ADDRESS = 'bounce'
POSTMASTER = 'postmaster'
BOUNCE_DIRECTORY = os.path.join(
os.path.dirname(__file__), 'bounces')
def get_extra_headers(list_name, is_group=True, skip=()):
list_id = '%s.%s' % (list_name, DOMAIN)
bounce = '%s#%s' % (BOUNCE_ADDRESS, DOMAIN)
postmaster = '%s#%s' % (POSTMASTER, DOMAIN)
unsub = '<mailto:%s?subject=unsubscribe%%20%s>' % (postmaster, list_name)
help = '<mailto:%s?subject=list-help>' % (postmaster,)
sub = '<mailto:%s?subject=subscribe%%20%s>' % (postmaster, list_name)
headers = [
('Sender', bounce),
('List-Name', list_name),
('List-Id', list_id),
('List-Unsubscribe', unsub),
('List-Help', help),
('List-Subscribe', sub),
]
if is_group:
headers.extend([
('Precedence', 'bulk'),
('X-Auto-Response-Suppress', 'OOF'),
])
headers = [(k, v) for k, v in headers if k.lower() not in skip]
return headers
def store_bounce_message(message):
mbox = mailbox.Maildir(BOUNCE_DIRECTORY)
mbox.add(message)
MEMBERS = ['foo#example.net', 'bar#example.org',
'clubadmin#example.org']
GROUPS = {
'group1': ['foo#example.net', 'bar#example.org'],
POSTMASTER: ['clubadmin#example.org'],
}
class ClubHandler:
def validate_sender(self, message):
from_ = message.get('From')
if not from_:
return False
realname, address = email.utils.parseaddr(from_)
if address not in MEMBERS:
return False
return True
def translate_recipient(self, local_part):
try:
return GROUPS[local_part]
except KeyError:
return None
async def handle_RCPT(self, server, session, envelope, address, rcpt_options):
local, domain = address.split('#')
if domain.lower() != DOMAIN:
return '550 wrong domain'
if local.lower() == BOUNCE:
envelope.is_bounce = True
return '250 OK'
translated = self.translate_recipient(local.lower())
if translated is None:
return '550 no such user'
envelope.rcpt_tos.extend(translated)
return '250 OK'
async def handle_DATA(self, server, session, envelope):
if getattr(envelope, 'is_bounce', False):
if len(envelope.rcpt_tos) > 0:
return '500 Cannot send bounce message to multiple recipients'
store_bounce_message(envelope.original_content)
return '250 OK'
message = email.message_from_bytes(envelope.original_content)
if not self.validate_sender(message):
return '500 I do not know you'
for header_key, header_value in get_extra_headers('club'):
message[header_key] = header_value
bounce = '%s#%s' % (BOUNCE_ADDRESS, DOMAIN)
with smtplib.SMTP('localhost', 25) as smtp:
smtp.sendmail(bounce, envelope.rcpt_tos, message.as_bytes())
return '250 OK'
if __name__ == '__main__':
controller = aiosmtpd.controller.Controller(ClubHandler, hostname=LISTEN_HOST, port=LISTEN_PORT)
controller.start()
print("Controller started")
try:
while True:
input()
except (EOFError, KeyboardInterrupt):
controller.stop()
The most important thing about running your own SMTP server is that you must not be an open relay. That means you must not accept messages from strangers and relay them to any destination on the internet, since that would enable spammers to send spam through your SMTP server -- which would quickly get you blocked.
Thus, your server should
relay from authenticated users/senders to remote destinations, or
relay from strangers to your own domains.
Since your question talks about resolving MX records for outgoing email, I'm assuming you want your server to accept emails from authenticated users. Thus you need to consider how your users will authenticate themselves to the server. aiosmtpd currently has an open pull request providing a basic SMTP AUTH implementation; you may use that, or you may implement your own (by subclassing aiosmtpd.smtp.SMTP and implementing the smtp_AUTH() method).
The second-most important thing about running your own SMTP server is that you must not lose emails without notifying the sender. When you accept an email from an authenticated user to be relayed to an external destination, you should let the user know (by sending an RFC 3464 Delivery Status Notification via email) if the message is delayed or if it is not delivered at all.
You should not drop the email immediately if the remote destination fails to receive it; you should try again later and repeatedly try until you deem that you have tried for long enough. Postfix, for instance, waits 10 minutes before trying to deliver the email after the first delivery attempt fails, and then it waits 20 minutes if the second attempt fails, and so on until the message has been attempted delivered for a couple days.
You should also take care to allow the host running your mail server to be rebooted, meaning you should store queued messages on disk. For this you might be able to use the mailbox module.
Of course, I haven't covered every little detail, but I think the above two points are the most important, and you didn't seem to cover them in your question.
You may consider the following features:
Message threading
Support for Delivery status
Support for POP and IMAP protocols
Supports for protocols such as RFC 2821 SMTP and RFC 2033 LMTP email message transport
Support Multiple message tagging
Support for PGP/MIME (RFC2015)
Support list-reply
Lets each user manage their own mail lists Supports
Control of message headers during composition
Support for address groups
Prevention of mailing list loops
Junk mail control

Using HTML templates to send emails in python

I'm trying to write a separate mail service, which is decoupled with our Flask application. I'm looking for a way to send welcome emails when users first log into our Flask application. I'm using Celery and RabbitMQ to do it asynchronously.
Here is my email function:
sen = 'example#gmail.com'
pwd = 'my_password'
#celery.task
def send_email(nickname, email):
msg = MIMEMultipart('alternative')
msg['Subject'] = 'my_sub'
msg['From'] = sen
msg['To'] = email
html = <b>test_body</b>
part1 = MIMEText(html, 'html')
msg.attach(part1)
server = smtplib.SMTP("smtp.gmail.com", 587)
server.ehlo()
server.starttls()
server.login(sen, pwd)
server.sendmail(sen, [email], msg.as_string())
server.close()
Initially I was using Flask's render_template to get the HTML body and subject. But I don't want to use the Flask extension (I have my reasons). So my questions are:
How can I use email templates so that the subject and body fields can be configured easily?
How can I put the default email sender and password in a config file/email template (might be related to q1)?
It seems to be that I have a lot of code to send a simple email. Can you suggest some optimization techniques (omitting steps)?
I've wrritten a simple module(mail.py) to send emails using templates(HTML/TEXT). Hope that helps!
https://github.com/ludmal/pylib
It may be simpler to use an external service.
A service (e.g. Mailchimp) is simple to integrate. You can design the template in their layer, and trigger emails by sending merge data from your app to the service API. Functionally it's a lot like rendering a template locally and mailing it out via SMTP, but they have sophisticated tools for adapting message format to devices, tracking bounces, improving deliverability, reporting etc.
Services like this often have a free tier for up to 1000's of emails per month.

New to Python, GMail SMTP error

I am writing a simple sendmail function to myself and I keep getting this error:
NameError: name 'SMTPException' is not defined
What is wrong with my code? Any suggestions?
import smtplib
sender = "user#gmail.com"
receiver = ["user#gmail.com"]
message = "Hello!"
try:
session = smptlib.SMTP('smtp.gmail.com',587)
session.ehlo()
session.starttls()
session.ehlo()
session.login(sender,'password')
session.sendmail(sender,receiver,message)
session.quit()
except SMTPException:
print('Error')
In Python, you will need to fully qualify the name by prefixing it with its module:
except smtplib.SMTPException:
This is true unless you specifically import the unqualified name (but I wouldn't recommend doing this for your program, just showing what's possible):
from smtplib import SMTPException
That misspelling occurred many times to me as well! One way to circumvent this "problem", is to use yagmail.
Jokes aside, I recently created yagmail to make it easier to send emails.
For example:
import yagmail
yag = yagmail.SMTP('user#gmail.com', 'password')
yag.send(contents = "Hello!")
It uses several shortenings here, for example when To is not defined, it will send a mail to the same email who registered on the server. Also the port and host are the default, which makes it very concise.
In fact, since it seems you want to close the connection immediately, you can even use this one-liner:
yagmail.SMTP('user#gmail.com', 'password').send(contents = "Hello!")
For security, you can keep your password in the keyring (see documentation) such that you do not have to keep your personal password in your scripts, very important! It'll even save you more precious screen-estate.
Going all-in with the package (#gmail.com is default), you can get away with the following:
yagmail.SMTP('user').send('', 'Hello!')
Good luck.
from smtplib import SMTPExeption
exep smtplib.SMTPExeption

SVN Notifications via GMAIL SMTP

I have got SVN running on Ubuntu 11.04 32bit and now want notifications using my GMAIL account for every commit.
I've commited a few things but don't actually recieve the commit emails for them. There aren't any errors that are displayed and I have looked through the logs but haven't found much useful information as of yet.
I've read quite alot of posts regarding this and editing the following files below including what they contain now. I've tried using sendmail and postfix but have had no luck with them hence which is why I am using Google's mail server. It would be grateful if someone could point me in the right direction or an alternative approach.
The links I found and have used.
http://sadomovalex.blogspot.com/2009/12/use-gmail-smtp-server-for-post-commit.html
http://iffee.wordpress.com/2009/04/08/svn-commit-to-google-apps-email-notification/
post-commit.tmpl
REPOS="$1"
REV="$2"
/home/megaz/svn/repos/ya/hooks/mailer.py commit "$REPOS" \
"$REV" /home/megaz/svn/repos/ya/hooks/mailer.conf
mailer.conf
[general]
smtp_hostname = smtp.gmail.com:587
smtp_username = #mygmailaddress
smtp_password = #mygmailpassword
smtp_use_ssl = true
smtp_use_tls = 1
[defaults]
diff = /usr/bin/diff -u -L %(label_from)s -L %(label_to)s %(from)s %(to)s
commit_subject_prefix = [SVN-Commit]
propchange_subject_prefix =
lock_subject_prefix =
unlock_subject_prefix =
from_addr = #my from address
to_addr = #my to address
reply_to = #my replyto address
generate_diffs = none
show_nonmatching_paths = yes
[maps]
mailer.py
class SMTPOutput(MailedOutput):
def start(self, group, params):
MailedOutput.start(self, group, params)
self.buffer = StringIO()
self.write = self.buffer.write
self.write(self.mail_headers(group, params))
def finish(self):
server = smtplib.SMTP(self.cfg.general.smtp_hostname)
# 2009-12-13 asadomov: add ssl configuration (e.g. for gmail smtp server)
if self.cfg.is_set('general.smtp_use_ssl') and self.cfg.general.smtp_use_ssl.lower() == "true":
server.ehlo()
server.starttls()
server.ehlo()
if self.cfg.is_set('general.smtp_username'):
server.login(self.cfg.general.smtp_username,
self.cfg.general.smtp_password)
server.sendmail(self.from_addr, self.to_addrs, self.buffer.getvalue())
server.quit()
I see, you have not really read the instructions. The code you have copy/pasted needs to replace a snippet in a larger file which you haven't downloaded. Also, the file name of the post-commit script should not have the .templ suffix; that's what they use for inactive example / template files in the distribution.
Perhaps this explains why you couldn't get Sendmail to work, either. At this point I'd recommend to go back to that, as it's simpler.
rename your "post-commit.templ" to "post-commit"
make sure you give exec rights (such as 755) to "post-commit"

Django sending email

In PHP I can send an email simply by calling mail(). In Django, I need to specify SMTP backends and other things.
Is there a simpler way to send email from Django?
There are several good mail-sending functions in the django.core.mail module.
For a tutorial please see Sending e-mail:
Although Python makes sending e-mail
relatively easy via the smtplib
library, Django provides a couple of
light wrappers over it. These wrappers
are provided to make sending e-mail
extra quick, to make it easy to test
e-mail sending during development, and
to provide support for platforms that
can’t use SMTP.
The simplest function that would most likely suit your purposes is the send_mail function:
send_mail(
subject,
message,
from_email,
recipient_list,
fail_silently=False,
auth_user=None,
auth_password=None,
connection=None)
In PHP you can only send mail with a simple mail() command on non-Windows systems. These will expect a local MTA like Postfix to be installed and correctly configured, as should be the case for most web servers. If you want to depend on third-party or decentralized mail service depends on how critical email is for your application. Serious dependency on speedy and reliable email transmission usually results in sending mail via SMTP to a central mail server (the "big pipe").
Still, if you want to have the same function as in PHP, try this:
import subprocess
def send_mail(from_addr, to_addr, subject, body):
cmdline = ["/usr/sbin/sendmail", "-f"]
cmdline.append(from_addr)
cmdline.append(to_addr)
mailer = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
dialog = "From: %s\nTo: %s\nSubject: %s\n\n%s\n.\n" % (from_addr, to_addr, subject, body)
return mailer.communicate(dialog)
And use it like:
send_mail ("Me <myself#mydomain.com>", "Recip Ient <other#hisdomain.com>", "Teh' Subject", "Mail body")
Either way, you need some backend (read MTA). Of the top of my head I can think of two things:
As already pointed out, you can for example use sendmail http://djangosnippets.org/snippets/1864/
Even better, use a Python MTA. There's Lamson, a Python email server (MTA): http://lamsonproject.org/docs/hooking_into_django.html

Categories