Skip to content

Commit

Permalink
some refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
caronc committed Sep 18, 2024
1 parent e8b2d42 commit 29d8031
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 32 deletions.
76 changes: 61 additions & 15 deletions apprise/plugins/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
from datetime import timedelta
from datetime import timezone

from ..apprise_attachment import AppriseAttachment
from .base import NotifyBase
from ..url import PrivacyMode
from ..common import NotifyFormat, NotifyType, PersistentStoreMode
Expand Down Expand Up @@ -367,6 +368,10 @@ class NotifyEmail(NotifyBase):
# Support attachments
attachment_support = True

# There is no reason a PGP Public Key should exceed 8K in size
# If it is more than this, then it is not accepted
max_pgp_public_key_size = 8000

# Default Notify Format
notify_format = NotifyFormat.HTML

Expand Down Expand Up @@ -469,6 +474,14 @@ class NotifyEmail(NotifyBase):
'type': 'list:string',
'map_to': 'reply_to',
},
'pgpkey': {
'name': _('PGP Public Key Path'),
'type': 'string',
'private': True,
# By default persistent storage is referenced
'default': '',
'map_to': 'pgp_key',
},
})

# Define any kwargs we're using
Expand All @@ -481,7 +494,7 @@ class NotifyEmail(NotifyBase):

