~sircmpwn/aerc

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch

[PATCH v2] Sign outgoing emails

Details
Message ID
<20201026160127.13808-1-ecs@d2evs.net>
DKIM signature
pass
Download raw message
Patch: +91 -10
---
v1 -> v2: update go-pgpmail to get the new Sign interface (thanks,
  emersion!) which fixes multipart emails and makes the code a lot
  cleaner. Don't sign emails by default if we have a matching PGP key.
  Add documentation.

The current UX is a :send flag with a config option to override it. If
you'd prefer something else, Drew, let me know and I'll send a v3.
 commands/compose/send.go | 85 +++++++++++++++++++++++++++++++++++++---
 config/config.go         |  1 +
 doc/aerc-config.5.scd    |  7 ++++
 go.mod                   |  4 +-
 go.sum                   |  4 +-
 5 files changed, 91 insertions(+), 10 deletions(-)

diff --git a/commands/compose/send.go b/commands/compose/send.go
index abbcb54..0f29aab 100644
--- a/commands/compose/send.go
+++ b/commands/compose/send.go
@@ -10,17 +10,20 @@ import (
	"strings"
	"time"

	"git.sr.ht/~sircmpwn/getopt"
	"github.com/emersion/go-pgpmail"
	"github.com/emersion/go-sasl"
	"github.com/emersion/go-smtp"
	"github.com/google/shlex"
	"github.com/miolini/datacounter"
	"github.com/pkg/errors"
	"golang.org/x/crypto/openpgp"
	"golang.org/x/oauth2"

	"git.sr.ht/~sircmpwn/aerc/lib"
	"git.sr.ht/~sircmpwn/aerc/models"
	"git.sr.ht/~sircmpwn/aerc/widgets"
	"git.sr.ht/~sircmpwn/aerc/worker/types"
	"golang.org/x/oauth2"
)

type Send struct{}
@@ -38,12 +41,27 @@ func (Send) Complete(aerc *widgets.Aerc, args []string) []string {
}

