Skip to content

Commit 56006d1

Browse files
committed
Merge support for APPENDLIMIT extension
1 parent 59f99f0 commit 56006d1

File tree

9 files changed

+72
-8
lines changed

9 files changed

+72
-8
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ includes:
139139
* [CHILDREN](https://tools.ietf.org/html/rfc3348)
140140
* [UNSELECT](https://tools.ietf.org/html/rfc3691)
141141
* [COMPRESS](https://tools.ietf.org/html/rfc4978)
142+
* [APPENDLIMIT](https://tools.ietf.org/html/rfc7889)
142143

143144
Support for other extensions is provided via separate packages. See below.
144145

@@ -150,7 +151,6 @@ Commands defined in IMAP extensions are available in other packages. See [the
150151
wiki](https://github.com/emersion/go-imap/wiki/Using-extensions#using-client-extensions)
151152
to learn how to use them.
152153

153-
* [APPENDLIMIT](https://github.com/emersion/go-imap-appendlimit)
154154
* [ENABLE](https://github.com/emersion/go-imap-enable)
155155
* [ID](https://github.com/ProtonMail/go-imap-id)
156156
* [IDLE](https://github.com/emersion/go-imap-idle)

backend/appendlimit.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package backend
2+
3+
import (
4+
"errors"
5+
)
6+
7+
// An error that should be returned by User.CreateMessage when the message size
8+
// is too big.
9+
var ErrTooBig = errors.New("Message size exceeding limit")
10+
11+
// A backend that supports retrieving per-user message size limits.
12+
type AppendLimitBackend interface {
13+
Backend
14+
15+
// Get the fixed maximum message size in octets that the backend will accept
16+
// when creating a new message. If there is no limit, return nil.
17+
CreateMessageLimit() *uint32
18+
}
19+
20+
// A user that supports retrieving per-user message size limits.
21+
type AppendLimitUser interface {
22+
User
23+
24+
// Get the fixed maximum message size in octets that the backend will accept
25+
// when creating a new message. If there is no limit, return nil.
26+
//
27+
// This overrides the global backend limit.
28+
CreateMessageLimit() *uint32
29+
}

imap.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ const (
1717
StatusUidNext StatusItem = "UIDNEXT"
1818
StatusUidValidity StatusItem = "UIDVALIDITY"
1919
StatusUnseen StatusItem = "UNSEEN"
20+
21+
StatusAppendLimit StatusItem = "APPENDLIMIT"
2022
)
2123

2224
// A FetchItem is a message data item that can be fetched.

mailbox.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,10 @@ type MailboxStatus struct {
216216
// Together with a UID, it is a unique identifier for a message.
217217
// Must be greater than or equal to 1.
218218
UidValidity uint32
219+
220+
// Per-mailbox limit of message size. Set only if server supports the
221+
// APPENDLIMIT extension.
222+
AppendLimit uint32
219223
}
220224

221225
// Create a new mailbox status that will contain the specified items.
@@ -258,6 +262,8 @@ func (status *MailboxStatus) Parse(fields []interface{}) error {
258262
status.UidNext, err = ParseNumber(f)
259263
case StatusUidValidity:
260264
status.UidValidity, err = ParseNumber(f)
265+
case StatusAppendLimit:
266+
status.AppendLimit, err = ParseNumber(f)
261267
default:
262268
status.Items[k] = f
263269
}
@@ -285,6 +291,8 @@ func (status *MailboxStatus) Format() []interface{} {
285291
v = status.UidNext
286292
case StatusUidValidity:
287293
v = status.UidValidity
294+
case StatusAppendLimit:
295+
v = status.AppendLimit
288296
}
289297

290298
fields = append(fields, RawString(k), v)

server/cmd_any_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func TestCapability(t *testing.T) {
2727
io.WriteString(c, "a001 CAPABILITY\r\n")
2828

2929
scanner.Scan()
30-
if scanner.Text() != "* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR SPECIAL-USE CHILDREN UNSELECT COMPRESS=DEFLATE AUTH=PLAIN" {
30+
if scanner.Text() != "* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR SPECIAL-USE CHILDREN UNSELECT COMPRESS=DEFLATE APPENDLIMIT AUTH=PLAIN" {
3131
t.Fatal("Bad capability:", scanner.Text())
3232
}
3333

@@ -88,7 +88,7 @@ func TestServer_Enable(t *testing.T) {
8888
io.WriteString(c, "a001 CAPABILITY\r\n")
8989

9090
scanner.Scan()
91-
if scanner.Text() != "* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR SPECIAL-USE CHILDREN UNSELECT COMPRESS=DEFLATE AUTH=PLAIN XNOOP" {
91+
if scanner.Text() != "* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR SPECIAL-USE CHILDREN UNSELECT COMPRESS=DEFLATE APPENDLIMIT AUTH=PLAIN XNOOP" {
9292
t.Fatal("Bad capability:", scanner.Text())
9393
}
9494

@@ -117,8 +117,8 @@ func TestServer_EnableAuth(t *testing.T) {
117117
io.WriteString(c, "a001 CAPABILITY\r\n")
118118

119119
scanner.Scan()
120-
if scanner.Text() != "* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR SPECIAL-USE CHILDREN UNSELECT COMPRESS=DEFLATE AUTH=PLAIN AUTH=XNOOP" &&
121-
scanner.Text() != "* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR SPECIAL-USE CHILDREN UNSELECT COMPRESS=DEFLATE AUTH=XNOOP AUTH=PLAIN" {
120+
if scanner.Text() != "* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR SPECIAL-USE CHILDREN UNSELECT COMPRESS=DEFLATE APPENDLIMIT AUTH=PLAIN AUTH=XNOOP" &&
121+
scanner.Text() != "* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR SPECIAL-USE CHILDREN UNSELECT COMPRESS=DEFLATE APPENDLIMIT AUTH=XNOOP AUTH=PLAIN" {
122122
t.Fatal("Bad capability:", scanner.Text())
123123
}
124124

server/cmd_auth.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,13 @@ func (cmd *Append) Handle(conn Conn) error {
253253
}
254254

255255
if err := mbox.CreateMessage(cmd.Flags, cmd.Date, cmd.Message); err != nil {
256+
if err == backend.ErrTooBig {
257+
return ErrStatusResp(&imap.StatusResp{
258+
Type: imap.StatusRespNo,
259+
Code: "TOOBIG",
260+
Info: "Message size exceeding limit",
261+
})
262+
}
256263
return err
257264
}
258265

server/cmd_noauth_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func testServerTLS(t *testing.T) (s *server.Server, c net.Conn, scanner *bufio.S
3030

3131
io.WriteString(c, "a001 CAPABILITY\r\n")
3232
scanner.Scan()
33-
if scanner.Text() != "* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR SPECIAL-USE CHILDREN UNSELECT COMPRESS=DEFLATE STARTTLS LOGINDISABLED" {
33+
if scanner.Text() != "* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR SPECIAL-USE CHILDREN UNSELECT COMPRESS=DEFLATE APPENDLIMIT STARTTLS LOGINDISABLED" {
3434
t.Fatal("Bad CAPABILITY response:", scanner.Text())
3535
}
3636
scanner.Scan()
@@ -61,7 +61,7 @@ func TestStartTLS(t *testing.T) {
6161
io.WriteString(c, "a001 CAPABILITY\r\n")
6262

6363
scanner.Scan()
64-
if scanner.Text() != "* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR SPECIAL-USE CHILDREN UNSELECT COMPRESS=DEFLATE AUTH=PLAIN" {
64+
if scanner.Text() != "* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR SPECIAL-USE CHILDREN UNSELECT COMPRESS=DEFLATE APPENDLIMIT AUTH=PLAIN" {
6565
t.Fatal("Bad CAPABILITY response:", scanner.Text())
6666
}
6767
}

server/conn.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,24 @@ func (c *conn) Close() error {
165165
func (c *conn) Capabilities() []string {
166166
caps := []string{"IMAP4rev1", "LITERAL+", "SASL-IR", "SPECIAL-USE", "CHILDREN", "UNSELECT", "COMPRESS=DEFLATE"}
167167

168+
appendLimitSet := false
169+
if c.ctx.State == imap.AuthenticatedState {
170+
if u, ok := c.ctx.User.(backend.AppendLimitUser); ok {
171+
if limit := u.CreateMessageLimit(); limit != nil {
172+
caps = append(caps, fmt.Sprintf("APPENDLIMIT=%v", *limit))
173+
appendLimitSet = true
174+
}
175+
}
176+
} else if be, ok := c.Server().Backend.(backend.AppendLimitBackend); ok {
177+
if limit := be.CreateMessageLimit(); limit != nil {
178+
caps = append(caps, fmt.Sprintf("APPENDLIMIT=%v", *limit))
179+
appendLimitSet = true
180+
}
181+
}
182+
if !appendLimitSet {
183+
caps = append(caps, "APPENDLIMIT")
184+
}
185+
168186
if c.ctx.State == imap.NotAuthenticatedState {
169187
if !c.IsTLS() && c.s.TLSConfig != nil {
170188
caps = append(caps, "STARTTLS")

server/server_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func TestServer_greeting(t *testing.T) {
4040
scanner.Scan() // Wait for greeting
4141
greeting := scanner.Text()
4242

43-
if greeting != "* OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR SPECIAL-USE CHILDREN UNSELECT COMPRESS=DEFLATE AUTH=PLAIN] IMAP4rev1 Service Ready" {
43+
if greeting != "* OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR SPECIAL-USE CHILDREN UNSELECT COMPRESS=DEFLATE APPENDLIMIT AUTH=PLAIN] IMAP4rev1 Service Ready" {
4444
t.Fatal("Bad greeting:", greeting)
4545
}
4646
}

0 commit comments

Comments
 (0)