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
21 changes: 20 additions & 1 deletion imapclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,10 @@ func (c *Client) readResponseData(typ string) error {
}
case "NOMODSEQ":
// ignore
case "NOTIFICATIONOVERFLOW":
if handler := c.options.unilateralDataHandler().NotificationOverflow; handler != nil {
handler()
}
default: // [SP 1*<any TEXT-CHAR except "]">]
if c.dec.SP() {
c.dec.DiscardUntilByte(']')
Expand Down Expand Up @@ -1179,14 +1183,29 @@ type UnilateralDataMailbox struct {
//
// The handler will be invoked in an arbitrary goroutine.
//
// These handlers are important when using the IDLE or NOTIFY commands, as the
// server will send unsolicited STATUS, FETCH, and EXPUNGE responses for
// mailbox events.
//
// See Options.UnilateralDataHandler.
type UnilateralDataHandler struct {
Expunge func(seqNum uint32)
Mailbox func(data *UnilateralDataMailbox)
Fetch func(msg *FetchMessageData)

// requires ENABLE METADATA or ENABLE SERVER-METADATA
// Requires ENABLE METADATA or ENABLE SERVER-METADATA.
Metadata func(mailbox string, entries []string)

// Called when the server sends an unsolicited STATUS response.
//
// Commonly used with NOTIFY to receive mailbox status updates
// for non-selected mailboxes (RFC 5465).
Status func(data *imap.StatusData)

// Called when the server sends NOTIFICATIONOVERFLOW (RFC 5465).
//
// Indicates the server has disabled all NOTIFY notifications.
NotificationOverflow func()
}

// command is an interface for IMAP commands.
Expand Down
99 changes: 99 additions & 0 deletions imapclient/notify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package imapclient

import (
"fmt"

"github.com/emersion/go-imap/v2"
"github.com/emersion/go-imap/v2/internal/imapwire"
)

// Notify sends a NOTIFY command (RFC 5465).
//
// The NOTIFY command allows clients to request server-push notifications
// for mailbox events like new messages, expunges, flag changes, etc.
//
// When NOTIFY SET is active, the server may send unsolicited responses at any
// time (STATUS, FETCH, EXPUNGE, LIST responses). These unsolicited responses
// are delivered via the UnilateralDataHandler callbacks set in
// imapclient.Options.
//
// When the server sends NOTIFICATIONOVERFLOW, the NotificationOverflow callback
// in UnilateralDataHandler will be called (if set).
func (c *Client) Notify(options *imap.NotifyOptions) (*NotifyCommand, error) {
cmd := &NotifyCommand{}
enc := c.beginCommand("NOTIFY", cmd)
if err := encodeNotifyOptions(enc.Encoder, options); err != nil {
enc.end()
return nil, err
}
enc.end()

if err := cmd.Wait(); err != nil {
return nil, err
}

return cmd, nil
}

// encodeNotifyOptions encodes NOTIFY command options to the encoder.
func encodeNotifyOptions(enc *imapwire.Encoder, options *imap.NotifyOptions) error {
if options == nil || len(options.Items) == 0 {
// NOTIFY NONE: disable all notifications.
enc.SP().Atom("NONE")
return nil
}

enc.SP().Atom("SET")

if options.Status {
enc.SP().List(1, func(i int) {
enc.Atom("STATUS")
})
}

for _, item := range options.Items {
if item.MailboxSpec == "" && len(item.Mailboxes) == 0 {
return fmt.Errorf("invalid NOTIFY item: must specify either MailboxSpec or Mailboxes")
}

enc.SP().List(1, func(_ int) {
if item.MailboxSpec != "" {
enc.Atom(string(item.MailboxSpec))
} else {
// len(item.Mailboxes) > 0, as per the check above.
if item.Subtree {
enc.Atom("SUBTREE").SP()
}
enc.List(len(item.Mailboxes), func(j int) {
enc.Mailbox(item.Mailboxes[j])
})
}

if len(item.Events) > 0 {
enc.SP().List(len(item.Events), func(j int) {
enc.Atom(string(item.Events[j]))
})
}
})

}

return nil
}

// NotifyCommand is a NOTIFY command.
//
// When NOTIFY SET is active, the server may send unsolicited responses at any
// time. These responses are delivered via UnilateralDataHandler
// (see Options.UnilateralDataHandler).
//
// If the server sends NOTIFICATIONOVERFLOW, the NotificationOverflow callback
// in UnilateralDataHandler will be called (if set).
type NotifyCommand struct {
commandBase
}

// Wait blocks until the NOTIFY command has completed.
func (cmd *NotifyCommand) Wait() error {
return cmd.wait()
}
Loading