mirror of
https://github.com/caronc/apprise.git
synced 2025-02-06 05:19:15 +01:00
Email improvements; name= and from= now synonymous (#738)
This commit is contained in:
parent
e7255df1da
commit
32992fa641
@ -598,7 +598,7 @@ class URLBase:
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def parse_url(url, verify_host=True):
|
||||
def parse_url(url, verify_host=True, plus_to_space=False):
|
||||
"""Parses the URL and returns it broken apart into a dictionary.
|
||||
|
||||
This is very specific and customized for Apprise.
|
||||
@ -618,7 +618,8 @@ class URLBase:
|
||||
"""
|
||||
|
||||
results = parse_url(
|
||||
url, default_schema='unknown', verify_host=verify_host)
|
||||
url, default_schema='unknown', verify_host=verify_host,
|
||||
plus_to_space=plus_to_space)
|
||||
|
||||
if not results:
|
||||
# We're done; we failed to parse our url
|
||||
|
@ -429,7 +429,7 @@ class NotifyBase(BASE_OBJECT):
|
||||
return params
|
||||
|
||||
@staticmethod
|
||||
def parse_url(url, verify_host=True):
|
||||
def parse_url(url, verify_host=True, plus_to_space=False):
|
||||
"""Parses the URL and returns it broken apart into a dictionary.
|
||||
|
||||
This is very specific and customized for Apprise.
|
||||
@ -447,7 +447,8 @@ class NotifyBase(BASE_OBJECT):
|
||||
A dictionary is returned containing the URL fully parsed if
|
||||
successful, otherwise None is returned.
|
||||
"""
|
||||
results = URLBase.parse_url(url, verify_host=verify_host)
|
||||
results = URLBase.parse_url(
|
||||
url, verify_host=verify_host, plus_to_space=plus_to_space)
|
||||
|
||||
if not results:
|
||||
# We're done; we failed to parse our url
|
||||
|
@ -43,6 +43,7 @@ from ..common import NotifyFormat, NotifyType
|
||||
from ..conversion import convert_between
|
||||
from ..utils import is_email, parse_emails
|
||||
from ..AppriseLocale import gettext_lazy as _
|
||||
from ..logger import logger
|
||||
|
||||
# Globally Default encoding mode set to Quoted Printable.
|
||||
charset.add_charset('utf-8', charset.QP, charset.QP, 'utf-8')
|
||||
@ -382,7 +383,7 @@ class NotifyEmail(NotifyBase):
|
||||
'name': {
|
||||
'name': _('From Name'),
|
||||
'type': 'string',
|
||||
'map_to': 'from_name',
|
||||
'map_to': 'from_addr',
|
||||
},
|
||||
'cc': {
|
||||
'name': _('Carbon Copy'),
|
||||
@ -419,9 +420,9 @@ class NotifyEmail(NotifyBase):
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(self, smtp_host=None, from_name=None,
|
||||
from_addr=None, secure_mode=None, targets=None, cc=None,
|
||||
bcc=None, reply_to=None, headers=None, **kwargs):
|
||||
def __init__(self, smtp_host=None, from_addr=None, secure_mode=None,
|
||||
targets=None, cc=None, bcc=None, reply_to=None, headers=None,
|
||||
**kwargs):
|
||||
"""
|
||||
Initialize Email Object
|
||||
|
||||
@ -460,31 +461,35 @@ class NotifyEmail(NotifyBase):
|
||||
|
||||
# Now we want to construct the To and From email
|
||||
# addresses from the URL provided
|
||||
self.from_addr = from_addr
|
||||
self.from_addr = [False, '']
|
||||
|
||||
if self.user and not self.from_addr:
|
||||
# detect our email address
|
||||
self.from_addr = '{}@{}'.format(
|
||||
if self.user and self.host:
|
||||
# Prepare the bases of our email
|
||||
self.from_addr = [self.app_id, '{}@{}'.format(
|
||||
re.split(r'[\s@]+', self.user)[0],
|
||||
self.host,
|
||||
)
|
||||
)]
|
||||
|
||||
result = is_email(self.from_addr)
|
||||
if from_addr:
|
||||
result = is_email(from_addr)
|
||||
if result:
|
||||
self.from_addr = (
|
||||
result['name'] if result['name'] else False,
|
||||
result['full_email'])
|
||||
else:
|
||||
self.from_addr[0] = from_addr
|
||||
|
||||
result = is_email(self.from_addr[1])
|
||||
if not result:
|
||||
# Parse Source domain based on from_addr
|
||||
msg = 'Invalid ~From~ email specified: {}'.format(self.from_addr)
|
||||
msg = 'Invalid ~From~ email specified: {}'.format(
|
||||
'{} <{}>'.format(self.from_addr[0], self.from_addr[1])
|
||||
if self.from_addr[0] else '{}'.format(self.from_addr[1]))
|
||||
self.logger.warning(msg)
|
||||
raise TypeError(msg)
|
||||
|
||||
# Store our email address
|
||||
self.from_addr = result['full_email']
|
||||
|
||||
# Set our from name
|
||||
self.from_name = from_name if from_name else result['name']
|
||||
|
||||
# Store our lookup
|
||||
self.names[self.from_addr] = \
|
||||
self.from_name if self.from_name else False
|
||||
self.names[self.from_addr[1]] = self.from_addr[0]
|
||||
|
||||
# Now detect the SMTP Server
|
||||
self.smtp_host = \
|
||||
@ -517,8 +522,7 @@ class NotifyEmail(NotifyBase):
|
||||
|
||||
else:
|
||||
# If our target email list is empty we want to add ourselves to it
|
||||
self.targets.append(
|
||||
(self.from_name if self.from_name else False, self.from_addr))
|
||||
self.targets.append((False, self.from_addr[1]))
|
||||
|
||||
# Validate recipients (cc:) and drop bad ones:
|
||||
for recipient in parse_emails(cc):
|
||||
@ -665,9 +669,6 @@ class NotifyEmail(NotifyBase):
|
||||
Perform Email Notification
|
||||
"""
|
||||
|
||||
# Initialize our default from name
|
||||
from_name = self.from_name if self.from_name else self.app_desc
|
||||
|
||||
if not self.targets:
|
||||
# There is no one to email; we're done
|
||||
self.logger.warning(
|
||||
@ -708,7 +709,9 @@ class NotifyEmail(NotifyBase):
|
||||
for addr in reply_to]
|
||||
|
||||
self.logger.debug(
|
||||
'Email From: {} <{}>'.format(from_name, self.from_addr))
|
||||
'Email From: {}'.format(
|
||||
formataddr(self.from_addr, charset='utf-8')))
|
||||
|
||||
self.logger.debug('Email To: {}'.format(to_addr))
|
||||
if cc:
|
||||
self.logger.debug('Email Cc: {}'.format(', '.join(cc)))
|
||||
@ -771,9 +774,7 @@ class NotifyEmail(NotifyBase):
|
||||
base[k] = Header(v, self._get_charset(v))
|
||||
|
||||
base['Subject'] = Header(title, self._get_charset(title))
|
||||
base['From'] = formataddr(
|
||||
(from_name if from_name else False, self.from_addr),
|
||||
charset='utf-8')
|
||||
base['From'] = formataddr(self.from_addr, charset='utf-8')
|
||||
base['To'] = formataddr((to_name, to_addr), charset='utf-8')
|
||||
base['Message-ID'] = make_msgid(domain=self.smtp_host)
|
||||
base['Date'] = \
|
||||
@ -833,7 +834,7 @@ class NotifyEmail(NotifyBase):
|
||||
for message in messages:
|
||||
try:
|
||||
socket.sendmail(
|
||||
self.from_addr,
|
||||
self.from_addr[1],
|
||||
message.to_addrs,
|
||||
message.body)
|
||||
|
||||
@ -868,12 +869,7 @@ class NotifyEmail(NotifyBase):
|
||||
"""
|
||||
|
||||
# Define an URL parameters
|
||||
params = {
|
||||
'from': self.from_addr,
|
||||
'mode': self.secure_mode,
|
||||
'smtp': self.smtp_host,
|
||||
'user': self.user,
|
||||
}
|
||||
params = {}
|
||||
|
||||
# Append our headers into our parameters
|
||||
params.update({'+{}'.format(k): v for k, v in self.headers.items()})
|
||||
@ -881,30 +877,60 @@ class NotifyEmail(NotifyBase):
|
||||
# Extend our parameters
|
||||
params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
|
||||
|
||||
if self.from_name:
|
||||
params['name'] = self.from_name
|
||||
from_addr = None
|
||||
if len(self.targets) == 1 and self.targets[0][1] != self.from_addr[1]:
|
||||
# A custom email was provided
|
||||
from_addr = self.from_addr[1]
|
||||
|
||||
if self.smtp_host != self.host:
|
||||
# Apply our SMTP Host only if it differs from the provided hostname
|
||||
params['smtp'] = self.smtp_host
|
||||
|
||||
if self.secure:
|
||||
# Mode is only requried if we're dealing with a secure connection
|
||||
params['mode'] = self.secure_mode
|
||||
|
||||
if self.from_addr[0] and self.from_addr[0] != self.app_id:
|
||||
# A custom name was provided
|
||||
params['from'] = self.from_addr[0] if not from_addr else \
|
||||
formataddr((self.from_addr[0], from_addr), charset='utf-8')
|
||||
|
||||
elif from_addr:
|
||||
params['from'] = formataddr((False, from_addr), charset='utf-8')
|
||||
|
||||
elif not self.user:
|
||||
params['from'] = \
|
||||
formataddr((False, self.from_addr[1]), charset='utf-8')
|
||||
|
||||
if len(self.cc) > 0:
|
||||
# Handle our Carbon Copy Addresses
|
||||
params['cc'] = ','.join(
|
||||
['{}{}'.format(
|
||||
'' if not e not in self.names
|
||||
else '{}:'.format(self.names[e]), e) for e in self.cc])
|
||||
params['cc'] = ','.join([
|
||||
formataddr(
|
||||
(self.names[e] if e in self.names else False, e),
|
||||
# Swap comma for it's escaped url code (if detected) since
|
||||
# we're using that as a delimiter
|
||||
charset='utf-8').replace(',', '%2C')
|
||||
for e in self.cc])
|
||||
|
||||
if len(self.bcc) > 0:
|
||||
# Handle our Blind Carbon Copy Addresses
|
||||
params['bcc'] = ','.join(
|
||||
['{}{}'.format(
|
||||
'' if not e not in self.names
|
||||
else '{}:'.format(self.names[e]), e) for e in self.bcc])
|
||||
params['bcc'] = ','.join([
|
||||
formataddr(
|
||||
(self.names[e] if e in self.names else False, e),
|
||||
# Swap comma for it's escaped url code (if detected) since
|
||||
# we're using that as a delimiter
|
||||
charset='utf-8').replace(',', '%2C')
|
||||
for e in self.bcc])
|
||||
|
||||
if self.reply_to:
|
||||
# Handle our Reply-To Addresses
|
||||
params['reply'] = ','.join(
|
||||
['{}{}'.format(
|
||||
'' if not e not in self.names
|
||||
else '{}:'.format(self.names[e]), e)
|
||||
for e in self.reply_to])
|
||||
params['reply'] = ','.join([
|
||||
formataddr(
|
||||
(self.names[e] if e in self.names else False, e),
|
||||
# Swap comma for it's escaped url code (if detected) since
|
||||
# we're using that as a delimiter
|
||||
charset='utf-8').replace(',', '%2C')
|
||||
for e in self.reply_to])
|
||||
|
||||
# pull email suffix from username (if present)
|
||||
user = None if not self.user else self.user.split('@')[0]
|
||||
@ -931,7 +957,7 @@ class NotifyEmail(NotifyBase):
|
||||
# or not
|
||||
has_targets = \
|
||||
not (len(self.targets) == 1
|
||||
and self.targets[0][1] == self.from_addr)
|
||||
and self.targets[0][1] == self.from_addr[1])
|
||||
|
||||
return '{schema}://{auth}{hostname}{port}/{targets}?{params}'.format(
|
||||
schema=self.secure_protocol if self.secure else self.protocol,
|
||||
@ -975,14 +1001,24 @@ class NotifyEmail(NotifyBase):
|
||||
if 'from' in results['qsd'] and len(results['qsd']['from']):
|
||||
from_addr = NotifyEmail.unquote(results['qsd']['from'])
|
||||
|
||||
if 'name' in results['qsd'] and len(results['qsd']['name']):
|
||||
# Depricate use of both `from=` and `name=` in the same url as
|
||||
# they will be synomomus of one another in the future.
|
||||
from_addr = formataddr(
|
||||
(NotifyEmail.unquote(results['qsd']['name']), from_addr),
|
||||
charset='utf-8')
|
||||
logger.warning(
|
||||
'Email name= and from= are synonymous; '
|
||||
'use one or the other.')
|
||||
|
||||
elif 'name' in results['qsd'] and len(results['qsd']['name']):
|
||||
# Extract from name to associate with from address
|
||||
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'])
|
||||
|
||||
if 'name' in results['qsd'] and len(results['qsd']['name']):
|
||||
# Extract from name to associate with from address
|
||||
results['from_name'] = NotifyEmail.unquote(results['qsd']['name'])
|
||||
|
||||
# Store SMTP Host if specified
|
||||
if 'smtp' in results['qsd'] and len(results['qsd']['smtp']):
|
||||
# Extract the smtp server
|
||||
|
@ -60,6 +60,7 @@ from ..utils import parse_emails
|
||||
from ..utils import parse_bool
|
||||
from ..utils import is_email
|
||||
from ..utils import validate_regex
|
||||
from ..logger import logger
|
||||
from ..AppriseLocale import gettext_lazy as _
|
||||
|
||||
# Provide some known codes Mailgun uses and what they translate to:
|
||||
@ -158,7 +159,7 @@ class NotifyMailgun(NotifyBase):
|
||||
'name': {
|
||||
'name': _('From Name'),
|
||||
'type': 'string',
|
||||
'map_to': 'from_name',
|
||||
'map_to': 'from_addr',
|
||||
},
|
||||
'from': {
|
||||
'alias_of': 'name',
|
||||
@ -200,7 +201,7 @@ class NotifyMailgun(NotifyBase):
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(self, apikey, targets, cc=None, bcc=None, from_name=None,
|
||||
def __init__(self, apikey, targets, cc=None, bcc=None, from_addr=None,
|
||||
region_name=None, headers=None, tokens=None, batch=False,
|
||||
**kwargs):
|
||||
"""
|
||||
@ -266,14 +267,15 @@ class NotifyMailgun(NotifyBase):
|
||||
self.from_addr = [
|
||||
self.app_id, '{user}@{host}'.format(
|
||||
user=self.user, host=self.host)]
|
||||
if from_name:
|
||||
result = is_email(from_name)
|
||||
|
||||
if from_addr:
|
||||
result = is_email(from_addr)
|
||||
if result:
|
||||
self.from_addr = (
|
||||
result['name'] if result['name'] else False,
|
||||
result['full_email'])
|
||||
else:
|
||||
self.from_addr[0] = from_name
|
||||
self.from_addr[0] = from_addr
|
||||
|
||||
if not is_email(self.from_addr[1]):
|
||||
# Parse Source domain based on from_addr
|
||||
@ -589,7 +591,7 @@ class NotifyMailgun(NotifyBase):
|
||||
params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
|
||||
|
||||
if self.from_addr[0]:
|
||||
# from_name specified; pass it back on the url
|
||||
# from_addr specified; pass it back on the url
|
||||
params['name'] = self.from_addr[0]
|
||||
|
||||
if self.cc:
|
||||
@ -644,17 +646,26 @@ class NotifyMailgun(NotifyBase):
|
||||
# We're done - no API Key found
|
||||
results['apikey'] = None
|
||||
|
||||
if 'name' in results['qsd'] and len(results['qsd']['name']):
|
||||
# Extract from name to associate with from address
|
||||
results['from_name'] = \
|
||||
NotifyMailgun.unquote(results['qsd']['name'])
|
||||
|
||||
# Support from= for consistency with `mail://`
|
||||
elif 'from' in results['qsd'] and len(results['qsd']['from']):
|
||||
# Extract from name to associate with from address
|
||||
results['from_name'] = \
|
||||
# Attempt to detect 'from' email address
|
||||
if 'from' in results['qsd'] and len(results['qsd']['from']):
|
||||
results['from_addr'] = \
|
||||
NotifyMailgun.unquote(results['qsd']['from'])
|
||||
|
||||
if 'name' in results['qsd'] and len(results['qsd']['name']):
|
||||
# Depricate use of both `from=` and `name=` in the same url as
|
||||
# they will be synomomus of one another in the future.
|
||||
results['from_addr'] = formataddr(
|
||||
(NotifyMailgun.unquote(results['qsd']['name']),
|
||||
results['from_addr']), charset='utf-8')
|
||||
logger.warning(
|
||||
'Mailgun name= and from= are synonymous; '
|
||||
'use one or the other.')
|
||||
|
||||
elif 'name' in results['qsd'] and len(results['qsd']['name']):
|
||||
# Extract from name to associate with from address
|
||||
results['from_addr'] = \
|
||||
NotifyMailgun.unquote(results['qsd']['name'])
|
||||
|
||||
if 'region' in results['qsd'] and len(results['qsd']['region']):
|
||||
# Extract from name to associate with from address
|
||||
results['region_name'] = \
|
||||
|
@ -519,7 +519,7 @@ def tidy_path(path):
|
||||
return path
|
||||
|
||||
|
||||
def parse_qsd(qs, simple=False):
|
||||
def parse_qsd(qs, simple=False, plus_to_space=False):
|
||||
"""
|
||||
Query String Dictionary Builder
|
||||
|
||||
@ -541,6 +541,11 @@ def parse_qsd(qs, simple=False):
|
||||
|
||||
if simple is set to true, then a ONE dictionary is returned and is not
|
||||
sub-parsed for additional elements
|
||||
|
||||
plus_to_space will cause all `+` references to become a space as
|
||||
per normal URL Encoded defininition. Normal URL parsing applies
|
||||
this, but `+` is very actively used character with passwords,
|
||||
api keys, tokens, etc. So Apprise does not do this by default.
|
||||
"""
|
||||
|
||||
# Our return result set:
|
||||
@ -575,7 +580,7 @@ def parse_qsd(qs, simple=False):
|
||||
key = unquote(key)
|
||||
key = '' if not key else key
|
||||
|
||||
val = nv[1].replace('+', ' ')
|
||||
val = nv[1].replace('+', ' ') if plus_to_space else nv[1]
|
||||
val = unquote(val)
|
||||
val = '' if not val else val.strip()
|
||||
|
||||
@ -609,7 +614,7 @@ def parse_qsd(qs, simple=False):
|
||||
|
||||
|
||||
def parse_url(url, default_schema='http', verify_host=True, strict_port=False,
|
||||
simple=False):
|
||||
simple=False, plus_to_space=False):
|
||||
"""A function that greatly simplifies the parsing of a url
|
||||
specified by the end user.
|
||||
|
||||
@ -722,7 +727,8 @@ def parse_url(url, default_schema='http', verify_host=True, strict_port=False,
|
||||
# Parse Query Arugments ?val=key&key=val
|
||||
# while ensuring that all keys are lowercase
|
||||
if qsdata:
|
||||
result.update(parse_qsd(qsdata, simple=simple))
|
||||
result.update(parse_qsd(
|
||||
qsdata, simple=simple, plus_to_space=plus_to_space))
|
||||
|
||||
# Now do a proper extraction of data; http:// is just substitued in place
|
||||
# to allow urlparse() to function as expected, we'll swap this back to the
|
||||
|
@ -396,6 +396,31 @@ def test_parse_url_general():
|
||||
assert result['qsd+']['KeY'] == 'ValueA'
|
||||
assert 'kEy' in result['qsd-']
|
||||
assert result['qsd-']['kEy'] == 'ValueB'
|
||||
assert result['qsd']['key'] == 'Value +C'
|
||||
assert result['qsd']['+key'] == result['qsd+']['KeY']
|
||||
assert result['qsd']['-key'] == result['qsd-']['kEy']
|
||||
|
||||
result = utils.parse_url(
|
||||
'http://hostname/?+KeY=ValueA&-kEy=ValueB&KEY=Value%20+C&:colon=y',
|
||||
plus_to_space=True)
|
||||
assert result['schema'] == 'http'
|
||||
assert result['host'] == 'hostname'
|
||||
assert result['port'] is None
|
||||
assert result['user'] is None
|
||||
assert result['password'] is None
|
||||
assert result['fullpath'] == '/'
|
||||
assert result['path'] == '/'
|
||||
assert result['query'] is None
|
||||
assert result['url'] == 'http://hostname/'
|
||||
assert '+key' in result['qsd']
|
||||
assert '-key' in result['qsd']
|
||||
assert ':colon' in result['qsd']
|
||||
assert result['qsd:']['colon'] == 'y'
|
||||
assert 'key' in result['qsd']
|
||||
assert 'KeY' in result['qsd+']
|
||||
assert result['qsd+']['KeY'] == 'ValueA'
|
||||
assert 'kEy' in result['qsd-']
|
||||
assert result['qsd-']['kEy'] == 'ValueB'
|
||||
assert result['qsd']['key'] == 'Value C'
|
||||
assert result['qsd']['+key'] == result['qsd+']['KeY']
|
||||
assert result['qsd']['-key'] == result['qsd-']['kEy']
|
||||
@ -893,7 +918,7 @@ def test_parse_url_simple():
|
||||
assert '-key' in result['qsd']
|
||||
assert ':colon' in result['qsd']
|
||||
assert result['qsd'][':colon'] == 'y'
|
||||
assert result['qsd']['key'] == 'Value C'
|
||||
assert result['qsd']['key'] == 'Value +C'
|
||||
assert result['qsd']['+key'] == 'ValueA'
|
||||
assert result['qsd']['-key'] == 'ValueB'
|
||||
|
||||
|
@ -178,9 +178,10 @@ TEST_URLS = (
|
||||
('mailtos://user:@nuxref.com', {
|
||||
'instance': NotifyEmail,
|
||||
}),
|
||||
# Invalid From Address
|
||||
# Invalid From Address; but just gets put as the from name instead
|
||||
# Hence the below generats From: "@ <user@nuxref.com>"
|
||||
('mailtos://user:pass@nuxref.com?from=@', {
|
||||
'instance': TypeError,
|
||||
'instance': NotifyEmail,
|
||||
}),
|
||||
# Invalid From Address
|
||||
('mailtos://nuxref.com?user=&pass=.', {
|
||||
@ -226,6 +227,10 @@ TEST_URLS = (
|
||||
# is set and tests that we gracfully handle them
|
||||
'test_smtplib_exceptions': True,
|
||||
}),
|
||||
# Use of both 'name' and 'from' together; these are synonymous
|
||||
('mailtos://user:pass@nuxref.com?'
|
||||
'from=jack@gmail.com&name=Jason<jason@gmail.com>', {
|
||||
'instance': NotifyEmail}),
|
||||
# Test no auth at all
|
||||
('mailto://localhost?from=test@example.com&to=test@example.com', {
|
||||
'instance': NotifyEmail,
|
||||
@ -435,7 +440,8 @@ def test_plugin_email_webbase_lookup(mock_smtp, mock_smtpssl):
|
||||
assert isinstance(obj, NotifyEmail)
|
||||
assert len(obj.targets) == 1
|
||||
assert (False, 'user@l2g.com') in obj.targets
|
||||
assert obj.from_addr == 'user@l2g.com'
|
||||
assert obj.from_addr[0] == obj.app_id
|
||||
assert obj.from_addr[1] == 'user@l2g.com'
|
||||
assert obj.password == 'pass'
|
||||
assert obj.user == 'user'
|
||||
assert obj.secure is True
|
||||
@ -569,6 +575,23 @@ def test_plugin_email_smtplib_send_multiple_recipients(mock_smtplib):
|
||||
mock.call().quit(),
|
||||
]
|
||||
|
||||
# No from= used in the above
|
||||
assert re.match(r'.*from=.*', obj.url()) is None
|
||||
# No mode= as this isn't a secure connection
|
||||
assert re.match(r'.*mode=.*', obj.url()) is None
|
||||
# No smtp= as the SMTP server is the same as the hostname in this case
|
||||
assert re.match(r'.*smtp=.*', obj.url()) is None
|
||||
# URL is assembled based on provided user
|
||||
assert re.match(
|
||||
r'^mailto://user:pass\@mail.example.org/.*', obj.url()) is not None
|
||||
|
||||
# Verify our added emails are still part of the URL
|
||||
assert re.match(r'.*/foo%40example.net[/?].*', obj.url()) is not None
|
||||
assert re.match(r'.*/bar%40example.com[/?].*', obj.url()) is not None
|
||||
|
||||
assert re.match(r'.*bcc=qux%40example.org.*', obj.url()) is not None
|
||||
assert re.match(r'.*cc=baz%40example.org.*', obj.url()) is not None
|
||||
|
||||
|
||||
@mock.patch('smtplib.SMTP')
|
||||
def test_plugin_email_smtplib_internationalization(mock_smtp):
|
||||
@ -672,7 +695,7 @@ def test_plugin_email_url_variations():
|
||||
# Test variations of username required to be an email address
|
||||
# user@example.com
|
||||
obj = Apprise.instantiate(
|
||||
'mailto://{user}:{passwd}@example.com'.format(
|
||||
'mailto://{user}:{passwd}@example.com?smtp=example.com'.format(
|
||||
user='apprise%40example21.ca',
|
||||
passwd='abcd123'),
|
||||
suppress_exceptions=False)
|
||||
@ -681,6 +704,17 @@ def test_plugin_email_url_variations():
|
||||
assert obj.password == 'abcd123'
|
||||
assert obj.user == 'apprise@example21.ca'
|
||||
|
||||
# No from= used in the above
|
||||
assert re.match(r'.*from=.*', obj.url()) is None
|
||||
# No mode= as this isn't a secure connection
|
||||
assert re.match(r'.*mode=.*', obj.url()) is None
|
||||
# No smtp= as the SMTP server is the same as the hostname in this case
|
||||
# even though it was explicitly specified
|
||||
assert re.match(r'.*smtp=.*', obj.url()) is None
|
||||
# URL is assembled based on provided user
|
||||
assert re.match(
|
||||
r'^mailto://apprise:abcd123\@example.com/.*', obj.url()) is not None
|
||||
|
||||
# test username specified in the url body (as an argument)
|
||||
# this always over-rides the entry at the front of the url
|
||||
obj = Apprise.instantiate(
|
||||
@ -693,10 +727,20 @@ def test_plugin_email_url_variations():
|
||||
assert obj.password == 'abcd123'
|
||||
assert obj.user == 'apprise@example21.ca'
|
||||
|
||||
# No from= used in the above
|
||||
assert re.match(r'.*from=.*', obj.url()) is None
|
||||
# No mode= as this isn't a secure connection
|
||||
assert re.match(r'.*mode=.*', obj.url()) is None
|
||||
# No smtp= as the SMTP server is the same as the hostname in this case
|
||||
assert re.match(r'.*smtp=.*', obj.url()) is None
|
||||
# URL is assembled based on provided user
|
||||
assert re.match(
|
||||
r'^mailto://apprise:abcd123\@example.com/.*', obj.url()) is not None
|
||||
|
||||
# test user and password specified in the url body (as an argument)
|
||||
# this always over-rides the entries at the front of the url
|
||||
obj = Apprise.instantiate(
|
||||
'mailto://_:_@example.com?user={user}&pass={passwd}'.format(
|
||||
'mailtos://_:_@example.com?user={user}&pass={passwd}'.format(
|
||||
user='apprise%40example21.ca',
|
||||
passwd='abcd123'),
|
||||
suppress_exceptions=False)
|
||||
@ -706,7 +750,20 @@ def test_plugin_email_url_variations():
|
||||
assert obj.user == 'apprise@example21.ca'
|
||||
assert len(obj.targets) == 1
|
||||
assert (False, 'apprise@example.com') in obj.targets
|
||||
assert obj.targets[0][1] == obj.from_addr
|
||||
assert obj.from_addr[0] == obj.app_id
|
||||
assert obj.from_addr[1] == 'apprise@example.com'
|
||||
assert obj.targets[0][0] is False
|
||||
assert obj.targets[0][1] == obj.from_addr[1]
|
||||
|
||||
# No from= used in the above
|
||||
assert re.match(r'.*from=.*', obj.url()) is None
|
||||
# Default mode is starttls
|
||||
assert re.match(r'.*mode=starttls.*', obj.url()) is not None
|
||||
# No smtp= as the SMTP server is the same as the hostname in this case
|
||||
assert re.match(r'.*smtp=.*', obj.url()) is None
|
||||
# URL is assembled based on provided user
|
||||
assert re.match(
|
||||
r'^mailtos://apprise:abcd123\@example.com/.*', obj.url()) is not None
|
||||
|
||||
# test user and password specified in the url body (as an argument)
|
||||
# this always over-rides the entries at the front of the url
|
||||
@ -723,13 +780,26 @@ def test_plugin_email_url_variations():
|
||||
assert obj.user == 'apprise@example21.ca'
|
||||
assert len(obj.targets) == 1
|
||||
assert (False, 'apprise@example.com') in obj.targets
|
||||
assert obj.targets[0][1] == obj.from_addr
|
||||
assert obj.from_addr[0] == obj.app_id
|
||||
assert obj.from_addr[1] == 'apprise@example.com'
|
||||
assert obj.targets[0][0] is False
|
||||
assert obj.targets[0][1] == obj.from_addr[1]
|
||||
assert obj.smtp_host == 'example.com'
|
||||
|
||||
# No from= used in the above
|
||||
assert re.match(r'.*from=.*', obj.url()) is None
|
||||
# No mode= as this isn't a secure connection
|
||||
assert re.match(r'.*mode=.*', obj.url()) is None
|
||||
# No smtp= as the SMTP server is the same as the hostname in this case
|
||||
assert re.match(r'.*smtp=.*', obj.url()) is None
|
||||
# URL is assembled based on provided user
|
||||
assert re.match(
|
||||
r'^mailto://apprise:abcd123\@example.com/.*', obj.url()) is not None
|
||||
|
||||
# test a complicated example
|
||||
obj = Apprise.instantiate(
|
||||
'mailtos://{user}:{passwd}@{host}:{port}'
|
||||
'?smtp={smtp_host}&format=text&from={this}&to={that}'.format(
|
||||
'?smtp={smtp_host}&format=text&from=Charles<{this}>&to={that}'.format(
|
||||
user='apprise%40example21.ca',
|
||||
passwd='abcd123',
|
||||
host='example.com',
|
||||
@ -747,7 +817,28 @@ def test_plugin_email_url_variations():
|
||||
assert obj.smtp_host == 'smtp.example.edu'
|
||||
assert len(obj.targets) == 1
|
||||
assert (False, 'to@example.jp') in obj.targets
|
||||
assert obj.from_addr == 'from@example.jp'
|
||||
assert obj.from_addr[0] == 'Charles'
|
||||
assert obj.from_addr[1] == 'from@example.jp'
|
||||
assert re.match(
|
||||
r'.*from=Charles\+%3Cfrom%40example.jp%3E.*', obj.url()) is not None
|
||||
|
||||
# Test Tagging under various urll encodings
|
||||
for toaddr in ('/john.smith+mytag@domain.com',
|
||||
'?to=john.smith+mytag@domain.com',
|
||||
'/john.smith%2Bmytag@domain.com',
|
||||
'?to=john.smith%2Bmytag@domain.com'):
|
||||
|
||||
obj = Apprise.instantiate(
|
||||
'mailto://user:pass@domain.com{}'.format(toaddr))
|
||||
assert isinstance(obj, NotifyEmail) is True
|
||||
assert obj.password == 'pass'
|
||||
assert obj.user == 'user'
|
||||
assert obj.host == 'domain.com'
|
||||
assert obj.from_addr[0] == obj.app_id
|
||||
assert obj.from_addr[1] == 'user@domain.com'
|
||||
assert len(obj.targets) == 1
|
||||
assert obj.targets[0][0] is False
|
||||
assert obj.targets[0][1] == 'john.smith+mytag@domain.com'
|
||||
|
||||
|
||||
def test_plugin_email_dict_variations():
|
||||
@ -784,7 +875,7 @@ def test_plugin_email_url_parsing(mock_smtp, mock_smtp_ssl):
|
||||
'mailtos://user:pass123@hotmail.com:444'
|
||||
'?to=user2@yahoo.com&name=test%20name')
|
||||
assert isinstance(results, dict)
|
||||
assert 'test name' == results['from_name']
|
||||
assert 'test name' == results['from_addr']
|
||||
assert 'user' == results['user']
|
||||
assert 444 == results['port']
|
||||
assert 'hotmail.com' == results['host']
|
||||
@ -832,7 +923,7 @@ def test_plugin_email_url_parsing(mock_smtp, mock_smtp_ssl):
|
||||
'mailtos://user:pass123@hotmail.com?smtp=override.com'
|
||||
'&name=test%20name&to=user2@yahoo.com&mode=ssl')
|
||||
assert isinstance(results, dict)
|
||||
assert 'test name' == results['from_name']
|
||||
assert 'test name' == results['from_addr']
|
||||
assert 'user' == results['user']
|
||||
assert 'hotmail.com' == results['host']
|
||||
assert 'pass123' == results['password']
|
||||
@ -918,11 +1009,22 @@ def test_plugin_email_url_parsing(mock_smtp, mock_smtp_ssl):
|
||||
# Verify our over-rides are in place
|
||||
assert obj.smtp_host == 'smtp.exmail.qq.com'
|
||||
assert obj.port == 465
|
||||
assert obj.from_addr == 'abc@xyz.cn'
|
||||
assert obj.from_addr[0] == obj.app_id
|
||||
assert obj.from_addr[1] == 'abc@xyz.cn'
|
||||
assert obj.secure_mode == 'ssl'
|
||||
# No entries in the reply_to
|
||||
assert not obj.reply_to
|
||||
|
||||
# No from= used in the above
|
||||
assert re.match(r'.*from=.*', obj.url()) is None
|
||||
# No Our secure connection is SSL
|
||||
assert re.match(r'.*mode=ssl.*', obj.url()) is not None
|
||||
# No smtp= as the SMTP server is the same as the hostname in this case
|
||||
assert re.match(r'.*smtp=smtp.exmail.qq.com.*', obj.url()) is not None
|
||||
# URL is assembled based on provided user
|
||||
assert re.match(
|
||||
r'^mailtos://abc:password@xyz.cn:465/.*', obj.url()) is not None
|
||||
|
||||
results = NotifyEmail.parse_url(
|
||||
"mailtos://abc:password@xyz.cn?"
|
||||
"smtp=smtp.exmail.qq.com&mode=ssl&port=465")
|
||||
@ -932,7 +1034,8 @@ def test_plugin_email_url_parsing(mock_smtp, mock_smtp_ssl):
|
||||
# Verify our over-rides are in place
|
||||
assert obj.smtp_host == 'smtp.exmail.qq.com'
|
||||
assert obj.port == 465
|
||||
assert obj.from_addr == 'abc@xyz.cn'
|
||||
assert obj.from_addr[0] == obj.app_id
|
||||
assert obj.from_addr[1] == 'abc@xyz.cn'
|
||||
assert obj.secure_mode == 'ssl'
|
||||
# No entries in the reply_to
|
||||
assert not obj.reply_to
|
||||
@ -946,9 +1049,27 @@ def test_plugin_email_url_parsing(mock_smtp, mock_smtp_ssl):
|
||||
assert isinstance(obj, NotifyEmail) is True
|
||||
# Verify our over-rides are in place
|
||||
assert obj.smtp_host == 'example.com'
|
||||
assert obj.from_addr == 'user@example.com'
|
||||
assert obj.from_addr[0] == obj.app_id
|
||||
assert obj.from_addr[1] == 'user@example.com'
|
||||
assert obj.secure_mode == 'starttls'
|
||||
assert obj.url().startswith(
|
||||
'mailtos://user:pass@example.com')
|
||||
# Test that our template over-ride worked
|
||||
assert 'reply=noreply%40example.com' in obj.url()
|
||||
|
||||
#
|
||||
# Test Reply-To Email with Name Inline
|
||||
#
|
||||
results = NotifyEmail.parse_url(
|
||||
"mailtos://user:pass@example.com?reply=Chris<noreply@example.ca>")
|
||||
obj = Apprise.instantiate(results, suppress_exceptions=False)
|
||||
assert isinstance(obj, NotifyEmail) is True
|
||||
# Verify our over-rides are in place
|
||||
assert obj.smtp_host == 'example.com'
|
||||
assert obj.from_addr[0] == obj.app_id
|
||||
assert obj.from_addr[1] == 'user@example.com'
|
||||
assert obj.secure_mode == 'starttls'
|
||||
assert obj.url().startswith(
|
||||
'mailtos://user:pass@example.com')
|
||||
# Test that our template over-ride worked
|
||||
assert 'reply=Chris+%3Cnoreply%40example.ca%3E' in obj.url()
|
||||
|
@ -95,6 +95,12 @@ apprise_url_tests = (
|
||||
'a' * 32, 'b' * 8, 'c' * 8), {
|
||||
'instance': TypeError,
|
||||
}),
|
||||
# Use of both 'name' and 'from' together; these are synonymous
|
||||
('mailgun://user@localhost.localdomain/{}-{}-{}?'
|
||||
'from=jack@gmail.com&name=Jason<jason@gmail.com>'.format(
|
||||
'a' * 32, 'b' * 8, 'c' * 8), {
|
||||
'instance': NotifyMailgun}),
|
||||
|
||||
# headers
|
||||
('mailgun://user@localhost.localdomain/{}-{}-{}'
|
||||
'?+X-Customer-Campaign-ID=Apprise'.format(
|
||||
|
Loading…
Reference in New Issue
Block a user