Fixed Email password handling when user= specified (#947)

This commit is contained in:
Chris Caron 2023-09-17 13:35:17 -04:00 committed by GitHub
parent 1b2d1ca824
commit e9c4de4020
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 80 additions and 4 deletions

View File

@ -650,6 +650,21 @@ class URLBase:
if 'user' in results['qsd']: if 'user' in results['qsd']:
results['user'] = results['qsd']['user'] results['user'] = results['qsd']['user']
# parse_url() always creates a 'password' and 'user' entry in the
# results returned. Entries are set to None if they weren't specified
if results['password'] is None and 'user' in results['qsd']:
# Handle cases where the user= provided in 2 locations, we want
# the original to fall back as a being a password (if one wasn't
# otherwise defined)
# e.g.
# mailtos://PASSWORD@hostname?user=admin@mail-domain.com
# - the PASSWORD gets lost in the parse url() since a user=
# over-ride is specified.
presults = parse_url(results['url'])
if presults:
# Store our Password
results['password'] = presults['user']
# Store our socket read timeout if specified # Store our socket read timeout if specified
if 'rto' in results['qsd']: if 'rto' in results['qsd']:
results['rto'] = results['qsd']['rto'] results['rto'] = results['qsd']['rto']

View File

@ -1040,6 +1040,10 @@ class NotifyEmail(NotifyBase):
# 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
if 'to' in results['qsd'] and len(results['qsd']['to']):
results['targets'].append(results['qsd']['to'])
# Attempt to detect 'from' email address # Attempt to detect 'from' email address
if 'from' in results['qsd'] and len(results['qsd']['from']): if 'from' in results['qsd'] and len(results['qsd']['from']):
from_addr = NotifyEmail.unquote(results['qsd']['from']) from_addr = NotifyEmail.unquote(results['qsd']['from'])
@ -1058,10 +1062,6 @@ class NotifyEmail(NotifyBase):
# Extract from name to associate with from address # Extract from name to associate with from address
from_addr = NotifyEmail.unquote(results['qsd']['name']) from_addr = NotifyEmail.unquote(results['qsd']['name'])
# Attempt to detect 'to' email address
if 'to' in results['qsd'] and len(results['qsd']['to']):
results['targets'].append(results['qsd']['to'])
# Store SMTP Host if specified # Store SMTP Host if specified
if 'smtp' in results['qsd'] and len(results['qsd']['smtp']): if 'smtp' in results['qsd'] and len(results['qsd']['smtp']):
# Extract the smtp server # Extract the smtp server

View File

@ -124,6 +124,12 @@ TEST_URLS = (
('mailtos://user:pass@nuxref.com:567?to=l2g@nuxref.com', { ('mailtos://user:pass@nuxref.com:567?to=l2g@nuxref.com', {
'instance': NotifyEmail, 'instance': NotifyEmail,
}), }),
('mailtos://user:pass@domain.com?user=admin@mail-domain.com', {
'instance': NotifyEmail,
}),
('mailtos://%20@domain.com?user=admin@mail-domain.com', {
'instance': NotifyEmail,
}),
('mailtos://user:pass@nuxref.com:567/l2g@nuxref.com', { ('mailtos://user:pass@nuxref.com:567/l2g@nuxref.com', {
'instance': NotifyEmail, 'instance': NotifyEmail,
}), }),
@ -1387,6 +1393,61 @@ def test_plugin_email_url_parsing(mock_smtp, mock_smtp_ssl):
assert pw == 'abc123' assert pw == 'abc123'
assert user == 'joe@mydomain.nl' assert user == 'joe@mydomain.nl'
mock_smtp.reset_mock()
mock_smtp_ssl.reset_mock()
response.reset_mock()
# Issue github.com/caronc/apprise/issue/941
# mail domain = mail-domain.com
# host domain = domain.subdomain.com
# PASSWORD needs to be fetched since a user= was provided
# - this is an edge case that is tested here
results = NotifyEmail.parse_url(
'mailtos://PASSWORD@domain.subdomain.com:587?'
'user=admin@mail-domain.com&to=mail@mail-domain.com')
assert isinstance(results, dict)
# From_Addr could not be detected at this stage, but will be
# handled during instantiation
assert '' == results['from_addr']
assert 'admin@mail-domain.com' == results['user']
assert results['port'] == 587
assert 'domain.subdomain.com' == results['host']
assert 'PASSWORD' == results['password']
assert 'mail@mail-domain.com' in results['targets']
obj = Apprise.instantiate(results, suppress_exceptions=False)
assert isinstance(obj, NotifyEmail) is True
# Not that our from_address takes on 'admin@domain.subdomain.com'
assert obj.from_addr == ['Apprise', 'admin@domain.subdomain.com']
assert mock_smtp.call_count == 0
assert mock_smtp_ssl.call_count == 0
assert response.starttls.call_count == 0
assert obj.notify("test") is True
assert mock_smtp.call_count == 1
assert response.starttls.call_count == 1
assert mock_smtp_ssl.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 == 'admin@domain.subdomain.com'
assert isinstance(_to, list)
assert len(_to) == 1
assert _to[0] == 'mail@mail-domain.com'
assert _msg.split('\n')[-3] == 'test'
user, pw = response.login.call_args[0]
assert user == 'admin@mail-domain.com'
assert pw == 'PASSWORD'
@mock.patch('smtplib.SMTP_SSL') @mock.patch('smtplib.SMTP_SSL')
@mock.patch('smtplib.SMTP') @mock.patch('smtplib.SMTP')