-
Notifications
You must be signed in to change notification settings - Fork 4
/
server.go
237 lines (198 loc) · 4.53 KB
/
server.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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
// Copyright 2014 The imapsrv Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the imapsrv.LICENSE file.
package imap
import (
"bufio"
"log"
"net"
)
// IMAP server configuration
type ServerConfig struct {
MaxClients uint
Listeners []Listener
Mailstores []Mailstore
}
// An IMAP Server
type Server struct {
// Server configuration
config *ServerConfig
// Number of active clients
activeClients uint
}
// A listener is listening on a given address. Ex: 0.0.0.0:193
type Listener struct {
Addr string
}
// An IMAP Client as seen by an IMAP server
type client struct {
conn net.Conn
bufin *bufio.Reader
bufout *bufio.Writer
id int
config *ServerConfig
}
// Create an IMAP server
func Create(config *ServerConfig) *Server {
server := new(Server)
server.config = config
return server
}
// Return the default server configuration
func DefaultServerConfig() *ServerConfig {
listeners := []Listener{
Listener{
Addr: "0.0.0.0:143",
},
}
return &ServerConfig{
Listeners: listeners,
MaxClients: 8,
}
}
// Add a mailstore to the config
func Store(m Mailstore) func(*Server) error {
return func(s *Server) error {
s.config.Mailstores = append(s.config.Mailstores, m)
return nil
}
}
// test if 2 listeners are equal
func equalListeners(l1, l2 []Listener) bool {
for i, l := range l1 {
if l != l2[i] {
return false
}
}
return true
}
// Add an interface to listen to
func Listen(Addr string) func(*Server) error {
return func(s *Server) error {
// if we only have the default config we should override it
dc := DefaultServerConfig()
l := Listener{
Addr: Addr,
}
if equalListeners(dc.Listeners, s.config.Listeners) {
s.config.Listeners = []Listener{l}
} else {
s.config.Listeners = append(s.config.Listeners, l)
}
return nil
}
}
// Set MaxClients config
func MaxClients(max uint) func(*Server) error {
return func(s *Server) error {
s.config.MaxClients = max
return nil
}
}
func NewServer(options ...func(*Server) error) *Server {
// set the default config
s := &Server{}
dc := DefaultServerConfig()
s.config = dc
// override the config with the functional options
for _, option := range options {
err := option(s)
if err != nil {
panic(err)
}
}
//Check if we can listen on default ports, if not try to find a free port
if equalListeners(dc.Listeners, s.config.Listeners) {
listener := s.config.Listeners[0]
l, err := net.Listen("tcp", listener.Addr)
if err != nil {
l, err = net.Listen("tcp4", ":0") // this will ask the OS to give us a free port
if err != nil {
panic("Can't listen on any port")
}
l.Close()
s.config.Listeners[0].Addr = l.Addr().String()
} else {
l.Close()
}
}
return s
}
// Start an IMAP server
func (s *Server) Start() error {
// Start listening for IMAP connections
for _, iface := range s.config.Listeners {
listener, err := net.Listen("tcp", iface.Addr)
if err != nil {
log.Fatalf("IMAP cannot listen on %s, %v", iface.Addr, err)
}
log.Print("IMAP server listening on ", iface.Addr)
clientNumber := 1
for {
// Accept a connection from a new client
conn, err := listener.Accept()
if err != nil {
log.Print("IMAP accept error, ", err)
continue
}
// Handle the client
client := &client{
conn: conn,
bufin: bufio.NewReader(conn),
bufout: bufio.NewWriter(conn),
id: clientNumber,
config: s.config,
}
go client.handle()
clientNumber += 1
}
}
return nil
}
// Handle requests from an IMAP client
func (c *client) handle() {
// Close the client on exit from this function
defer c.close()
// Handle parser panics gracefully
defer func() {
if e := recover(); e != nil {
err := e.(parseError)
c.logError(err)
fatalResponse(c.bufout, err)
}
}()
// Create a parser
parser := createParser(c.bufin)
// Write the welcome message
err := ok("*", "IMAP4rev1 Service Ready").write(c.bufout)
if err != nil {
c.logError(err)
return
}
// Create a session
sess := createSession(c.id, c.config)
for {
// Get the next IMAP command
command := parser.next()
// Execute the IMAP command
response := command.execute(sess)
// Write back the response
err = response.write(c.bufout)
if err != nil {
c.logError(err)
return
}
// Should the connection be closed?
if response.closeConnection {
return
}
}
}
// Close an IMAP client
func (c *client) close() {
c.conn.Close()
}
// Log an error
func (c *client) logError(err error) {
log.Printf("IMAP client %d, %v", c.id, err)
}