aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/emersion/go-imap/responses
diff options
context:
space:
mode:
authorJordan <me@jordan.im>2023-02-04 23:54:03 -0700
committerJordan <me@jordan.im>2023-02-04 23:54:03 -0700
commitc4159d895ac399ca55326f7b4ff8bfbf8402e654 (patch)
tree45340ca429c16f683b375695d01e03d65ebf22b0 /vendor/github.com/emersion/go-imap/responses
downloadpigeon-c4159d895ac399ca55326f7b4ff8bfbf8402e654.tar.gz
pigeon-c4159d895ac399ca55326f7b4ff8bfbf8402e654.zip
initial commit
Diffstat (limited to 'vendor/github.com/emersion/go-imap/responses')
-rw-r--r--vendor/github.com/emersion/go-imap/responses/authenticate.go61
-rw-r--r--vendor/github.com/emersion/go-imap/responses/capability.go20
-rw-r--r--vendor/github.com/emersion/go-imap/responses/enabled.go33
-rw-r--r--vendor/github.com/emersion/go-imap/responses/expunge.go43
-rw-r--r--vendor/github.com/emersion/go-imap/responses/fetch.go70
-rw-r--r--vendor/github.com/emersion/go-imap/responses/idle.go38
-rw-r--r--vendor/github.com/emersion/go-imap/responses/list.go57
-rw-r--r--vendor/github.com/emersion/go-imap/responses/responses.go35
-rw-r--r--vendor/github.com/emersion/go-imap/responses/search.go41
-rw-r--r--vendor/github.com/emersion/go-imap/responses/select.go142
-rw-r--r--vendor/github.com/emersion/go-imap/responses/status.go53
11 files changed, 593 insertions, 0 deletions
diff --git a/vendor/github.com/emersion/go-imap/responses/authenticate.go b/vendor/github.com/emersion/go-imap/responses/authenticate.go
new file mode 100644
index 0000000..8e134cb
--- /dev/null
+++ b/vendor/github.com/emersion/go-imap/responses/authenticate.go
@@ -0,0 +1,61 @@
+package responses
+
+import (
+ "encoding/base64"
+
+ "github.com/emersion/go-imap"
+ "github.com/emersion/go-sasl"
+)
+
+// An AUTHENTICATE response.
+type Authenticate struct {
+ Mechanism sasl.Client
+ InitialResponse []byte
+ RepliesCh chan []byte
+}
+
+// Implements
+func (r *Authenticate) Replies() <-chan []byte {
+ return r.RepliesCh
+}
+
+func (r *Authenticate) writeLine(l string) error {
+ r.RepliesCh <- []byte(l + "\r\n")
+ return nil
+}
+
+func (r *Authenticate) cancel() error {
+ return r.writeLine("*")
+}
+
+func (r *Authenticate) Handle(resp imap.Resp) error {
+ cont, ok := resp.(*imap.ContinuationReq)
+ if !ok {
+ return ErrUnhandled
+ }
+
+ // Empty challenge, send initial response as stated in RFC 2222 section 5.1
+ if cont.Info == "" && r.InitialResponse != nil {
+ encoded := base64.StdEncoding.EncodeToString(r.InitialResponse)
+ if err := r.writeLine(encoded); err != nil {
+ return err
+ }
+ r.InitialResponse = nil
+ return nil
+ }
+
+ challenge, err := base64.StdEncoding.DecodeString(cont.Info)
+ if err != nil {
+ r.cancel()
+ return err
+ }
+
+ reply, err := r.Mechanism.Next(challenge)
+ if err != nil {
+ r.cancel()
+ return err
+ }
+
+ encoded := base64.StdEncoding.EncodeToString(reply)
+ return r.writeLine(encoded)
+}
diff --git a/vendor/github.com/emersion/go-imap/responses/capability.go b/vendor/github.com/emersion/go-imap/responses/capability.go
new file mode 100644
index 0000000..483cb2e
--- /dev/null
+++ b/vendor/github.com/emersion/go-imap/responses/capability.go
@@ -0,0 +1,20 @@
+package responses
+
+import (
+ "github.com/emersion/go-imap"
+)
+
+// A CAPABILITY response.
+// See RFC 3501 section 7.2.1
+type Capability struct {
+ Caps []string
+}
+
+func (r *Capability) WriteTo(w *imap.Writer) error {
+ fields := []interface{}{imap.RawString("CAPABILITY")}
+ for _, cap := range r.Caps {
+ fields = append(fields, imap.RawString(cap))
+ }
+
+ return imap.NewUntaggedResp(fields).WriteTo(w)
+}
diff --git a/vendor/github.com/emersion/go-imap/responses/enabled.go b/vendor/github.com/emersion/go-imap/responses/enabled.go
new file mode 100644
index 0000000..fc4e27b
--- /dev/null
+++ b/vendor/github.com/emersion/go-imap/responses/enabled.go
@@ -0,0 +1,33 @@
+package responses
+
+import (
+ "github.com/emersion/go-imap"
+)
+
+// An ENABLED response, defined in RFC 5161 section 3.2.
+type Enabled struct {
+ Caps []string
+}
+
+func (r *Enabled) Handle(resp imap.Resp) error {
+ name, fields, ok := imap.ParseNamedResp(resp)
+ if !ok || name != "ENABLED" {
+ return ErrUnhandled
+ }
+
+ if caps, err := imap.ParseStringList(fields); err != nil {
+ return err
+ } else {
+ r.Caps = append(r.Caps, caps...)
+ }
+
+ return nil
+}
+
+func (r *Enabled) WriteTo(w *imap.Writer) error {
+ fields := []interface{}{imap.RawString("ENABLED")}
+ for _, cap := range r.Caps {
+ fields = append(fields, imap.RawString(cap))
+ }
+ return imap.NewUntaggedResp(fields).WriteTo(w)
+}
diff --git a/vendor/github.com/emersion/go-imap/responses/expunge.go b/vendor/github.com/emersion/go-imap/responses/expunge.go
new file mode 100644
index 0000000..bce3bf1
--- /dev/null
+++ b/vendor/github.com/emersion/go-imap/responses/expunge.go
@@ -0,0 +1,43 @@
+package responses
+
+import (
+ "github.com/emersion/go-imap"
+)
+
+const expungeName = "EXPUNGE"
+
+// An EXPUNGE response.
+// See RFC 3501 section 7.4.1
+type Expunge struct {
+ SeqNums chan uint32
+}
+
+func (r *Expunge) Handle(resp imap.Resp) error {
+ name, fields, ok := imap.ParseNamedResp(resp)
+ if !ok || name != expungeName {
+ return ErrUnhandled
+ }
+
+ if len(fields) == 0 {
+ return errNotEnoughFields
+ }
+
+ seqNum, err := imap.ParseNumber(fields[0])
+ if err != nil {
+ return err
+ }
+
+ r.SeqNums <- seqNum
+ return nil
+}
+
+func (r *Expunge) WriteTo(w *imap.Writer) error {
+ for seqNum := range r.SeqNums {
+ resp := imap.NewUntaggedResp([]interface{}{seqNum, imap.RawString(expungeName)})
+ if err := resp.WriteTo(w); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/emersion/go-imap/responses/fetch.go b/vendor/github.com/emersion/go-imap/responses/fetch.go
new file mode 100644
index 0000000..691ebcb
--- /dev/null
+++ b/vendor/github.com/emersion/go-imap/responses/fetch.go
@@ -0,0 +1,70 @@
+package responses
+
+import (
+ "github.com/emersion/go-imap"
+)
+
+const fetchName = "FETCH"
+
+// A FETCH response.
+// See RFC 3501 section 7.4.2
+type Fetch struct {
+ Messages chan *imap.Message
+ SeqSet *imap.SeqSet
+ Uid bool
+}
+
+func (r *Fetch) Handle(resp imap.Resp) error {
+ name, fields, ok := imap.ParseNamedResp(resp)
+ if !ok || name != fetchName {
+ return ErrUnhandled
+ } else if len(fields) < 1 {
+ return errNotEnoughFields
+ }
+
+ seqNum, err := imap.ParseNumber(fields[0])
+ if err != nil {
+ return err
+ }
+
+ msgFields, _ := fields[1].([]interface{})
+ msg := &imap.Message{SeqNum: seqNum}
+ if err := msg.Parse(msgFields); err != nil {
+ return err
+ }
+
+ if r.Uid && msg.Uid == 0 {
+ // we requested UIDs and got a message without one --> unilateral update --> ignore
+ return ErrUnhandled
+ }
+
+ var num uint32
+ if r.Uid {
+ num = msg.Uid
+ } else {
+ num = seqNum
+ }
+
+ // Check whether we obtained a result we requested with our SeqSet
+ // If the result is not contained in our SeqSet we have to handle an additional special case:
+ // In case we requested UIDs with a dynamic sequence (i.e. * or n:*) and the maximum UID of the mailbox
+ // is less then our n, the server will supply us with the max UID (cf. RFC 3501 §6.4.8 and §9 `seq-range`).
+ // Thus, such a result is correct and has to be returned by us.
+ if !r.SeqSet.Contains(num) && (!r.Uid || !r.SeqSet.Dynamic()) {
+ return ErrUnhandled
+ }
+
+ r.Messages <- msg
+ return nil
+}
+
+func (r *Fetch) WriteTo(w *imap.Writer) error {
+ var err error
+ for msg := range r.Messages {
+ resp := imap.NewUntaggedResp([]interface{}{msg.SeqNum, imap.RawString(fetchName), msg.Format()})
+ if err == nil {
+ err = resp.WriteTo(w)
+ }
+ }
+ return err
+}
diff --git a/vendor/github.com/emersion/go-imap/responses/idle.go b/vendor/github.com/emersion/go-imap/responses/idle.go
new file mode 100644
index 0000000..b5efcac
--- /dev/null
+++ b/vendor/github.com/emersion/go-imap/responses/idle.go
@@ -0,0 +1,38 @@
+package responses
+
+import (
+ "github.com/emersion/go-imap"
+)
+
+// An IDLE response.
+type Idle struct {
+ RepliesCh chan []byte
+ Stop <-chan struct{}
+
+ gotContinuationReq bool
+}
+
+func (r *Idle) Replies() <-chan []byte {
+ return r.RepliesCh
+}
+
+func (r *Idle) stop() {
+ r.RepliesCh <- []byte("DONE\r\n")
+}
+
+func (r *Idle) Handle(resp imap.Resp) error {
+ // Wait for a continuation request
+ if _, ok := resp.(*imap.ContinuationReq); ok && !r.gotContinuationReq {
+ r.gotContinuationReq = true
+
+ // We got a continuation request, wait for r.Stop to be closed
+ go func() {
+ <-r.Stop
+ r.stop()
+ }()
+
+ return nil
+ }
+
+ return ErrUnhandled
+}
diff --git a/vendor/github.com/emersion/go-imap/responses/list.go b/vendor/github.com/emersion/go-imap/responses/list.go
new file mode 100644
index 0000000..e080fc1
--- /dev/null
+++ b/vendor/github.com/emersion/go-imap/responses/list.go
@@ -0,0 +1,57 @@
+package responses
+
+import (
+ "github.com/emersion/go-imap"
+)
+
+const (
+ listName = "LIST"
+ lsubName = "LSUB"
+)
+
+// A LIST response.
+// If Subscribed is set to true, LSUB will be used instead.
+// See RFC 3501 section 7.2.2
+type List struct {
+ Mailboxes chan *imap.MailboxInfo
+ Subscribed bool
+}
+
+func (r *List) Name() string {
+ if r.Subscribed {
+ return lsubName
+ } else {
+ return listName
+ }
+}
+
+func (r *List) Handle(resp imap.Resp) error {
+ name, fields, ok := imap.ParseNamedResp(resp)
+ if !ok || name != r.Name() {
+ return ErrUnhandled
+ }
+
+ mbox := &imap.MailboxInfo{}
+ if err := mbox.Parse(fields); err != nil {
+ return err
+ }
+
+ r.Mailboxes <- mbox
+ return nil
+}
+
+func (r *List) WriteTo(w *imap.Writer) error {
+ respName := r.Name()
+
+ for mbox := range r.Mailboxes {
+ fields := []interface{}{imap.RawString(respName)}
+ fields = append(fields, mbox.Format()...)
+
+ resp := imap.NewUntaggedResp(fields)
+ if err := resp.WriteTo(w); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/emersion/go-imap/responses/responses.go b/vendor/github.com/emersion/go-imap/responses/responses.go
new file mode 100644
index 0000000..4d035ee
--- /dev/null
+++ b/vendor/github.com/emersion/go-imap/responses/responses.go
@@ -0,0 +1,35 @@
+// IMAP responses defined in RFC 3501.
+package responses
+
+import (
+ "errors"
+
+ "github.com/emersion/go-imap"
+)
+
+// ErrUnhandled is used when a response hasn't been handled.
+var ErrUnhandled = errors.New("imap: unhandled response")
+
+var errNotEnoughFields = errors.New("imap: not enough fields in response")
+
+// Handler handles responses.
+type Handler interface {
+ // Handle processes a response. If the response cannot be processed,
+ // ErrUnhandledResp must be returned.
+ Handle(resp imap.Resp) error
+}
+
+// HandlerFunc is a function that handles responses.
+type HandlerFunc func(resp imap.Resp) error
+
+// Handle implements Handler.
+func (f HandlerFunc) Handle(resp imap.Resp) error {
+ return f(resp)
+}
+
+// Replier is a Handler that needs to send raw data (for instance
+// AUTHENTICATE).
+type Replier interface {
+ Handler
+ Replies() <-chan []byte
+}
diff --git a/vendor/github.com/emersion/go-imap/responses/search.go b/vendor/github.com/emersion/go-imap/responses/search.go
new file mode 100644
index 0000000..028dbc7
--- /dev/null
+++ b/vendor/github.com/emersion/go-imap/responses/search.go
@@ -0,0 +1,41 @@
+package responses
+
+import (
+ "github.com/emersion/go-imap"
+)
+
+const searchName = "SEARCH"
+
+// A SEARCH response.
+// See RFC 3501 section 7.2.5
+type Search struct {
+ Ids []uint32
+}
+
+func (r *Search) Handle(resp imap.Resp) error {
+ name, fields, ok := imap.ParseNamedResp(resp)
+ if !ok || name != searchName {
+ return ErrUnhandled
+ }
+
+ r.Ids = make([]uint32, len(fields))
+ for i, f := range fields {
+ if id, err := imap.ParseNumber(f); err != nil {
+ return err
+ } else {
+ r.Ids[i] = id
+ }
+ }
+
+ return nil
+}
+
+func (r *Search) WriteTo(w *imap.Writer) (err error) {
+ fields := []interface{}{imap.RawString(searchName)}
+ for _, id := range r.Ids {
+ fields = append(fields, id)
+ }
+
+ resp := imap.NewUntaggedResp(fields)
+ return resp.WriteTo(w)
+}
diff --git a/vendor/github.com/emersion/go-imap/responses/select.go b/vendor/github.com/emersion/go-imap/responses/select.go
new file mode 100644
index 0000000..e450963
--- /dev/null
+++ b/vendor/github.com/emersion/go-imap/responses/select.go
@@ -0,0 +1,142 @@
+package responses
+
+import (
+ "fmt"
+
+ "github.com/emersion/go-imap"
+)
+
+// A SELECT response.
+type Select struct {
+ Mailbox *imap.MailboxStatus
+}
+
+func (r *Select) Handle(resp imap.Resp) error {
+ if r.Mailbox == nil {
+ r.Mailbox = &imap.MailboxStatus{Items: make(map[imap.StatusItem]interface{})}
+ }
+ mbox := r.Mailbox
+
+ switch resp := resp.(type) {
+ case *imap.DataResp:
+ name, fields, ok := imap.ParseNamedResp(resp)
+ if !ok || name != "FLAGS" {
+ return ErrUnhandled
+ } else if len(fields) < 1 {
+ return errNotEnoughFields
+ }
+
+ flags, _ := fields[0].([]interface{})
+ mbox.Flags, _ = imap.ParseStringList(flags)
+ case *imap.StatusResp:
+ if len(resp.Arguments) < 1 {
+ return ErrUnhandled
+ }
+
+ var item imap.StatusItem
+ switch resp.Code {
+ case "UNSEEN":
+ mbox.UnseenSeqNum, _ = imap.ParseNumber(resp.Arguments[0])
+ case "PERMANENTFLAGS":
+ flags, _ := resp.Arguments[0].([]interface{})
+ mbox.PermanentFlags, _ = imap.ParseStringList(flags)
+ case "UIDNEXT":
+ mbox.UidNext, _ = imap.ParseNumber(resp.Arguments[0])
+ item = imap.StatusUidNext
+ case "UIDVALIDITY":
+ mbox.UidValidity, _ = imap.ParseNumber(resp.Arguments[0])
+ item = imap.StatusUidValidity
+ default:
+ return ErrUnhandled
+ }
+
+ if item != "" {
+ mbox.ItemsLocker.Lock()
+ mbox.Items[item] = nil
+ mbox.ItemsLocker.Unlock()
+ }
+ default:
+ return ErrUnhandled
+ }
+ return nil
+}
+
+func (r *Select) WriteTo(w *imap.Writer) error {
+ mbox := r.Mailbox
+
+ if mbox.Flags != nil {
+ flags := make([]interface{}, len(mbox.Flags))
+ for i, f := range mbox.Flags {
+ flags[i] = imap.RawString(f)
+ }
+ res := imap.NewUntaggedResp([]interface{}{imap.RawString("FLAGS"), flags})
+ if err := res.WriteTo(w); err != nil {
+ return err
+ }
+ }
+
+ if mbox.PermanentFlags != nil {
+ flags := make([]interface{}, len(mbox.PermanentFlags))
+ for i, f := range mbox.PermanentFlags {
+ flags[i] = imap.RawString(f)
+ }
+ statusRes := &imap.StatusResp{
+ Type: imap.StatusRespOk,
+ Code: imap.CodePermanentFlags,
+ Arguments: []interface{}{flags},
+ Info: "Flags permitted.",
+ }
+ if err := statusRes.WriteTo(w); err != nil {
+ return err
+ }
+ }
+
+ if mbox.UnseenSeqNum > 0 {
+ statusRes := &imap.StatusResp{
+ Type: imap.StatusRespOk,
+ Code: imap.CodeUnseen,
+ Arguments: []interface{}{mbox.UnseenSeqNum},
+ Info: fmt.Sprintf("Message %d is first unseen", mbox.UnseenSeqNum),
+ }
+ if err := statusRes.WriteTo(w); err != nil {
+ return err
+ }
+ }
+
+ for k := range r.Mailbox.Items {
+ switch k {
+ case imap.StatusMessages:
+ res := imap.NewUntaggedResp([]interface{}{mbox.Messages, imap.RawString("EXISTS")})
+ if err := res.WriteTo(w); err != nil {
+ return err
+ }
+ case imap.StatusRecent:
+ res := imap.NewUntaggedResp([]interface{}{mbox.Recent, imap.RawString("RECENT")})
+ if err := res.WriteTo(w); err != nil {
+ return err
+ }
+ case imap.StatusUidNext:
+ statusRes := &imap.StatusResp{
+ Type: imap.StatusRespOk,
+ Code: imap.CodeUidNext,
+ Arguments: []interface{}{mbox.UidNext},
+ Info: "Predicted next UID",
+ }
+ if err := statusRes.WriteTo(w); err != nil {
+ return err
+ }
+ case imap.StatusUidValidity:
+ statusRes := &imap.StatusResp{
+ Type: imap.StatusRespOk,
+ Code: imap.CodeUidValidity,
+ Arguments: []interface{}{mbox.UidValidity},
+ Info: "UIDs valid",
+ }
+ if err := statusRes.WriteTo(w); err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/emersion/go-imap/responses/status.go b/vendor/github.com/emersion/go-imap/responses/status.go
new file mode 100644
index 0000000..6a8570c
--- /dev/null
+++ b/vendor/github.com/emersion/go-imap/responses/status.go
@@ -0,0 +1,53 @@
+package responses
+
+import (
+ "errors"
+
+ "github.com/emersion/go-imap"
+ "github.com/emersion/go-imap/utf7"
+)
+
+const statusName = "STATUS"
+
+// A STATUS response.
+// See RFC 3501 section 7.2.4
+type Status struct {
+ Mailbox *imap.MailboxStatus
+}
+
+func (r *Status) Handle(resp imap.Resp) error {
+ if r.Mailbox == nil {
+ r.Mailbox = &imap.MailboxStatus{}
+ }
+ mbox := r.Mailbox
+
+ name, fields, ok := imap.ParseNamedResp(resp)
+ if !ok || name != statusName {
+ return ErrUnhandled
+ } else if len(fields) < 2 {
+ return errNotEnoughFields
+ }
+
+ if name, err := imap.ParseString(fields[0]); err != nil {
+ return err
+ } else if name, err := utf7.Encoding.NewDecoder().String(name); err != nil {
+ return err
+ } else {
+ mbox.Name = imap.CanonicalMailboxName(name)
+ }
+
+ var items []interface{}
+ if items, ok = fields[1].([]interface{}); !ok {
+ return errors.New("STATUS response expects a list as second argument")
+ }
+
+ mbox.Items = nil
+ return mbox.Parse(items)
+}
+
+func (r *Status) WriteTo(w *imap.Writer) error {
+ mbox := r.Mailbox
+ name, _ := utf7.Encoding.NewEncoder().String(mbox.Name)
+ fields := []interface{}{imap.RawString(statusName), imap.FormatMailboxName(name), mbox.Format()}
+ return imap.NewUntaggedResp(fields).WriteTo(w)
+}