sendmail/main.go

202 lines
4.8 KiB
Go

package main
import (
"bufio"
"errors"
"flag"
"fmt"
"os"
"strings"
"time"
)
var noVersion = "dev"
var version string
type attachmentList []string
func (a *attachmentList) String() string {
var str string
for _, attachment := range *a {
str += fmt.Sprintf("%s\n", attachment)
}
return str
}
func (a *attachmentList) Set(file string) error {
if _, err := os.Lstat(file); err != nil {
return err
}
if file == "" {
return errors.New("no file provided")
}
*a = append(*a, file)
return nil
}
func readFromConsole(result chan string) {
var text, line string
var err error
counter := 0
reader := bufio.NewReader(os.Stdin)
for counter < 3 {
line, err = reader.ReadString('\n')
if line == "\n" {
counter++
} else {
counter = 0
}
text += fmt.Sprintf("%s\n", line)
}
if err != nil {
Error.F("Error in reading text from console\n%s", err)
os.Exit(1)
}
result <- strings.TrimRight(text, "\n")
return
}
func toList(input string) []string {
var result = []string{}
list := strings.Split(input, ",")
for _, element := range list {
if element != "" {
result = append(result, element)
}
}
return result
}
func initializeConfig(configPath, section string) *Config {
if configPath == "" {
configPath = "/etc/sendmail.toml"
// Ignore non existing config only if `-conf` not used on command line.
if _, err := os.Stat(configPath); os.IsNotExist(err) {
return NewConfig()
}
}
config, err := readConfig(configPath, section)
if err != nil {
if os.IsNotExist(err) {
Error.F("Error in parsing the configuration\n%s", err)
os.Exit(-2)
}
}
Debug.F("---\nConfig from %s\n%s", configPath, *config)
return config
}
func main() {
var err error
var configPath, section, serverAddress, user, password, to, cc, bcc, from, subject, text string
var encryption, dbg, versionFlag, interactive bool
var serverPortAux int
var serverPort int64
var attachments attachmentList
flag.BoolVar(&versionFlag, "version", false, "Prints the version and exits")
flag.StringVar(&configPath, "conf", "", "Path to a config file (defaults to /etc/sendmail.toml)")
flag.StringVar(&section, "section", "default", "Section of the conf to read (defaults to \"default\")")
flag.BoolVar(&dbg, "dbg", false, "Enable debugging output")
flag.BoolVar(&interactive, "interactive", false, "Assume composing mail manually, do not timeout waiting input")
flag.StringVar(&serverAddress, "server-address", "", "The SMTP server address")
flag.IntVar(&serverPortAux, "server-port", 0, "The SMTP server")
flag.BoolVar(&encryption, "force-ssl", false, "Force the use of ssl (defalut: false)")
flag.StringVar(&user, "user", "", "The user to authenticate with to the server")
flag.StringVar(&password, "password", "", "The password to authenticate with to the server")
flag.StringVar(&to, "to", "", "Comma-separated list of recipient(s)")
flag.StringVar(&cc, "cc", "", "Comma-separated list of carbon-copy recipient(s)")
flag.StringVar(&bcc, "bcc", "", "Comma-separated list of blind carbon-copy recipient(s)")
flag.StringVar(&from, "from", "", "Sender of the mail (used as default account user to log in on the SMTP server)")
flag.StringVar(&subject, "sub", "", "Subject of the mail")
flag.Var(&attachments, "attach", "Attachment to the mail (may be repeated)")
flag.Parse()
LogInit(dbg)
if versionFlag {
showVersion := noVersion
if version != "" {
showVersion = version
}
Info.F("Version: %s", showVersion)
os.Exit(0)
}
if flag.NArg() == 0 {
result := make(chan string, 1)
go readFromConsole(result)
if interactive {
text = <-result
} else {
select {
case text = <-result:
Info.Ln("text read from console")
case <-time.After(5 * time.Second):
Error.Ln("timeout reading from console")
os.Exit(3)
}
}
} else {
for _, arg := range flag.Args() {
text += fmt.Sprintf("%s\n", arg)
}
}
serverPort = int64(serverPortAux)
Debug.F(
`
---
parameters:
conf: %s
dbg: %t
address: %s
port: %d
encryption: %t
user: %s
password: %s
to: %s
from: %s
subject: %s
attachments: %s`,
configPath,
dbg,
serverAddress,
serverPort,
encryption,
user,
password,
to,
from,
subject,
attachments,
)
config := initializeConfig(configPath, section)
config.Server.Merge("Address", serverAddress)
config.Server.Merge("Port", serverPort)
config.Server.Merge("Encryption", encryption)
config.Server.Merge("User", user)
config.Server.Merge("Password", password)
config.Merge("From", from)
config.Merge("To", toList(to))
config.Merge("Cc", toList(cc))
config.Merge("Bcc", toList(bcc))
config.Merge("Subject", subject)
config.Merge("Text", text)
config.Merge("Attachments", attachments)
Debug.F("---\nPre-validation config\n%s", config)
err = config.Validate()
if err != nil {
Error.F("Config validation failed:\n%s\n", err)
os.Exit(1)
}
Info.F("Sending mail | to: %s", config.To)
SendMail(config)
}