diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..01055dd --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +email +.idea diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..1c5acca --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +language: go +go: +- '1.6' +script: +- make all +deploy: + provider: releases + file: email + on: + tags: true + branch: master + repo: wrouesnel/emailcli + api_key: + secure: LwWC9VndftJuJjeonMk2JRxOg3u312CGybU7JcXeIjtDuLwGvNkK5zsHSRIX8+Oj4tfhEcTbdY2XHS9C0tiJ2EUklsMNZBRnEE+i56WW9GVgJc/mCmTZpN9j1ilNeHASjAYreJ71F7NeRc0rM4hy18LJQTbZEwN9JCLhqGfOc1e5vSNvVKZv7+6/4UHdKDSlVlmeEMNrLpS/QoTmSpGQyk0JxXVsK2ZW+va3huysKh913B3D4KNmtNMwdMsAAv/q3uvxlpGYG+QkGfKrF8qbgyoVJXsNc//ibbGNtPDOdk/MZv/d2KD4VgKpL8wvb75IbAG4LkvM3WDDcNTEOviECKDREJfZDCczah2RLKn+6Mev8uWpcnS4rs3uHD/7pQUuZb5nTtXgBOfLSLtl/rfs8nrzCyEN9j+JJ7Qc+Kyi1anjAuqZDw+0aD0xc1VXda0vUdYpJWt21dOAQtJw0BuibgqTJeE3m6s7LDkUssrd5ih5QI2mr3mSWJ0faUvRc8FeSun4MvolurvP1rYhNIr9BJm6E1l9pKUfMRncPsuIX5Rdzxu1+vfM5EhbIT5quBSUtVkGUzhDcr223wGbOKGDLiLTO8eBkLGU5HHRzqcAb0BEi4ppKQzd+pvQ32Rx+Hww7KwPbexln46iD1OkHrxz96AzB/B6RFuSX5vxy7zmFMY= diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..3ac2aba --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Will Rouesnel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d41fce6 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ + +GO_SRC := $(shell find -type f -name "*.go") + +all: vet test email + +# Simple go build +email: $(GO_SRC) + CGO_ENABLED=0 GOOS=linux go build -a -ldflags "-extldflags '-static' -X main.Version=$(shell git describe --long --dirty)" -o email . + +vet: + go vet . + +test: + go test -v . + +.PHONY: test vet diff --git a/README.md b/README.md new file mode 100644 index 0000000..95864fd --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# Emailcli + +Because surprisingly, everything else out there just barely fails to +be useful to me. + +This utility does exactly one thing: wrap a Golang email library in a +command line interface. + +## Install + + go get github.com/wrouesnel/emailcli + +## Usage + +``` +email --username test@gmail.com --password somepassword \ + --host smtp.gmail.com --port 587 \ + --subject "Test mail" \ + --body "Test Body" test@gmail.com +``` + +For security, it also supports reading settings from environment +variables: +``` +export EMAIL_PASSWORD=somepassword +email --username test@gmail.com \ + --host smtp.gmail.com --port 587 \ + --subject "Test mail" \ + --body "Test Body" test@gmail.com +``` + +All command line variables can be used as environment variables by +appending EMAIL_ to the parameter name and capitalizing. \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 0000000..3dba03b --- /dev/null +++ b/main.go @@ -0,0 +1,87 @@ +package main + +import ( + "fmt" + "os" + + "github.com/jordan-wright/email" + "gopkg.in/alecthomas/kingpin.v2" + "net" + "net/smtp" + "io/ioutil" +) + +var ( + username = kingpin.Flag("username", "Username to authenticate to the SMTP server with").Envar("EMAIL_USERNAME").String() + password = kingpin.Flag("password", "Password to authenticate to the SMTP server with").Envar("EMAIL_PASSWORD").String() + + //usetls = kingpin.Flag("use-tls", "Use TLS to authenticate").Envar("EMAIL_USETLS").Bool() + host =kingpin.Flag("host", "Hostname").Envar("EMAIL_HOST").String() + port = kingpin.Flag("port", "Port number").Envar("EMAIL_PORT").Default("25").Uint16() + + attachments = kingpin.Flag("attach", "Files to attach to the email.").Envar("EMAIL_ATTACH").ExistingFiles() + + subject = kingpin.Flag("subject", "Subject line of email.").Envar("EMAIL_SUBJECT").String() + body = kingpin.Flag("body", "Body of email. Read from stdin if blank.").Envar("EMAIL_BODY").String() + + from = kingpin.Flag("from", "From address for email").Envar("EMAIL_FROM").String() + to = kingpin.Arg("to", "Email recipients").Strings() + + timeout = kingpin.Flag("timeout", "Timeout for mail sending").Envar("EMAIL_TIMEOUT").Duration() + poolsize = kingpin.Flag("concurrent-sends", "Max concurrent email send jobs").Envar("EMAIL_CONCURRENT_SENDS").Default("1").Int() +) + +func main() { + kingpin.Parse() + + if *timeout == 0 { + *timeout = -1 + } + + var bodytxt []byte + if *body == "" { + var err error + bodytxt, err = ioutil.ReadAll(os.Stdin) + if err != nil { + println(err) + os.Exit(1) + } + } else { + bodytxt = []byte(*body) + } + + err:= func() error { + sendPool := email.NewPool( + net.JoinHostPort(*host, fmt.Sprintf("%v", *port)), + *poolsize, + smtp.PlainAuth("", *username, *password, *host), + ) + defer sendPool.Close() + + for _, recipient := range *to { + m := email.NewEmail() + m.From = *from + m.To = []string{recipient} + m.Subject = *subject + m.Text = bodytxt + + for _, filename := range *attachments { + _, err := m.AttachFile(filename) + if err != nil { + println(err) + return err + } + } + + if err := sendPool.Send(m, *timeout); err != nil { + println("Error sending mail:", recipient, err.Error()) + } + } + return nil + }() + + if err != nil { + os.Exit(1) + } + os.Exit(0) +}