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 config
 20
 21import (
 22	"fmt"
 23	"net"
 24	"net/url"
 25	"path/filepath"
 26	"strings"
 27)
 28
 29// Endpoint represents a site address. It contains the original input value,
 30// and the component parts of an address. The component parts may be updated to
 31// the correct values as setup proceeds, but the original value should never be
 32// changed.
 33type Endpoint struct {
 34	Original, Scheme, Host, Port, Path string
 35}
 36
 37// String returns a human-friendly print of the address.
 38func (e Endpoint) String() string {
 39	if e.Original != "" {
 40		return e.Original
 41	}
 42
 43	if e.Scheme == "unix" {
 44		return "unix://" + e.Path
 45	}
 46
 47	if e.Host == "" && e.Port == "" {
 48		return ""
 49	}
 50	s := e.Scheme
 51	if s != "" {
 52		s += "://"
 53	}
 54
 55	host := e.Host
 56	if strings.Contains(host, ":") {
 57		host = "[" + host + "]"
 58	}
 59	s += host
 60
 61	if e.Port != "" {
 62		s += ":" + e.Port
 63	}
 64	if e.Path != "" {
 65		s += e.Path
 66	}
 67	return s
 68}
 69
 70func (e Endpoint) Network() string {
 71	if e.Scheme == "unix" {
 72		return "unix"
 73	}
 74	return "tcp"
 75}
 76
 77func (e Endpoint) Address() string {
 78	if e.Scheme == "unix" {
 79		return e.Path
 80	}
 81	return net.JoinHostPort(e.Host, e.Port)
 82}
 83
 84func (e Endpoint) IsTLS() bool {
 85	return e.Scheme == "tls"
 86}
 87
 88// ParseEndpoint parses an endpoint string into a structured format with separate
 89// scheme, host, port, and path portions, as well as the original input string.
 90func ParseEndpoint(str string) (Endpoint, error) {
 91	input := str
 92
 93	u, err := url.Parse(str)
 94	if err != nil {
 95		return Endpoint{}, err
 96	}
 97
 98	switch u.Scheme {
 99	case "tcp", "tls":
100		// ALL GREEN
101
102		// scheme:OPAQUE URL syntax
103		if u.Host == "" && u.Opaque != "" {
104			u.Host = u.Opaque
105		}
106	case "unix":
107		// scheme:OPAQUE URL syntax
108		if u.Path == "" && u.Opaque != "" {
109			u.Path = u.Opaque
110		}
111
112		var actualPath string
113		if u.Host != "" {
114			actualPath += u.Host
115		}
116		if u.Path != "" {
117			actualPath += u.Path
118		}
119
120		if !filepath.IsAbs(actualPath) {
121			actualPath = filepath.Join(RuntimeDirectory, actualPath)
122		}
123
124		return Endpoint{Original: input, Scheme: u.Scheme, Path: actualPath}, err
125	default:
126		return Endpoint{}, fmt.Errorf("unsupported scheme: %s (%+v)", input, u)
127	}
128
129	// separate host and port
130	host, port, err := net.SplitHostPort(u.Host)
131	if err != nil {
132		host, port, err = net.SplitHostPort(u.Host + ":")
133		if err != nil {
134			host = u.Host
135		}
136	}
137	if port == "" {
138		return Endpoint{}, fmt.Errorf("port is required")
139	}
140
141	return Endpoint{Original: input, Scheme: u.Scheme, Host: host, Port: port, Path: u.Path}, err
142}