diff options
author | Robin Jarry <robin@jarry.cc> | 2023-08-20 20:45:51 +0200 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2023-08-26 21:41:05 +0200 |
commit | c077d2bf0f5d1a3aa0f97f7942c3d6938c9c43ff (patch) | |
tree | e87a7e3bde30b275f6dd6ac1273f531dad3115a0 /widgets | |
parent | d620a75f347a88d152e2dc63014178fb9ca2bb73 (diff) | |
download | aerc-c077d2bf0f5d1a3aa0f97f7942c3d6938c9c43ff.tar.gz |
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>
Diffstat (limited to 'widgets')
-rw-r--r-- | widgets/account-wizard.go | 229 | ||||
-rw-r--r-- | widgets/selector.go | 12 |
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] } |