maddy

Fork https://github.com/foxcpp/maddy

git clone git://git.lin.moe/go/maddy.git

  1/*
  2Maddy Mail Server - Composable all-in-one email server.
  3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors
  4
  5This program is free software: you can redistribute it and/or modify
  6it under the terms of the GNU General Public License as published by
  7the Free Software Foundation, either version 3 of the License, or
  8(at your option) any later version.
  9
 10This program is distributed in the hope that it will be useful,
 11but WITHOUT ANY WARRANTY; without even the implied warranty of
 12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13GNU General Public License for more details.
 14
 15You should have received a copy of the GNU General Public License
 16along with this program.  If not, see <https://www.gnu.org/licenses/>.
 17*/
 18
 19package updatepipe
 20
 21import (
 22	"bufio"
 23	"fmt"
 24	"io"
 25	"net"
 26	"os"
 27
 28	mess "github.com/foxcpp/go-imap-mess"
 29	"github.com/foxcpp/maddy/framework/log"
 30)
 31
 32// UnixSockPipe implements the UpdatePipe interface by serializating updates
 33// to/from a Unix domain socket. Due to the way Unix sockets work, only one
 34// Listen goroutine can be running.
 35//
 36// The socket is stream-oriented and consists of the following messages:
 37//
 38//	SENDER_ID;JSON_SERIALIZED_INTERNAL_OBJECT\n
 39//
 40// And SENDER_ID is Process ID and UnixSockPipe address concated as a string.
 41// It is used to deduplicate updates sent to Push and recevied via Listen.
 42//
 43// The SockPath field specifies the socket path to use. The actual socket
 44// is initialized on the first call to Listen or (Init)Push.
 45type UnixSockPipe struct {
 46	SockPath string
 47	Log      log.Logger
 48
 49	listener net.Listener
 50	sender   net.Conn
 51}
 52
 53var _ P = &UnixSockPipe{}
 54
 55func (usp *UnixSockPipe) myID() string {
 56	return fmt.Sprintf("%d-%p", os.Getpid(), usp)
 57}
 58
 59func (usp *UnixSockPipe) readUpdates(conn net.Conn, updCh chan<- mess.Update) {
 60	scnr := bufio.NewScanner(conn)
 61	for scnr.Scan() {
 62		id, upd, err := parseUpdate(scnr.Text())
 63		if err != nil {
 64			usp.Log.Error("malformed update received", err, "str", scnr.Text())
 65		}
 66
 67		// It is our own update, skip.
 68		if id == usp.myID() {
 69			continue
 70		}
 71
 72		updCh <- *upd
 73	}
 74}
 75
 76func (usp *UnixSockPipe) Listen(upd chan<- mess.Update) error {
 77	l, err := net.Listen("unix", usp.SockPath)
 78	if err != nil {
 79		return err
 80	}
 81	usp.listener = l
 82	go func() {
 83		for {
 84			conn, err := l.Accept()
 85			if err != nil {
 86				return
 87			}
 88			go usp.readUpdates(conn, upd)
 89		}
 90	}()
 91	return nil
 92}
 93
 94func (usp *UnixSockPipe) InitPush() error {
 95	sock, err := net.Dial("unix", usp.SockPath)
 96	if err != nil {
 97		return err
 98	}
 99
100	usp.sender = sock
101	return nil
102}
103
104func (usp *UnixSockPipe) Push(upd mess.Update) error {
105	if usp.sender == nil {
106		if err := usp.InitPush(); err != nil {
107			return err
108		}
109	}
110
111	updStr, err := formatUpdate(usp.myID(), upd)
112	if err != nil {
113		return err
114	}
115
116	_, err = io.WriteString(usp.sender, updStr)
117	return err
118}
119
120func (usp *UnixSockPipe) Close() error {
121	if usp.sender != nil {
122		usp.sender.Close()
123	}
124	if usp.listener != nil {
125		usp.listener.Close()
126		os.Remove(usp.SockPath)
127	}
128	return nil
129}