mirror of
https://github.com/caronc/apprise.git
synced 2025-02-16 18:21:01 +01:00
Emails - parse host
from user login if not specified (#1095)
This commit is contained in:
parent
6cd528cd5c
commit
1cef49e198
@ -45,7 +45,7 @@ from .NotifyBase import NotifyBase
|
|||||||
from ..URLBase import PrivacyMode
|
from ..URLBase import PrivacyMode
|
||||||
from ..common import NotifyFormat, NotifyType
|
from ..common import NotifyFormat, NotifyType
|
||||||
from ..conversion import convert_between
|
from ..conversion import convert_between
|
||||||
from ..utils import is_email, parse_emails
|
from ..utils import is_email, parse_emails, is_hostname
|
||||||
from ..AppriseLocale import gettext_lazy as _
|
from ..AppriseLocale import gettext_lazy as _
|
||||||
from ..logger import logger
|
from ..logger import logger
|
||||||
|
|
||||||
@ -566,12 +566,20 @@ class NotifyEmail(NotifyBase):
|
|||||||
# Apply any defaults based on certain known configurations
|
# Apply any defaults based on certain known configurations
|
||||||
self.NotifyEmailDefaults(secure_mode=secure_mode, **kwargs)
|
self.NotifyEmailDefaults(secure_mode=secure_mode, **kwargs)
|
||||||
|
|
||||||
if self.user and self.host:
|
if self.user:
|
||||||
# Prepare the bases of our email
|
if self.host:
|
||||||
self.from_addr = [self.app_id, '{}@{}'.format(
|
# Prepare the bases of our email
|
||||||
re.split(r'[\s@]+', self.user)[0],
|
self.from_addr = [self.app_id, '{}@{}'.format(
|
||||||
self.host,
|
re.split(r'[\s@]+', self.user)[0],
|
||||||
)]
|
self.host,
|
||||||
|
)]
|
||||||
|
|
||||||
|
else:
|
||||||
|
result = is_email(self.user)
|
||||||
|
if result:
|
||||||
|
# Prepare the bases of our email and include domain
|
||||||
|
self.host = result['domain']
|
||||||
|
self.from_addr = [self.app_id, self.user]
|
||||||
|
|
||||||
if from_addr:
|
if from_addr:
|
||||||
result = is_email(from_addr)
|
result = is_email(from_addr)
|
||||||
@ -1037,11 +1045,25 @@ class NotifyEmail(NotifyBase):
|
|||||||
us to re-instantiate this object.
|
us to re-instantiate this object.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
results = NotifyBase.parse_url(url)
|
results = NotifyBase.parse_url(url, verify_host=False)
|
||||||
if not results:
|
if not results:
|
||||||
# We're done early as we couldn't load the results
|
# We're done early as we couldn't load the results
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
# Prepare our target lists
|
||||||
|
results['targets'] = []
|
||||||
|
|
||||||
|
if not is_hostname(results['host'], ipv4=False, ipv6=False,
|
||||||
|
underscore=False):
|
||||||
|
|
||||||
|
if is_email(NotifyEmail.unquote(results['host'])):
|
||||||
|
# Don't lose defined email addresses
|
||||||
|
results['targets'].append(NotifyEmail.unquote(results['host']))
|
||||||
|
|
||||||
|
# Detect if we have a valid hostname or not; be sure to reset it's
|
||||||
|
# value if invalid; we'll attempt to figure this out later on
|
||||||
|
results['host'] = ''
|
||||||
|
|
||||||
# The From address is a must; either through the use of templates
|
# The From address is a must; either through the use of templates
|
||||||
# from= entry and/or merging the user and hostname together, this
|
# from= entry and/or merging the user and hostname together, this
|
||||||
# must be calculated or parse_url will fail.
|
# must be calculated or parse_url will fail.
|
||||||
@ -1052,7 +1074,7 @@ class NotifyEmail(NotifyBase):
|
|||||||
|
|
||||||
# Get our potential email targets; if none our found we'll just
|
# Get our potential email targets; if none our found we'll just
|
||||||
# add one to ourselves
|
# add one to ourselves
|
||||||
results['targets'] = NotifyEmail.split_path(results['fullpath'])
|
results['targets'] += NotifyEmail.split_path(results['fullpath'])
|
||||||
|
|
||||||
# Attempt to detect 'to' email address
|
# Attempt to detect 'to' email address
|
||||||
if 'to' in results['qsd'] and len(results['qsd']['to']):
|
if 'to' in results['qsd'] and len(results['qsd']['to']):
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
# POSSIBILITY OF SUCH DAMAGE.
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import pytest
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
@ -56,13 +57,13 @@ TEST_URLS = (
|
|||||||
# NotifyEmail
|
# NotifyEmail
|
||||||
##################################
|
##################################
|
||||||
('mailto://', {
|
('mailto://', {
|
||||||
'instance': None,
|
'instance': TypeError,
|
||||||
}),
|
}),
|
||||||
('mailtos://', {
|
('mailtos://', {
|
||||||
'instance': None,
|
'instance': TypeError,
|
||||||
}),
|
}),
|
||||||
('mailto://:@/', {
|
('mailto://:@/', {
|
||||||
'instance': None
|
'instance': TypeError,
|
||||||
}),
|
}),
|
||||||
# No Username
|
# No Username
|
||||||
('mailtos://:pass@nuxref.com:567', {
|
('mailtos://:pass@nuxref.com:567', {
|
||||||
@ -441,9 +442,12 @@ def test_plugin_email(mock_smtp, mock_smtpssl):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Handle our exception
|
# Handle our exception
|
||||||
if instance is None:
|
if instance is None:
|
||||||
|
print('%s generated %s' % (url, str(e)))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
if not isinstance(e, instance):
|
if not isinstance(e, instance):
|
||||||
|
print('%s Exception (expected %s); got %s' % (
|
||||||
|
url, str(instance), str(e)))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
@ -1815,3 +1819,190 @@ def test_plugin_email_variables_1087():
|
|||||||
assert email.smtp_host == 'smtp.alt.lan'
|
assert email.smtp_host == 'smtp.alt.lan'
|
||||||
assert email.targets == [(False, 'alteriks@alt.lan')]
|
assert email.targets == [(False, 'alteriks@alt.lan')]
|
||||||
assert email.password == 'abcd'
|
assert email.password == 'abcd'
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch('smtplib.SMTP_SSL')
|
||||||
|
@mock.patch('smtplib.SMTP')
|
||||||
|
def test_plugin_host_detection_from_source_email(mock_smtp, mock_smtp_ssl):
|
||||||
|
"""
|
||||||
|
NotifyEmail() Discord Issue reporting that the following did not work:
|
||||||
|
mailtos://?smtp=mobile.charter.net&pass=password&user=name@spectrum.net
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
response = mock.Mock()
|
||||||
|
mock_smtp_ssl.return_value = response
|
||||||
|
mock_smtp.return_value = response
|
||||||
|
|
||||||
|
results = NotifyEmail.parse_url(
|
||||||
|
'mailtos://spectrum.net?smtp=mobile.charter.net'
|
||||||
|
'&pass=password&user=name@spectrum.net')
|
||||||
|
|
||||||
|
assert isinstance(results, dict)
|
||||||
|
assert 'name@spectrum.net' == results['user']
|
||||||
|
assert 'spectrum.net' == results['host']
|
||||||
|
assert 'mobile.charter.net' == results['smtp_host']
|
||||||
|
assert 'password' == results['password']
|
||||||
|
|
||||||
|
obj = Apprise.instantiate(results, suppress_exceptions=False)
|
||||||
|
assert isinstance(obj, NotifyEmail) is True
|
||||||
|
|
||||||
|
assert len(obj.targets) == 1
|
||||||
|
assert (False, 'name@spectrum.net') in obj.targets
|
||||||
|
assert obj.from_addr[0] == obj.app_id
|
||||||
|
assert obj.from_addr[1] == 'name@spectrum.net'
|
||||||
|
assert obj.password == 'password'
|
||||||
|
assert obj.user == 'name@spectrum.net'
|
||||||
|
assert obj.secure is True
|
||||||
|
assert obj.port == 587
|
||||||
|
assert obj.smtp_host == 'mobile.charter.net'
|
||||||
|
|
||||||
|
assert mock_smtp.call_count == 0
|
||||||
|
assert mock_smtp_ssl.call_count == 0
|
||||||
|
assert obj.notify('body', 'title') 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 == 'name@spectrum.net'
|
||||||
|
assert isinstance(_to, list)
|
||||||
|
assert len(_to) == 1
|
||||||
|
assert _to[0] == 'name@spectrum.net'
|
||||||
|
assert _msg.split('\n')[-3] == 'body'
|
||||||
|
|
||||||
|
#
|
||||||
|
# Now let's do a shortened version of the same URL where the host isn't
|
||||||
|
# specified but is parseable from he user login
|
||||||
|
#
|
||||||
|
mock_smtp.reset_mock()
|
||||||
|
mock_smtp_ssl.reset_mock()
|
||||||
|
response.reset_mock()
|
||||||
|
|
||||||
|
results = NotifyEmail.parse_url(
|
||||||
|
'mailtos://?smtp=mobile.charter.net'
|
||||||
|
'&pass=password&user=name@spectrum.net')
|
||||||
|
|
||||||
|
assert isinstance(results, dict)
|
||||||
|
assert 'name@spectrum.net' == results['user']
|
||||||
|
assert '' == results['host'] # No hostname defined; it's detected later
|
||||||
|
assert 'mobile.charter.net' == results['smtp_host']
|
||||||
|
assert 'password' == results['password']
|
||||||
|
|
||||||
|
obj = Apprise.instantiate(results, suppress_exceptions=False)
|
||||||
|
assert isinstance(obj, NotifyEmail) is True
|
||||||
|
|
||||||
|
assert len(obj.targets) == 1
|
||||||
|
assert (False, 'name@spectrum.net') in obj.targets
|
||||||
|
assert obj.from_addr[0] == obj.app_id
|
||||||
|
assert obj.from_addr[1] == 'name@spectrum.net'
|
||||||
|
assert obj.password == 'password'
|
||||||
|
assert obj.user == 'name@spectrum.net'
|
||||||
|
assert obj.secure is True
|
||||||
|
assert obj.port == 587
|
||||||
|
assert obj.smtp_host == 'mobile.charter.net'
|
||||||
|
|
||||||
|
assert mock_smtp.call_count == 0
|
||||||
|
assert mock_smtp_ssl.call_count == 0
|
||||||
|
assert obj.notify('body', 'title') 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 == 'name@spectrum.net'
|
||||||
|
assert isinstance(_to, list)
|
||||||
|
assert len(_to) == 1
|
||||||
|
assert _to[0] == 'name@spectrum.net'
|
||||||
|
assert _msg.split('\n')[-3] == 'body'
|
||||||
|
|
||||||
|
#
|
||||||
|
# Now let's do a shortened version of the same URL where the host isn't
|
||||||
|
# specified but is parseable from he user login
|
||||||
|
#
|
||||||
|
mock_smtp.reset_mock()
|
||||||
|
mock_smtp_ssl.reset_mock()
|
||||||
|
response.reset_mock()
|
||||||
|
|
||||||
|
results = NotifyEmail.parse_url(
|
||||||
|
'mailtos://?smtp=mobile.charter.net'
|
||||||
|
'&pass=password&user=userid-without-domain')
|
||||||
|
|
||||||
|
assert isinstance(results, dict)
|
||||||
|
assert 'userid-without-domain' == results['user']
|
||||||
|
assert '' == results['host'] # No hostname defined
|
||||||
|
assert 'mobile.charter.net' == results['smtp_host']
|
||||||
|
assert 'password' == results['password']
|
||||||
|
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
# We will fail
|
||||||
|
Apprise.instantiate(results, suppress_exceptions=False)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Now support target emails in place of the hostname
|
||||||
|
#
|
||||||
|
|
||||||
|
mock_smtp.reset_mock()
|
||||||
|
mock_smtp_ssl.reset_mock()
|
||||||
|
response.reset_mock()
|
||||||
|
|
||||||
|
results = NotifyEmail.parse_url(
|
||||||
|
'mailtos://John Doe<john%40yahoo.ca>?smtp=mobile.charter.net'
|
||||||
|
'&pass=password&user=name@spectrum.net')
|
||||||
|
|
||||||
|
assert isinstance(results, dict)
|
||||||
|
assert 'name@spectrum.net' == results['user']
|
||||||
|
assert '' == results['host'] # No hostname defined; it's detected later
|
||||||
|
assert 'mobile.charter.net' == results['smtp_host']
|
||||||
|
assert 'password' == results['password']
|
||||||
|
|
||||||
|
obj = Apprise.instantiate(results, suppress_exceptions=False)
|
||||||
|
assert isinstance(obj, NotifyEmail) is True
|
||||||
|
|
||||||
|
assert len(obj.targets) == 1
|
||||||
|
assert ('John Doe', 'john@yahoo.ca') in obj.targets
|
||||||
|
assert obj.from_addr[0] == obj.app_id
|
||||||
|
assert obj.from_addr[1] == 'name@spectrum.net'
|
||||||
|
assert obj.password == 'password'
|
||||||
|
assert obj.user == 'name@spectrum.net'
|
||||||
|
assert obj.secure is True
|
||||||
|
assert obj.port == 587
|
||||||
|
assert obj.smtp_host == 'mobile.charter.net'
|
||||||
|
|
||||||
|
assert mock_smtp.call_count == 0
|
||||||
|
assert mock_smtp_ssl.call_count == 0
|
||||||
|
assert obj.notify('body', 'title') 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 == 'name@spectrum.net'
|
||||||
|
assert isinstance(_to, list)
|
||||||
|
assert len(_to) == 1
|
||||||
|
assert _to[0] == 'john@yahoo.ca'
|
||||||
|
assert _msg.split('\n')[-3] == 'body'
|
||||||
|
Loading…
Reference in New Issue
Block a user