|
|
|
@ -0,0 +1,107 @@ |
|
|
|
|
package main |
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"flag" |
|
|
|
|
"fmt" |
|
|
|
|
"github.com/emersion/go-smtp" |
|
|
|
|
"io" |
|
|
|
|
"net" |
|
|
|
|
"os" |
|
|
|
|
"regexp" |
|
|
|
|
"strings" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
func sendMail(from string, to string, socket string) (err error) { |
|
|
|
|
conn, err := net.Dial("unix", socket) |
|
|
|
|
if err != nil { |
|
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "Failed opening the socket <%s>: %s", socket, err) |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
client, err := smtp.NewClientLMTP(conn, "localhost") |
|
|
|
|
if err != nil { |
|
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "Failed establishing the connection: %s", err) |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
defer func() { |
|
|
|
|
quitErr := client.Quit() |
|
|
|
|
if quitErr != nil { |
|
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "Failed closing the connection: %s", quitErr) |
|
|
|
|
} |
|
|
|
|
}() |
|
|
|
|
err = client.Mail(from, &smtp.MailOptions{}) |
|
|
|
|
if err != nil { |
|
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "Failed establishing the envelope FROM <%s>: %s", from, err) |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
err = client.Rcpt(to) |
|
|
|
|
if err != nil { |
|
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "Failed establishing the envelope FROM <%s> TO <%s>: %s", from, to, err) |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
var sendError *smtp.SMTPError |
|
|
|
|
writer, err := client.LMTPData(func(rcpt string, status *smtp.SMTPError) { |
|
|
|
|
if status != nil { |
|
|
|
|
sendError = status |
|
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "Failed sending the message FROM <%s> TO <%s>: %s", from, rcpt, status) |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
if err != nil { |
|
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "Failed opening the writing stream FROM <%s> TO <%s>: %s", from, to, err) |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
_, err = io.Copy(writer, os.Stdin) |
|
|
|
|
if err != nil { |
|
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "Failed copying the message FROM <%s> TO <%s> data: %s", from, to, err) |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
err = writer.Close() |
|
|
|
|
if err != nil { |
|
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "Failed closing the writing stream FROM <%s> TO <%s>: %s", from, to, err) |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
if sendError != nil { |
|
|
|
|
return sendError |
|
|
|
|
} |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var UnwrapRe = regexp.MustCompile("^.*\\s*<([^<>]+)>\\s*$") |
|
|
|
|
|
|
|
|
|
func unwrap(from string) string { |
|
|
|
|
result := UnwrapRe.FindStringSubmatch(from) |
|
|
|
|
if result == nil { |
|
|
|
|
return from |
|
|
|
|
} |
|
|
|
|
return result[1] |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func escapeAts(from string) string { |
|
|
|
|
count := strings.Count(from, "@") |
|
|
|
|
if count <= 1 { |
|
|
|
|
return from |
|
|
|
|
} |
|
|
|
|
return strings.Replace(from, "@", ".at.", count - 1) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func main() { |
|
|
|
|
from := flag.String("from", "", "The original envelope sender of the message.") |
|
|
|
|
to := flag.String("to", "", "The envelope recipient of the message.") |
|
|
|
|
socket := flag.String("socket", "/var/run/dovecot/lmtp", "The LMTP Unix socket to send on.") |
|
|
|
|
escapeDoubledAts := flag.Bool("escapeDoubledAts", true, "True to escape multiple @ symbols.") |
|
|
|
|
unwrapFrom := flag.Bool("unwrapBrackets", true, "True to unwrap unnecessary <> symbols.") |
|
|
|
|
replaceUnknown := flag.Bool("replaceUnknown", true, "True to replace 'unknown' with a default value") |
|
|
|
|
flag.Parse() |
|
|
|
|
|
|
|
|
|
if *unwrapFrom { |
|
|
|
|
*from = unwrap(*from) |
|
|
|
|
} |
|
|
|
|
if *escapeDoubledAts { |
|
|
|
|
*from = escapeAts(*from) |
|
|
|
|
} |
|
|
|
|
if *replaceUnknown && *from == "unknown" { |
|
|
|
|
*from = "unknown@unknown.invalid" |
|
|
|
|
} |
|
|
|
|
if err := sendMail(*from, *to, *socket); err != nil { |
|
|
|
|
os.Exit(1) |
|
|
|
|
} |
|
|
|
|
} |