Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions imapserver/capability.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package imapserver

import (
"fmt"

"github.com/emersion/go-imap/v2"
"github.com/emersion/go-imap/v2/internal/imapwire"
)
Expand Down Expand Up @@ -86,6 +88,21 @@ func (c *Conn) availableCaps() []imap.Cap {

// Capabilities which require backend support and apply to both
// IMAP4rev1 and IMAP4rev2
Comment on lines 89 to 90
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we keep this comment next to the addAvailableCaps list below?

if appendLimitSession, ok := c.session.(SessionAppendLimit); ok {
if appendLimitSession.DiscloseLimit() {
limit := appendLimitSession.AppendLimit()
caps = append(caps, imap.Cap(fmt.Sprintf("APPENDLIMIT=%d", limit)))
} else {
caps = append(caps, imap.CapAppendLimit)
}
} else if limit, ok := available.AppendLimit(); ok {
if limit != nil {
caps = append(caps, imap.Cap(fmt.Sprintf("APPENDLIMIT=%d", *limit)))
} else {
caps = append(caps, imap.CapAppendLimit)
}
}

addAvailableCaps(&caps, available, []imap.Cap{
imap.CapSpecialUse,
imap.CapCreateSpecialUse,
Expand Down
19 changes: 19 additions & 0 deletions imapserver/imapmemserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,25 @@ type serverSession struct {
}

var _ imapserver.Session = (*serverSession)(nil)
var _ imapserver.SessionAppendLimit = (*serverSession)(nil)

// AppendLimit implements the SessionAppendLimit interface.
func (sess *serverSession) AppendLimit() uint32 {
if sess.UserSession != nil {
return sess.UserSession.AppendLimit()
}
// Default value for unauthenticated sessions
return 104857600 // 100 MiB
}

// DiscloseLimit implements the SessionAppendLimit interface.
func (sess *serverSession) DiscloseLimit() bool {
if sess.UserSession != nil {
return sess.UserSession.DiscloseLimit()
}
// Default for unauthenticated sessions - true means we show the limit
return true
}

func (sess *serverSession) Login(username, password string) error {
u := sess.server.user(username)
Expand Down
40 changes: 39 additions & 1 deletion imapserver/imapmemserver/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,35 @@ type (
type UserSession struct {
*user // immutable
*mailbox // may be nil

// appendLimit is the maximum size in bytes that can be uploaded to this server
// in an APPEND command
appendLimit uint32

// discloseLimit indicates whether the append limit should be advertised in the
// CAPABILITY response
discloseLimit bool
}

var _ imapserver.SessionIMAP4rev2 = (*UserSession)(nil)
var _ imapserver.SessionAppendLimit = (*UserSession)(nil)

// NewUserSession creates a new user session.
func NewUserSession(user *User) *UserSession {
return &UserSession{user: user}
return &UserSession{
user: user,
appendLimit: 104857600, // 100 MiB default
discloseLimit: true, // By default, disclose the limit in CAPABILITY
}
}

// NewUserSessionWithAppendLimit creates a new user session with a custom append limit.
func NewUserSessionWithAppendLimit(user *User, appendLimit uint32, discloseLimit bool) *UserSession {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we keep this simple and only have a single global append limit in imapmemserver?

return &UserSession{
user: user,
appendLimit: appendLimit,
discloseLimit: discloseLimit,
}
}

func (sess *UserSession) Close() error {
Expand Down Expand Up @@ -138,3 +160,19 @@ func (sess *UserSession) Idle(w *imapserver.UpdateWriter, stop <-chan struct{})
}
return sess.mailbox.Idle(w, stop)
}

// AppendLimit implements the SessionAppendLimit interface.
// It returns the maximum size in bytes that can be uploaded to this server in an APPEND command.
func (sess *UserSession) AppendLimit() uint32 {
// If appendLimit is not set (0), return a default large value
if sess.appendLimit == 0 {
return 104857600 // 100 MiB default
}
return sess.appendLimit
}

// DiscloseLimit implements the SessionAppendLimit interface.
// It indicates whether the append limit should be advertised in the CAPABILITY response.
func (sess *UserSession) DiscloseLimit() bool {
return sess.discloseLimit
}
15 changes: 15 additions & 0 deletions imapserver/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,18 @@ type SessionUnauthenticate interface {
// Authenticated state
Unauthenticate() error
}

// SessionAppendLimit is an IMAP session which has the same APPEND limit for
// all mailboxes.
type SessionAppendLimit interface {
Session

// AppendLimit returns the maximum size in bytes that can be uploaded to this server
// in an APPEND command.
AppendLimit() uint32

// DiscloseLimit indicates whether the limit should be advertised in the CAPABILITY
// response. If false, only "APPENDLIMIT" will be listed, without the actual limit.
// If true, "APPENDLIMIT=<limit>" will be listed.
DiscloseLimit() bool
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of this method? Servers can already reject literals which are too large in APPEND without advertising a limit (by returning an error).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPENDLIMIT capability can be advertised as APPENDLIMIT only or with explicit limit APPENDLIMIT=. In first you have to try to discover the limit while in the second you know before you try. The method allows to server to configure whether it will advertise it or not.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I'm not sure I'm following. If APPENDLIMIT is listed in Caps, then we already know that the server wants to disclose the limit?

How about the following:

  • If AppendLimit is not listed in Caps, never advertise AppendLimit
  • If AppendLimit is listed in Caps:
    • If SessionAppendLimit is implemented and returns a non-zero value, advertise APPENDLIMIT=n
    • Otherwise, advertise APPENDLIMIT without a value

}