Skip to content

Commit

Permalink
Merge pull request #24 from snoonetIRC/master+ignore-invalid-addr
Browse files Browse the repository at this point in the history
Ignore invalid email addresses when sending
  • Loading branch information
linuxdaemon authored Sep 11, 2019
2 parents 49bcfda + 488b79d commit fe67da3
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 2 deletions.
8 changes: 7 additions & 1 deletion mailer/daemon.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import random
import smtplib
import time
import traceback
from typing import Union
Expand All @@ -18,7 +19,12 @@ def send_message(self, smtp_config: Union[str, SMTPConfig], message):
if isinstance(smtp_config, str):
smtp_config = self.config.smtp_configs[smtp_config]

smtp_config.send(message)
try:
smtp_config.send(message)
except smtplib.SMTPRecipientsRefused as e:
self.error_reporter.report_warning(
"Invalid email recipient(s) {!r}, ignoring message".format(e.recipients)
)

def on_error(self):
"""Must only be called while handling an exception"""
Expand Down
71 changes: 71 additions & 0 deletions tests/test_email_send.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import os
import smtplib
from pathlib import Path
from tempfile import NamedTemporaryFile
from textwrap import dedent

import pytest
from mock import patch
from responses import RequestsMock

from mailer.config import SMTPConfig


class MockSMTPConfig(SMTPConfig):
def send(self, message):
raise smtplib.SMTPRecipientsRefused([message['To']])


@pytest.fixture()
def mock_smtp_config():
with patch('mailer.config.SMTPConfig', MockSMTPConfig) as mocked:
yield mocked


@pytest.fixture()
def mock_api():
with RequestsMock() as reqs:
reqs.add(
'GET', 'https://localhost:8888/api/user?name=nick',
match_querystring=True,
json={
'nc': {
'REGSERVER': 'test.example.com',
}
}
)
yield reqs


@pytest.fixture()
def override_config():
old = os.getenv('MAILER_CONFIG')
try:
os.environ['MAILER_CONFIG'] = 'travis/config.tests.yaml'
yield
finally:
if old is None:
del os.environ['MAILER_CONFIG']
else:
os.environ['MAILER_CONFIG'] = old


def test_invalid_recipient(override_config, mock_smtp_config, mock_api):
from mailer.daemon import Daemon
daemon = Daemon()
with patch.object(daemon.config, 'error_reporter') as reporter:
with NamedTemporaryFile(delete=False) as tmp:
tmp.write(dedent("""\
From: [email protected]
To: "nick" <a.user@!somedomain.org>
nick=nick
type=registration
code=1234
""").encode())

daemon.handle_email(Path(tmp.name))

reporter.report_warning.assert_called_with(
"Invalid email recipient(s) ['nick <a.user@!somedomain.org>'], ignoring message"
)
2 changes: 1 addition & 1 deletion tests/test_email_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def test_config_template():

@pytest.fixture()
def temp_file():
with NamedTemporaryFile() as f:
with NamedTemporaryFile(delete=False) as f:
yield f


Expand Down
104 changes: 104 additions & 0 deletions travis/config.tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
mail_dir: emails
smtp:
network1:
server: smtp.example.com
from_addr: [email protected]
port: 587
tls: true
user_name: ''
password: ''
reports:
from_addr: [email protected]
server: smtp-relay.gmail.com
port: 587
tls: true
smtp_helo: snoonet.org

reports:
default:
to: [email protected]
smtp: reports
delay: 1h # By default send reports once an hour
stats:
# Statistics reports
delay: 1d # Only send statistics once per day
errors:
# Error reports
delay: 10m # Send an error summary at most once every 10 minutes

# requires:
# https://github.com/snoonetIRC/anope-modules/blob/master/modules/m_data_api.cpp
# https://github.com/snoonetIRC/anope-modules/blob/master/modules/m_store_server.cpp
api: https://localhost:8888

default_formats:
registration:
subject: Nickname registration for {nick}
message: |-
Hi,
You have requested to register the nickname {nick} on {network}.
Please type \" /msg NickServ CONFIRM {code} \" in your IRC Client to complete registration.
If you don't know why this mail was sent to you, please ignore it silently.
{network} administrators.
reset:
subject: Reset password request for {nick}
message: |-
Hi,
You have requested to have the password for {nick} reset.
To reset your password, type \" /msg NickServ CONFIRM {nick} {code} \"
After you do this, you will be logged in to your account by anope.
Then change your password with \" /msg NickServ SET PASSWORD newpasshere \".
If you don't know why this mail was sent to you, please ignore it silently.
{network} administrators.
emailchange:
subject: Email confirmation
message: |-
Hi,
You have requested to change your email address to {newmail}.
Please type \" /msg NickServ CONFIRM {code} \" in your IRC client to confirm this change.
If you don't know why this mail was sent to you, please ignore it silently.
{network} administrators.
memo:
subject: New memo
message: |-
Hi {nick},
You've just received a new memo from {sender}. This is memo number {memo_id}.
Memo text:
{text}
Automated message from {network}. Please do not reply to this email.
networks:
- name: Net1
server: '*.example.com'
smtp: network1

- name: Snoonet
# Any server not already matched uses this config
server: '*'
smtp: snoonet
formats:
memo:
subject: New memo from {sender}
message: |-
Hi {nick},
You've just received a new memo from {sender}. This is memo number {memo_id}.
Memo text:
{text}
Automated message from {network}. Please do not reply to this email.
1 change: 1 addition & 0 deletions travis/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ pytest-cov==2.7.1
pytest-travis-fold==1.3.0
codecov==2.0.15
mock==3.0.5
responses==0.10.6
-r ../requirements.txt

0 comments on commit fe67da3

Please sign in to comment.