-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
transaction.go
207 lines (176 loc) · 5.64 KB
/
transaction.go
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
package msmtpd
import (
"bufio"
"context"
"crypto/tls"
"net"
"net/mail"
"sync"
"time"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)
// Protocol represents the protocol used in the SMTP session
type Protocol string
const (
// SMTP means Simple Mail Transfer Protocol
SMTP Protocol = "SMTP"
// ESMTP means Extended Simple Mail Transfer Protocol, because it has some extra features
// Simple Mail Transfer Protocol doesn't have
ESMTP Protocol = "ESMTP"
)
// Transaction used to handle all SMTP protocol interactions with client
type Transaction struct {
// ID is unique transaction identificator
ID string `json:"id"`
// StartedAt depicts moment when transaction was initiated
StartedAt time.Time
// ServerName depicts how out smtp server names itself
ServerName string
// Addr depicts network address of remote client
Addr net.Addr
// PTRs are DNS PTR record is exactly the opposite of the 'A' record, which provides the IP address associated with a
// domain name.
PTRs []string
// TLS Connection details, if encryption is enabled
// Ptrs are DNS pointer record which provides the domain name associated with an IP address.
TLS *tls.ConnectionState
// Encrypted means connection is encrypted by TLS
Encrypted bool
// Secured means TLS handshake succeeded
Secured bool
// Logger is logging system inherited from server
Logger Logger
// HeloName is how client introduced himself via HELO/EHLO command
HeloName string
// Protocol used, SMTP or ESMTP
Protocol Protocol
// Username as provided by via authorization process command
Username string
// Password from authentication, if authenticated
Password string
// MailFrom stores address from which this message is originated as client says via `MAIL FROM:`
MailFrom mail.Address
// RcptTo stores addresses for which this message should be delivered as client says via `RCPT TO:`
RcptTo []mail.Address
// Body stores unparsed message body
Body []byte
// Parsed stores parsed message body
Parsed *mail.Message
// mu is mutex used to protect writing facts, counters and flags
mu *sync.Mutex
// facts are map of string data related to transaction
facts map[string]string
// counters are map of float data related to transaction
counters map[string]float64
// flags are map of bool data related to transaction
flags map[string]bool
// Aliases are actual users addresses used by delivery plugins
Aliases []mail.Address
// ctx is main transaction context
ctx context.Context
// cancel cancels ctx context
cancel context.CancelFunc
// Span is OpenTelemetry span being used in transaction
Span trace.Span
server *Server
conn net.Conn
reader *bufio.Reader
writer *bufio.Writer
scanner *bufio.Scanner
// closeHandlersCalled used to ensure close handlers are called only once
closeHandlersCalled bool
// dataHandlersCalledProperly shows if data handlers for transaction are called properly,
// so we consider it is delivered
dataHandlersCalledProperly bool
}
// Context returns transaction context, which is canceled when transaction is closed
func (t *Transaction) Context() context.Context {
if t.ctx == nil {
return context.TODO()
}
return t.ctx
}
/*
* Metadata manipulation
*/
// SetFact sets string parameter Transaction.facts
func (t *Transaction) SetFact(name, value string) {
t.mu.Lock()
defer t.mu.Unlock()
t.Span.SetAttributes(attribute.String(name, value))
t.facts[name] = value
}
// GetFact returns string fact from Transaction.facts
func (t *Transaction) GetFact(name string) (value string, found bool) {
value, found = t.facts[name]
return
}
// Incr increments transaction counter
func (t *Transaction) Incr(key string, delta float64) (newVal float64) {
t.mu.Lock()
defer t.mu.Unlock()
old, found := t.counters[key]
if found {
newVal = old + delta
t.counters[key] = newVal
t.LogTrace("Incrementing %s by %v from %v to %v", key, delta, old, newVal)
t.Span.SetAttributes(attribute.Float64(key, newVal))
return newVal
}
t.counters[key] = delta
t.LogTrace("Setting counter %s to %v", key, delta)
t.Span.SetAttributes(attribute.Float64(key, delta))
return t.counters[key]
}
// GetCounter returns counter value
func (t *Transaction) GetCounter(key string) (val float64, found bool) {
val, found = t.counters[key]
return
}
// SetFlag set flag enabled for transaction
func (t *Transaction) SetFlag(name string) {
t.mu.Lock()
defer t.mu.Unlock()
t.Span.SetAttributes(attribute.Bool(name, true))
t.flags[name] = true
}
// UnsetFlag unsets boolean flag from transaction
func (t *Transaction) UnsetFlag(name string) {
t.mu.Lock()
defer t.mu.Unlock()
_, found := t.flags[name]
if found {
delete(t.flags, name)
}
}
// IsFlagSet returns true, if flag is set
func (t *Transaction) IsFlagSet(name string) bool {
val, found := t.flags[name]
if found {
return val
}
return false
}
/*
* Karma manipulation
*/
const karmaCounterName = "karma"
// Karma returns current transaction karma
func (t *Transaction) Karma() int {
karma, found := t.counters[karmaCounterName]
if found {
return int(karma)
}
return 0
}
// Love grants good points to karma, promising message to enter Paradise for SMTP transactions, aka dovecot server socket for accepting messages via SMTP
func (t *Transaction) Love(delta int) (newVal int) {
t.LogDebug("Granting %v love for transaction", delta)
return int(t.Incr(karmaCounterName, float64(delta)))
}
// Hate grants bad points to karma, restricting message to enter Paradise for SMTP transactions, aka dovecot server socket for accepting messages via SMTP
func (t *Transaction) Hate(delta int) (newVal int) {
t.LogDebug("Granting %v hate for transaction", delta)
return int(t.Incr(karmaCounterName, -float64(delta)))
}