1/*2Maddy Mail Server - Composable all-in-one email server.3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors45This program is free software: you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation, either version 3 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program. If not, see <https://www.gnu.org/licenses/>.17*/1819package config2021import (22 "fmt"23 "net"24 "net/url"25 "path/filepath"26 "strings"27)2829// 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 to31// the correct values as setup proceeds, but the original value should never be32// changed.33type Endpoint struct {34 Original, Scheme, Host, Port, Path string35}3637// String returns a human-friendly print of the address.38func (e Endpoint) String() string {39 if e.Original != "" {40 return e.Original41 }4243 if e.Scheme == "unix" {44 return "unix://" + e.Path45 }4647 if e.Host == "" && e.Port == "" {48 return ""49 }50 s := e.Scheme51 if s != "" {52 s += "://"53 }5455 host := e.Host56 if strings.Contains(host, ":") {57 host = "[" + host + "]"58 }59 s += host6061 if e.Port != "" {62 s += ":" + e.Port63 }64 if e.Path != "" {65 s += e.Path66 }67 return s68}6970func (e Endpoint) Network() string {71 if e.Scheme == "unix" {72 return "unix"73 }74 return "tcp"75}7677func (e Endpoint) Address() string {78 if e.Scheme == "unix" {79 return e.Path80 }81 return net.JoinHostPort(e.Host, e.Port)82}8384func (e Endpoint) IsTLS() bool {85 return e.Scheme == "tls"86}8788// ParseEndpoint parses an endpoint string into a structured format with separate89// scheme, host, port, and path portions, as well as the original input string.90func ParseEndpoint(str string) (Endpoint, error) {91 input := str9293 u, err := url.Parse(str)94 if err != nil {95 return Endpoint{}, err96 }9798 switch u.Scheme {99 case "tcp", "tls":100 // ALL GREEN101102 // scheme:OPAQUE URL syntax103 if u.Host == "" && u.Opaque != "" {104 u.Host = u.Opaque105 }106 case "unix":107 // scheme:OPAQUE URL syntax108 if u.Path == "" && u.Opaque != "" {109 u.Path = u.Opaque110 }111112 var actualPath string113 if u.Host != "" {114 actualPath += u.Host115 }116 if u.Path != "" {117 actualPath += u.Path118 }119120 if !filepath.IsAbs(actualPath) {121 actualPath = filepath.Join(RuntimeDirectory, actualPath)122 }123124 return Endpoint{Original: input, Scheme: u.Scheme, Path: actualPath}, err125 default:126 return Endpoint{}, fmt.Errorf("unsupported scheme: %s (%+v)", input, u)127 }128129 // separate host and port130 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.Host135 }136 }137 if port == "" {138 return Endpoint{}, fmt.Errorf("port is required")139 }140141 return Endpoint{Original: input, Scheme: u.Scheme, Host: host, Port: port, Path: u.Path}, err142}