mirror of
https://git.sr.ht/~rjarry/aerc
synced 2026-03-02 18:23:33 +01:00
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>
105 lines
2.4 KiB
Go
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{}
|
|
}
|