mirror of https://github.com/caronc/apprise
PGP Encryption working
parent
1a9d1dddd5
commit
a9ed27ee02
|
@ -34,6 +34,7 @@ import typing as t
|
||||||
from email.mime.text import MIMEText
|
from email.mime.text import MIMEText
|
||||||
from email.mime.application import MIMEApplication
|
from email.mime.application import MIMEApplication
|
||||||
from email.mime.multipart import MIMEMultipart
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
from email.mime.base import MIMEBase
|
||||||
from email.utils import formataddr, make_msgid
|
from email.utils import formataddr, make_msgid
|
||||||
from email.header import Header
|
from email.header import Header
|
||||||
from email import charset
|
from email import charset
|
||||||
|
@ -787,6 +788,10 @@ class NotifyEmail(NotifyBase):
|
||||||
'There are no Email recipients to notify')
|
'There are no Email recipients to notify')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
elif self.use_pgp and not PGP_SUPPORT:
|
||||||
|
self.logger.warning('PGP Support unavailable')
|
||||||
|
return False
|
||||||
|
|
||||||
messages: t.List[EmailMessage] = []
|
messages: t.List[EmailMessage] = []
|
||||||
|
|
||||||
# Create a copy of the targets list
|
# Create a copy of the targets list
|
||||||
|
@ -887,10 +892,33 @@ class NotifyEmail(NotifyBase):
|
||||||
|
|
||||||
if self.use_pgp:
|
if self.use_pgp:
|
||||||
self.logger.debug("Securing email with PGP Encryption")
|
self.logger.debug("Securing email with PGP Encryption")
|
||||||
|
# Set our header information to include in the encryption
|
||||||
|
base['From'] = formataddr(
|
||||||
|
(None, self.from_addr[1]), charset='utf-8')
|
||||||
|
base['To'] = formataddr((None, to_addr), charset='utf-8')
|
||||||
|
base['Subject'] = Header(title, self._get_charset(title))
|
||||||
|
|
||||||
# Apply our encryption
|
# Apply our encryption
|
||||||
encrypted_content = self.pgp_encrypt_message(base.as_string())
|
encrypted_content = self.pgp_encrypt_message(base.as_string())
|
||||||
if encrypted_content:
|
if encrypted_content:
|
||||||
base = MIMEText(encrypted_content, "plain")
|
# prepare our messsage
|
||||||
|
base = MIMEMultipart(
|
||||||
|
"encrypted", protocol="application/pgp-encrypted")
|
||||||
|
|
||||||
|
# Store Autocrypt header (DeltaChat Support)
|
||||||
|
base.add_header(
|
||||||
|
"Autocrypt",
|
||||||
|
"addr=%s; prefer-encrypt=mutual" % formataddr(
|
||||||
|
(False, to_addr), charset='utf-8'))
|
||||||
|
|
||||||
|
# Set Encryption Info Part
|
||||||
|
enc_payload = MIMEText("Version: 1", "plain")
|
||||||
|
enc_payload.set_type("application/pgp-encrypted")
|
||||||
|
base.attach(enc_payload)
|
||||||
|
|
||||||
|
enc_payload = MIMEBase("application", "octet-stream")
|
||||||
|
enc_payload.set_payload(encrypted_content)
|
||||||
|
base.attach(enc_payload)
|
||||||
|
|
||||||
# Apply any provided custom headers
|
# Apply any provided custom headers
|
||||||
for k, v in self.headers.items():
|
for k, v in self.headers.items():
|
||||||
|
@ -984,8 +1012,14 @@ class NotifyEmail(NotifyBase):
|
||||||
finally:
|
finally:
|
||||||
# Gracefully terminate the connection with the server
|
# Gracefully terminate the connection with the server
|
||||||
if socket is not None: # pragma: no branch
|
if socket is not None: # pragma: no branch
|
||||||
|
try:
|
||||||
socket.quit()
|
socket.quit()
|
||||||
|
|
||||||
|
except (SocketError, smtplib.SMTPException):
|
||||||
|
# No need to make this a bigger issue as we were exiting
|
||||||
|
# anyway
|
||||||
|
pass
|
||||||
|
|
||||||
# Reduce our dictionary (eliminate expired keys if any)
|
# Reduce our dictionary (eliminate expired keys if any)
|
||||||
self.pgp_public_keys = {
|
self.pgp_public_keys = {
|
||||||
key: value for key, value in self.pgp_public_keys.items()
|
key: value for key, value in self.pgp_public_keys.items()
|
||||||
|
@ -1079,8 +1113,7 @@ class NotifyEmail(NotifyBase):
|
||||||
os.path.basename(pub_path))
|
os.path.basename(pub_path))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@property
|
def pgp_pubkey(self, email=None):
|
||||||
def pgp_pubkey(self):
|
|
||||||
"""
|
"""
|
||||||
Returns a list of filenames worth scanning for
|
Returns a list of filenames worth scanning for
|
||||||
"""
|
"""
|
||||||
|
@ -1107,9 +1140,14 @@ class NotifyEmail(NotifyBase):
|
||||||
'pub.asc',
|
'pub.asc',
|
||||||
]
|
]
|
||||||
|
|
||||||
# Prepare our key files:
|
emails = []
|
||||||
email = self.from_addr[1]
|
if email:
|
||||||
|
emails.append(email)
|
||||||
|
|
||||||
|
# Prepare our key files:
|
||||||
|
emails.append(self.from_addr[1])
|
||||||
|
|
||||||
|
for email in emails:
|
||||||
_entry = email.split('@')[0].lower()
|
_entry = email.split('@')[0].lower()
|
||||||
if _entry not in fnames:
|
if _entry not in fnames:
|
||||||
fnames.insert(0, f'{_entry}-pub.asc')
|
fnames.insert(0, f'{_entry}-pub.asc')
|
||||||
|
@ -1125,17 +1163,17 @@ class NotifyEmail(NotifyBase):
|
||||||
if os.path.isfile(os.path.join(self.store.path, fname))),
|
if os.path.isfile(os.path.join(self.store.path, fname))),
|
||||||
None)
|
None)
|
||||||
|
|
||||||
def pgp_public_key(self, path=None):
|
def pgp_public_key(self, path=None, email=None):
|
||||||
"""
|
"""
|
||||||
Opens a spcified pgp public file and returns the key from it which
|
Opens a spcified pgp public file and returns the key from it which
|
||||||
is used to encrypt the message
|
is used to encrypt the message
|
||||||
"""
|
"""
|
||||||
if path is None:
|
if path is None:
|
||||||
path = self.pgp_pubkey
|
path = self.pgp_pubkey(email=email)
|
||||||
|
|
||||||
if not path:
|
if not path:
|
||||||
if self.pgp_generate_keys(path=self.store.path):
|
if self.pgp_generate_keys(path=self.store.path):
|
||||||
path = self.pgp_pubkey
|
path = self.pgp_pubkey(email=email)
|
||||||
if path:
|
if path:
|
||||||
# We should get a hit now
|
# We should get a hit now
|
||||||
return self.pgp_public_key(path=path)
|
return self.pgp_public_key(path=path)
|
||||||
|
|
|
@ -2069,7 +2069,7 @@ def test_plugin_email_pgp(mock_smtp, mock_smtpssl, tmpdir):
|
||||||
obj = Apprise.instantiate('mailto://user:pass@nuxref.com?pgp=yes')
|
obj = Apprise.instantiate('mailto://user:pass@nuxref.com?pgp=yes')
|
||||||
|
|
||||||
# Nothing to lookup
|
# Nothing to lookup
|
||||||
assert obj.pgp_pubkey is None
|
assert obj.pgp_pubkey() is None
|
||||||
assert obj.pgp_public_key() is None
|
assert obj.pgp_public_key() is None
|
||||||
assert obj.pgp_encrypt_message("message") is False
|
assert obj.pgp_encrypt_message("message") is False
|
||||||
# Keys can not be generated in memory mode
|
# Keys can not be generated in memory mode
|
||||||
|
@ -2090,15 +2090,15 @@ def test_plugin_email_pgp(mock_smtp, mock_smtpssl, tmpdir):
|
||||||
assert obj.store.mode == PersistentStoreMode.FLUSH
|
assert obj.store.mode == PersistentStoreMode.FLUSH
|
||||||
|
|
||||||
# Still no public key
|
# Still no public key
|
||||||
assert obj.pgp_pubkey is None
|
assert obj.pgp_pubkey() is None
|
||||||
|
|
||||||
assert obj.pgp_generate_keys() is True
|
assert obj.pgp_generate_keys() is True
|
||||||
# Now we'll have a public key
|
# Now we'll have a public key
|
||||||
assert isinstance(obj.pgp_pubkey, str)
|
assert isinstance(obj.pgp_pubkey(), str)
|
||||||
|
|
||||||
# Prepare PGP
|
# Prepare PGP
|
||||||
obj = Apprise.instantiate(
|
obj = Apprise.instantiate(
|
||||||
f'mailto://pgp:pass@nuxref.com?pgp=yes&pgpkey={obj.pgp_pubkey}',
|
'mailto://pgp:pass@nuxref.com?pgp=yes&pgpkey=%s' % obj.pgp_pubkey(),
|
||||||
asset=asset)
|
asset=asset)
|
||||||
|
|
||||||
# We will find our key
|
# We will find our key
|
||||||
|
|
Loading…
Reference in New Issue