Initial support for PGP decryption & signatures
This commit is contained in:
parent
89f1684ea4
commit
f3158b36f1
17 changed files with 523 additions and 82 deletions
4
aerc.go
4
aerc.go
|
@ -166,6 +166,10 @@ func main() {
|
||||||
ui.EnableMouse()
|
ui.EnableMouse()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.Println("Initializing PGP keyring")
|
||||||
|
lib.InitKeyring()
|
||||||
|
defer lib.UnlockKeyring()
|
||||||
|
|
||||||
logger.Println("Starting Unix server")
|
logger.Println("Starting Unix server")
|
||||||
as, err := lib.StartServer(logger)
|
as, err := lib.StartServer(logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package account
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"git.sr.ht/~sircmpwn/aerc/lib"
|
||||||
"git.sr.ht/~sircmpwn/aerc/widgets"
|
"git.sr.ht/~sircmpwn/aerc/widgets"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,7 +38,10 @@ func (ViewMessage) Execute(aerc *widgets.Aerc, args []string) error {
|
||||||
if deleted {
|
if deleted {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
viewer := widgets.NewMessageViewer(acct, aerc.Config(), store, msg)
|
lib.NewMessageStoreView(msg, store, aerc.DecryptKeys,
|
||||||
aerc.NewTab(viewer, msg.Envelope.Subject)
|
func(view lib.MessageView) {
|
||||||
|
viewer := widgets.NewMessageViewer(acct, aerc.Config(), view)
|
||||||
|
aerc.NewTab(viewer, msg.Envelope.Subject)
|
||||||
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,8 +68,11 @@ func (Delete) Execute(aerc *widgets.Aerc, args []string) error {
|
||||||
acct.Messages().Scroll()
|
acct.Messages().Scroll()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
nextMv := widgets.NewMessageViewer(acct, aerc.Config(), store, next)
|
lib.NewMessageStoreView(next, store, aerc.DecryptKeys,
|
||||||
aerc.ReplaceTab(mv, nextMv, next.Envelope.Subject)
|
func(view lib.MessageView) {
|
||||||
|
nextMv := widgets.NewMessageViewer(acct, aerc.Config(), view)
|
||||||
|
aerc.ReplaceTab(mv, nextMv, next.Envelope.Subject)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
acct.Messages().Scroll()
|
acct.Messages().Scroll()
|
||||||
|
|
|
@ -2,6 +2,7 @@ package msgview
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.sr.ht/~sircmpwn/aerc/commands/account"
|
"git.sr.ht/~sircmpwn/aerc/commands/account"
|
||||||
|
"git.sr.ht/~sircmpwn/aerc/lib"
|
||||||
"git.sr.ht/~sircmpwn/aerc/widgets"
|
"git.sr.ht/~sircmpwn/aerc/widgets"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,7 +37,10 @@ func (NextPrevMsg) Execute(aerc *widgets.Aerc, args []string) error {
|
||||||
aerc.RemoveTab(mv)
|
aerc.RemoveTab(mv)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
nextMv := widgets.NewMessageViewer(acct, aerc.Config(), store, nextMsg)
|
lib.NewMessageStoreView(nextMsg, store, aerc.DecryptKeys,
|
||||||
aerc.ReplaceTab(mv, nextMv, nextMsg.Envelope.Subject)
|
func(view lib.MessageView) {
|
||||||
|
nextMv := widgets.NewMessageViewer(acct, aerc.Config(), view)
|
||||||
|
aerc.ReplaceTab(mv, nextMv, nextMsg.Envelope.Subject)
|
||||||
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
6
go.mod
6
go.mod
|
@ -9,8 +9,9 @@ require (
|
||||||
github.com/ddevault/go-libvterm v0.0.0-20190526194226-b7d861da3810
|
github.com/ddevault/go-libvterm v0.0.0-20190526194226-b7d861da3810
|
||||||
github.com/emersion/go-imap v1.0.4
|
github.com/emersion/go-imap v1.0.4
|
||||||
github.com/emersion/go-imap-idle v0.0.0-20190519112320-2704abd7050e
|
github.com/emersion/go-imap-idle v0.0.0-20190519112320-2704abd7050e
|
||||||
github.com/emersion/go-maildir v0.0.0-20191218233049-14e25d3ea720
|
github.com/emersion/go-maildir v0.2.0
|
||||||
github.com/emersion/go-message v0.11.1
|
github.com/emersion/go-message v0.11.1
|
||||||
|
github.com/emersion/go-pgpmail v0.0.0-20200303213726-db035a3a4139
|
||||||
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b
|
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b
|
||||||
github.com/emersion/go-smtp v0.12.1
|
github.com/emersion/go-smtp v0.12.1
|
||||||
github.com/fsnotify/fsnotify v1.4.7
|
github.com/fsnotify/fsnotify v1.4.7
|
||||||
|
@ -33,6 +34,7 @@ require (
|
||||||
github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 // indirect
|
github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 // indirect
|
||||||
github.com/stretchr/testify v1.3.0
|
github.com/stretchr/testify v1.3.0
|
||||||
github.com/zenhack/go.notmuch v0.0.0-20190821052706-5a1961965cfb
|
github.com/zenhack/go.notmuch v0.0.0-20190821052706-5a1961965cfb
|
||||||
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a // indirect
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 // indirect
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 // indirect
|
||||||
|
@ -41,4 +43,6 @@ require (
|
||||||
gopkg.in/yaml.v2 v2.2.8 // indirect
|
gopkg.in/yaml.v2 v2.2.8 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
replace golang.org/x/crypto => github.com/ProtonMail/crypto v1.0.1-0.20191122234321-e77a1f03baa0
|
||||||
|
|
||||||
replace github.com/gdamore/tcell => git.sr.ht/~sircmpwn/tcell v0.0.0-20190807054800-3fdb6bc01a50
|
replace github.com/gdamore/tcell => git.sr.ht/~sircmpwn/tcell v0.0.0-20190807054800-3fdb6bc01a50
|
||||||
|
|
34
go.sum
34
go.sum
|
@ -3,15 +3,14 @@ git.sr.ht/~sircmpwn/getopt v0.0.0-20190808004552-daaf1274538b h1:da5JBQ6dcW14aWn
|
||||||
git.sr.ht/~sircmpwn/getopt v0.0.0-20190808004552-daaf1274538b/go.mod h1:wMEGFFFNuPos7vHmWXfszqImLppbc0wEhh6JBfJIUgw=
|
git.sr.ht/~sircmpwn/getopt v0.0.0-20190808004552-daaf1274538b/go.mod h1:wMEGFFFNuPos7vHmWXfszqImLppbc0wEhh6JBfJIUgw=
|
||||||
git.sr.ht/~sircmpwn/tcell v0.0.0-20190807054800-3fdb6bc01a50 h1:GEZXdK3vfsEGlRwlybiAvOnYLA4YKaVWxAQSn/BSkNw=
|
git.sr.ht/~sircmpwn/tcell v0.0.0-20190807054800-3fdb6bc01a50 h1:GEZXdK3vfsEGlRwlybiAvOnYLA4YKaVWxAQSn/BSkNw=
|
||||||
git.sr.ht/~sircmpwn/tcell v0.0.0-20190807054800-3fdb6bc01a50/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
|
git.sr.ht/~sircmpwn/tcell v0.0.0-20190807054800-3fdb6bc01a50/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFDnH08=
|
|
||||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||||
|
github.com/ProtonMail/crypto v1.0.1-0.20191122234321-e77a1f03baa0 h1:HNSciyt/mdq/xCqb5HSYimGr6Djb5rC8694BJqX2xOo=
|
||||||
|
github.com/ProtonMail/crypto v1.0.1-0.20191122234321-e77a1f03baa0/go.mod h1:MBriIAodHvZ+YvwvMJWCTmseW/LkeVRPWp/iZKvee4g=
|
||||||
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
|
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
|
||||||
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
|
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/ddevault/go-libvterm v0.0.0-20190526194226-b7d861da3810 h1:VlHKuIrEvuGlED53TkovT4AVUjrqTyeCt3wiqw1OsFc=
|
github.com/ddevault/go-libvterm v0.0.0-20190526194226-b7d861da3810 h1:VlHKuIrEvuGlED53TkovT4AVUjrqTyeCt3wiqw1OsFc=
|
||||||
github.com/ddevault/go-libvterm v0.0.0-20190526194226-b7d861da3810/go.mod h1:Ow1oE1Hr4xE7eWY2/Ih2kbcOyyXDH7G0XKv/I4yiCYs=
|
github.com/ddevault/go-libvterm v0.0.0-20190526194226-b7d861da3810/go.mod h1:Ow1oE1Hr4xE7eWY2/Ih2kbcOyyXDH7G0XKv/I4yiCYs=
|
||||||
|
@ -19,11 +18,12 @@ github.com/emersion/go-imap v1.0.4 h1:uiCAIHM6Z5Jwkma1zdNDWWXxSCqb+/xHBkHflD7XBr
|
||||||
github.com/emersion/go-imap v1.0.4/go.mod h1:yKASt+C3ZiDAiCSssxg9caIckWF/JG7ZQTO7GAmvicU=
|
github.com/emersion/go-imap v1.0.4/go.mod h1:yKASt+C3ZiDAiCSssxg9caIckWF/JG7ZQTO7GAmvicU=
|
||||||
github.com/emersion/go-imap-idle v0.0.0-20190519112320-2704abd7050e h1:L7bswVJZcf2YHofgom49oFRwVqmBj/qZqDy9/SJpZMY=
|
github.com/emersion/go-imap-idle v0.0.0-20190519112320-2704abd7050e h1:L7bswVJZcf2YHofgom49oFRwVqmBj/qZqDy9/SJpZMY=
|
||||||
github.com/emersion/go-imap-idle v0.0.0-20190519112320-2704abd7050e/go.mod h1:o14zPKCmEH5WC1vU5SdPoZGgNvQx7zzKSnxPQlobo78=
|
github.com/emersion/go-imap-idle v0.0.0-20190519112320-2704abd7050e/go.mod h1:o14zPKCmEH5WC1vU5SdPoZGgNvQx7zzKSnxPQlobo78=
|
||||||
github.com/emersion/go-maildir v0.0.0-20191218233049-14e25d3ea720 h1:0Hj2cVnV1NunzYK6Y9fjTdpeqHClF+QNTyiCyhIJ/0E=
|
github.com/emersion/go-maildir v0.2.0 h1:fC4+UVGl8GcQGbFF7AWab2JMf4VbKz+bMNv07xxhzs8=
|
||||||
github.com/emersion/go-maildir v0.0.0-20191218233049-14e25d3ea720/go.mod h1:I2j27lND/SRLgxROe50Vam81MSaqPFvJ0OHNnDZ7n84=
|
github.com/emersion/go-maildir v0.2.0/go.mod h1:I2j27lND/SRLgxROe50Vam81MSaqPFvJ0OHNnDZ7n84=
|
||||||
github.com/emersion/go-message v0.11.1 h1:0C/S4JIXDTSfXB1vpqdimAYyK4+79fgEAMQ0dSL+Kac=
|
github.com/emersion/go-message v0.11.1 h1:0C/S4JIXDTSfXB1vpqdimAYyK4+79fgEAMQ0dSL+Kac=
|
||||||
github.com/emersion/go-message v0.11.1/go.mod h1:C4jnca5HOTo4bGN9YdqNQM9sITuT3Y0K6bSUw9RklvY=
|
github.com/emersion/go-message v0.11.1/go.mod h1:C4jnca5HOTo4bGN9YdqNQM9sITuT3Y0K6bSUw9RklvY=
|
||||||
github.com/emersion/go-sasl v0.0.0-20190817083125-240c8404624e h1:ba7YsgX5OV8FjGi5ZWml8Jng6oBrJAb3ahqWMJ5Ce8Q=
|
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-sasl v0.0.0-20190817083125-240c8404624e/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=
|
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b h1:uhWtEWBHgop1rqEk2klKaxPAkVDCXexai6hSuRQ7Nvs=
|
||||||
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
|
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
|
||||||
|
@ -39,21 +39,16 @@ github.com/go-ini/ini v1.52.0 h1:3UeUAveYUTCYV/G0jNDiIrrtIeAl1oAjshYyU2PaAlQ=
|
||||||
github.com/go-ini/ini v1.52.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
github.com/go-ini/ini v1.52.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
|
|
||||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/kyoh86/xdg v1.2.0 h1:CERuT/ShdTDj+A2UaX3hQ3mOV369+Sj+wyn2nIRIIkI=
|
github.com/kyoh86/xdg v1.2.0 h1:CERuT/ShdTDj+A2UaX3hQ3mOV369+Sj+wyn2nIRIIkI=
|
||||||
github.com/kyoh86/xdg v1.2.0/go.mod h1:/mg8zwu1+qe76oTFUBnyS7rJzk7LLC0VGEzJyJ19DHs=
|
github.com/kyoh86/xdg v1.2.0/go.mod h1:/mg8zwu1+qe76oTFUBnyS7rJzk7LLC0VGEzJyJ19DHs=
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.2 h1:mCMFu6PgSozg9tDNMMK3g18oJBX7oYGrC09mS6CXfO4=
|
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
|
@ -61,11 +56,9 @@ github.com/martinlindhe/base36 v1.0.0 h1:eYsumTah144C0A8P1T/AVSUk5ZoLnhfYFM3OGQx
|
||||||
github.com/martinlindhe/base36 v1.0.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8=
|
github.com/martinlindhe/base36 v1.0.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8=
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-pointer v0.0.0-20180825124634-49522c3f3791 h1:PfHMsLQJwoc0ccjK0sam6J0wQo4s8mOuAo2yQGw+T2U=
|
|
||||||
github.com/mattn/go-pointer v0.0.0-20180825124634-49522c3f3791/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
|
github.com/mattn/go-pointer v0.0.0-20180825124634-49522c3f3791/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
|
||||||
github.com/mattn/go-pointer v0.0.0-20190911064623-a0a44394634f h1:QTRRO+ozoYgT3CQRIzNVYJRU3DB8HRnkZv6mr4ISmMA=
|
github.com/mattn/go-pointer v0.0.0-20190911064623-a0a44394634f h1:QTRRO+ozoYgT3CQRIzNVYJRU3DB8HRnkZv6mr4ISmMA=
|
||||||
github.com/mattn/go-pointer v0.0.0-20190911064623-a0a44394634f/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
|
github.com/mattn/go-pointer v0.0.0-20190911064623-a0a44394634f/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
|
||||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
|
||||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
|
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
|
||||||
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
|
@ -75,27 +68,20 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/riywo/loginshell v0.0.0-20190610082906-2ed199a032f6 h1:0QWE8TiOGSB+korydW5z4hPQ5QBVqLos+M2ta4pHaY0=
|
github.com/riywo/loginshell v0.0.0-20190610082906-2ed199a032f6 h1:0QWE8TiOGSB+korydW5z4hPQ5QBVqLos+M2ta4pHaY0=
|
||||||
github.com/riywo/loginshell v0.0.0-20190610082906-2ed199a032f6/go.mod h1:/PfPXh0EntGc3QAAyUaviy4S9tzy4Zp0e2ilq4voC6E=
|
github.com/riywo/loginshell v0.0.0-20190610082906-2ed199a032f6/go.mod h1:/PfPXh0EntGc3QAAyUaviy4S9tzy4Zp0e2ilq4voC6E=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
|
|
||||||
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
||||||
github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 h1:N8Bg45zpk/UcpNGnfJt2y/3lRWASHNTUET8owPYCgYI=
|
|
||||||
github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/zenhack/go.notmuch v0.0.0-20190821052706-5a1961965cfb h1:eZBIw4TilXSAEYcWKf51bERhwH431YwntDYus0Bgxh0=
|
|
||||||
github.com/zenhack/go.notmuch v0.0.0-20190821052706-5a1961965cfb/go.mod h1:zJtFvR3NinVdmBiLyB4MyXKmqyVfZEb2cK97ISfTgV8=
|
github.com/zenhack/go.notmuch v0.0.0-20190821052706-5a1961965cfb/go.mod h1:zJtFvR3NinVdmBiLyB4MyXKmqyVfZEb2cK97ISfTgV8=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190523035834-f03afa92d3ff/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
@ -103,22 +89,18 @@ golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BG
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
|
||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
|
||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/ini.v1 v1.44.0 h1:YRJzTUp0kSYWUVFF5XAbDFfyiqwsl0Vb9R8TVP5eRi0=
|
|
||||||
gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
|
81
lib/keystore.go
Normal file
81
lib/keystore.go
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/kyoh86/xdg"
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
"golang.org/x/crypto/openpgp/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Keyring openpgp.EntityList
|
||||||
|
|
||||||
|
locked bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitKeyring() {
|
||||||
|
os.MkdirAll(path.Join(xdg.DataHome(), "aerc"), 0700)
|
||||||
|
|
||||||
|
lockpath := path.Join(xdg.DataHome(), "aerc", "keyring.lock")
|
||||||
|
lockfile, err := os.OpenFile(lockpath, os.O_CREATE|os.O_EXCL, 0600)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: Consider connecting to main process over IPC socket
|
||||||
|
locked = false
|
||||||
|
} else {
|
||||||
|
locked = true
|
||||||
|
lockfile.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
keypath := path.Join(xdg.DataHome(), "aerc", "keyring.asc")
|
||||||
|
keyfile, err := os.Open(keypath)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return
|
||||||
|
} else if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer keyfile.Close()
|
||||||
|
|
||||||
|
Keyring, err = openpgp.ReadKeyRing(keyfile)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnlockKeyring() {
|
||||||
|
if !locked {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lockpath := path.Join(xdg.DataHome(), "aerc", "keyring.lock")
|
||||||
|
os.Remove(lockpath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ImportKeys(r io.Reader) error {
|
||||||
|
keys, err := openpgp.ReadKeyRing(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
Keyring = append(Keyring, keys...)
|
||||||
|
if locked {
|
||||||
|
keypath := path.Join(xdg.DataHome(), "aerc", "keyring.asc")
|
||||||
|
keyfile, err := os.OpenFile(keypath, os.O_CREATE|os.O_APPEND, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer keyfile.Close()
|
||||||
|
|
||||||
|
for _, key := range keys {
|
||||||
|
if key.PrivateKey != nil {
|
||||||
|
err = key.SerializePrivate(keyfile, &packet.Config{})
|
||||||
|
} else {
|
||||||
|
err = key.Serialize(keyfile)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
128
lib/messageview.go
Normal file
128
lib/messageview.go
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/emersion/go-message"
|
||||||
|
_ "github.com/emersion/go-message/charset"
|
||||||
|
"github.com/emersion/go-pgpmail"
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
|
||||||
|
"git.sr.ht/~sircmpwn/aerc/models"
|
||||||
|
"git.sr.ht/~sircmpwn/aerc/worker/lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is an abstraction for viewing a message with semi-transparent PGP
|
||||||
|
// support.
|
||||||
|
type MessageView interface {
|
||||||
|
// Returns the MessageInfo for this message
|
||||||
|
MessageInfo() *models.MessageInfo
|
||||||
|
|
||||||
|
// Returns the BodyStructure for this message
|
||||||
|
BodyStructure() *models.BodyStructure
|
||||||
|
|
||||||
|
// Returns the message store that this message was originally sourced from
|
||||||
|
Store() *MessageStore
|
||||||
|
|
||||||
|
// Fetches a specific body part for this message
|
||||||
|
FetchBodyPart(parent *models.BodyStructure,
|
||||||
|
part []int, cb func(io.Reader))
|
||||||
|
|
||||||
|
PGPDetails() *openpgp.MessageDetails
|
||||||
|
}
|
||||||
|
|
||||||
|
func usePGP(info *models.BodyStructure) bool {
|
||||||
|
if info.MIMEType == "application" {
|
||||||
|
if info.MIMESubType == "pgp-encrypted" ||
|
||||||
|
info.MIMESubType == "pgp-signature" {
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, part := range info.Parts {
|
||||||
|
if usePGP(part) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type MessageStoreView struct {
|
||||||
|
messageInfo *models.MessageInfo
|
||||||
|
messageStore *MessageStore
|
||||||
|
message []byte
|
||||||
|
details *openpgp.MessageDetails
|
||||||
|
bodyStructure *models.BodyStructure
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMessageStoreView(messageInfo *models.MessageInfo,
|
||||||
|
store *MessageStore, decryptKeys openpgp.PromptFunction,
|
||||||
|
cb func(MessageView)) {
|
||||||
|
|
||||||
|
msv := &MessageStoreView{messageInfo, store,
|
||||||
|
nil, nil, messageInfo.BodyStructure}
|
||||||
|
|
||||||
|
if usePGP(messageInfo.BodyStructure) {
|
||||||
|
store.FetchFull([]uint32{messageInfo.Uid}, func(reader io.Reader) {
|
||||||
|
pgpReader, err := pgpmail.Read(reader, Keyring, decryptKeys, nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
msv.message, err = ioutil.ReadAll(pgpReader.MessageDetails.UnverifiedBody)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
decrypted, err := message.Read(bytes.NewBuffer(msv.message))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
bs, err := lib.ParseEntityStructure(decrypted)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
msv.bodyStructure = bs
|
||||||
|
msv.details = pgpReader.MessageDetails
|
||||||
|
cb(msv)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
cb(msv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msv *MessageStoreView) MessageInfo() *models.MessageInfo {
|
||||||
|
return msv.messageInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msv *MessageStoreView) BodyStructure() *models.BodyStructure {
|
||||||
|
return msv.bodyStructure
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msv *MessageStoreView) Store() *MessageStore {
|
||||||
|
return msv.messageStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msv *MessageStoreView) PGPDetails() *openpgp.MessageDetails {
|
||||||
|
return msv.details
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msv *MessageStoreView) FetchBodyPart(parent *models.BodyStructure,
|
||||||
|
part []int, cb func(io.Reader)) {
|
||||||
|
|
||||||
|
if msv.message == nil {
|
||||||
|
msv.messageStore.FetchBodyPart(msv.messageInfo.Uid, parent, part, cb)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(msv.message)
|
||||||
|
msg, err := message.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
reader, err := lib.FetchEntityPartReader(msg, part)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
cb(reader)
|
||||||
|
}
|
|
@ -16,6 +16,10 @@ type Drawable interface {
|
||||||
Invalidate()
|
Invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RootDrawable interface {
|
||||||
|
Initialize(ui *UI)
|
||||||
|
}
|
||||||
|
|
||||||
type Interactive interface {
|
type Interactive interface {
|
||||||
// Returns true if the event was handled by this component
|
// Returns true if the event was handled by this component
|
||||||
Event(event tcell.Event) bool
|
Event(event tcell.Event) bool
|
||||||
|
|
|
@ -55,6 +55,10 @@ func Initialize(content DrawableInteractiveBeeper) (*UI, error) {
|
||||||
content.OnBeep(screen.Beep)
|
content.OnBeep(screen.Beep)
|
||||||
content.Focus(true)
|
content.Focus(true)
|
||||||
|
|
||||||
|
if root, ok := content.(RootDrawable); ok {
|
||||||
|
root.Initialize(&state)
|
||||||
|
}
|
||||||
|
|
||||||
return &state, nil
|
return &state, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package widgets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -10,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"github.com/google/shlex"
|
"github.com/google/shlex"
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
|
||||||
"git.sr.ht/~sircmpwn/aerc/config"
|
"git.sr.ht/~sircmpwn/aerc/config"
|
||||||
"git.sr.ht/~sircmpwn/aerc/lib"
|
"git.sr.ht/~sircmpwn/aerc/lib"
|
||||||
|
@ -32,7 +34,9 @@ type Aerc struct {
|
||||||
pendingKeys []config.KeyStroke
|
pendingKeys []config.KeyStroke
|
||||||
prompts *ui.Stack
|
prompts *ui.Stack
|
||||||
tabs *ui.Tabs
|
tabs *ui.Tabs
|
||||||
|
ui *ui.UI
|
||||||
beep func() error
|
beep func() error
|
||||||
|
getpasswd *GetPasswd
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAerc(conf *config.AercConfig, logger *log.Logger,
|
func NewAerc(conf *config.AercConfig, logger *log.Logger,
|
||||||
|
@ -160,6 +164,10 @@ func (aerc *Aerc) Focus(focus bool) {
|
||||||
|
|
||||||
func (aerc *Aerc) Draw(ctx *ui.Context) {
|
func (aerc *Aerc) Draw(ctx *ui.Context) {
|
||||||
aerc.grid.Draw(ctx)
|
aerc.grid.Draw(ctx)
|
||||||
|
if aerc.getpasswd != nil {
|
||||||
|
aerc.getpasswd.Draw(ctx.Subcontext(4, 4,
|
||||||
|
ctx.Width()-8, ctx.Height()-8))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (aerc *Aerc) getBindings() *config.KeyBindings {
|
func (aerc *Aerc) getBindings() *config.KeyBindings {
|
||||||
|
@ -198,6 +206,10 @@ func (aerc *Aerc) simulate(strokes []config.KeyStroke) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (aerc *Aerc) Event(event tcell.Event) bool {
|
func (aerc *Aerc) Event(event tcell.Event) bool {
|
||||||
|
if aerc.getpasswd != nil {
|
||||||
|
return aerc.getpasswd.Event(event)
|
||||||
|
}
|
||||||
|
|
||||||
if aerc.focused != nil {
|
if aerc.focused != nil {
|
||||||
return aerc.focused.Event(event)
|
return aerc.focused.Event(event)
|
||||||
}
|
}
|
||||||
|
@ -484,3 +496,39 @@ func (aerc *Aerc) CloseBackends() error {
|
||||||
}
|
}
|
||||||
return returnErr
|
return returnErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (aerc *Aerc) GetPassword(title string, prompt string, cb func(string)) {
|
||||||
|
aerc.getpasswd = NewGetPasswd(title, prompt, func(pw string) {
|
||||||
|
aerc.getpasswd = nil
|
||||||
|
aerc.Invalidate()
|
||||||
|
cb(pw)
|
||||||
|
})
|
||||||
|
aerc.getpasswd.OnInvalidate(func(_ ui.Drawable) {
|
||||||
|
aerc.Invalidate()
|
||||||
|
})
|
||||||
|
aerc.Invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aerc *Aerc) Initialize(ui *ui.UI) {
|
||||||
|
aerc.ui = ui
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aerc *Aerc) DecryptKeys(keys []openpgp.Key, symmetric bool) ([]byte, error) {
|
||||||
|
// HACK HACK HACK
|
||||||
|
for _, key := range keys {
|
||||||
|
var ident *openpgp.Identity
|
||||||
|
for _, ident = range key.Entity.Identities {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
aerc.GetPassword("Decrypt PGP private key",
|
||||||
|
fmt.Sprintf("Enter password for %s (%8X)",
|
||||||
|
ident.Name, key.PublicKey.KeyId),
|
||||||
|
func(pass string) {
|
||||||
|
key.PrivateKey.Decrypt([]byte(pass))
|
||||||
|
})
|
||||||
|
for aerc.getpasswd != nil {
|
||||||
|
aerc.ui.Tick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
61
widgets/getpasswd.go
Normal file
61
widgets/getpasswd.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package widgets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
|
||||||
|
"git.sr.ht/~sircmpwn/aerc/lib/ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GetPasswd struct {
|
||||||
|
ui.Invalidatable
|
||||||
|
callback func(string)
|
||||||
|
title string
|
||||||
|
prompt string
|
||||||
|
input *ui.TextInput
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGetPasswd(title string, prompt string, cb func(string)) *GetPasswd {
|
||||||
|
getpasswd := &GetPasswd{
|
||||||
|
callback: cb,
|
||||||
|
title: title,
|
||||||
|
prompt: prompt,
|
||||||
|
input: ui.NewTextInput("").Password(true).Prompt("Password: "),
|
||||||
|
}
|
||||||
|
getpasswd.input.OnInvalidate(func(_ ui.Drawable) {
|
||||||
|
getpasswd.Invalidate()
|
||||||
|
})
|
||||||
|
getpasswd.input.Focus(true)
|
||||||
|
return getpasswd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gp *GetPasswd) Draw(ctx *ui.Context) {
|
||||||
|
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
|
||||||
|
ctx.Fill(0, 0, ctx.Width(), 1, ' ', tcell.StyleDefault.Reverse(true))
|
||||||
|
ctx.Printf(1, 0, tcell.StyleDefault.Reverse(true), "%s", gp.title)
|
||||||
|
ctx.Printf(1, 1, tcell.StyleDefault, gp.prompt)
|
||||||
|
gp.input.Draw(ctx.Subcontext(1, 3, ctx.Width()-2, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gp *GetPasswd) Invalidate() {
|
||||||
|
gp.DoInvalidate(gp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gp *GetPasswd) Event(event tcell.Event) bool {
|
||||||
|
switch event := event.(type) {
|
||||||
|
case *tcell.EventKey:
|
||||||
|
switch event.Key() {
|
||||||
|
case tcell.KeyEnter:
|
||||||
|
gp.input.Focus(false)
|
||||||
|
gp.callback(gp.input.String())
|
||||||
|
default:
|
||||||
|
gp.input.Event(event)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
gp.input.Event(event)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gp *GetPasswd) Focus(f bool) {
|
||||||
|
// Who cares
|
||||||
|
}
|
|
@ -31,7 +31,7 @@ func (filter HeaderLayoutFilter) forMessage(msg *models.MessageInfo) HeaderLayou
|
||||||
// grid builds a ui grid, populating each cell by calling a callback function
|
// grid builds a ui grid, populating each cell by calling a callback function
|
||||||
// with the current header string.
|
// with the current header string.
|
||||||
func (layout HeaderLayout) grid(cb func(string) ui.Drawable) (grid *ui.Grid, height int) {
|
func (layout HeaderLayout) grid(cb func(string) ui.Drawable) (grid *ui.Grid, height int) {
|
||||||
rowCount := len(layout) + 1 // extra row for spacer
|
rowCount := len(layout)
|
||||||
grid = ui.MakeGrid(rowCount, 1, ui.SIZE_EXACT, ui.SIZE_WEIGHT)
|
grid = ui.MakeGrid(rowCount, 1, ui.SIZE_EXACT, ui.SIZE_WEIGHT)
|
||||||
for i, cols := range layout {
|
for i, cols := range layout {
|
||||||
r := ui.MakeGrid(1, len(cols), ui.SIZE_EXACT, ui.SIZE_WEIGHT)
|
r := ui.MakeGrid(1, len(cols), ui.SIZE_EXACT, ui.SIZE_WEIGHT)
|
||||||
|
@ -40,6 +40,5 @@ func (layout HeaderLayout) grid(cb func(string) ui.Drawable) (grid *ui.Grid, hei
|
||||||
}
|
}
|
||||||
grid.AddChild(r).At(i, 0)
|
grid.AddChild(r).At(i, 0)
|
||||||
}
|
}
|
||||||
grid.AddChild(ui.NewFill(' ')).At(rowCount-1, 0)
|
|
||||||
return grid, rowCount
|
return grid, rowCount
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,8 +165,11 @@ func (ml *MessageList) MouseEvent(localX int, localY int, event tcell.Event) {
|
||||||
if msg == nil {
|
if msg == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
viewer := NewMessageViewer(acct, ml.aerc.Config(), store, msg)
|
lib.NewMessageStoreView(msg, store, ml.aerc.DecryptKeys,
|
||||||
ml.aerc.NewTab(viewer, msg.Envelope.Subject)
|
func(view lib.MessageView) {
|
||||||
|
viewer := NewMessageViewer(acct, ml.aerc.Config(), view)
|
||||||
|
ml.aerc.NewTab(viewer, msg.Envelope.Subject)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
case tcell.WheelDown:
|
case tcell.WheelDown:
|
||||||
if ml.store != nil {
|
if ml.store != nil {
|
||||||
|
|
|
@ -30,9 +30,8 @@ type MessageViewer struct {
|
||||||
conf *config.AercConfig
|
conf *config.AercConfig
|
||||||
err error
|
err error
|
||||||
grid *ui.Grid
|
grid *ui.Grid
|
||||||
msg *models.MessageInfo
|
|
||||||
switcher *PartSwitcher
|
switcher *PartSwitcher
|
||||||
store *lib.MessageStore
|
msg lib.MessageView
|
||||||
}
|
}
|
||||||
|
|
||||||
type PartSwitcher struct {
|
type PartSwitcher struct {
|
||||||
|
@ -46,8 +45,8 @@ type PartSwitcher struct {
|
||||||
mv *MessageViewer
|
mv *MessageViewer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMessageViewer(acct *AccountView, conf *config.AercConfig,
|
func NewMessageViewer(acct *AccountView,
|
||||||
store *lib.MessageStore, msg *models.MessageInfo) *MessageViewer {
|
conf *config.AercConfig, msg lib.MessageView) *MessageViewer {
|
||||||
|
|
||||||
hf := HeaderLayoutFilter{
|
hf := HeaderLayoutFilter{
|
||||||
layout: HeaderLayout(conf.Viewer.HeaderLayout),
|
layout: HeaderLayout(conf.Viewer.HeaderLayout),
|
||||||
|
@ -58,25 +57,40 @@ func NewMessageViewer(acct *AccountView, conf *config.AercConfig,
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
layout := hf.forMessage(msg)
|
layout := hf.forMessage(msg.MessageInfo())
|
||||||
header, headerHeight := layout.grid(
|
header, headerHeight := layout.grid(
|
||||||
func(header string) ui.Drawable {
|
func(header string) ui.Drawable {
|
||||||
return &HeaderView{
|
return &HeaderView{
|
||||||
Name: header,
|
Name: header,
|
||||||
Value: fmtHeader(msg, header, acct.UiConfig().TimestampFormat),
|
Value: fmtHeader(msg.MessageInfo(), header,
|
||||||
|
acct.UiConfig().TimestampFormat),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
grid := ui.NewGrid().Rows([]ui.GridSpec{
|
rows := []ui.GridSpec{
|
||||||
{ui.SIZE_EXACT, headerHeight},
|
{ui.SIZE_EXACT, headerHeight},
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.PGPDetails() != nil {
|
||||||
|
height := 1
|
||||||
|
if msg.PGPDetails().IsSigned && msg.PGPDetails().IsEncrypted {
|
||||||
|
height = 2
|
||||||
|
}
|
||||||
|
rows = append(rows, ui.GridSpec{ui.SIZE_EXACT, height})
|
||||||
|
}
|
||||||
|
|
||||||
|
rows = append(rows, []ui.GridSpec{
|
||||||
|
{ui.SIZE_EXACT, 1},
|
||||||
{ui.SIZE_WEIGHT, 1},
|
{ui.SIZE_WEIGHT, 1},
|
||||||
}).Columns([]ui.GridSpec{
|
}...)
|
||||||
|
|
||||||
|
grid := ui.NewGrid().Rows(rows).Columns([]ui.GridSpec{
|
||||||
{ui.SIZE_WEIGHT, 1},
|
{ui.SIZE_WEIGHT, 1},
|
||||||
})
|
})
|
||||||
|
|
||||||
switcher := &PartSwitcher{}
|
switcher := &PartSwitcher{}
|
||||||
err := createSwitcher(acct, switcher, conf, store, msg)
|
err := createSwitcher(acct, switcher, conf, msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &MessageViewer{
|
return &MessageViewer{
|
||||||
err: err,
|
err: err,
|
||||||
|
@ -86,14 +100,20 @@ func NewMessageViewer(acct *AccountView, conf *config.AercConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
grid.AddChild(header).At(0, 0)
|
grid.AddChild(header).At(0, 0)
|
||||||
grid.AddChild(switcher).At(1, 0)
|
if msg.PGPDetails() != nil {
|
||||||
|
grid.AddChild(NewPGPInfo(msg.PGPDetails())).At(1, 0)
|
||||||
|
grid.AddChild(ui.NewFill(' ')).At(2, 0)
|
||||||
|
grid.AddChild(switcher).At(3, 0)
|
||||||
|
} else {
|
||||||
|
grid.AddChild(ui.NewFill(' ')).At(1, 0)
|
||||||
|
grid.AddChild(switcher).At(2, 0)
|
||||||
|
}
|
||||||
|
|
||||||
mv := &MessageViewer{
|
mv := &MessageViewer{
|
||||||
acct: acct,
|
acct: acct,
|
||||||
conf: conf,
|
conf: conf,
|
||||||
grid: grid,
|
grid: grid,
|
||||||
msg: msg,
|
msg: msg,
|
||||||
store: store,
|
|
||||||
switcher: switcher,
|
switcher: switcher,
|
||||||
}
|
}
|
||||||
switcher.mv = mv
|
switcher.mv = mv
|
||||||
|
@ -122,8 +142,8 @@ func fmtHeader(msg *models.MessageInfo, header string, timefmt string) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func enumerateParts(acct *AccountView, conf *config.AercConfig, store *lib.MessageStore,
|
func enumerateParts(acct *AccountView, conf *config.AercConfig,
|
||||||
msg *models.MessageInfo, body *models.BodyStructure,
|
msg lib.MessageView, body *models.BodyStructure,
|
||||||
index []int) ([]*PartViewer, error) {
|
index []int) ([]*PartViewer, error) {
|
||||||
|
|
||||||
var parts []*PartViewer
|
var parts []*PartViewer
|
||||||
|
@ -134,14 +154,14 @@ func enumerateParts(acct *AccountView, conf *config.AercConfig, store *lib.Messa
|
||||||
pv := &PartViewer{part: part}
|
pv := &PartViewer{part: part}
|
||||||
parts = append(parts, pv)
|
parts = append(parts, pv)
|
||||||
subParts, err := enumerateParts(
|
subParts, err := enumerateParts(
|
||||||
acct, conf, store, msg, part, curindex)
|
acct, conf, msg, part, curindex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
parts = append(parts, subParts...)
|
parts = append(parts, subParts...)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
pv, err := NewPartViewer(acct, conf, store, msg, part, curindex)
|
pv, err := NewPartViewer(acct, conf, msg, part, curindex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -150,17 +170,17 @@ func enumerateParts(acct *AccountView, conf *config.AercConfig, store *lib.Messa
|
||||||
return parts, nil
|
return parts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSwitcher(acct *AccountView, switcher *PartSwitcher, conf *config.AercConfig,
|
func createSwitcher(acct *AccountView, switcher *PartSwitcher,
|
||||||
store *lib.MessageStore, msg *models.MessageInfo) error {
|
conf *config.AercConfig, msg lib.MessageView) error {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
switcher.selected = -1
|
switcher.selected = -1
|
||||||
switcher.showHeaders = conf.Viewer.ShowHeaders
|
switcher.showHeaders = conf.Viewer.ShowHeaders
|
||||||
switcher.alwaysShowMime = conf.Viewer.AlwaysShowMime
|
switcher.alwaysShowMime = conf.Viewer.AlwaysShowMime
|
||||||
|
|
||||||
if len(msg.BodyStructure.Parts) == 0 {
|
if len(msg.BodyStructure().Parts) == 0 {
|
||||||
switcher.selected = 0
|
switcher.selected = 0
|
||||||
pv, err := NewPartViewer(acct, conf, store, msg, msg.BodyStructure, []int{1})
|
pv, err := NewPartViewer(acct, conf, msg, msg.BodyStructure(), []int{1})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -169,8 +189,8 @@ func createSwitcher(acct *AccountView, switcher *PartSwitcher, conf *config.Aerc
|
||||||
switcher.Invalidate()
|
switcher.Invalidate()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
switcher.parts, err = enumerateParts(acct, conf, store,
|
switcher.parts, err = enumerateParts(acct, conf, msg,
|
||||||
msg, msg.BodyStructure, []int{})
|
msg.BodyStructure(), []int{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -228,7 +248,7 @@ func (mv *MessageViewer) OnInvalidate(fn func(d ui.Drawable)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mv *MessageViewer) Store() *lib.MessageStore {
|
func (mv *MessageViewer) Store() *lib.MessageStore {
|
||||||
return mv.store
|
return mv.msg.Store()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mv *MessageViewer) SelectedAccount() *AccountView {
|
func (mv *MessageViewer) SelectedAccount() *AccountView {
|
||||||
|
@ -239,7 +259,7 @@ func (mv *MessageViewer) SelectedMessage() (*models.MessageInfo, error) {
|
||||||
if mv.msg == nil {
|
if mv.msg == nil {
|
||||||
return nil, errors.New("no message selected")
|
return nil, errors.New("no message selected")
|
||||||
}
|
}
|
||||||
return mv.msg, nil
|
return mv.msg.MessageInfo(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mv *MessageViewer) MarkedMessages() ([]*models.MessageInfo, error) {
|
func (mv *MessageViewer) MarkedMessages() ([]*models.MessageInfo, error) {
|
||||||
|
@ -250,8 +270,7 @@ func (mv *MessageViewer) MarkedMessages() ([]*models.MessageInfo, error) {
|
||||||
func (mv *MessageViewer) ToggleHeaders() {
|
func (mv *MessageViewer) ToggleHeaders() {
|
||||||
switcher := mv.switcher
|
switcher := mv.switcher
|
||||||
mv.conf.Viewer.ShowHeaders = !mv.conf.Viewer.ShowHeaders
|
mv.conf.Viewer.ShowHeaders = !mv.conf.Viewer.ShowHeaders
|
||||||
err := createSwitcher(
|
err := createSwitcher(mv.acct, switcher, mv.conf, mv.msg)
|
||||||
mv.acct, switcher, mv.conf, mv.store, mv.msg)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mv.acct.Logger().Printf(
|
mv.acct.Logger().Printf(
|
||||||
"warning: error during create switcher - %v", err)
|
"warning: error during create switcher - %v", err)
|
||||||
|
@ -265,9 +284,9 @@ func (mv *MessageViewer) SelectedMessagePart() *PartInfo {
|
||||||
|
|
||||||
return &PartInfo{
|
return &PartInfo{
|
||||||
Index: part.index,
|
Index: part.index,
|
||||||
Msg: part.msg,
|
Msg: part.msg.MessageInfo(),
|
||||||
Part: part.part,
|
Part: part.part,
|
||||||
Store: part.store,
|
Store: mv.Store(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,22 +439,20 @@ type PartViewer struct {
|
||||||
fetched bool
|
fetched bool
|
||||||
filter *exec.Cmd
|
filter *exec.Cmd
|
||||||
index []int
|
index []int
|
||||||
msg *models.MessageInfo
|
msg lib.MessageView
|
||||||
pager *exec.Cmd
|
pager *exec.Cmd
|
||||||
pagerin io.WriteCloser
|
pagerin io.WriteCloser
|
||||||
part *models.BodyStructure
|
part *models.BodyStructure
|
||||||
showHeaders bool
|
showHeaders bool
|
||||||
sink io.WriteCloser
|
sink io.WriteCloser
|
||||||
source io.Reader
|
source io.Reader
|
||||||
store *lib.MessageStore
|
|
||||||
term *Terminal
|
term *Terminal
|
||||||
selecter *Selecter
|
selecter *Selecter
|
||||||
grid *ui.Grid
|
grid *ui.Grid
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPartViewer(acct *AccountView, conf *config.AercConfig,
|
func NewPartViewer(acct *AccountView, conf *config.AercConfig,
|
||||||
store *lib.MessageStore, msg *models.MessageInfo,
|
msg lib.MessageView, part *models.BodyStructure,
|
||||||
part *models.BodyStructure,
|
|
||||||
index []int) (*PartViewer, error) {
|
index []int) (*PartViewer, error) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -452,6 +469,7 @@ func NewPartViewer(acct *AccountView, conf *config.AercConfig,
|
||||||
|
|
||||||
pager = exec.Command(cmd[0], cmd[1:]...)
|
pager = exec.Command(cmd[0], cmd[1:]...)
|
||||||
|
|
||||||
|
info := msg.MessageInfo()
|
||||||
for _, f := range conf.Filters {
|
for _, f := range conf.Filters {
|
||||||
mime := strings.ToLower(part.MIMEType) +
|
mime := strings.ToLower(part.MIMEType) +
|
||||||
"/" + strings.ToLower(part.MIMESubType)
|
"/" + strings.ToLower(part.MIMESubType)
|
||||||
|
@ -464,13 +482,13 @@ func NewPartViewer(acct *AccountView, conf *config.AercConfig,
|
||||||
var header string
|
var header string
|
||||||
switch f.Header {
|
switch f.Header {
|
||||||
case "subject":
|
case "subject":
|
||||||
header = msg.Envelope.Subject
|
header = info.Envelope.Subject
|
||||||
case "from":
|
case "from":
|
||||||
header = models.FormatAddresses(msg.Envelope.From)
|
header = models.FormatAddresses(info.Envelope.From)
|
||||||
case "to":
|
case "to":
|
||||||
header = models.FormatAddresses(msg.Envelope.To)
|
header = models.FormatAddresses(info.Envelope.To)
|
||||||
case "cc":
|
case "cc":
|
||||||
header = models.FormatAddresses(msg.Envelope.Cc)
|
header = models.FormatAddresses(info.Envelope.Cc)
|
||||||
}
|
}
|
||||||
if f.Regex.Match([]byte(header)) {
|
if f.Regex.Match([]byte(header)) {
|
||||||
filter = exec.Command("sh", "-c", f.Command)
|
filter = exec.Command("sh", "-c", f.Command)
|
||||||
|
@ -521,7 +539,6 @@ func NewPartViewer(acct *AccountView, conf *config.AercConfig,
|
||||||
part: part,
|
part: part,
|
||||||
showHeaders: conf.Viewer.ShowHeaders,
|
showHeaders: conf.Viewer.ShowHeaders,
|
||||||
sink: pipe,
|
sink: pipe,
|
||||||
store: store,
|
|
||||||
term: term,
|
term: term,
|
||||||
selecter: selecter,
|
selecter: selecter,
|
||||||
grid: grid,
|
grid: grid,
|
||||||
|
@ -577,11 +594,12 @@ func (pv *PartViewer) attemptCopy() {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
if pv.showHeaders && pv.msg.RFC822Headers != nil {
|
info := pv.msg.MessageInfo()
|
||||||
|
if pv.showHeaders && info.RFC822Headers != nil {
|
||||||
// header need to bypass the filter, else we run into issues
|
// header need to bypass the filter, else we run into issues
|
||||||
// with the filter messing with newlines etc.
|
// with the filter messing with newlines etc.
|
||||||
// hence all writes in this block go directly to the pager
|
// hence all writes in this block go directly to the pager
|
||||||
fields := pv.msg.RFC822Headers.Fields()
|
fields := info.RFC822Headers.Fields()
|
||||||
for fields.Next() {
|
for fields.Next() {
|
||||||
var value string
|
var value string
|
||||||
var err error
|
var err error
|
||||||
|
@ -594,8 +612,8 @@ func (pv *PartViewer) attemptCopy() {
|
||||||
pv.pagerin.Write([]byte(field))
|
pv.pagerin.Write([]byte(field))
|
||||||
}
|
}
|
||||||
// virtual header
|
// virtual header
|
||||||
if len(pv.msg.Labels) != 0 {
|
if len(info.Labels) != 0 {
|
||||||
labels := fmtHeader(pv.msg, "Labels", "")
|
labels := fmtHeader(info, "Labels", "")
|
||||||
pv.pagerin.Write([]byte(fmt.Sprintf("Labels: %s\n", labels)))
|
pv.pagerin.Write([]byte(fmt.Sprintf("Labels: %s\n", labels)))
|
||||||
}
|
}
|
||||||
pv.pagerin.Write([]byte{'\n'})
|
pv.pagerin.Write([]byte{'\n'})
|
||||||
|
@ -635,7 +653,8 @@ func (pv *PartViewer) Draw(ctx *ui.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !pv.fetched {
|
if !pv.fetched {
|
||||||
pv.store.FetchBodyPart(pv.msg.Uid, pv.msg.BodyStructure, pv.index, pv.SetSource)
|
pv.msg.FetchBodyPart(pv.msg.BodyStructure(),
|
||||||
|
pv.index, pv.SetSource)
|
||||||
pv.fetched = true
|
pv.fetched = true
|
||||||
}
|
}
|
||||||
if pv.err != nil {
|
if pv.err != nil {
|
||||||
|
|
93
widgets/pgpinfo.go
Normal file
93
widgets/pgpinfo.go
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
package widgets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"git.sr.ht/~sircmpwn/aerc/lib/ui"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
pgperrors "golang.org/x/crypto/openpgp/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PGPInfo struct {
|
||||||
|
ui.Invalidatable
|
||||||
|
details *openpgp.MessageDetails
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPGPInfo(details *openpgp.MessageDetails) *PGPInfo {
|
||||||
|
return &PGPInfo{details: details}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PGPInfo) DrawSignature(ctx *ui.Context, offs bool) {
|
||||||
|
errorStyle := tcell.StyleDefault.Background(tcell.ColorRed).
|
||||||
|
Foreground(tcell.ColorWhite).Bold(true)
|
||||||
|
softErrorStyle := tcell.StyleDefault.Foreground(tcell.ColorYellow).
|
||||||
|
Reverse(true).Bold(true)
|
||||||
|
validStyle := tcell.StyleDefault.Foreground(tcell.ColorGreen).Bold(true)
|
||||||
|
header := "Signature "
|
||||||
|
if offs {
|
||||||
|
header += " "
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Nicer prompt for TOFU, fetch from keyserver, etc
|
||||||
|
if errors.Is(p.details.SignatureError, pgperrors.ErrUnknownIssuer) ||
|
||||||
|
p.details.SignedBy == nil {
|
||||||
|
|
||||||
|
x := ctx.Printf(0, 0, tcell.StyleDefault.Bold(true), "%s", header)
|
||||||
|
x += ctx.Printf(x, 0, softErrorStyle, " Unknown ")
|
||||||
|
x += ctx.Printf(x, 0, tcell.StyleDefault,
|
||||||
|
" Signed with unknown key (%8X); authenticity unknown",
|
||||||
|
p.details.SignedByKeyId)
|
||||||
|
} else if p.details.SignatureError != nil {
|
||||||
|
x := ctx.Printf(0, 0, tcell.StyleDefault.Bold(true), "%s", header)
|
||||||
|
x += ctx.Printf(x, 0, errorStyle, " ✗ Invalid! ")
|
||||||
|
x += ctx.Printf(x, 0, tcell.StyleDefault.
|
||||||
|
Foreground(tcell.ColorRed).Bold(true),
|
||||||
|
" This message may have been tampered with! (%s)",
|
||||||
|
p.details.SignatureError.Error())
|
||||||
|
} else {
|
||||||
|
entity := p.details.SignedBy.Entity
|
||||||
|
var ident *openpgp.Identity
|
||||||
|
// TODO: Pick identity more intelligently
|
||||||
|
for _, ident = range entity.Identities {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', validStyle)
|
||||||
|
x := ctx.Printf(0, 0, tcell.StyleDefault.Bold(true), "%s", header)
|
||||||
|
x += ctx.Printf(x, 0, validStyle, "✓ Signed ")
|
||||||
|
x += ctx.Printf(x, 0, tcell.StyleDefault,
|
||||||
|
"by %s (%8X)", ident.Name, p.details.SignedByKeyId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PGPInfo) DrawEncryption(ctx *ui.Context, y int) {
|
||||||
|
validStyle := tcell.StyleDefault.Foreground(tcell.ColorGreen).Bold(true)
|
||||||
|
entity := p.details.DecryptedWith.Entity
|
||||||
|
var ident *openpgp.Identity
|
||||||
|
// TODO: Pick identity more intelligently
|
||||||
|
for _, ident = range entity.Identities {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
x := ctx.Printf(0, y, tcell.StyleDefault.Bold(true), "Encryption ")
|
||||||
|
x += ctx.Printf(x, y, validStyle, "✓ Encrypted ")
|
||||||
|
x += ctx.Printf(x, y, tcell.StyleDefault,
|
||||||
|
"for %s (%8X) ", ident.Name, p.details.DecryptedWith.PublicKey.KeyId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PGPInfo) Draw(ctx *ui.Context) {
|
||||||
|
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
|
||||||
|
if p.details.IsSigned && p.details.IsEncrypted {
|
||||||
|
p.DrawSignature(ctx, true)
|
||||||
|
p.DrawEncryption(ctx, 1)
|
||||||
|
} else if p.details.IsSigned {
|
||||||
|
p.DrawSignature(ctx, false)
|
||||||
|
} else if p.details.IsEncrypted {
|
||||||
|
p.DrawEncryption(ctx, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PGPInfo) Invalidate() {
|
||||||
|
p.DoInvalidate(p)
|
||||||
|
}
|
|
@ -57,7 +57,7 @@ func splitMIME(m string) (string, string) {
|
||||||
return parts[0], parts[1]
|
return parts[0], parts[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseEntityStructure(e *message.Entity) (*models.BodyStructure, error) {
|
func ParseEntityStructure(e *message.Entity) (*models.BodyStructure, error) {
|
||||||
var body models.BodyStructure
|
var body models.BodyStructure
|
||||||
contentType, ctParams, err := e.Header.ContentType()
|
contentType, ctParams, err := e.Header.ContentType()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -86,7 +86,7 @@ func parseEntityStructure(e *message.Entity) (*models.BodyStructure, error) {
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ps, err := parseEntityStructure(part)
|
ps, err := ParseEntityStructure(part)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not parse child entity structure: %v", err)
|
return nil, fmt.Errorf("could not parse child entity structure: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -189,7 +189,7 @@ func MessageInfo(raw RawMessage) (*models.MessageInfo, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not read message: %v", err)
|
return nil, fmt.Errorf("could not read message: %v", err)
|
||||||
}
|
}
|
||||||
bs, err := parseEntityStructure(msg)
|
bs, err := ParseEntityStructure(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not get structure: %v", err)
|
return nil, fmt.Errorf("could not get structure: %v", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue