mirror of
https://github.com/wiggin77/mailrelay.git
synced 2025-02-19 17:10:47 +01:00
recv & send
This commit is contained in:
commit
3fd8f73c8b
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
mailrelay
|
||||
test
|
||||
vendor
|
17
.vscode/launch.json
vendored
Normal file
17
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${fileDirname}",
|
||||
"env": {},
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
}
|
71
Gopkg.lock
generated
Normal file
71
Gopkg.lock
generated
Normal file
@ -0,0 +1,71 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
digest = "1:0a2a75a7b0d611bf7ecb4e5a0054242815dc27e857b4b9f8ec62225993fd11b7"
|
||||
name = "github.com/asaskevich/EventBus"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "68a521d7cbbb7a859c2608b06342f384b3bd5f5a"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:9d8902545dea4a3e1440ff60428426de594266b60e2981394540a133128a03ce"
|
||||
name = "github.com/flashmob/go-guerrilla"
|
||||
packages = [
|
||||
".",
|
||||
"backends",
|
||||
"log",
|
||||
"mail",
|
||||
"response",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "1c628e503aedc9237ea6a9b13b3d3cdd0aab8f22"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747"
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:9e9193aa51197513b3abcb108970d831fbcf40ef96aa845c4f03276e1fa316d2"
|
||||
name = "github.com/sirupsen/logrus"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc"
|
||||
version = "v1.0.5"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:38f553aff0273ad6f367cb0a0f8b6eecbaef8dc6cb8b50e57b6a81c1d5b1e332"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["ssh/terminal"]
|
||||
pruneopts = "UT"
|
||||
revision = "e657309f52e71501f9934566ac06dc5c2f7f11a1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:f343f077a5b0bc3a3788b3a04e24dd417e3e25b2acb529c413e212d2c42416ef"
|
||||
name = "golang.org/x/sys"
|
||||
packages = [
|
||||
"unix",
|
||||
"windows",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "62eef0e2fa9b2c385f7b2778e763486da6880d37"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
input-imports = [
|
||||
"github.com/flashmob/go-guerrilla",
|
||||
"github.com/flashmob/go-guerrilla/backends",
|
||||
"github.com/flashmob/go-guerrilla/log",
|
||||
"github.com/flashmob/go-guerrilla/mail",
|
||||
"github.com/pkg/errors",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
34
Gopkg.toml
Normal file
34
Gopkg.toml
Normal file
@ -0,0 +1,34 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/flashmob/go-guerrilla"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
123
client.go
Normal file
123
client.go
Normal file
@ -0,0 +1,123 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/smtp"
|
||||
"net/textproto"
|
||||
|
||||
"github.com/flashmob/go-guerrilla/mail"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type closeable interface {
|
||||
Close() error
|
||||
}
|
||||
|
||||
// sendMail sends the contents of the envelope to a SMTP server.
|
||||
func sendMail(e *mail.Envelope, config *mailRelayConfig) error {
|
||||
server := fmt.Sprintf("%s:%d", config.Server, config.Port)
|
||||
to := getTo(e)
|
||||
|
||||
var msg bytes.Buffer
|
||||
msg.Write(e.Data.Bytes())
|
||||
msg.WriteString("\r\n")
|
||||
|
||||
fmt.Println("==== Starting email send ====")
|
||||
defer fmt.Println("==== Finished email send ====")
|
||||
var err error
|
||||
var conn *tls.Conn
|
||||
var client *smtp.Client
|
||||
var writer io.WriteCloser
|
||||
|
||||
tlsconfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
ServerName: config.Server,
|
||||
}
|
||||
|
||||
if conn, err = tls.Dial("tcp", server, tlsconfig); err != nil {
|
||||
return errors.Wrap(err, "dial error")
|
||||
}
|
||||
|
||||
if client, err = smtp.NewClient(conn, config.Server); err != nil {
|
||||
close(conn, "conn")
|
||||
return errors.Wrap(err, "newclient error")
|
||||
}
|
||||
shouldCloseClient := true
|
||||
defer func(shouldClose *bool) {
|
||||
if *shouldClose {
|
||||
close(client, "client")
|
||||
}
|
||||
}(&shouldCloseClient)
|
||||
|
||||
auth := smtp.PlainAuth("", config.Username, config.Password, config.Server)
|
||||
if err = client.Auth(auth); err != nil {
|
||||
return errors.Wrap(err, "auth error")
|
||||
}
|
||||
|
||||
if err = client.Mail(e.MailFrom.String()); err != nil {
|
||||
return errors.Wrap(err, "mail error")
|
||||
}
|
||||
|
||||
for _, addy := range to {
|
||||
if err = client.Rcpt(addy); err != nil {
|
||||
return errors.Wrap(err, "rcpt error")
|
||||
}
|
||||
}
|
||||
|
||||
if writer, err = client.Data(); err != nil {
|
||||
return errors.Wrap(err, "data error")
|
||||
}
|
||||
_, err = writer.Write(msg.Bytes())
|
||||
close(writer, "writer")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "write error")
|
||||
}
|
||||
|
||||
if err = client.Quit(); isQuitError(err) {
|
||||
return errors.Wrap(err, "quit error")
|
||||
}
|
||||
// We only need to close client if some other error prevented us
|
||||
// from getting to `client.Quit`
|
||||
shouldCloseClient = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func close(c closeable, what string) {
|
||||
err := c.Close()
|
||||
if err != nil {
|
||||
fmt.Printf("!!!!! Error closing %s: %v\n", what, err)
|
||||
}
|
||||
}
|
||||
|
||||
func isQuitError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
e, ok := err.(*textproto.Error)
|
||||
if ok {
|
||||
// SMTP codes 221 or 250 are acceptable here
|
||||
if e.Code == 221 || e.Code == 250 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// getTo returns the array of email addresses in the envelope.
|
||||
func getTo(e *mail.Envelope) []string {
|
||||
var ret []string
|
||||
for _, addy := range e.RcptTo {
|
||||
ret = append(ret, addy.String())
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func display(b []byte) {
|
||||
s := string(b)
|
||||
fmt.Println("################################")
|
||||
fmt.Printf("%s\n", s)
|
||||
fmt.Println("################################")
|
||||
}
|
70
main.go
Normal file
70
main.go
Normal file
@ -0,0 +1,70 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
)
|
||||
|
||||
type loggerLevels struct {
|
||||
Debug *log.Logger
|
||||
Error *log.Logger
|
||||
}
|
||||
|
||||
type mailRelayConfig struct {
|
||||
Server string `json:"server"`
|
||||
Port int `json:"port"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// Logger provides application logging.
|
||||
var Logger loggerLevels
|
||||
|
||||
func main() {
|
||||
|
||||
Logger.Debug = log.New(os.Stdout, "debug: ", log.Ldate|log.Ltime|log.Lshortfile)
|
||||
Logger.Error = log.New(os.Stderr, "error: ", log.Ldate|log.Ltime|log.Lshortfile)
|
||||
|
||||
var configFile string
|
||||
flag.StringVar(&configFile, "config", "/etc/mailrelay.json", "specifies JSON config file")
|
||||
flag.Parse()
|
||||
|
||||
appConfig, err := loadConfig(configFile)
|
||||
if err != nil {
|
||||
Logger.Error.Fatalf("loading config: %v", err)
|
||||
}
|
||||
|
||||
err = Start(appConfig)
|
||||
if err != nil {
|
||||
Logger.Error.Fatalf("starting server: %v", err)
|
||||
}
|
||||
|
||||
// Wait for SIGINT
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
signal.Notify(c, os.Kill)
|
||||
|
||||
// Block until a signal is received.
|
||||
s := <-c
|
||||
fmt.Println("Got signal:", s)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func loadConfig(path string) (*mailRelayConfig, error) {
|
||||
var cfg mailRelayConfig
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
parser := json.NewDecoder(file)
|
||||
if err := parser.Decode(&cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cfg, nil
|
||||
}
|
71
server.go
Normal file
71
server.go
Normal file
@ -0,0 +1,71 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
guerrilla "github.com/flashmob/go-guerrilla"
|
||||
"github.com/flashmob/go-guerrilla/backends"
|
||||
"github.com/flashmob/go-guerrilla/log"
|
||||
"github.com/flashmob/go-guerrilla/mail"
|
||||
)
|
||||
|
||||
// Start starts the server.
|
||||
func Start(appConfig *mailRelayConfig) (err error) {
|
||||
|
||||
cfg := &guerrilla.AppConfig{LogFile: log.OutputStdout.String(), AllowedHosts: []string{"warpmail.net"}}
|
||||
sc := guerrilla.ServerConfig{
|
||||
ListenInterface: "0.0.0.0:2525",
|
||||
IsEnabled: true,
|
||||
}
|
||||
cfg.Servers = append(cfg.Servers, sc)
|
||||
|
||||
bcfg := backends.BackendConfig{
|
||||
"save_workers_size": 3,
|
||||
"save_process": "HeadersParser|Header|Hasher|Debugger|MailRelay",
|
||||
"log_received_mails": true,
|
||||
"primary_mail_host": "homeoffice.com",
|
||||
"username": appConfig.Username,
|
||||
"password": appConfig.Password,
|
||||
"server": appConfig.Server,
|
||||
"port": appConfig.Port,
|
||||
}
|
||||
cfg.BackendConfig = bcfg
|
||||
|
||||
d := guerrilla.Daemon{Config: cfg}
|
||||
d.AddProcessor("MailRelay", mailRelayProcessor)
|
||||
|
||||
return d.Start()
|
||||
}
|
||||
|
||||
// mailRelayProcessor decorator relays emails to another SMTP server.
|
||||
var mailRelayProcessor = func() backends.Decorator {
|
||||
config := &mailRelayConfig{}
|
||||
initFunc := backends.InitializeWith(func(backendConfig backends.BackendConfig) error {
|
||||
configType := backends.BaseConfig(&mailRelayConfig{})
|
||||
bcfg, err := backends.Svc.ExtractConfig(backendConfig, configType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config = bcfg.(*mailRelayConfig)
|
||||
return nil
|
||||
})
|
||||
backends.Svc.AddInitializer(initFunc)
|
||||
|
||||
return func(p backends.Processor) backends.Processor {
|
||||
return backends.ProcessWith(
|
||||
func(e *mail.Envelope, task backends.SelectTask) (backends.Result, error) {
|
||||
if task == backends.TaskSaveMail {
|
||||
|
||||
err := sendMail(e, config)
|
||||
if err != nil {
|
||||
fmt.Printf("!!! %v\n", err)
|
||||
return backends.NewResult(fmt.Sprintf("554 Error: %s", err)), err
|
||||
}
|
||||
|
||||
return p.Process(e, task)
|
||||
}
|
||||
return p.Process(e, task)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user