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")
}