Task

Write a function to send an email.

The function should have parameters for setting From, To and Cc addresses; the Subject, and the message text, and optionally fields for the server name and login details.

 If appropriate, explain what notifications of problems/success are given.
 Solutions using libraries or functions from the language are preferred, but failing that, external programs can be used with an explanation.
 Note how portable the solution given is between operating systems when multi-OS languages are used.


(Remember to obfuscate any sensitive data used in examples)




package main
 
import (
	"bufio"
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"net/smtp"
	"os"
	"strings"
)
 
type Message struct {
	From    string
	To      []string
	Cc      []string
	Subject string
	Content string
}
 
func (m Message) Bytes() (r []byte) {
	to := strings.Join(m.To, ",")
	cc := strings.Join(m.Cc, ",")
 
	r = append(r, []byte("From: "+m.From+"\n")...)
	r = append(r, []byte("To: "+to+"\n")...)
	r = append(r, []byte("Cc: "+cc+"\n")...)
	r = append(r, []byte("Subject: "+m.Subject+"\n\n")...)
	r = append(r, []byte(m.Content)...)
 
	return
}
 
func (m Message) Send(host string, port int, user, pass string) (err error) {
	err = check(host, user, pass)
	if err != nil {
		return
	}
 
	err = smtp.SendMail(fmt.Sprintf("%v:%v", host, port),
		smtp.PlainAuth("", user, pass, host),
		m.From,
		m.To,
		m.Bytes(),
	)
 
	return
}
 
func check(host, user, pass string) error {
	if host == "" {
		return errors.New("Bad host")
	}
	if user == "" {
		return errors.New("Bad username")
	}
	if pass == "" {
		return errors.New("Bad password")
	}
 
	return nil
}
 
func main() {
	var flags struct {
		host string
		port int
		user string
		pass string
	}
	flag.StringVar(&flags.host, "host", "", "SMTP server to connect to")
	flag.IntVar(&flags.port, "port", 587, "Port to connect to SMTP server on")
	flag.StringVar(&flags.user, "user", "", "Username to authenticate with")
	flag.StringVar(&flags.pass, "pass", "", "Password to authenticate with")
	flag.Parse()
 
	err := check(flags.host, flags.user, flags.pass)
	if err != nil {
		flag.Usage()
		os.Exit(1)
	}
 
	bufin := bufio.NewReader(os.Stdin)
 
	fmt.Printf("From: ")
	from, err := bufin.ReadString('\n')
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		os.Exit(1)
	}
	from = strings.Trim(from, " \t\n\r")
 
	var to []string
	for {
		fmt.Printf("To (Blank to finish): ")
		tmp, err := bufin.ReadString('\n')
		if err != nil {
			fmt.Printf("Error: %v\n", err)
			os.Exit(1)
		}
		tmp = strings.Trim(tmp, " \t\n\r")
 
		if tmp == "" {
			break
		}
 
		to = append(to, tmp)
	}
 
	var cc []string
	for {
		fmt.Printf("Cc (Blank to finish): ")
		tmp, err := bufin.ReadString('\n')
		if err != nil {
			fmt.Printf("Error: %v\n", err)
			os.Exit(1)
		}
		tmp = strings.Trim(tmp, " \t\n\r")
 
		if tmp == "" {
			break
		}
 
		cc = append(cc, tmp)
	}
 
	fmt.Printf("Subject: ")
	subject, err := bufin.ReadString('\n')
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		os.Exit(1)
	}
	subject = strings.Trim(subject, " \t\n\r")
 
	fmt.Printf("Content (Until EOF):\n")
	content, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		os.Exit(1)
	}
	content = bytes.Trim(content, " \t\n\r")
 
	m := Message{
		From:    from,
		To:      to,
		Cc:      cc,
		Subject: subject,
		Content: string(content),
	}
 
	fmt.Printf("\nSending message...\n")
	err = m.Send(flags.host, flags.port, flags.user, flags.pass)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		os.Exit(1)
	}
 
	fmt.Printf("Message sent.\n")
}