Skip to content

Commit

Permalink
mailto:// templates allow for port and ssl-mode over-ride (#621)
Browse files Browse the repository at this point in the history
Also added some more in depth unit tests
  • Loading branch information
caronc authored Jul 6, 2022
1 parent fcedb47 commit 113fa36
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 9 deletions.
23 changes: 14 additions & 9 deletions apprise/plugins/NotifyEmail.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,15 +534,15 @@ def __init__(self, smtp_host=None, from_name=None,
)

# Apply any defaults based on certain known configurations
self.NotifyEmailDefaults()
self.NotifyEmailDefaults(secure_mode=secure_mode, **kwargs)

# if there is still no smtp_host then we fall back to the hostname
if not self.smtp_host:
self.smtp_host = self.host

return

def NotifyEmailDefaults(self):
def NotifyEmailDefaults(self, secure_mode=None, port=None, **kwargs):
"""
A function that prefills defaults based on the email
it was provided.
Expand Down Expand Up @@ -571,18 +571,23 @@ def NotifyEmailDefaults(self):
'Applying %s Defaults' %
EMAIL_TEMPLATES[i][0],
)
self.port = EMAIL_TEMPLATES[i][2]\
.get('port', self.port)
# the secure flag can not be altered if defined in the template
self.secure = EMAIL_TEMPLATES[i][2]\
.get('secure', self.secure)
self.secure_mode = EMAIL_TEMPLATES[i][2]\
.get('secure_mode', self.secure_mode)

# The SMTP Host check is already done above; if it was
# specified we wouldn't even reach this part of the code.
self.smtp_host = EMAIL_TEMPLATES[i][2]\
.get('smtp_host', self.smtp_host)

if self.smtp_host is None:
# default to our host
self.smtp_host = self.host
# The following can be over-ridden if defined manually in the
# Apprise URL. Otherwise they take on the template value
if not port:
self.port = EMAIL_TEMPLATES[i][2]\
.get('port', self.port)
if not secure_mode:
self.secure_mode = EMAIL_TEMPLATES[i][2]\
.get('secure_mode', self.secure_mode)

# Adjust email login based on the defined usertype. If no entry
# was specified, then we default to having them all set (which
Expand Down
107 changes: 107 additions & 0 deletions test/test_plugin_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -728,3 +728,110 @@ def test_plugin_email_dict_variations():
'password': 'abd123',
'host': 'example.com'}, suppress_exceptions=False)
assert isinstance(obj, plugins.NotifyEmail) is True


@mock.patch('smtplib.SMTP_SSL')
@mock.patch('smtplib.SMTP')
def test_plugin_email_url_parsing(mock_smtp, mock_smtp_ssl):
"""
NotifyEmail() Test email url parsing
"""

# Disable Throttling to speed testing
plugins.NotifyEmail.request_rate_per_sec = 0

response = mock.Mock()
mock_smtp_ssl.return_value = response
mock_smtp.return_value = response

# Test variations of username required to be an email address
# [email protected]; we also test an over-ride port on a template driven
# mailto:// entry
results = plugins.NotifyEmail.parse_url(
'mailtos://user:[email protected]:444'
'[email protected]&name=test%20name')
assert isinstance(results, dict)
assert 'test name' == results['from_name']
assert 'user' == results['user']
assert 444 == results['port']
assert 'hotmail.com' == results['host']
assert 'pass123' == results['password']
assert '[email protected]' in results['targets']

obj = Apprise.instantiate(results, suppress_exceptions=False)
assert isinstance(obj, plugins.NotifyEmail) is True

assert mock_smtp.call_count == 0
assert mock_smtp_ssl.call_count == 0
assert obj.notify("test") is True
assert mock_smtp.call_count == 1
assert mock_smtp_ssl.call_count == 0
assert response.starttls.call_count == 1
assert response.login.call_count == 1
assert response.sendmail.call_count == 1
# Store our Sent Arguments
# Syntax is:
# sendmail(from_addr, to_addrs, msg, mail_options=(), rcpt_options=())
# [0] [1] [2]
_from = response.sendmail.call_args[0][0]
_to = response.sendmail.call_args[0][1]
_msg = response.sendmail.call_args[0][2]
assert _from == '[email protected]'
assert isinstance(_to, list)
assert len(_to) == 1
assert _to[0] == '[email protected]'
assert _msg.endswith('test')

# Our URL port was over-ridden (on template) to use 444
# We can verify that this was correctly saved
assert obj.url().startswith(
'mailtos://user:[email protected]:444/user2%40yahoo.com')
assert 'mode=starttls' in obj.url()
assert 'smtp=smtp-mail.outlook.com' in obj.url()

mock_smtp.reset_mock()
response.reset_mock()

# The below switches the `name` with the `to` to verify the results
# are the same; it also verfies that the mode gets changed to SSL
# instead of STARTTLS
results = plugins.NotifyEmail.parse_url(
'mailtos://user:[email protected]?smtp=override.com'
'&name=test%20name&[email protected]&mode=ssl')
assert isinstance(results, dict)
assert 'test name' == results['from_name']
assert 'user' == results['user']
assert 'hotmail.com' == results['host']
assert 'pass123' == results['password']
assert '[email protected]' in results['targets']
assert 'ssl' == results['secure_mode']
obj = Apprise.instantiate(results, suppress_exceptions=False)
assert isinstance(obj, plugins.NotifyEmail) is True

assert mock_smtp.call_count == 0
assert mock_smtp_ssl.call_count == 0
assert obj.notify("test") is True
assert mock_smtp.call_count == 0
assert mock_smtp_ssl.call_count == 1
assert response.starttls.call_count == 0
assert response.login.call_count == 1
assert response.sendmail.call_count == 1
# Store our Sent Arguments
# Syntax is:
# sendmail(from_addr, to_addrs, msg, mail_options=(), rcpt_options=())
# [0] [1] [2]
_from = response.sendmail.call_args[0][0]
_to = response.sendmail.call_args[0][1]
_msg = response.sendmail.call_args[0][2]
assert _from == '[email protected]'
assert isinstance(_to, list)
assert len(_to) == 1
assert _to[0] == '[email protected]'
assert _msg.endswith('test')

assert obj.url().startswith(
'mailtos://user:[email protected]/user2%40yahoo.com')
# Test that our template over-ride worked
assert 'mode=ssl' in obj.url()
assert 'smtp=override.com' in obj.url()

0 comments on commit 113fa36

Please sign in to comment.