Add Unix socket for communicating with aerc
This commit is contained in:
parent
b3a66866b9
commit
7a489cb001
4 changed files with 139 additions and 0 deletions
10
aerc.go
10
aerc.go
|
@ -18,6 +18,7 @@ import (
|
||||||
"git.sr.ht/~sircmpwn/aerc/commands/msgview"
|
"git.sr.ht/~sircmpwn/aerc/commands/msgview"
|
||||||
"git.sr.ht/~sircmpwn/aerc/commands/terminal"
|
"git.sr.ht/~sircmpwn/aerc/commands/terminal"
|
||||||
"git.sr.ht/~sircmpwn/aerc/config"
|
"git.sr.ht/~sircmpwn/aerc/config"
|
||||||
|
"git.sr.ht/~sircmpwn/aerc/lib"
|
||||||
libui "git.sr.ht/~sircmpwn/aerc/lib/ui"
|
libui "git.sr.ht/~sircmpwn/aerc/lib/ui"
|
||||||
"git.sr.ht/~sircmpwn/aerc/widgets"
|
"git.sr.ht/~sircmpwn/aerc/widgets"
|
||||||
)
|
)
|
||||||
|
@ -149,6 +150,15 @@ func main() {
|
||||||
}
|
}
|
||||||
defer ui.Close()
|
defer ui.Close()
|
||||||
|
|
||||||
|
logger.Println("Starting Unix server")
|
||||||
|
as, err := lib.StartServer(logger)
|
||||||
|
if err != nil {
|
||||||
|
logger.Printf("Failed to start Unix server: %v (non-fatal)", err)
|
||||||
|
} else {
|
||||||
|
defer as.Close()
|
||||||
|
as.OnMailto = aerc.Mailto
|
||||||
|
}
|
||||||
|
|
||||||
for !ui.ShouldExit() {
|
for !ui.ShouldExit() {
|
||||||
for aerc.Tick() {
|
for aerc.Tick() {
|
||||||
// Continue updating our internal state
|
// Continue updating our internal state
|
||||||
|
|
82
lib/socket.go
Normal file
82
lib/socket.go
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/kyoh86/xdg"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AercServer struct {
|
||||||
|
logger *log.Logger
|
||||||
|
listener net.Listener
|
||||||
|
OnMailto func(addr *url.URL) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartServer(logger *log.Logger) (*AercServer, error) {
|
||||||
|
sockpath := path.Join(xdg.RuntimeDir(), "aerc.sock")
|
||||||
|
l, err := net.Listen("unix", sockpath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
as := &AercServer{
|
||||||
|
logger: logger,
|
||||||
|
listener: l,
|
||||||
|
}
|
||||||
|
// TODO: stash clients and close them on exit... bleh racey
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
conn, err := l.Accept()
|
||||||
|
if err != nil {
|
||||||
|
// TODO: Something more useful, in some cases, on wednesdays,
|
||||||
|
// after 2 PM, I guess?
|
||||||
|
as.logger.Println("Closing Unix server: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go as.handleClient(conn)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return as, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *AercServer) Close() {
|
||||||
|
as.listener.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastId int64 = 0 // access via atomic
|
||||||
|
|
||||||
|
func (as *AercServer) handleClient(conn net.Conn) {
|
||||||
|
clientId := atomic.AddInt64(&lastId, 1)
|
||||||
|
as.logger.Printf("Accepted Unix connection %d", clientId)
|
||||||
|
scanner := bufio.NewScanner(conn)
|
||||||
|
conn.SetDeadline(time.Now().Add(1 * time.Minute))
|
||||||
|
for scanner.Scan() {
|
||||||
|
conn.SetDeadline(time.Now().Add(1 * time.Minute))
|
||||||
|
msg := scanner.Text()
|
||||||
|
if !strings.ContainsRune(msg, ':') {
|
||||||
|
conn.Write([]byte("error: invalid command\n"))
|
||||||
|
}
|
||||||
|
as.logger.Printf("unix:%d: got message %s", clientId, msg)
|
||||||
|
prefix := msg[:strings.IndexRune(msg, ':')]
|
||||||
|
switch prefix {
|
||||||
|
case "mailto":
|
||||||
|
mailto, err := url.Parse(msg)
|
||||||
|
if err != nil {
|
||||||
|
conn.Write([]byte(fmt.Sprintf("error: %v\n", err)))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if as.OnMailto != nil {
|
||||||
|
err = as.OnMailto(mailto)
|
||||||
|
}
|
||||||
|
conn.Write([]byte(fmt.Sprintf("result: %v\n", err)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
as.logger.Printf("Closed Unix connection %d", clientId)
|
||||||
|
}
|
|
@ -1,7 +1,10 @@
|
||||||
package widgets
|
package widgets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
|
@ -302,3 +305,40 @@ func (aerc *Aerc) BeginExCommand() {
|
||||||
aerc.statusbar.Push(exline)
|
aerc.statusbar.Push(exline)
|
||||||
aerc.focus(exline)
|
aerc.focus(exline)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (aerc *Aerc) Mailto(addr *url.URL) error {
|
||||||
|
acct := aerc.SelectedAccount()
|
||||||
|
if acct == nil {
|
||||||
|
return errors.New("No account selected")
|
||||||
|
}
|
||||||
|
defaults := make(map[string]string)
|
||||||
|
defaults["To"] = addr.Opaque
|
||||||
|
headerMap := map[string]string{
|
||||||
|
"cc": "Cc",
|
||||||
|
"in-reply-to": "In-Reply-To",
|
||||||
|
"subject": "Subject",
|
||||||
|
}
|
||||||
|
for key, vals := range addr.Query() {
|
||||||
|
if header, ok := headerMap[strings.ToLower(key)]; ok {
|
||||||
|
defaults[header] = strings.Join(vals, ",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
composer := NewComposer(aerc.Config(),
|
||||||
|
acct.AccountConfig(), acct.Worker()).Defaults(defaults)
|
||||||
|
composer.FocusSubject()
|
||||||
|
title := "New email"
|
||||||
|
if subj, ok := defaults["Subject"]; ok {
|
||||||
|
title = subj
|
||||||
|
composer.FocusTerminal()
|
||||||
|
}
|
||||||
|
tab := aerc.NewTab(composer, title)
|
||||||
|
composer.OnSubjectChange(func(subject string) {
|
||||||
|
if subject == "" {
|
||||||
|
tab.Name = "New email"
|
||||||
|
} else {
|
||||||
|
tab.Name = subject
|
||||||
|
}
|
||||||
|
tab.Content.Invalidate()
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -138,6 +138,13 @@ func (c *Composer) FocusTerminal() *Composer {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Composer) FocusSubject() *Composer {
|
||||||
|
c.focusable[c.focused].Focus(false)
|
||||||
|
c.focused = 2
|
||||||
|
c.focusable[c.focused].Focus(true)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Composer) OnSubjectChange(fn func(subject string)) {
|
func (c *Composer) OnSubjectChange(fn func(subject string)) {
|
||||||
c.headers.subject.OnChange(func() {
|
c.headers.subject.OnChange(func() {
|
||||||
fn(c.headers.subject.input.String())
|
fn(c.headers.subject.input.String())
|
||||||
|
|
Loading…
Reference in a new issue