aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Jarry <robin@jarry.cc>2023-08-20 20:45:51 +0200
committerRobin Jarry <robin@jarry.cc>2023-08-26 21:41:05 +0200
commitc077d2bf0f5d1a3aa0f97f7942c3d6938c9c43ff (patch)
treee87a7e3bde30b275f6dd6ac1273f531dad3115a0
parentd620a75f347a88d152e2dc63014178fb9ca2bb73 (diff)
downloadaerc-c077d2bf0f5d1a3aa0f97f7942c3d6938c9c43ff.tar.gz
aerc-c077d2bf0f5d1a3aa0f97f7942c3d6938c9c43ff.zip
wizard: add protocol & transport fields
In preparation for other protocols, add a new "Protocol" field both in the source and outgoing sections. For now, there is only one source protocol and one outgoing protocol. Rename the "mode" fields to "transport". They will be reused later to include different authentication mechanisms. Signed-off-by: Robin Jarry <robin@jarry.cc> Reviewed-by: Tristan Partin <tristan@partin.io> Tested-by: Tim Culverhouse <tim@timculverhouse.com>
-rw-r--r--widgets/account-wizard.go229
-rw-r--r--widgets/selector.go12
2 files changed, 152 insertions, 89 deletions
diff --git a/widgets/account-wizard.go b/widgets/account-wizard.go
index ffbdb28b..b4af40b2 100644
--- a/widgets/account-wizard.go
+++ b/widgets/account-wizard.go
@@ -18,6 +18,7 @@ import (
"git.sr.ht/~rjarry/aerc/config"
"git.sr.ht/~rjarry/aerc/lib/ui"
+ "git.sr.ht/~rjarry/aerc/log"
)
const (
@@ -27,12 +28,6 @@ const (
CONFIGURE_COMPLETE = iota
)
-const (
- SSL_TLS = iota
- STARTTLS = iota
- INSECURE = iota
-)
-
type AccountWizard struct {
aerc *Aerc
step int
@@ -42,21 +37,26 @@ type AccountWizard struct {
// CONFIGURE_BASICS
accountName *ui.TextInput
email *ui.TextInput
+ discovered map[string]string
fullName *ui.TextInput
basics []ui.Interactive
// CONFIGURE_SOURCE
+ sourceProtocol *Selector
+ sourceTransport *Selector
+
sourceUsername *ui.TextInput
sourcePassword *ui.TextInput
sourceServer *ui.TextInput
- sourceMode int
sourceStr *ui.Text
sourceUrl url.URL
source []ui.Interactive
// CONFIGURE_OUTGOING
+ outgoingProtocol *Selector
+ outgoingTransport *Selector
+
outgoingUsername *ui.TextInput
outgoingPassword *ui.TextInput
outgoingServer *ui.TextInput
- outgoingMode int
outgoingStr *ui.Text
outgoingUrl url.URL
outgoingCopyTo *ui.TextInput
@@ -86,6 +86,22 @@ after the setup.
aerc.AddDialog(warning)
}
+const (
+ // protocols
+ IMAP = "IMAP"
+ SMTP = "SMTP"
+ // transports
+ SSL_TLS = "SSL/TLS"
+ STARTTLS = "STARTTLS"
+ INSECURE = "Insecure"
+)
+
+var (
+ sources = []string{IMAP}
+ outgoings = []string{SMTP}
+ transports = []string{SSL_TLS, STARTTLS, INSECURE}
+)
+
func NewAccountWizard(aerc *Aerc) *AccountWizard {
wizard := &AccountWizard{
accountName: ui.NewTextInput("", config.Ui).Prompt("> "),
@@ -102,6 +118,11 @@ func NewAccountWizard(aerc *Aerc) *AccountWizard {
outgoingStr: ui.NewText("Connection URL: smtps://", config.Ui.GetStyle(config.STYLE_DEFAULT)),
outgoingUsername: ui.NewTextInput("", config.Ui).Prompt("> "),
outgoingCopyTo: ui.NewTextInput("", config.Ui).Prompt("> "),
+
+ sourceProtocol: NewSelector(sources, 0, config.Ui).Chooser(true),
+ sourceTransport: NewSelector(transports, 0, config.Ui).Chooser(true),
+ outgoingProtocol: NewSelector(outgoings, 0, config.Ui).Chooser(true),
+ outgoingTransport: NewSelector(transports, 0, config.Ui).Chooser(true),
}
// Autofill some stuff for the user
@@ -116,6 +137,11 @@ func NewAccountWizard(aerc *Aerc) *AccountWizard {
wizard.sourceUri()
wizard.outgoingUri()
})
+ wizard.sourceProtocol.OnSelect(func(option string) {
+ wizard.sourceServer.Set("")
+ wizard.autofill()
+ wizard.sourceUri()
+ })
wizard.sourceServer.OnChange(func(_ *ui.TextInput) {
wizard.sourceUri()
})
@@ -137,6 +163,9 @@ func NewAccountWizard(aerc *Aerc) *AccountWizard {
wizard.outgoingUri()
}
})
+ wizard.sourceTransport.OnSelect(func(option string) {
+ wizard.sourceUri()
+ })
var once sync.Once
wizard.sourcePassword.OnChange(func(_ *ui.TextInput) {
wizard.outgoingPassword.Set(wizard.sourcePassword.String())
@@ -150,6 +179,11 @@ func NewAccountWizard(aerc *Aerc) *AccountWizard {
})
}
})
+ wizard.outgoingProtocol.OnSelect(func(option string) {
+ wizard.outgoingServer.Set("")
+ wizard.autofill()
+ wizard.outgoingUri()
+ })
wizard.outgoingServer.OnChange(func(_ *ui.TextInput) {
wizard.outgoingUri()
})
@@ -164,6 +198,9 @@ func NewAccountWizard(aerc *Aerc) *AccountWizard {
}
wizard.outgoingUri()
})
+ wizard.outgoingTransport.OnSelect(func(option string) {
+ wizard.outgoingUri()
+ })
basics := ui.NewGrid().Rows([]ui.GridSpec{
{Strategy: ui.SIZE_EXACT, Size: ui.Const(8)}, // Introduction
@@ -211,26 +248,10 @@ func NewAccountWizard(aerc *Aerc) *AccountWizard {
At(8, 0)
selector := NewSelector([]string{"Next"}, 0, config.Ui).
OnChoose(func(option string) {
- email := wizard.email.String()
- if strings.ContainsRune(email, '@') {
- server := email[strings.IndexRune(email, '@')+1:]
- hostport, srv := getSRV(server, []string{"imaps", "imap"})
- if hostport != "" {
- wizard.sourceServer.Set(hostport)
- if srv == "imaps" {
- wizard.sourceMode = SSL_TLS
- } else {
- wizard.sourceMode = STARTTLS
- }
- wizard.sourceUri()
- }
- hostport, _ = getSRV(server, []string{"submission"})
- if hostport != "" {
- wizard.outgoingServer.Set(hostport)
- wizard.outgoingMode = STARTTLS
- wizard.outgoingUri()
- }
- }
+ wizard.discoverServices()
+ wizard.autofill()
+ wizard.sourceUri()
+ wizard.outgoingUri()
wizard.advance(option)
})
basics.AddChild(selector).At(9, 0)
@@ -288,22 +309,7 @@ func NewAccountWizard(aerc *Aerc) *AccountWizard {
ui.NewText("Connection mode",
config.Ui.GetStyle(config.STYLE_HEADER))).
At(10, 0)
- sourceMode := NewSelector([]string{
- "IMAP over SSL/TLS",
- "IMAP with STARTTLS",
- "Insecure IMAP",
- }, 0, config.Ui).Chooser(true).OnSelect(func(option string) {
- switch option {
- case "IMAP over SSL/TLS":
- wizard.sourceMode = SSL_TLS
- case "IMAP with STARTTLS":
- wizard.sourceMode = STARTTLS
- case "Insecure IMAP":
- wizard.sourceMode = INSECURE
- }
- wizard.sourceUri()
- })
- incoming.AddChild(sourceMode).At(11, 0)
+ incoming.AddChild(wizard.sourceTransport).At(11, 0)
selector = NewSelector([]string{"Previous", "Next"}, 1, config.Ui).
OnChoose(wizard.advance)
incoming.AddChild(ui.NewFill(' ', tcell.StyleDefault)).At(12, 0)
@@ -313,7 +319,8 @@ func NewAccountWizard(aerc *Aerc) *AccountWizard {
wizard.sourceUsername,
wizard.sourcePassword,
wizard.sourceServer,
- sourceMode, selector,
+ wizard.sourceTransport,
+ selector,
}
outgoing := ui.NewGrid().Rows([]ui.GridSpec{
@@ -366,25 +373,10 @@ func NewAccountWizard(aerc *Aerc) *AccountWizard {
outgoing.AddChild(ui.NewFill(' ', tcell.StyleDefault)).
At(9, 0)
outgoing.AddChild(
- ui.NewText("Connection mode",
+ ui.NewText("Transport security",
config.Ui.GetStyle(config.STYLE_HEADER))).
At(10, 0)
- outgoingMode := NewSelector([]string{
- "SMTP over SSL/TLS",
- "SMTP with STARTTLS",
- "Insecure SMTP",
- }, 0, config.Ui).Chooser(true).OnSelect(func(option string) {
- switch option {
- case "SMTP over SSL/TLS":
- wizard.outgoingMode = SSL_TLS
- case "SMTP with STARTTLS":
- wizard.outgoingMode = STARTTLS
- case "Insecure SMTP":
- wizard.outgoingMode = INSECURE
- }
- wizard.outgoingUri()
- })
- outgoing.AddChild(outgoingMode).At(11, 0)
+ outgoing.AddChild(wizard.outgoingTransport).At(11, 0)
selector = NewSelector([]string{"Previous", "Next"}, 1, config.Ui).
OnChoose(wizard.advance)
outgoing.AddChild(ui.NewFill(' ', tcell.StyleDefault)).At(12, 0)
@@ -399,7 +391,9 @@ func NewAccountWizard(aerc *Aerc) *AccountWizard {
wizard.outgoingUsername,
wizard.outgoingPassword,
wizard.outgoingServer,
- outgoingMode, wizard.outgoingCopyTo, selector,
+ wizard.outgoingTransport,
+ wizard.outgoingCopyTo,
+ selector,
}
complete := ui.NewGrid().Rows([]ui.GridSpec{
@@ -572,13 +566,15 @@ func (wizard *AccountWizard) sourceUri() url.URL {
user := wizard.sourceUsername.String()
pass := wizard.sourcePassword.String()
var scheme string
- switch wizard.sourceMode {
- case SSL_TLS:
- scheme = "imaps"
- case STARTTLS:
- scheme = "imap"
- case INSECURE:
- scheme = "imap+insecure"
+ if wizard.sourceProtocol.Selected() == IMAP {
+ switch wizard.sourceTransport.Selected() {
+ case STARTTLS:
+ scheme = "imap"
+ case INSECURE:
+ scheme = "imap+insecure"
+ default:
+ scheme = "imaps"
+ }
}
var (
userpass *url.Userinfo
@@ -612,13 +608,15 @@ func (wizard *AccountWizard) outgoingUri() url.URL {
user := wizard.outgoingUsername.String()
pass := wizard.outgoingPassword.String()
var scheme string
- switch wizard.outgoingMode {
- case SSL_TLS:
- scheme = "smtps"
- case STARTTLS:
- scheme = "smtp"
- case INSECURE:
- scheme = "smtp+insecure"
+ if wizard.outgoingProtocol.Selected() == SMTP {
+ switch wizard.outgoingTransport.Selected() {
+ case INSECURE:
+ scheme = "smtp+insecure"
+ case STARTTLS:
+ scheme = "smtp"
+ default:
+ scheme = "smtps"
+ }
}
var (
userpass *url.Userinfo
@@ -730,19 +728,72 @@ func (wizard *AccountWizard) Event(event tcell.Event) bool {
return false
}
-func getSRV(host string, services []string) (string, string) {
- var hostport, srv string
- for _, srv = range services {
- _, addrs, err := net.LookupSRV(srv, "tcp", host)
- if err != nil {
- continue
+func (wizard *AccountWizard) discoverServices() {
+ email := wizard.email.String()
+ if !strings.ContainsRune(email, '@') {
+ return
+ }
+ domain := email[strings.IndexRune(email, '@')+1:]
+ var wg sync.WaitGroup
+ type Service struct{ srv, hostport string }
+ services := make(chan Service)
+
+ for _, service := range []string{"imaps", "imap", "submission"} {
+ wg.Add(1)
+ go func(srv string) {
+ defer log.PanicHandler()
+ defer wg.Done()
+ _, addrs, err := net.LookupSRV(srv, "tcp", domain)
+ if err != nil {
+ log.Tracef("SRV lookup for _%s._tcp.%s failed: %s",
+ srv, domain, err)
+ } else if addrs[0].Target != "" && addrs[0].Port > 0 {
+ services <- Service{
+ srv: srv,
+ hostport: net.JoinHostPort(
+ strings.TrimSuffix(addrs[0].Target, "."),
+ strconv.Itoa(int(addrs[0].Port))),
+ }
+ }
+ }(service)
+ }
+ go func() {
+ defer log.PanicHandler()
+ wg.Wait()
+ close(services)
+ }()
+
+ wizard.discovered = make(map[string]string)
+ for s := range services {
+ wizard.discovered[s.srv] = s.hostport
+ }
+}
+
+func (wizard *AccountWizard) autofill() {
+ if wizard.sourceServer.String() == "" {
+ if wizard.sourceProtocol.Selected() == IMAP {
+ if s, ok := wizard.discovered["imaps"]; ok {
+ wizard.sourceServer.Set(s)
+ wizard.sourceTransport.Select(SSL_TLS)
+ } else if s, ok := wizard.discovered["imap"]; ok {
+ wizard.sourceServer.Set(s)
+ wizard.sourceTransport.Select(STARTTLS)
+ }
}
- if addrs[0].Target != "" && addrs[0].Port > 0 {
- hostport = net.JoinHostPort(
- strings.TrimSuffix(addrs[0].Target, "."),
- strconv.Itoa(int(addrs[0].Port)))
- break
+ }
+ if wizard.outgoingServer.String() == "" {
+ if wizard.outgoingProtocol.Selected() == SMTP {
+ if s, ok := wizard.discovered["submission"]; ok {
+ switch {
+ case strings.HasSuffix(s, ":587"):
+ wizard.outgoingTransport.Select(SSL_TLS)
+ case strings.HasSuffix(s, ":465"):
+ wizard.outgoingTransport.Select(STARTTLS)
+ default:
+ wizard.outgoingTransport.Select(INSECURE)
+ }
+ wizard.outgoingServer.Set(s)
+ }
}
}
- return hostport, srv
}
diff --git a/widgets/selector.go b/widgets/selector.go
index fb8c8094..00479d4f 100644
--- a/widgets/selector.go
+++ b/widgets/selector.go
@@ -122,6 +122,18 @@ func (sel *Selector) OnSelect(fn func(option string)) *Selector {
return sel
}
+func (sel *Selector) Select(option string) {
+ for i, opt := range sel.options {
+ if option == opt {
+ sel.focus = i
+ if sel.onSelect != nil {
+ sel.onSelect(opt)
+ }
+ break
+ }
+ }
+}
+
func (sel *Selector) Selected() string {
return sel.options[sel.focus]
}