Files
aerc-fork-mirror/lib/ipc/receive.go
Jason Cox 480637d9ed ipc: disable IPC completely when disable-ipc=true
Truly disable IPC when disable-ipc is set to true in aerc.conf. Don't
run commands over IPC and don't start an IPC server.

Being able to disable IPC in the config is useful because it allows
making aerc open mailto links in a new instance without modifying the
aerc.desktop file. There are of course potential security benefits as
well.

Changelog-changed: The `disable-ipc` option in `aerc.conf` completely
 disables IPC.
Signed-off-by: Jason Cox <me@jasoncarloscox.com>
Acked-by: Robin Jarry <robin@jarry.cc>
2024-04-13 21:46:44 +02:00

105 lines
2.4 KiB
Go

package ipc
import (
"bufio"
"context"
"errors"
"net"
"os"
"sync/atomic"
"time"
"git.sr.ht/~rjarry/aerc/lib/log"
"git.sr.ht/~rjarry/aerc/lib/xdg"
)
type AercServer struct {
listener net.Listener
handler Handler
startup context.Context
}
func StartServer(handler Handler, startup context.Context) (*AercServer, error) {
sockpath := xdg.RuntimePath("aerc.sock")
// remove the socket if it is not connected to a session
if _, err := ConnectAndExec(nil); err != nil {
os.Remove(sockpath)
}
log.Debugf("Starting Unix server: %s", sockpath)
l, err := net.Listen("unix", sockpath)
if err != nil {
return nil, err
}
as := &AercServer{listener: l, handler: handler, startup: startup}
go as.Serve()
return as, nil
}
func (as *AercServer) Close() {
as.listener.Close()
}
var lastId int64 = 0 // access via atomic
func (as *AercServer) Serve() {
defer log.PanicHandler()
<-as.startup.Done()
for {
conn, err := as.listener.Accept()
switch {
case errors.Is(err, net.ErrClosed):
log.Infof("shutting down UNIX listener")
return
case err != nil:
log.Errorf("ipc: accepting connection failed: %v", err)
continue
}
defer conn.Close()
clientId := atomic.AddInt64(&lastId, 1)
log.Debugf("unix:%d accepted connection", clientId)
scanner := bufio.NewScanner(conn)
err = conn.SetDeadline(time.Now().Add(1 * time.Minute))
if err != nil {
log.Errorf("unix:%d failed to set deadline: %v", clientId, err)
}
for scanner.Scan() {
// allow up to 1 minute between commands
err = conn.SetDeadline(time.Now().Add(1 * time.Minute))
if err != nil {
log.Errorf("unix:%d failed to update deadline: %v", clientId, err)
}
msg, err := DecodeRequest(scanner.Bytes())
log.Tracef("unix:%d got message %s", clientId, scanner.Text())
if err != nil {
log.Errorf("unix:%d failed to parse request: %v", clientId, err)
continue
}
response := as.handleMessage(msg)
result, err := response.Encode()
if err != nil {
log.Errorf("unix:%d failed to encode result: %v", clientId, err)
continue
}
_, err = conn.Write(append(result, '\n'))
if err != nil {
log.Errorf("unix:%d failed to send response: %v", clientId, err)
break
}
}
log.Tracef("unix:%d closed connection", clientId)
}
}
func (as *AercServer) handleMessage(req *Request) *Response {
err := as.handler.Command(req.Arguments)
if err != nil {
return &Response{Error: err.Error()}
}
return &Response{}
}