Skip to content
Open
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
61 changes: 48 additions & 13 deletions acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,35 @@ import (
"strings"
)

// IMAP4 ACL extension (RFC 2086)
// IMAP4 ACL extension (RFC 4314, obsoletes RFC 2086)

// Right describes a set of operations controlled by the IMAP ACL extension.
type Right byte

const (
// Standard rights
RightLookup = Right('l') // mailbox is visible to LIST/LSUB commands
RightRead = Right('r') // SELECT the mailbox, perform CHECK, FETCH, PARTIAL, SEARCH, COPY from mailbox
RightSeen = Right('s') // keep seen/unseen information across sessions (STORE SEEN flag)
RightWrite = Right('w') // STORE flags other than SEEN and DELETED
RightInsert = Right('i') // perform APPEND, COPY into mailbox
RightPost = Right('p') // send mail to submission address for mailbox, not enforced by IMAP4 itself
RightCreate = Right('c') // CREATE new sub-mailboxes in any implementation-defined hierarchy
RightDelete = Right('d') // STORE DELETED flag, perform EXPUNGE
RightAdminister = Right('a') // perform SETACL
// Standard rights (RFC 4314 Section 2)
RightLookup = Right('l') // mailbox is visible to LIST/LSUB commands
RightRead = Right('r') // SELECT the mailbox, perform CHECK, FETCH, PARTIAL, SEARCH, COPY from mailbox
RightSeen = Right('s') // keep seen/unseen information across sessions (STORE SEEN flag)
RightWrite = Right('w') // STORE flags other than SEEN and DELETED
RightInsert = Right('i') // perform APPEND, COPY into mailbox
RightPost = Right('p') // send mail to submission address for mailbox, not enforced by IMAP4 itself
RightCreateChild = Right('k') // CREATE new sub-mailboxes (new in RFC 4314, replaces 'c')
RightDeleteMbox = Right('x') // DELETE mailbox (new in RFC 4314, replaces 'd' for mailbox deletion)
RightDeleteMsg = Right('t') // STORE DELETED flag (new in RFC 4314)
RightExpunge = Right('e') // perform EXPUNGE (new in RFC 4314, split from 'd')
RightAdminister = Right('a') // perform SETACL, DELETEACL, GETACL, LISTRIGHTS

// Obsolete rights from RFC 2086 (still supported for backwards compatibility)
RightCreate = Right('c') // obsolete, use RightCreateChild instead
RightDelete = Right('d') // obsolete, use RightDeleteMsg + RightExpunge instead
)

// RightSetAll contains all standard rights.
var RightSetAll = RightSet("lrswipcda")
// RightSetAll contains all standard rights (RFC 4314).
var RightSetAll = RightSet("lrswipkxtea")

// RightSetAllCompat contains all rights including obsolete RFC 2086 rights.
var RightSetAllCompat = RightSet("lrswipkxteacd")

// RightsIdentifier is an ACL identifier.
type RightsIdentifier string
Expand Down Expand Up @@ -102,3 +111,29 @@ func (rs1 RightSet) Equal(rs2 RightSet) bool {

return true
}

// ACLEntry represents a single ACL entry for a mailbox.
type ACLEntry struct {
Identifier RightsIdentifier // User identifier (email address, group, "anyone", etc.)
Rights RightSet // Rights granted to this identifier
}

// GetACLData represents the response to a GETACL command.
type GetACLData struct {
Mailbox string // Mailbox name
ACL []ACLEntry // List of ACL entries
}

// ListRightsData represents the response to a LISTRIGHTS command.
type ListRightsData struct {
Mailbox string // Mailbox name
Identifier RightsIdentifier // User identifier
RequiredRights RightSet // Rights that are always granted (usually empty)
OptionalRights []RightSet // Groups of optional rights that may be granted
}

// MyRightsData represents the response to a MYRIGHTS command.
type MyRightsData struct {
Mailbox string // Mailbox name
Rights RightSet // Rights the user has on this mailbox
}
88 changes: 88 additions & 0 deletions imapclient/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,47 @@ func (cmd *SetACLCommand) Wait() error {
return cmd.wait()
}

// DeleteACL sends a DELETEACL command.
//
// This command requires support for the ACL extension.
func (c *Client) DeleteACL(mailbox string, ri imap.RightsIdentifier) *DeleteACLCommand {
cmd := &DeleteACLCommand{}
enc := c.beginCommand("DELETEACL", cmd)
enc.SP().Mailbox(mailbox).SP().String(string(ri))
enc.end()
return cmd
}

// DeleteACLCommand is a DELETEACL command.
type DeleteACLCommand struct {
commandBase
}

func (cmd *DeleteACLCommand) Wait() error {
return cmd.wait()
}

// ListRights sends a LISTRIGHTS command.
//
// This command requires support for the ACL extension.
func (c *Client) ListRights(mailbox string, ri imap.RightsIdentifier) *ListRightsCommand {
cmd := &ListRightsCommand{}
enc := c.beginCommand("LISTRIGHTS", cmd)
enc.SP().Mailbox(mailbox).SP().String(string(ri))
enc.end()
return cmd
}

// ListRightsCommand is a LISTRIGHTS command.
type ListRightsCommand struct {
commandBase
data ListRightsData
}

func (cmd *ListRightsCommand) Wait() (*ListRightsData, error) {
return &cmd.data, cmd.wait()
}

// GetACL sends a GETACL command.
//
// This command requires support for the ACL extension.
Expand Down Expand Up @@ -83,6 +124,17 @@ func (c *Client) handleGetACL() error {
return nil
}

func (c *Client) handleListRights() error {
data, err := readListRights(c.dec)
if err != nil {
return fmt.Errorf("in listrights-response: %v", err)
}
if cmd := findPendingCmdByType[*ListRightsCommand](c); cmd != nil {
cmd.data = *data
}
return nil
}

// MyRightsCommand is a MYRIGHTS command.
type MyRightsCommand struct {
commandBase
Expand Down Expand Up @@ -136,3 +188,39 @@ func readGetACL(dec *imapwire.Decoder) (*GetACLData, error) {

return data, nil
}

// ListRightsData is the data returned by the LISTRIGHTS command.
type ListRightsData struct {
Mailbox string
Identifier imap.RightsIdentifier
RequiredRights imap.RightSet
OptionalRights []imap.RightSet
}

func readListRights(dec *imapwire.Decoder) (*ListRightsData, error) {
var (
data ListRightsData
identifierStr string
requiredStr string
)

if !dec.ExpectMailbox(&data.Mailbox) || !dec.ExpectSP() ||
!dec.ExpectAString(&identifierStr) || !dec.ExpectSP() ||
!dec.ExpectAString(&requiredStr) {
return nil, dec.Err()
}

data.Identifier = imap.RightsIdentifier(identifierStr)
data.RequiredRights = imap.RightSet(requiredStr)

// Read optional rights groups
for dec.SP() {
var optionalStr string
if !dec.ExpectAString(&optionalStr) {
return nil, dec.Err()
}
data.OptionalRights = append(data.OptionalRights, imap.RightSet(optionalStr))
}

return &data, nil
}
Loading