aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDrew DeVault <sir@cmpwn.com>2019-07-19 14:15:48 -0400
committerDrew DeVault <sir@cmpwn.com>2019-07-19 14:15:48 -0400
commit7a489cb0011a34a68d3e77d0174076857cc37902 (patch)
treed21df8ea9b872ccc08cff117f203ad0d80b94a26
parentb3a66866b95d77f202f571efedd2f7ec309aacf5 (diff)
downloadaerc-7a489cb0011a34a68d3e77d0174076857cc37902.tar.gz
Add Unix socket for communicating with aerc
-rw-r--r--aerc.go10
-rw-r--r--lib/socket.go82
-rw-r--r--widgets/aerc.go40
-rw-r--r--widgets/compose.go7
4 files changed, 139 insertions, 0 deletions
diff --git a/aerc.go b/aerc.go
index 40e66053..d44d3ba1 100644
--- a/aerc.go
+++ b/aerc.go
@@ -18,6 +18,7 @@ import (
"git.sr.ht/~sircmpwn/aerc/commands/msgview"
"git.sr.ht/~sircmpwn/aerc/commands/terminal"
"git.sr.ht/~sircmpwn/aerc/config"
+ "git.sr.ht/~sircmpwn/aerc/lib"
libui "git.sr.ht/~sircmpwn/aerc/lib/ui"
"git.sr.ht/~sircmpwn/aerc/widgets"
)
@@ -149,6 +150,15 @@ func main() {
}
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 aerc.Tick() {
// Continue updating our internal state
diff --git a/lib/socket.go b/lib/socket.go
new file mode 100644
index 00000000..c2565790
--- /dev/null
+++ b/lib/socket.go
@@ -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)
+}
diff --git a/widgets/aerc.go b/widgets/aerc.go
index a73caecc..14cf3c44 100644
--- a/widgets/aerc.go
+++ b/widgets/aerc.go
@@ -1,7 +1,10 @@
package widgets
import (
+ "errors"
"log"
+ "net/url"
+ "strings"
"time"
"github.com/gdamore/tcell"
@@ -302,3 +305,40 @@ func (aerc *Aerc) BeginExCommand() {
aerc.statusbar.Push(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
+}
diff --git a/widgets/compose.go b/widgets/compose.go
index f1c80149..401815ce 100644
--- a/widgets/compose.go
+++ b/widgets/compose.go
@@ -138,6 +138,13 @@ func (c *Composer) FocusTerminal() *Composer {
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)) {
c.headers.subject.OnChange(func() {
fn(c.headers.subject.input.String())