func (Send) Execute(aerc *widgets.Aerc, args []string) error {
	if len(args) > 1 {
		return errors.New("Usage: send")
	}
	composer, _ := aerc.SelectedTab().(*widgets.Composer)
	config := composer.Config()

	opts, optind, err := getopt.Getopts(args, "s")
	if err != nil {
		return err
	}

	sign := config.Sign

	for _, opt := range opts {
		switch opt.Option {
		case 's':
			sign = true
		}
	}

	if len(args) != optind {
		return errors.New("Usage: send [-s]")
	}

	if config.Outgoing == "" {
		return errors.New(
			"No outgoing mail transport configured for this account")
@@ -84,6 +102,39 @@ func (Send) Execute(aerc *widgets.Aerc, args []string) error {
		return errors.Wrap(err, "ParseAddress(config.From)")
	}

	// TODO: display prompt on more than one signing key
	var entity *openpgp.Entity
	if sign {
		for _, e := range lib.Keyring {
			found := false
			for _, i := range e.Identities {
				if i.UserId.Email == from.Address {
					found = true
					break
				}
			}
			if !found {
				continue
			}
			sk, _ := e.SigningKey(time.Now())
			if sk.PrivateKey == nil {
				continue
			}
			if sk.PrivateKey.Encrypted {
				_, err = aerc.DecryptKeys([]openpgp.Key{sk}, false)
				if err != nil {
					return err
				}
			}
			entity = e
			break
		}
	}

	if entity == nil && sign == true {
		return errors.New("No signing key")
	}

	var (
		saslClient sasl.Client
		conn       *smtp.Client
@@ -211,7 +262,18 @@ func (Send) Execute(aerc *widgets.Aerc, args []string) error {
			return 0, errors.Wrap(err, "conn.Data")
		}
		defer wc.Close()
		ctr := datacounter.NewWriterCounter(wc)
		var ctr *datacounter.WriterCounter
		if sign {
			clear, err := pgpmail.Sign(wc, header.Header.Header,
				entity, nil)
			if err != nil {
				return 0, err
			}
			defer clear.Close()
			ctr = datacounter.NewWriterCounter(clear)
		} else {
			ctr = datacounter.NewWriterCounter(wc)
		}
		composer.WriteMessage(header, ctr)
		return int(ctr.Count()), nil
	}
@@ -235,7 +297,18 @@ func (Send) Execute(aerc *widgets.Aerc, args []string) error {
		if err != nil {
			return 0, errors.Wrap(err, "cmd.Start")
		}
		ctr := datacounter.NewWriterCounter(wc)
		var ctr *datacounter.WriterCounter
		if sign {
			clear, err := pgpmail.Sign(wc, header.Header.Header,
				entity, nil)
			if err != nil {
				return 0, err
			}
			defer clear.Close()
			ctr = datacounter.NewWriterCounter(clear)
		} else {
			ctr = datacounter.NewWriterCounter(wc)
		}
		composer.WriteMessage(header, ctr)
		wc.Close() // force close to make sendmail send
		err = cmd.Wait()
diff --git a/config/config.go b/config/config.go
index 3ae26c1..0a1bf9f 100644
--- a/config/config.go
+++ b/config/config.go
@@ -87,6 +87,7 @@ type AccountConfig struct {
	SignatureFile   string
	SignatureCmd    string
	FoldersSort     []string `ini:"folders-sort" delim:","`
	Sign            bool `ini:"sign"`
}

type BindingConfig struct {
diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd
index c96ea12..0782f51 100644
--- a/doc/aerc-config.5.scd
+++ b/doc/aerc-config.5.scd
@@ -437,6 +437,13 @@ Note that many of these configuration options are written for you, such as

	Default: Drafts

; TODO: document PGP setup
*sign*
	Specifies whether to sign outgoing emails be default. There must be a
	signing key whose email is this address's *from* field in the keyring.

	Default: false

*source*
	Specifies the source for reading incoming emails on this account. This key
	is required for all accounts. It should be a connection string, and the
diff --git a/go.mod b/go.mod
index 380b7a1..811515f 100644
--- a/go.mod
+++ b/go.mod
@@ -12,7 +12,7 @@ require (
	github.com/emersion/go-imap-sortthread v1.1.1-0.20201009054724-d020d96306b3
	github.com/emersion/go-maildir v0.2.0
	github.com/emersion/go-message v0.12.1-0.20200824204225-9094bd0b8bc0
	github.com/emersion/go-pgpmail v0.0.0-20200303213726-db035a3a4139
	github.com/emersion/go-pgpmail v0.0.0-20201026150356-2b3d55a5a61f
	github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21
	github.com/emersion/go-smtp v0.12.1
	github.com/fsnotify/fsnotify v1.4.7
@@ -35,7 +35,7 @@ require (
	github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 // indirect
	github.com/stretchr/testify v1.3.0
	github.com/zenhack/go.notmuch v0.0.0-20190821052706-5a1961965cfb
	golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073
	golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
	golang.org/x/net v0.0.0-20200301022130-244492dfa37a // indirect
	golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
	golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 // indirect
diff --git a/go.sum b/go.sum
index f4f418f..7f43695 100644
--- a/go.sum
+++ b/go.sum
@@ -28,8 +28,8 @@ github.com/emersion/go-message v0.10.4-0.20190609165112-592ace5bc1ca/go.mod h1:3
github.com/emersion/go-message v0.11.1/go.mod h1:C4jnca5HOTo4bGN9YdqNQM9sITuT3Y0K6bSUw9RklvY=
github.com/emersion/go-message v0.12.1-0.20200824204225-9094bd0b8bc0 h1:G2VV/Wp2opDvR0ecue3UY/IX1/8OlTmMKKi+ENe1nG0=
github.com/emersion/go-message v0.12.1-0.20200824204225-9094bd0b8bc0/go.mod h1:C4jnca5HOTo4bGN9YdqNQM9sITuT3Y0K6bSUw9RklvY=
github.com/emersion/go-pgpmail v0.0.0-20200303213726-db035a3a4139 h1:JTUbkRuQFtDrl5KHWR2jrh9SUeSDEEEjUcHJkXdAE2Q=
github.com/emersion/go-pgpmail v0.0.0-20200303213726-db035a3a4139/go.mod h1:+Ovy1VQCUKPdjWkOiWvFoiFaWXkqn1PA793VvfEYWQU=
github.com/emersion/go-pgpmail v0.0.0-20201026150356-2b3d55a5a61f h1:oTVNNAVIrcCrcwJC1xvTvD5WmonUBvSPFDHg4hkCi/w=
github.com/emersion/go-pgpmail v0.0.0-20201026150356-2b3d55a5a61f/go.mod h1:9Sy6uI+dlTN56tcWMtBQHqNDeea27xYItaiZ/3XC76g=
github.com/emersion/go-sasl v0.0.0-20190520160400-47d427600317/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
github.com/emersion/go-sasl v0.0.0-20190817083125-240c8404624e/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b h1:uhWtEWBHgop1rqEk2klKaxPAkVDCXexai6hSuRQ7Nvs=
-- 
2.29.1
Reply to thread Export thread (mbox)