def __init__(self, smtp_host=None, from_addr=None, secure_mode=None,
targets=None, cc=None, bcc=None, reply_to=None, headers=None,
use_pgp=None, **kwargs):
use_pgp=None, pgp_key=None, **kwargs):
"""
Initialize Email Object
Expand Down Expand Up @@ -529,6 +542,18 @@ def __init__(self, smtp_host=None, from_addr=None, secure_mode=None,
'PGP Support is not available on this installation; '
'ask admin to install PGPy')

# Our template object is just an AppriseAttachment object
if pgp_key:
self.pgp_key = AppriseAttachment(asset=self.asset)
# Add our definition to our pgp_key reference
self.pgp_key.add(pgp_key)
# Enforce maximum file size
self.pgp_key[0].max_file_size = self.max_pgp_public_key_size

else:
# No key; use auto-generation
self.pgp_key = None

# Now detect secure mode
if secure_mode:
self.secure_mode = None \
Expand Down Expand Up @@ -861,6 +886,7 @@ def send(self, body, title='', notify_type=NotifyType.INFO, attach=None,
base = mixed

if self.use_pgp:
self.logger.debug("Securing email with PGP Encryption")
# Apply our encryption
encrypted_content = self.pgp_encrypt_message(base.as_string())
if encrypted_content:
Expand Down Expand Up @@ -1054,10 +1080,26 @@ def pgp_generate_keys(self, path=None):
return True

@property
def pgp_fnames(self):
def pgp_pubkey(self):
"""
Returns a list of filenames worth scanning for
"""
if self.pgp_key is not None:
# If our code reaches here, then we fetch our public key
pgp_key = self.pgp_key[0]
if not pgp_key:
# We could not access the attachment
self.logger.error(
'Could not access PGP Public Key {}.'.format(
pgp_key.url(privacy=True)))
return False

return pgp_key.path

elif not self.store.path:
# No path
return None

fnames = [
'pgp-public.asc',
'pgp-pub.asc',
Expand All @@ -1077,28 +1119,23 @@ def pgp_fnames(self):
if _entry not in fnames:
fnames.insert(0, f'{_entry}-pub.asc')

return fnames
return next(
(os.path.join(self.store.path, fname)
for fname in fnames
if os.path.isfile(os.path.join(self.store.path, fname))),
None)

def pgp_public_key(self, path=None):
"""
Opens a spcified pgp public file and returns the key from it which
is used to encrypt the message
"""
if path is None:
path = next(
(os.path.join(self.store.path, fname)
for fname in self.pgp_fnames
if os.path.isfile(os.path.join(self.store.path, fname))),
None)
path = self.pgp_pubkey

if not path:
if self.pgp_generate_keys(path=self.store.path):
path = next(
(os.path.join(self.store.path, fname)
for fname in self.pgp_fnames
if os.path.isfile(
os.path.join(self.store.path, fname))), None)

path = self.pgp_pubkey
if path:
# We should get a hit now
return self.pgp_public_key(path=path)
Expand Down Expand Up @@ -1138,7 +1175,6 @@ def pgp_public_key(self, path=None):
self.logger.debug(f'I/O Exception: {e}')
return None

self.store.set(ps_key, public_key, expires=86400)
self.pgp_public_keys[ps_key] = {
'public_key': public_key,
'expires':
Expand Down Expand Up @@ -1179,6 +1215,11 @@ def url(self, privacy=False, *args, **kwargs):
'pgp': 'yes' if self.use_pgp else 'no',
}

# Store oure public key back into your URL
if self.pgp_key is not None:
params['pgp_key'] = NotifyEmail.quote(
self.pgp_key[0].url(privacy=privacy), safe=':')

# Append our headers into our parameters
params.update({'+{}'.format(k): v for k, v in self.headers.items()})

Expand Down Expand Up @@ -1335,6 +1376,11 @@ def parse_url(url):
parse_bool(results['qsd'].get(
'pgp', NotifyEmail.template_args['pgp']['default']))

# Get PGP Public Key Override
if 'pgpkey' in results['qsd'] and results['qsd']['pgpkey']:
results['pgp_key'] = \
NotifyEmail.unquote(results['qsd']['pgpkey'])

# The From address is a must; either through the use of templates
# from= entry and/or merging the user and hostname together, this
# must be calculated or parse_url will fail.
Expand Down
49 changes: 32 additions & 17 deletions test/test_plugin_email.py
Original file line number Diff line number Diff line change
Expand Up @@ -2068,27 +2068,42 @@ def test_plugin_email_pgp(mock_smtp, mock_smtpssl, tmpdir):
# Initialize our email (no from name)
obj = Apprise.instantiate('mailto://user:[email protected]?pgp=yes')

# Test our names
fnames = obj.pgp_fnames
assert isinstance(fnames, list)
# Nothing to lookup
assert obj.pgp_pubkey is None
assert obj.pgp_public_key() is None
assert obj.pgp_encrypt_message("message") is False
# Keys can not be generated in memory mode
assert obj.pgp_generate_keys() is False

# login is pgp
obj = Apprise.instantiate('mailto://pgp:[email protected]?pgp=yes')
# The reason... no location to store data
assert obj.store.mode == PersistentStoreMode.MEMORY

# Test our names
fnames = obj.pgp_fnames
assert isinstance(fnames, list)
tmpdir0 = tmpdir.mkdir('tmp00')
asset = AppriseAsset(
storage_mode=PersistentStoreMode.FLUSH,
storage_path=str(tmpdir0),
)

# login is pgp
obj = Apprise.instantiate('mailto://chris:[email protected]?pgp=yes')
fnames = obj.pgp_fnames
assert isinstance(fnames, list)
# Prepare PGP
obj = Apprise.instantiate(
'mailto://pgp:[email protected]?pgp=yes', asset=asset)
assert obj.store.mode == PersistentStoreMode.FLUSH

# Still no public key
assert obj.pgp_pubkey is None

assert obj.pgp_generate_keys() is True
# Now we'll have a public key
assert isinstance(obj.pgp_pubkey, str)

# Prepare PGP
obj = Apprise.instantiate(
f'mailto://pgp:[email protected]?pgp=yes&pgpkey={obj.pgp_pubkey}',
asset=asset)

# We will find our key
assert obj.pgp_public_key() is not None

# Attempt to generate keys
obj = Apprise.instantiate('mailto://chris:[email protected]?pgp=yes')
# We're in memory mode
assert obj.store.mode == PersistentStoreMode.MEMORY
assert obj.pgp_generate_keys() is False
tmpdir1 = tmpdir.mkdir('tmp01')
# However explicitly setting a path works
assert obj.pgp_generate_keys(str(tmpdir1)) is True
Expand Down
18 changes: 18 additions & 0 deletions test/var/pgp/corrupt-pub.asc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----

xsBNBGbo3ycBCACjECe63GpZanFYLE678OIhzpvY0J6pCBCZblGxXOuwuv7VPIq7
XQN91UdOL8huDb/JYRxarDoS6+JvyempbzZt0S+veXDl4pRFb+Q4a5sp1l/Mduw0
rDFErwfF8SpMPBI2WJUhN9n72UEqXVDvTPdcAka8v8p7dS6/RKZzch8P+EjgJME0
p9+N/2Lrbi2nDDXD+xD4Odw83J5V8Xn/jie3GCxtXda5XIX3EzTSB30elLLNBMcq
ooooooooooooooooooooooooooooo6bRS9TT34rZZk7qyB2iroq9SdIBCGyn1q4r
uIskVNCsqgP9cMa+S1XePUT77VNNN1yzhACpABEBAAHNIUNocmlzIENhcm9uIDxs
ZWFkMmdvbGRAZ21haWwuooooooooooooooooooooooooooooogILCQIVCAIWAgIe
ARYhBEHHWtq4Kh8dGraFnkmZAD9B29oPAAoJEEmZAD9B29oPAawIAImCijTdvDl8
Sibwo7gL4ooooooooooooooooooooooooooooofjiEEW8gVQ4W2KDs74aCGkQtQJ
irvNA7WnuyMyXZyvhYa63U7GTk5RdVkMygT0a5n8/8HVAenZrBL6VNaZYw/LlgWd
0knhsmqdGTsjKuYdZ3Cooooooooooooooooooooooooooooo2GWBnvOQje+lQGIf
rE6TIwsf4QoKXSkTakzggbpZZl2hg2O6dJiij1cH+DYFVTaVXw4rVmo8ckTJ9DiF
T9H/EmsNqlSKTTv1Aw4raCFZ+T/Ocsw/vIOoEtVhiT/mfDcIbi0VB3EhYvI3eFso
yiZsjyu9xY0=
=ZY2q
-----END PGP PUBLIC KEY BLOCK-----
31 changes: 31 additions & 0 deletions test/var/pgp/valid-prv.asc
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
-----BEGIN PGP PRIVATE KEY BLOCK-----

xcLYBGbo3ycBCACjECe63GpZanFYLE678OIhzpvY0J6pCBCZblGxXOuwuv7VPIq7
XQN91UdOL8huDb/JYRxarDoS6+JvyempbzZt0S+veXDl4pRFb+Q4a5sp1l/Mduw0
rDFErwfF8SpMPBI2WJUhN9n72UEqXVDvTPdcAka8v8p7dS6/RKZzch8P+EjgJME0
p9+N/2Lrbi2nDDXD+xD4Odw83J5V8Xn/jie3GCxtXda5XIX3EzTSB30elLLNBMcq
N5xJBTrjhciDzU85Gb+bUecnoj9Oj6bRS9TT34rZZk7qyB2iroq9SdIBCGyn1q4r
uIskVNCsqgP9cMa+S1XePUT77VNNN1yzhACpABEBAAEAB/4tOpPqjqyo9I9Px6pn
Et+GRQqRTvxTIjuIc0MRkRaGxLdeahaI9bm8M2Y9158ed43Uy6zTsaXCDc+W9khr
iL9uInG5mFOqT/iUcf65b49wQVf9HJdT3Ncll+7uBoCW+KqMjHGA7z71TkN2/r8u
QQjzamY4gHInYE+BGgeZSfQ3t1MJMSdjQopPGDwSsco5hQJYtVH8K/0/Ig5S5E9Q
KD9ku3W9bCliERkljIwEbbyDv/vmbxPKdWW83T+UQK6CQhkH6h69EoMGQ76a6y/H
UuppNSpxuR4BiX4ZlcUyARrLluaRS0K1/OZCoScA0LLjY8pCEBoWT0uhhvy3t2xD
/bipBADS3bM3yGGZKtNgLPQx0BAyWk07OD3AlObykz4yTIc9DZj1bzhHKgAhwUAN
k7StwA22HoxMCKSoxhherZaXAQaJJOJKNXw3DphHCexrBq77nxBu3yo9UStj04Lx
tCEibclsQcwgh7TjjjDQdRYiirZvu9IGQBf27xKvTepibn7NnwQAxfcePfXsHza3
7CuJbxOGFaPf4ENSpFRYSZbH3dErtSlGDzz8e8jI0Ck9LQgp9MfjksWUwaMQbXdV
zNbQe1lAWQxtN9amVvEWvrAJhbhEU6RLsSjpZ9W5r3xAbfkoDg/icjbdoOqwI6LE
aTEhwaz+XZMLYJiT22AMJyC7TcL6fLcD/0nBRheQBqTsuYKimKI5yZ3ZdlGHfaLN
OqMGfuaEQCUAhSaXNliuP3XAWfiVXCaRw9De+Eod6DfGMGTTx8EVy7N4y4w3TORp
fFKaMGD3oiw1Eh63K1jV2yWPPpOnyc+YtXCPuGS+n/3CITc5cKxaPapQtCJA9Gw0
OaZ7ikUNs0Q9NbfNIUNocmlzIENhcm9uIDxsZWFkMmdvbGRAZ21haWwuY29tPsLA
ggQTAQgALAUCZujfJwIbBgILCQIVCAIWAgIeARYhBEHHWtq4Kh8dGraFnkmZAD9B
29oPAAoJEEmZAD9B29oPAawIAImCijTdvDl8Sibwo7gL4ayF4S3KhaKCYORcMM1o
e4pesy5ME6fjiEEW8gVQ4W2KDs74aCGkQtQJirvNA7WnuyMyXZyvhYa63U7GTk5R
dVkMygT0a5n8/8HVAenZrBL6VNaZYw/LlgWd0knhsmqdGTsjKuYdZ3CHED85pv/M
Owe0pyGOQKtJ1t9qwc6l2GWBnvOQje+lQGIfrE6TIwsf4QoKXSkTakzggbpZZl2h
g2O6dJiij1cH+DYFVTaVXw4rVmo8ckTJ9DiFT9H/EmsNqlSKTTv1Aw4raCFZ+T/O
csw/vIOoEtVhiT/mfDcIbi0VB3EhYvI3eFsoyiZsjyu9xY0=
=dBp6
-----END PGP PRIVATE KEY BLOCK-----
18 changes: 18 additions & 0 deletions test/var/pgp/valid-pub.asc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----

xsBNBGbo3ycBCACjECe63GpZanFYLE678OIhzpvY0J6pCBCZblGxXOuwuv7VPIq7
XQN91UdOL8huDb/JYRxarDoS6+JvyempbzZt0S+veXDl4pRFb+Q4a5sp1l/Mduw0
rDFErwfF8SpMPBI2WJUhN9n72UEqXVDvTPdcAka8v8p7dS6/RKZzch8P+EjgJME0
p9+N/2Lrbi2nDDXD+xD4Odw83J5V8Xn/jie3GCxtXda5XIX3EzTSB30elLLNBMcq
N5xJBTrjhciDzU85Gb+bUecnoj9Oj6bRS9TT34rZZk7qyB2iroq9SdIBCGyn1q4r
uIskVNCsqgP9cMa+S1XePUT77VNNN1yzhACpABEBAAHNIUNocmlzIENhcm9uIDxs
ZWFkMmdvbGRAZ21haWwuY29tPsLAggQTAQgALAUCZujfJwIbBgILCQIVCAIWAgIe
ARYhBEHHWtq4Kh8dGraFnkmZAD9B29oPAAoJEEmZAD9B29oPAawIAImCijTdvDl8
Sibwo7gL4ayF4S3KhaKCYORcMM1oe4pesy5ME6fjiEEW8gVQ4W2KDs74aCGkQtQJ
irvNA7WnuyMyXZyvhYa63U7GTk5RdVkMygT0a5n8/8HVAenZrBL6VNaZYw/LlgWd
0knhsmqdGTsjKuYdZ3CHED85pv/MOwe0pyGOQKtJ1t9qwc6l2GWBnvOQje+lQGIf
rE6TIwsf4QoKXSkTakzggbpZZl2hg2O6dJiij1cH+DYFVTaVXw4rVmo8ckTJ9DiF
T9H/EmsNqlSKTTv1Aw4raCFZ+T/Ocsw/vIOoEtVhiT/mfDcIbi0VB3EhYvI3eFso
yiZsjyu9xY0=
=ZY2q
-----END PGP PUBLIC KEY BLOCK-----

0 comments on commit 29d8031

Please sign in to comment.