-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
144 lines (116 loc) · 3.08 KB
/
main.go
File metadata and controls
144 lines (116 loc) · 3.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package smtp
import (
"context"
"crypto/tls"
"fmt"
"net"
"net/smtp"
"github.com/scorify/schema"
)
type Schema struct {
Server string `key:"server"`
Port int `key:"port" default:"25"`
Username string `key:"username"`
Password string `key:"password"`
Sender string `key:"sender"`
Recipient string `key:"recipient"`
Body string `key:"body"`
Secure bool `key:"secure"`
}
func Validate(config string) error {
conf := Schema{}
err := schema.Unmarshal([]byte(config), &conf)
if err != nil {
return err
}
if conf.Server == "" {
return fmt.Errorf("target is required; got %q", conf.Server)
}
if conf.Port <= 0 || conf.Port > 65535 {
return fmt.Errorf("provided invalid port: %d", conf.Port)
}
if conf.Username == "" {
return fmt.Errorf("username is required; got %q", conf.Username)
}
if conf.Password == "" {
return fmt.Errorf("password is required; got %q", conf.Password)
}
if conf.Sender == "" {
return fmt.Errorf("sender is required; got %q", conf.Sender)
}
if conf.Recipient == "" {
return fmt.Errorf("recipient is required; got %q", conf.Recipient)
}
if conf.Body == "" {
return fmt.Errorf("body is required; got %q", conf.Body)
}
return nil
}
// net/smtp does not allow you to send smtp.PlainAuth with not using TLS
// This is able to trick net/smtp to always thinking you are authenticating over TLS
type unencryptedAuth struct {
smtp.Auth
}
func (a unencryptedAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
server.TLS = true
return a.Auth.Start(server)
}
func Run(ctx context.Context, config string) error {
conf := Schema{}
err := schema.Unmarshal([]byte(config), &conf)
if err != nil {
return err
}
var conn net.Conn
connStr := fmt.Sprintf("%s:%d", conf.Server, conf.Port)
if conf.Secure {
deadline, ok := ctx.Deadline()
if !ok {
return fmt.Errorf("context deadline is not set")
}
conn, err = tls.DialWithDialer(
&net.Dialer{Deadline: deadline},
"tcp",
connStr,
&tls.Config{InsecureSkipVerify: true},
)
} else {
dialer := &net.Dialer{}
conn, err = dialer.DialContext(ctx, "tcp", connStr)
}
if err != nil {
return err
}
defer conn.Close()
client, err := smtp.NewClient(conn, conf.Server)
if err != nil {
return fmt.Errorf("failed creating new smtp client: %w", err)
}
defer client.Close()
err = client.Auth(unencryptedAuth{smtp.PlainAuth("", conf.Username, conf.Password, conf.Server)})
if err != nil {
return fmt.Errorf("failed to authenticate to server: %w", err)
}
err = client.Mail(conf.Sender)
if err != nil {
return fmt.Errorf("failed to set sender %q: %w", conf.Sender, err)
}
err = client.Rcpt(conf.Recipient)
if err != nil {
return fmt.Errorf("failed to set recipient %q: %w", conf.Recipient, err)
}
wc, err := client.Data()
if err != nil {
return fmt.Errorf("failed to create data writer: %w", err)
}
_, err = fmt.Fprint(wc, conf.Body)
if err != nil {
defer wc.Close()
return fmt.Errorf("failed to write email body: %w", err)
}
err = wc.Close()
if err != nil {
return fmt.Errorf("failed to close body writer: %w", err)
}
return nil
}