-
Notifications
You must be signed in to change notification settings - Fork 0
/
protocol_loop.go
94 lines (76 loc) · 1.62 KB
/
protocol_loop.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
package smtpd
import (
"bufio"
"log"
"strings"
)
type protocolLoop struct {
ex *Exchange
commands map[string]commandFactory
command command
next []string
waiting bool
}
func (l *protocolLoop) Run() {
defer l.ex.Close()
scanner := bufio.NewScanner(l.ex)
l.ex.Reply(ReplyServiceReady, "Simple Mail Transfer Service Ready")
for {
got := scanner.Scan()
line := scanner.Text()
if !got {
err := scanner.Err()
if err != nil {
log.Println(err)
}
break
}
if !l.waiting {
l.processCommand(line)
continue
}
name := l.readCommandName(line)
if name == CommandQuit {
l.ex.Reply(ReplyServiceClosing, "Service closing transmission channel")
break
}
factory, ok := l.commands[name]
if !ok {
l.ex.Reply(ReplyUnknown, "unrecognized command")
continue
}
if factory == nil {
l.ex.Reply(ReplyNotImplemented, "not implemented")
continue
}
command := factory.New()
if !l.canRun(name, command) {
l.ex.Reply(ReplyBadSequence, "bad command sequence")
continue
}
l.command = command
l.processCommand(line)
}
}
func (l *protocolLoop) processCommand(line string) {
r, done := l.command.Process(line, l.ex)
l.waiting = done
if r != nil {
l.ex.Reply(r.Code, r.Message)
}
if done {
next := l.command.Next()
if next != nil {
l.next = next
}
}
}
func (l *protocolLoop) canRun(name string, c command) bool {
return l.isStateless(c) || contains(l.next, name)
}
func (l *protocolLoop) isStateless(c command) bool {
return c.Next() == nil
}
func (l *protocolLoop) readCommandName(line string) string {
return strings.ToUpper(safeSubstring(line, 4))
}