Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Saving generated emails locally #29

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/pius.1
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ Hostname of SMTP server. [default: \fIlocalhost\fP]
Use the pexpect module for signing and drop to the gpg shell for entering the passphrase. [default: false]
.IP "\fB\-I\fP, \fB\-\-import\fP"
Also import the unsigned keys from the keyring into the default keyring. Ignored if \fB\-r\fP is not specified, or if it's the same as the default keyring.
.IP "\fB\-L\fP, \fB\-\-save\-to\-mail\-dir\fP"
Instead of calling SMTP, save the email to this directory. Useful for signing from an air gapped machine. The saved email files can be sent using your own MTA such as sendmail or mailx.
.IP "\fB\-m\fP \fIFROM\-EMAIL\fP, \fB\-\-mail=\fP\fIFROM\-EMAIL\fP"
Email the encrypted, signed keys to the respective email addresses using \fIFROM\-EMAIL\fP as the sender. See also \fB\-H\fP and \fB\-P\fP.
.IP "\fB\-M\fP \fIFILE\fP, \fB\-\-mail\-text=\fP\fIFILE\fP"
Expand Down
62 changes: 35 additions & 27 deletions libpius/mailer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
import smtplib
import socket
import os

from email import message, quoprimime
from email.utils import formatdate
Expand All @@ -21,7 +22,7 @@

class PiusMailer(object):
def __init__(self, mail, host, port, user, tls, no_mime, override, msg_text,
tmp_dir):
tmp_dir, local_mail_dir):
self.mail = mail
self.host = host
self.port = port
Expand All @@ -32,6 +33,7 @@ def __init__(self, mail, host, port, user, tls, no_mime, override, msg_text,
self.address_override = override
self.message_text = msg_text
self.tmp_dir = tmp_dir
self.local_mail_dir = local_mail_dir

@staticmethod
def add_options(parser):
Expand Down Expand Up @@ -254,36 +256,42 @@ def _send_mail(self, to, msg):
msg['From'] = self.mail
if self.address_override:
msg['To'] = self.address_override
env_to = [msg['To']]
else:
msg['To'] = to
env_to = [msg['To'], self.mail]
msg['Date'] = formatdate(localtime=True)

try:
smtp = smtplib.SMTP(self.host, self.port)
if self.tls:
# NOTE WELL: SECURITY IMPORTANT NOTE!
# In python 2.6 if you attempt to starttls() and the server doesn't
# understand an exception is raised. However before that, it just
# carried on # and one could attempt to auth over a plain-text session.
# This is BAD!
#
# So, in order be secure on older pythons we ehlo() and then check the
# response before attempting startls.
smtp.ehlo()
if not smtp.has_extn('STARTTLS'):
# Emulate 2.6 behavior
raise smtplib.SMTPException('Server does not support STARTTLS')
smtp.starttls()
# must re-ehlo after STARTTLS
smtp.ehlo()
# Don't want to send auth information unless we're TLS'd
if self.user:
smtp.login(self.user, self.password)
if self.address_override:
env_to = self.address_override
else:
# BCC the user...
env_to = [msg['To'], self.mail]
if self.local_mail_dir:
if not os.path.isdir(self.local_mail_dir):
os.mkdir(self.local_mail_dir)
if not self.address_ovrride:
msg['Bcc'] = self.mail
email = open(os.path.join(self.local_mail_dir, msg['To']), 'w')
email.write(str(msg))
email.close()
else:
try:
smtp = smtplib.SMTP(self.host, self.port)
if self.tls:
# NOTE WELL: SECURITY IMPORTANT NOTE!
# In python 2.6 if you attempt to starttls() and the server doesn't
# understand an exception is raised. However before that, it just
# carried on # and one could attempt to auth over a plain-text session.
# This is BAD!
#
# So, in order be secure on older pythons we ehlo() and then check the
# response before attempting startls.
smtp.ehlo()
if not smtp.has_extn('STARTTLS'):
# Emulate 2.6 behavior
raise smtplib.SMTPException('Server does not support STARTTLS')
smtp.starttls()
# must re-ehlo after STARTTLS
smtp.ehlo()
# Don't want to send auth information unless we're TLS'd
if self.user:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The more I think about this approach the more I like it.

However... how do we handle the email_override and the BCC-the-user bit here?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ping?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the extremely late reply. I think these options can be handled by moving their logic to the beginning of the add_options method. Then they will be printed out to the email file or passed to sendmail.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But you aren't actually writing it to the file... env_to isn't in the object you write out.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, keeping py2 compat for now. BUt also as mentioned I think we should only add that Bcc header to the message when we're saving it as a proxy for a proper envelope header.

smtp.login(self.user, self.password)

smtp.sendmail(self.mail, env_to, msg.as_string())
smtp.quit()
Expand Down
7 changes: 6 additions & 1 deletion pius
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ def main():
' into the default keyring. Ignored if -r is not'
' specified, or if it\'s the same as the default'
' keyring.')
parser.add_option('-L', '--save-to-mail-dir', dest='local_mail_dir',
metavar='FILE',
help='Instead of calling SMTP, save'
' the email to this directory.')
parser.add_option('-m', '--mail', dest='mail', metavar='EMAIL', nargs=1,
type='email',
help='Email the encrypted, signed keys to the'
Expand Down Expand Up @@ -238,7 +242,8 @@ def main():
options.mail_no_pgp_mime,
options.mail_override,
options.mail_text,
options.tmp_dir
options.tmp_dir,
options.local_mail_dir
)
else:
mailer = None
Expand Down