mirror of
https://blitiri.com.ar/repos/chasquid
synced 2026-10-29 15:02:30 +00:00
0210c5fd92
This patch contains the result of running `go fix ./...`, which applies simplifications that make use of modern language features. There are no logic or behaviour changes.
143 lines
2.5 KiB
Go
143 lines
2.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"errors"
|
|
"flag"
|
|
"io"
|
|
"net"
|
|
"net/smtp"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
var (
|
|
addr = flag.String("addr", "", "Address of the SMTP server")
|
|
|
|
user = flag.String("user", "", "Username to use in SMTP AUTH")
|
|
password = flag.String("password", "", "Password to use in SMTP AUTH")
|
|
|
|
from = flag.String("from", "", "From address to use in the message")
|
|
|
|
serverCert = flag.String("server_cert", "",
|
|
"Path to the server certificate to expect")
|
|
|
|
confPath = flag.String("c", "smtpc.conf",
|
|
"Path to the configuration file")
|
|
)
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
loadConfig()
|
|
|
|
// Read message from stdin.
|
|
rawMsg, err := io.ReadAll(os.Stdin)
|
|
notnil(err)
|
|
|
|
// RCPT TO from the command line.
|
|
tos := make([]string, len(flag.Args()))
|
|
for i, to := range flag.Args() {
|
|
tos[i] = to
|
|
}
|
|
|
|
// Connect to the server.
|
|
var conn net.Conn
|
|
if *serverCert != "" {
|
|
cert := loadCert(*serverCert)
|
|
rootCAs := x509.NewCertPool()
|
|
|
|
rootCAs.AddCert(cert)
|
|
tlsConfig := &tls.Config{
|
|
ServerName: cert.DNSNames[0],
|
|
RootCAs: rootCAs,
|
|
}
|
|
|
|
conn, err = tls.Dial("tcp", *addr, tlsConfig)
|
|
} else {
|
|
conn, err = net.Dial("tcp", *addr)
|
|
}
|
|
notnil(err)
|
|
defer conn.Close()
|
|
|
|
// Send the message.
|
|
client, err := smtp.NewClient(conn, *addr)
|
|
notnil(err)
|
|
|
|
if *user != "" {
|
|
auth := smtp.PlainAuth("", *user, *password, *addr)
|
|
err = client.Auth(auth)
|
|
notnil(err)
|
|
}
|
|
|
|
if *from == "" {
|
|
*from = *user
|
|
}
|
|
err = client.Mail(*from)
|
|
notnil(err)
|
|
|
|
for _, to := range tos {
|
|
err = client.Rcpt(to)
|
|
notnil(err)
|
|
}
|
|
|
|
w, err := client.Data()
|
|
notnil(err)
|
|
_, err = io.Copy(w, bytes.NewReader(rawMsg))
|
|
notnil(err)
|
|
err = w.Close()
|
|
notnil(err)
|
|
|
|
err = client.Quit()
|
|
notnil(err)
|
|
}
|
|
|
|
func loadConfig() {
|
|
data, err := os.ReadFile(*confPath)
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return
|
|
}
|
|
notnil(err)
|
|
|
|
for line := range strings.SplitSeq(string(data), "\n") {
|
|
k, v, ok := strings.Cut(line, " ")
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
k = strings.TrimSpace(k)
|
|
|
|
// Set the flag but only if it wasn't already set.
|
|
// Command-line flags take precedence.
|
|
isSet := false
|
|
flag.Visit(func(f *flag.Flag) {
|
|
if f.Name == k {
|
|
isSet = true
|
|
}
|
|
})
|
|
if !isSet {
|
|
flag.Lookup(k).Value.Set(strings.TrimSpace(v))
|
|
}
|
|
}
|
|
}
|
|
|
|
func loadCert(path string) *x509.Certificate {
|
|
data, err := os.ReadFile(path)
|
|
notnil(err)
|
|
|
|
block, _ := pem.Decode(data)
|
|
|
|
cert, err := x509.ParseCertificate(block.Bytes)
|
|
notnil(err)
|
|
|
|
return cert
|
|
}
|
|
|
|
func notnil(err error) {
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|