diff --git a/imap/command.go b/imap/command.go index f1f8298..94d7d62 100644 --- a/imap/command.go +++ b/imap/command.go @@ -355,6 +355,13 @@ func defaultCommands() map[string]*CommandConfig { // RFC 3691 "UNSELECT": &CommandConfig{States: sel, Exclusive: true}, + // RFC 4314 + "SETACL": &CommandConfig{States: auth}, + "DELETEACL": &CommandConfig{States: auth}, + "GETACL": &CommandConfig{States: auth, Filter: LabelFilter("ACL")}, + "LISTRIGHTS": &CommandConfig{States: auth, Filter: NameFilter}, + "MYRIGHTS": &CommandConfig{States: auth, Filter: NameFilter}, + // RFC 4315 "UID EXPUNGE": &CommandConfig{States: sel, Filter: NameFilter}, diff --git a/imap/doc.go b/imap/doc.go index 33b895d..ee5730d 100644 --- a/imap/doc.go +++ b/imap/doc.go @@ -95,6 +95,7 @@ The following RFCs are implemented by this package: http://tools.ietf.org/html/rfc3501 -- INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1 http://tools.ietf.org/html/rfc3516 -- IMAP4 Binary Content Extension http://tools.ietf.org/html/rfc3691 -- Internet Message Access Protocol (IMAP) UNSELECT command + http://tools.ietf.org/html/rfc4314 -- IMAP4 Access Control List (ACL) Extension http://tools.ietf.org/html/rfc4315 -- Internet Message Access Protocol (IMAP) - UIDPLUS extension http://tools.ietf.org/html/rfc4616 -- The PLAIN Simple Authentication and Security Layer (SASL) Mechanism http://tools.ietf.org/html/rfc4959 -- IMAP Extension for Simple Authentication and Security Layer (SASL) Initial Client Response diff --git a/imap/imap.go b/imap/imap.go index 20b170a..e8929e4 100644 --- a/imap/imap.go +++ b/imap/imap.go @@ -477,6 +477,58 @@ func (c *Client) ID(info ...string) (cmd *Command, err error) { return c.Send("ID", f) } +// SetAcl command changes the access control list on the specified +// mailbox so that the specified Acl.identifier is granted permissions as +// specified in Acl.Rights. +// See RFC 4314 for additional information. +func (c *Client) SetAcl(mbox string, acl *Acl) (cmd *Command, err error) { + if !c.Caps["ACL"] { + return nil, NotAvailableError("ACL") + } + return c.Send("SETACL", c.Quote(UTF7Encode(mbox)), c.Quote(acl.Identifier), c.Quote(acl.Rights)) +} + +// DeleteAcl command removes any pair for the +// specified identifier from the access control list for the specified mailbox +// See RFC 4314 for additional information. +func (c *Client) DeleteAcl(mbox string, identifier string) (cmd *Command, err error) { + if !c.Caps["ACL"] { + return nil, NotAvailableError("ACL") + } + return c.Send("DELETEACL", c.Quote(UTF7Encode(mbox)), c.Quote(identifier)) +} + +// GetAcl returns identifier right pairs of ACL +// Each pair contains the identifier for which the entry applies followed by the +// set of rights that the identifier has. +// See RFC 4314 for additional information. +func (c *Client) GetAcl(mbox string) (cmd *Command, err error) { + if !c.Caps["ACL"] { + return nil, NotAvailableError("ACL") + } + return c.Send("GETACL", c.Quote(UTF7Encode(mbox))) +} + +// ListRights command takes a mailbox name and an identifier and +// returns information about what rights can be granted to the +// identifier in the ACL for the mailbox. +// See RFC 4314 for additional information. +func (c *Client) ListRights(mbox string, identifier string) (cmd *Command, err error) { + if !c.Caps["ACL"] { + return nil, NotAvailableError("ACL") + } + return c.Send("LISTRIGHTS", c.Quote(UTF7Encode(mbox)), c.Quote(identifier)) +} + +// MyRights command returns the set of rights that the user has to mailbox +// See RFC 4314 for additional information. +func (c *Client) MyRights(mbox string) (cmd *Command, err error) { + if !c.Caps["ACL"] { + return nil, NotAvailableError("ACL") + } + return c.Send("MYRIGHTS", c.Quote(UTF7Encode(mbox))) +} + // CompressDeflate enables data compression using the DEFLATE algorithm. The // compression level must be between -1 and 9 (see compress/flate). See RFC 4978 // for additional information. diff --git a/imap/response.go b/imap/response.go index 45d6109..3cfae8a 100644 --- a/imap/response.go +++ b/imap/response.go @@ -337,6 +337,88 @@ func (rsp *Response) QuotaRoot() (mbox string, roots []string) { return } +// Acl represents a single identifier right pair a mailbox +// returned in a GETACL response, as described in RFC 4314. +type Acl struct { + Identifier string // Identifier (user) + Rights string // Rights +} + +// Acl returns the mailbox name and an array of Acl pairs. +// Each Acl pair contains and identifier for which the entry applies +// and set of rights that the identifier has. +func (rsp *Response) Acl() (mbox string, acl []*Acl) { + type vt struct { + mbox string + acl []*Acl + } + v, ok := rsp.Decoded.(*vt) + if !ok && rsp.Decoded == nil && rsp.Label == "ACL" { + mbox = AsMailbox(rsp.Fields[1]) + acllist := rsp.Fields[2:] + if len(acllist)%2 != 0 { + return + } + acl = make([]*Acl, len(acllist)/2) + for i := 0; i < len(acllist); i += 2 { + acl[i/2] = &Acl{ + Identifier: AsString(acllist[i]), + Rights: AsString(acllist[i+1]), + } + } + } else if ok { + mbox, acl = v.mbox, v.acl + } + return +} + +// ListRights response occurs as a result of a LISTRIGHTS command. +// The first two strings are the mailbox name and identifier for which +// this rights list applies. Following is an array containing the +// (possibly empty) set of rights the identifier will always be granted +// in the mailbox. +func (rsp *Response) ListRights() (mbox string, rights string, optional []string) { + type vt struct { + mbox string + rights string + optional []string + } + v, ok := rsp.Decoded.(*vt) + if !ok && rsp.Decoded == nil && rsp.Label == "LISTRIGHTS" { + mbox = AsMailbox(rsp.Fields[2]) + rights = AsString(rsp.Fields[3]) + if len(rsp.Fields) < 4 { + return + } + optional = make([]string, len(rsp.Fields)-4) + for i := 4; i < len(rsp.Fields); i++ { + optional[i-4] = AsString(rsp.Fields[i]) + } + + } else if ok { + mbox, rights, optional = v.mbox, v.rights, v.optional + } + return +} + +// MyRights response occurs as a result of a MYRIGHTS command. The +// first string is the mailbox name for which these rights apply. The +// second string is the set of rights that the client has. +func (rsp *Response) MyRights() (mbox string, rights string) { + type vt struct { + mbox string + rights string + } + v, ok := rsp.Decoded.(*vt) + if !ok && rsp.Decoded == nil && rsp.Label == "MYRIGHTS" { + mbox = AsMailbox(rsp.Fields[1]) + rights = AsString(rsp.Fields[2]) + } else if ok { + mbox, rights = v.mbox, v.rights + } + return +} + // ResponseError wraps a Response pointer for use in an error context, such as // when a command fails with a NO or BAD status condition. For Status and Done // response types, the value of Response.Info may be presented to the user.