mirror of https://github.com/caronc/apprise
Email improvements; name= and from= now synonymous (#738)
parent
e7255df1da
commit
32992fa641
|
@ -598,7 +598,7 @@ class URLBase:
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@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.
|
"""Parses the URL and returns it broken apart into a dictionary.
|
||||||
|
|
||||||
This is very specific and customized for Apprise.
|
This is very specific and customized for Apprise.
|
||||||
|
@ -618,7 +618,8 @@ class URLBase:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
results = parse_url(
|
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:
|
if not results:
|
||||||
# We're done; we failed to parse our url
|
# We're done; we failed to parse our url
|
||||||
|
|
|
@ -429,7 +429,7 @@ class NotifyBase(BASE_OBJECT):
|
||||||
return params
|
return params
|
||||||
|
|
||||||
@staticmethod
|
@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.
|
"""Parses the URL and returns it broken apart into a dictionary.
|
||||||
|
|
||||||
This is very specific and customized for Apprise.
|
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
|
A dictionary is returned containing the URL fully parsed if
|
||||||
successful, otherwise None is returned.
|
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:
|
if not results:
|
||||||
# We're done; we failed to parse our url
|
# We're done; we failed to parse our url
|
||||||
|
|
|
@ -43,6 +43,7 @@ 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
|
||||||
from ..AppriseLocale import gettext_lazy as _
|
from ..AppriseLocale import gettext_lazy as _
|
||||||
|
from ..logger import logger
|
||||||
|
|
||||||
# Globally Default encoding mode set to Quoted Printable.
|
# Globally Default encoding mode set to Quoted Printable.
|
||||||
charset.add_charset('utf-8', charset.QP, charset.QP, 'utf-8')
|
charset.add_charset('utf-8', charset.QP, charset.QP, 'utf-8')
|
||||||
|
@ -382,7 +383,7 @@ class NotifyEmail(NotifyBase):
|
||||||
'name': {
|
'name': {
|
||||||
'name': _('From Name'),
|
'name': _('From Name'),
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'map_to': 'from_name',
|
'map_to': 'from_addr',
|
||||||
},
|
},
|
||||||
'cc': {
|
'cc': {
|
||||||
'name': _('Carbon Copy'),
|
'name': _('Carbon Copy'),
|
||||||
|
@ -419,9 +420,9 @@ class NotifyEmail(NotifyBase):
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, smtp_host=None, from_name=None,
|
def __init__(self, smtp_host=None, from_addr=None, secure_mode=None,
|
||||||
from_addr=None, secure_mode=None, targets=None, cc=None,
|
targets=None, cc=None, bcc=None, reply_to=None, headers=None,
|
||||||
bcc=None, reply_to=None, headers=None, **kwargs):
|
**kwargs):
|
||||||
"""
|
"""
|
||||||
Initialize Email Object
|
Initialize Email Object
|
||||||
|
|
||||||
|
@ -460,31 +461,35 @@ class NotifyEmail(NotifyBase):
|
||||||
|
|
||||||
# Now we want to construct the To and From email
|
# Now we want to construct the To and From email
|
||||||
# addresses from the URL provided
|
# addresses from the URL provided
|
||||||
self.from_addr = from_addr
|
self.from_addr = [False, '']
|
||||||
|
|
||||||
if self.user and not self.from_addr:
|
if self.user and self.host:
|
||||||
# detect our email address
|
# Prepare the bases of our email
|
||||||
self.from_addr = '{}@{}'.format(
|
self.from_addr = [self.app_id, '{}@{}'.format(
|
||||||
re.split(r'[\s@]+', self.user)[0],
|
re.split(r'[\s@]+', self.user)[0],
|
||||||
self.host,
|
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:
|
if not result:
|
||||||
# Parse Source domain based on from_addr
|
# 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)
|
self.logger.warning(msg)
|
||||||
raise TypeError(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
|
# Store our lookup
|
||||||
self.names[self.from_addr] = \
|
self.names[self.from_addr[1]] = self.from_addr[0]
|
||||||
self.from_name if self.from_name else False
|
|
||||||
|
|
||||||
# Now detect the SMTP Server
|
# Now detect the SMTP Server
|
||||||
self.smtp_host = \
|
self.smtp_host = \
|
||||||
|
@ -517,8 +522,7 @@ class NotifyEmail(NotifyBase):
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# If our target email list is empty we want to add ourselves to it
|
# If our target email list is empty we want to add ourselves to it
|
||||||
self.targets.append(
|
self.targets.append((False, self.from_addr[1]))
|
||||||
(self.from_name if self.from_name else False, self.from_addr))
|
|
||||||
|
|
||||||
# Validate recipients (cc:) and drop bad ones:
|
# Validate recipients (cc:) and drop bad ones:
|
||||||
for recipient in parse_emails(cc):
|
for recipient in parse_emails(cc):
|
||||||
|
@ -665,9 +669,6 @@ class NotifyEmail(NotifyBase):
|
||||||
Perform Email Notification
|
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:
|
if not self.targets:
|
||||||
# There is no one to email; we're done
|
# There is no one to email; we're done
|
||||||
self.logger.warning(
|
self.logger.warning(
|
||||||
|
@ -708,7 +709,9 @@ class NotifyEmail(NotifyBase):
|
||||||
for addr in reply_to]
|
for addr in reply_to]
|
||||||
|
|
||||||
self.logger.debug(
|
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))
|
self.logger.debug('Email To: {}'.format(to_addr))
|
||||||
if cc:
|
if cc:
|
||||||
self.logger.debug('Email Cc: {}'.format(', '.join(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[k] = Header(v, self._get_charset(v))
|
||||||
|
|
||||||
base['Subject'] = Header(title, self._get_charset(title))
|
base['Subject'] = Header(title, self._get_charset(title))
|
||||||
base['From'] = formataddr(
|
base['From'] = formataddr(self.from_addr, charset='utf-8')
|
||||||
(from_name if from_name else False, self.from_addr),
|
|
||||||
charset='utf-8')
|
|
||||||
base['To'] = formataddr((to_name, to_addr), charset='utf-8')
|
base['To'] = formataddr((to_name, to_addr), charset='utf-8')
|
||||||
base['Message-ID'] = make_msgid(domain=self.smtp_host)
|
base['Message-ID'] = make_msgid(domain=self.smtp_host)
|
||||||
base['Date'] = \
|
base['Date'] = \
|
||||||
|
@ -833,7 +834,7 @@ class NotifyEmail(NotifyBase):
|
||||||
for message in messages:
|
for message in messages:
|
||||||
try:
|
try:
|
||||||
socket.sendmail(
|
socket.sendmail(
|
||||||
self.from_addr,
|
self.from_addr[1],
|
||||||
message.to_addrs,
|
message.to_addrs,
|
||||||
message.body)
|
message.body)
|
||||||
|
|
||||||
|
@ -868,12 +869,7 @@ class NotifyEmail(NotifyBase):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Define an URL parameters
|
# Define an URL parameters
|
||||||
params = {
|
params = {}
|
||||||
'from': self.from_addr,
|
|
||||||
'mode': self.secure_mode,
|
|
||||||
'smtp': self.smtp_host,
|
|
||||||
'user': self.user,
|
|
||||||
}
|
|
||||||
|
|
||||||
# Append our headers into our parameters
|
# Append our headers into our parameters
|
||||||
params.update({'+{}'.format(k): v for k, v in self.headers.items()})
|
params.update({'+{}'.format(k): v for k, v in self.headers.items()})
|
||||||
|
@ -881,29 +877,59 @@ class NotifyEmail(NotifyBase):
|
||||||
# Extend our parameters
|
# Extend our parameters
|
||||||
params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
|
params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
|
||||||
|
|
||||||
if self.from_name:
|
from_addr = None
|
||||||
params['name'] = self.from_name
|
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:
|
if len(self.cc) > 0:
|
||||||
# Handle our Carbon Copy Addresses
|
# Handle our Carbon Copy Addresses
|
||||||
params['cc'] = ','.join(
|
params['cc'] = ','.join([
|
||||||
['{}{}'.format(
|
formataddr(
|
||||||
'' if not e not in self.names
|
(self.names[e] if e in self.names else False, e),
|
||||||
else '{}:'.format(self.names[e]), e) for e in self.cc])
|
# 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:
|
if len(self.bcc) > 0:
|
||||||
# Handle our Blind Carbon Copy Addresses
|
# Handle our Blind Carbon Copy Addresses
|
||||||
params['bcc'] = ','.join(
|
params['bcc'] = ','.join([
|
||||||
['{}{}'.format(
|
formataddr(
|
||||||
'' if not e not in self.names
|
(self.names[e] if e in self.names else False, e),
|
||||||
else '{}:'.format(self.names[e]), e) for e in self.bcc])
|
# 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:
|
if self.reply_to:
|
||||||
# Handle our Reply-To Addresses
|
# Handle our Reply-To Addresses
|
||||||
params['reply'] = ','.join(
|
params['reply'] = ','.join([
|
||||||
['{}{}'.format(
|
formataddr(
|
||||||
'' if not e not in self.names
|
(self.names[e] if e in self.names else False, e),
|
||||||
else '{}:'.format(self.names[e]), 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])
|
for e in self.reply_to])
|
||||||
|
|
||||||
# pull email suffix from username (if present)
|
# pull email suffix from username (if present)
|
||||||
|
@ -931,7 +957,7 @@ class NotifyEmail(NotifyBase):
|
||||||
# or not
|
# or not
|
||||||
has_targets = \
|
has_targets = \
|
||||||
not (len(self.targets) == 1
|
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(
|
return '{schema}://{auth}{hostname}{port}/{targets}?{params}'.format(
|
||||||
schema=self.secure_protocol if self.secure else self.protocol,
|
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']):
|
if 'from' in results['qsd'] and len(results['qsd']['from']):
|
||||||
from_addr = NotifyEmail.unquote(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
|
# 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']):
|
||||||
results['targets'].append(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
|
# 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
|
||||||
|
|
|
@ -60,6 +60,7 @@ from ..utils import parse_emails
|
||||||
from ..utils import parse_bool
|
from ..utils import parse_bool
|
||||||
from ..utils import is_email
|
from ..utils import is_email
|
||||||
from ..utils import validate_regex
|
from ..utils import validate_regex
|
||||||
|
from ..logger import logger
|
||||||
from ..AppriseLocale import gettext_lazy as _
|
from ..AppriseLocale import gettext_lazy as _
|
||||||
|
|
||||||
# Provide some known codes Mailgun uses and what they translate to:
|
# Provide some known codes Mailgun uses and what they translate to:
|
||||||
|
@ -158,7 +159,7 @@ class NotifyMailgun(NotifyBase):
|
||||||
'name': {
|
'name': {
|
||||||
'name': _('From Name'),
|
'name': _('From Name'),
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'map_to': 'from_name',
|
'map_to': 'from_addr',
|
||||||
},
|
},
|
||||||
'from': {
|
'from': {
|
||||||
'alias_of': 'name',
|
'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,
|
region_name=None, headers=None, tokens=None, batch=False,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -266,14 +267,15 @@ class NotifyMailgun(NotifyBase):
|
||||||
self.from_addr = [
|
self.from_addr = [
|
||||||
self.app_id, '{user}@{host}'.format(
|
self.app_id, '{user}@{host}'.format(
|
||||||
user=self.user, host=self.host)]
|
user=self.user, host=self.host)]
|
||||||
if from_name:
|
|
||||||
result = is_email(from_name)
|
if from_addr:
|
||||||
|
result = is_email(from_addr)
|
||||||
if result:
|
if result:
|
||||||
self.from_addr = (
|
self.from_addr = (
|
||||||
result['name'] if result['name'] else False,
|
result['name'] if result['name'] else False,
|
||||||
result['full_email'])
|
result['full_email'])
|
||||||
else:
|
else:
|
||||||
self.from_addr[0] = from_name
|
self.from_addr[0] = from_addr
|
||||||
|
|
||||||
if not is_email(self.from_addr[1]):
|
if not is_email(self.from_addr[1]):
|
||||||
# Parse Source domain based on from_addr
|
# Parse Source domain based on from_addr
|
||||||
|
@ -589,7 +591,7 @@ class NotifyMailgun(NotifyBase):
|
||||||
params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
|
params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
|
||||||
|
|
||||||
if self.from_addr[0]:
|
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]
|
params['name'] = self.from_addr[0]
|
||||||
|
|
||||||
if self.cc:
|
if self.cc:
|
||||||
|
@ -644,17 +646,26 @@ class NotifyMailgun(NotifyBase):
|
||||||
# We're done - no API Key found
|
# We're done - no API Key found
|
||||||
results['apikey'] = None
|
results['apikey'] = None
|
||||||
|
|
||||||
if 'name' in results['qsd'] and len(results['qsd']['name']):
|
# Attempt to detect 'from' email address
|
||||||
# Extract from name to associate with from address
|
if 'from' in results['qsd'] and len(results['qsd']['from']):
|
||||||
results['from_name'] = \
|
results['from_addr'] = \
|
||||||
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'] = \
|
|
||||||
NotifyMailgun.unquote(results['qsd']['from'])
|
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']):
|
if 'region' in results['qsd'] and len(results['qsd']['region']):
|
||||||
# Extract from name to associate with from address
|
# Extract from name to associate with from address
|
||||||
results['region_name'] = \
|
results['region_name'] = \
|
||||||
|
|
|
@ -519,7 +519,7 @@ def tidy_path(path):
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
def parse_qsd(qs, simple=False):
|
def parse_qsd(qs, simple=False, plus_to_space=False):
|
||||||
"""
|
"""
|
||||||
Query String Dictionary Builder
|
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
|
if simple is set to true, then a ONE dictionary is returned and is not
|
||||||
sub-parsed for additional elements
|
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:
|
# Our return result set:
|
||||||
|
@ -575,7 +580,7 @@ def parse_qsd(qs, simple=False):
|
||||||
key = unquote(key)
|
key = unquote(key)
|
||||||
key = '' if not key else 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 = unquote(val)
|
||||||
val = '' if not val else val.strip()
|
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,
|
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
|
"""A function that greatly simplifies the parsing of a url
|
||||||
specified by the end user.
|
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
|
# Parse Query Arugments ?val=key&key=val
|
||||||
# while ensuring that all keys are lowercase
|
# while ensuring that all keys are lowercase
|
||||||
if qsdata:
|
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
|
# 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
|
# 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 result['qsd+']['KeY'] == 'ValueA'
|
||||||
assert 'kEy' in result['qsd-']
|
assert 'kEy' in result['qsd-']
|
||||||
assert result['qsd-']['kEy'] == 'ValueB'
|
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'] == 'Value C'
|
||||||
assert result['qsd']['+key'] == result['qsd+']['KeY']
|
assert result['qsd']['+key'] == result['qsd+']['KeY']
|
||||||
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 '-key' in result['qsd']
|
||||||
assert ':colon' in result['qsd']
|
assert ':colon' in result['qsd']
|
||||||
assert result['qsd'][':colon'] == 'y'
|
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'] == 'ValueA'
|
||||||
assert result['qsd']['-key'] == 'ValueB'
|
assert result['qsd']['-key'] == 'ValueB'
|
||||||
|
|
||||||
|
|
|
@ -178,9 +178,10 @@ TEST_URLS = (
|
||||||
('mailtos://user:@nuxref.com', {
|
('mailtos://user:@nuxref.com', {
|
||||||
'instance': NotifyEmail,
|
'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=@', {
|
('mailtos://user:pass@nuxref.com?from=@', {
|
||||||
'instance': TypeError,
|
'instance': NotifyEmail,
|
||||||
}),
|
}),
|
||||||
# Invalid From Address
|
# Invalid From Address
|
||||||
('mailtos://nuxref.com?user=&pass=.', {
|
('mailtos://nuxref.com?user=&pass=.', {
|
||||||
|
@ -226,6 +227,10 @@ TEST_URLS = (
|
||||||
# is set and tests that we gracfully handle them
|
# is set and tests that we gracfully handle them
|
||||||
'test_smtplib_exceptions': True,
|
'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
|
# Test no auth at all
|
||||||
('mailto://localhost?from=test@example.com&to=test@example.com', {
|
('mailto://localhost?from=test@example.com&to=test@example.com', {
|
||||||
'instance': NotifyEmail,
|
'instance': NotifyEmail,
|
||||||
|
@ -435,7 +440,8 @@ def test_plugin_email_webbase_lookup(mock_smtp, mock_smtpssl):
|
||||||
assert isinstance(obj, NotifyEmail)
|
assert isinstance(obj, NotifyEmail)
|
||||||
assert len(obj.targets) == 1
|
assert len(obj.targets) == 1
|
||||||
assert (False, 'user@l2g.com') in obj.targets
|
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.password == 'pass'
|
||||||
assert obj.user == 'user'
|
assert obj.user == 'user'
|
||||||
assert obj.secure is True
|
assert obj.secure is True
|
||||||
|
@ -569,6 +575,23 @@ def test_plugin_email_smtplib_send_multiple_recipients(mock_smtplib):
|
||||||
mock.call().quit(),
|
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')
|
@mock.patch('smtplib.SMTP')
|
||||||
def test_plugin_email_smtplib_internationalization(mock_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
|
# Test variations of username required to be an email address
|
||||||
# user@example.com
|
# user@example.com
|
||||||
obj = Apprise.instantiate(
|
obj = Apprise.instantiate(
|
||||||
'mailto://{user}:{passwd}@example.com'.format(
|
'mailto://{user}:{passwd}@example.com?smtp=example.com'.format(
|
||||||
user='apprise%40example21.ca',
|
user='apprise%40example21.ca',
|
||||||
passwd='abcd123'),
|
passwd='abcd123'),
|
||||||
suppress_exceptions=False)
|
suppress_exceptions=False)
|
||||||
|
@ -681,6 +704,17 @@ def test_plugin_email_url_variations():
|
||||||
assert obj.password == 'abcd123'
|
assert obj.password == 'abcd123'
|
||||||
assert obj.user == 'apprise@example21.ca'
|
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)
|
# test username specified in the url body (as an argument)
|
||||||
# this always over-rides the entry at the front of the url
|
# this always over-rides the entry at the front of the url
|
||||||
obj = Apprise.instantiate(
|
obj = Apprise.instantiate(
|
||||||
|
@ -693,10 +727,20 @@ def test_plugin_email_url_variations():
|
||||||
assert obj.password == 'abcd123'
|
assert obj.password == 'abcd123'
|
||||||
assert obj.user == 'apprise@example21.ca'
|
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)
|
# test user and password specified in the url body (as an argument)
|
||||||
# this always over-rides the entries at the front of the url
|
# this always over-rides the entries at the front of the url
|
||||||
obj = Apprise.instantiate(
|
obj = Apprise.instantiate(
|
||||||
'mailto://_:_@example.com?user={user}&pass={passwd}'.format(
|
'mailtos://_:_@example.com?user={user}&pass={passwd}'.format(
|
||||||
user='apprise%40example21.ca',
|
user='apprise%40example21.ca',
|
||||||
passwd='abcd123'),
|
passwd='abcd123'),
|
||||||
suppress_exceptions=False)
|
suppress_exceptions=False)
|
||||||
|
@ -706,7 +750,20 @@ def test_plugin_email_url_variations():
|
||||||
assert obj.user == 'apprise@example21.ca'
|
assert obj.user == 'apprise@example21.ca'
|
||||||
assert len(obj.targets) == 1
|
assert len(obj.targets) == 1
|
||||||
assert (False, 'apprise@example.com') in obj.targets
|
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)
|
# test user and password specified in the url body (as an argument)
|
||||||
# this always over-rides the entries at the front of the url
|
# 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 obj.user == 'apprise@example21.ca'
|
||||||
assert len(obj.targets) == 1
|
assert len(obj.targets) == 1
|
||||||
assert (False, 'apprise@example.com') in obj.targets
|
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'
|
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
|
# test a complicated example
|
||||||
obj = Apprise.instantiate(
|
obj = Apprise.instantiate(
|
||||||
'mailtos://{user}:{passwd}@{host}:{port}'
|
'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',
|
user='apprise%40example21.ca',
|
||||||
passwd='abcd123',
|
passwd='abcd123',
|
||||||
host='example.com',
|
host='example.com',
|
||||||
|
@ -747,7 +817,28 @@ def test_plugin_email_url_variations():
|
||||||
assert obj.smtp_host == 'smtp.example.edu'
|
assert obj.smtp_host == 'smtp.example.edu'
|
||||||
assert len(obj.targets) == 1
|
assert len(obj.targets) == 1
|
||||||
assert (False, 'to@example.jp') in obj.targets
|
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():
|
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'
|
'mailtos://user:pass123@hotmail.com:444'
|
||||||
'?to=user2@yahoo.com&name=test%20name')
|
'?to=user2@yahoo.com&name=test%20name')
|
||||||
assert isinstance(results, dict)
|
assert isinstance(results, dict)
|
||||||
assert 'test name' == results['from_name']
|
assert 'test name' == results['from_addr']
|
||||||
assert 'user' == results['user']
|
assert 'user' == results['user']
|
||||||
assert 444 == results['port']
|
assert 444 == results['port']
|
||||||
assert 'hotmail.com' == results['host']
|
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'
|
'mailtos://user:pass123@hotmail.com?smtp=override.com'
|
||||||
'&name=test%20name&to=user2@yahoo.com&mode=ssl')
|
'&name=test%20name&to=user2@yahoo.com&mode=ssl')
|
||||||
assert isinstance(results, dict)
|
assert isinstance(results, dict)
|
||||||
assert 'test name' == results['from_name']
|
assert 'test name' == results['from_addr']
|
||||||
assert 'user' == results['user']
|
assert 'user' == results['user']
|
||||||
assert 'hotmail.com' == results['host']
|
assert 'hotmail.com' == results['host']
|
||||||
assert 'pass123' == results['password']
|
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
|
# Verify our over-rides are in place
|
||||||
assert obj.smtp_host == 'smtp.exmail.qq.com'
|
assert obj.smtp_host == 'smtp.exmail.qq.com'
|
||||||
assert obj.port == 465
|
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'
|
assert obj.secure_mode == 'ssl'
|
||||||
# No entries in the reply_to
|
# No entries in the reply_to
|
||||||
assert not obj.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(
|
results = NotifyEmail.parse_url(
|
||||||
"mailtos://abc:password@xyz.cn?"
|
"mailtos://abc:password@xyz.cn?"
|
||||||
"smtp=smtp.exmail.qq.com&mode=ssl&port=465")
|
"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
|
# Verify our over-rides are in place
|
||||||
assert obj.smtp_host == 'smtp.exmail.qq.com'
|
assert obj.smtp_host == 'smtp.exmail.qq.com'
|
||||||
assert obj.port == 465
|
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'
|
assert obj.secure_mode == 'ssl'
|
||||||
# No entries in the reply_to
|
# No entries in the reply_to
|
||||||
assert not obj.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
|
assert isinstance(obj, NotifyEmail) is True
|
||||||
# Verify our over-rides are in place
|
# Verify our over-rides are in place
|
||||||
assert obj.smtp_host == 'example.com'
|
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.secure_mode == 'starttls'
|
||||||
assert obj.url().startswith(
|
assert obj.url().startswith(
|
||||||
'mailtos://user:pass@example.com')
|
'mailtos://user:pass@example.com')
|
||||||
# Test that our template over-ride worked
|
# Test that our template over-ride worked
|
||||||
assert 'reply=noreply%40example.com' in obj.url()
|
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), {
|
'a' * 32, 'b' * 8, 'c' * 8), {
|
||||||
'instance': TypeError,
|
'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
|
# headers
|
||||||
('mailgun://user@localhost.localdomain/{}-{}-{}'
|
('mailgun://user@localhost.localdomain/{}-{}-{}'
|
||||||
'?+X-Customer-Campaign-ID=Apprise'.format(
|
'?+X-Customer-Campaign-ID=Apprise'.format(
|
||||||
|
|
Loading…
Reference in New Issue