Support multiple types of ssl / auth
This commit is contained in:
parent
99804336ea
commit
783c49b578
5 changed files with 297 additions and 1 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +1,2 @@
|
||||||
_*test.sh
|
_*test.sh
|
||||||
trello2mail
|
/trello2mail
|
||||||
|
|
102
cmd/trello2mail/config.go
Normal file
102
cmd/trello2mail/config.go
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
// "log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
// "net"
|
||||||
|
// "net/mail"
|
||||||
|
// "gopkg.in/russross/blackfriday.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConfigEntry struct {
|
||||||
|
Type string
|
||||||
|
Ptr interface{}
|
||||||
|
Values []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
EmailFrom string
|
||||||
|
EmailTo string
|
||||||
|
EmailSubject string
|
||||||
|
SmtpHostname string
|
||||||
|
SmtpPort uint16
|
||||||
|
SmtpUsername string
|
||||||
|
SmtpPassword string
|
||||||
|
SmtpAuthType string
|
||||||
|
SmtpSecurityType string
|
||||||
|
TrelloUrl string
|
||||||
|
TrelloToken string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfig() *Config {
|
||||||
|
return &Config{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (config *Config) ParseEnv() (int, error) {
|
||||||
|
// map env variables to config pointers
|
||||||
|
dataMap := map[string](ConfigEntry){
|
||||||
|
"EMAIL_FROM": ConfigEntry{"string", &(config.EmailFrom), nil},
|
||||||
|
"EMAIL_TO": ConfigEntry{"string", &(config.EmailTo), nil},
|
||||||
|
"EMAIL_SUBJECT": ConfigEntry{"string", &(config.EmailSubject), nil},
|
||||||
|
"TRELLO_URL": ConfigEntry{"string", &(config.TrelloUrl), nil},
|
||||||
|
"TRELLO_TOKEN": ConfigEntry{"string", &(config.TrelloToken), nil},
|
||||||
|
|
||||||
|
"SMTP_HOSTNAME": ConfigEntry{"string", &(config.SmtpHostname), nil},
|
||||||
|
"SMTP_USERNAME": ConfigEntry{"string", &(config.SmtpUsername), nil},
|
||||||
|
"SMTP_PASSWORD": ConfigEntry{"string", &(config.SmtpPassword), nil},
|
||||||
|
"SMTP_PORT": ConfigEntry{"uint16", &(config.SmtpPort), nil},
|
||||||
|
|
||||||
|
"SMTP_AUTH_TYPE": ConfigEntry{"string", &(config.SmtpAuthType), []string{"none", "plain", "login"}},
|
||||||
|
"SMTP_SECURITY_TYPE": ConfigEntry{"string", &(config.SmtpSecurityType), []string{"none", "tls", "starttls"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for envVar, mapEntry := range dataMap {
|
||||||
|
envValue := os.Getenv(envVar)
|
||||||
|
if len(envValue) == 0 {
|
||||||
|
return -1, errors.New(fmt.Sprintf(
|
||||||
|
"Empty environment variable. Please set %s value", envVar))
|
||||||
|
}
|
||||||
|
|
||||||
|
if mapEntry.Values != nil {
|
||||||
|
allowedValue := false
|
||||||
|
for _, v := range mapEntry.Values {
|
||||||
|
if v == envValue {
|
||||||
|
allowedValue = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !allowedValue {
|
||||||
|
return -1, errors.New(fmt.Sprintf(
|
||||||
|
"Wrong value for %s=%s. Value must be one of %v", envVar, envValue, mapEntry.Values))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch mapEntry.Type {
|
||||||
|
case "string":
|
||||||
|
*(mapEntry.Ptr.(*string)) = envValue
|
||||||
|
|
||||||
|
case "uint16":
|
||||||
|
u64, err := strconv.ParseUint(envValue, 10, 16)
|
||||||
|
if err != nil {
|
||||||
|
return -1, errors.New(fmt.Sprintf(
|
||||||
|
"Unable to convert %s=%s to unsigned int", envVar, envValue))
|
||||||
|
}
|
||||||
|
*(mapEntry.Ptr.(*uint16)) = uint16(u64)
|
||||||
|
|
||||||
|
case "bool":
|
||||||
|
b, err := strconv.ParseBool(envValue)
|
||||||
|
if err != nil {
|
||||||
|
return -1, errors.New(fmt.Sprintf(
|
||||||
|
"Unable to convert %s=%s to boolean", envVar, envValue))
|
||||||
|
}
|
||||||
|
*(mapEntry.Ptr.(*bool)) = b
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -1, errors.New(fmt.Sprintf("Undefined parser for %s<%s>", envVar, mapEntry.Type))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("%#v\n", config)
|
||||||
|
return 0, nil
|
||||||
|
}
|
91
cmd/trello2mail/mail.go
Normal file
91
cmd/trello2mail/mail.go
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
// "errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
// "os"
|
||||||
|
// "strconv"
|
||||||
|
// "net"
|
||||||
|
// "net/mail"
|
||||||
|
"net/smtp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MailHeaders map[string]string
|
||||||
|
|
||||||
|
func (headers *MailHeaders) ParseConfig(config Config) (int, error) {
|
||||||
|
(*headers)["From"] = config.EmailFrom
|
||||||
|
(*headers)["To"] = config.EmailTo
|
||||||
|
(*headers)["Subject"] = config.EmailSubject
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuth(config Config) *smtp.Auth {
|
||||||
|
|
||||||
|
switch config.SmtpAuthType {
|
||||||
|
case "plain":
|
||||||
|
auth := smtp.PlainAuth(
|
||||||
|
"",
|
||||||
|
config.SmtpUsername,
|
||||||
|
config.SmtpPassword,
|
||||||
|
config.SmtpHostname,
|
||||||
|
)
|
||||||
|
return &auth
|
||||||
|
|
||||||
|
case "login":
|
||||||
|
auth := LoginAuth(config.SmtpUsername, config.SmtpPassword)
|
||||||
|
return &auth
|
||||||
|
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTLS(config Config) *tls.Config {
|
||||||
|
// TLS config
|
||||||
|
return &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
ServerName: config.SmtpHostname,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSmtpClient(config Config) *smtp.Client {
|
||||||
|
address := fmt.Sprintf("%s:%d", config.SmtpHostname, config.SmtpPort)
|
||||||
|
tlsConfig := NewTLS(config)
|
||||||
|
switch config.SmtpSecurityType {
|
||||||
|
case "tls":
|
||||||
|
fmt.Printf("Creating TLS connection to %s...\n", address)
|
||||||
|
conn, err := tls.Dial("tcp", address, tlsConfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Creating SMTP client...")
|
||||||
|
c, err := smtp.NewClient(conn, config.SmtpHostname)
|
||||||
|
if err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
|
||||||
|
case "starttls":
|
||||||
|
fmt.Println("Creating SMTP client...")
|
||||||
|
c, err := smtp.Dial(address)
|
||||||
|
if err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Creating StartTLS connection to %s...\n", address)
|
||||||
|
c.StartTLS(tlsConfig)
|
||||||
|
|
||||||
|
return c
|
||||||
|
|
||||||
|
default:
|
||||||
|
// no SSL/TLS
|
||||||
|
fmt.Println("Creating SMTP client...")
|
||||||
|
c, err := smtp.Dial(address)
|
||||||
|
if err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
62
cmd/trello2mail/main.go
Normal file
62
cmd/trello2mail/main.go
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
// Examples
|
||||||
|
// - Sending emails with SSL : https://gist.github.com/chrisgillis/10888032
|
||||||
|
// - Project layout https://github.com/golang-standards/project-layout
|
||||||
|
// - Markdown rendering https://github.com/russross/blackfriday
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
// "net"
|
||||||
|
// "net/mail"
|
||||||
|
// "gopkg.in/russross/blackfriday.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BuildContent(config Config) []string {
|
||||||
|
// run taskell (download tasks from trello and export markdown)
|
||||||
|
// read file as an array
|
||||||
|
// insert trello board url
|
||||||
|
// convert to HTML
|
||||||
|
|
||||||
|
// output := blackfriday.Run(input, blackfriday.WithNoExtensions())
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ImportFromTrello() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Setup config
|
||||||
|
config := NewConfig()
|
||||||
|
if _, err := config.ParseEnv(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Printf("%#v\n", config)
|
||||||
|
|
||||||
|
// Build headers
|
||||||
|
headers := make(MailHeaders)
|
||||||
|
headers.ParseConfig(*config)
|
||||||
|
|
||||||
|
// Connect & authenticate
|
||||||
|
fmt.Println("Connecting...")
|
||||||
|
client := NewSmtpClient(*config)
|
||||||
|
|
||||||
|
// Build auth
|
||||||
|
authConfig := NewAuth(*config)
|
||||||
|
fmt.Printf("Authenticating...\n")
|
||||||
|
|
||||||
|
if err := client.Auth(*authConfig); err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println("Disconnecting...")
|
||||||
|
client.Quit()
|
||||||
|
|
||||||
|
// Write email
|
||||||
|
// mdTasklist := ImportFromTrello(config)
|
||||||
|
// htmlTasklist := ConvertMarkdown(markdown)
|
||||||
|
// BuildEmail(config, htmlTasklist)
|
||||||
|
|
||||||
|
}
|
41
cmd/trello2mail/smtp_login_auth.go
Normal file
41
cmd/trello2mail/smtp_login_auth.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// MIT license (c) andelf 2013
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/smtp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type loginAuth struct {
|
||||||
|
username, password string
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoginAuth(username, password string) smtp.Auth {
|
||||||
|
return &loginAuth{username, password}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
|
||||||
|
return "LOGIN", []byte(a.username), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
|
||||||
|
if more {
|
||||||
|
switch string(fromServer) {
|
||||||
|
case "Username:":
|
||||||
|
return []byte(a.username), nil
|
||||||
|
case "Password:":
|
||||||
|
return []byte(a.password), nil
|
||||||
|
default:
|
||||||
|
return nil, errors.New("Unkown fromServer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// usage:
|
||||||
|
// auth := LoginAuth("loginname", "password")
|
||||||
|
// err := smtp.SendMail(smtpServer + ":25", auth, fromAddress, toAddresses, []byte(message))
|
||||||
|
// or
|
||||||
|
// client, err := smtp.Dial(smtpServer)
|
||||||
|
// client.Auth(LoginAuth("loginname", "password"))
|
Loading…
Reference in a new issue