diff options
author | Jordan <me@jordan.im> | 2023-02-04 23:54:03 -0700 |
---|---|---|
committer | Jordan <me@jordan.im> | 2023-02-04 23:54:03 -0700 |
commit | c4159d895ac399ca55326f7b4ff8bfbf8402e654 (patch) | |
tree | 45340ca429c16f683b375695d01e03d65ebf22b0 /vendor/github.com/emersion/go-imap/response.go | |
download | pigeon-c4159d895ac399ca55326f7b4ff8bfbf8402e654.tar.gz pigeon-c4159d895ac399ca55326f7b4ff8bfbf8402e654.zip |
initial commit
Diffstat (limited to 'vendor/github.com/emersion/go-imap/response.go')
-rw-r--r-- | vendor/github.com/emersion/go-imap/response.go | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/vendor/github.com/emersion/go-imap/response.go b/vendor/github.com/emersion/go-imap/response.go new file mode 100644 index 0000000..611d03e --- /dev/null +++ b/vendor/github.com/emersion/go-imap/response.go @@ -0,0 +1,181 @@ +package imap + +import ( + "strings" +) + +// Resp is an IMAP response. It is either a *DataResp, a +// *ContinuationReq or a *StatusResp. +type Resp interface { + resp() +} + +// ReadResp reads a single response from a Reader. +func ReadResp(r *Reader) (Resp, error) { + atom, err := r.ReadAtom() + if err != nil { + return nil, err + } + tag, ok := atom.(string) + if !ok { + return nil, newParseError("response tag is not an atom") + } + + if tag == "+" { + if err := r.ReadSp(); err != nil { + r.UnreadRune() + } + + resp := &ContinuationReq{} + resp.Info, err = r.ReadInfo() + if err != nil { + return nil, err + } + + return resp, nil + } + + if err := r.ReadSp(); err != nil { + return nil, err + } + + // Can be either data or status + // Try to parse a status + var fields []interface{} + if atom, err := r.ReadAtom(); err == nil { + fields = append(fields, atom) + + if err := r.ReadSp(); err == nil { + if name, ok := atom.(string); ok { + status := StatusRespType(name) + switch status { + case StatusRespOk, StatusRespNo, StatusRespBad, StatusRespPreauth, StatusRespBye: + resp := &StatusResp{ + Tag: tag, + Type: status, + } + + char, _, err := r.ReadRune() + if err != nil { + return nil, err + } + r.UnreadRune() + + if char == '[' { + // Contains code & arguments + resp.Code, resp.Arguments, err = r.ReadRespCode() + if err != nil { + return nil, err + } + } + + resp.Info, err = r.ReadInfo() + if err != nil { + return nil, err + } + + return resp, nil + } + } + } else { + r.UnreadRune() + } + } else { + r.UnreadRune() + } + + // Not a status so it's data + resp := &DataResp{Tag: tag} + + var remaining []interface{} + remaining, err = r.ReadLine() + if err != nil { + return nil, err + } + + resp.Fields = append(fields, remaining...) + return resp, nil +} + +// DataResp is an IMAP response containing data. +type DataResp struct { + // The response tag. Can be either "" for untagged responses, "+" for continuation + // requests or a previous command's tag. + Tag string + // The parsed response fields. + Fields []interface{} +} + +// NewUntaggedResp creates a new untagged response. +func NewUntaggedResp(fields []interface{}) *DataResp { + return &DataResp{ + Tag: "*", + Fields: fields, + } +} + +func (r *DataResp) resp() {} + +func (r *DataResp) WriteTo(w *Writer) error { + tag := RawString(r.Tag) + if tag == "" { + tag = RawString("*") + } + + fields := []interface{}{RawString(tag)} + fields = append(fields, r.Fields...) + return w.writeLine(fields...) +} + +// ContinuationReq is a continuation request response. +type ContinuationReq struct { + // The info message sent with the continuation request. + Info string +} + +func (r *ContinuationReq) resp() {} + +func (r *ContinuationReq) WriteTo(w *Writer) error { + if err := w.writeString("+"); err != nil { + return err + } + + if r.Info != "" { + if err := w.writeString(string(sp) + r.Info); err != nil { + return err + } + } + + return w.writeCrlf() +} + +// ParseNamedResp attempts to parse a named data response. +func ParseNamedResp(resp Resp) (name string, fields []interface{}, ok bool) { + data, ok := resp.(*DataResp) + if !ok || len(data.Fields) == 0 { + return + } + + // Some responses (namely EXISTS and RECENT) are formatted like so: + // [num] [name] [...] + // Which is fucking stupid. But we handle that here by checking if the + // response name is a number and then rearranging it. + if len(data.Fields) > 1 { + name, ok := data.Fields[1].(string) + if ok { + if _, err := ParseNumber(data.Fields[0]); err == nil { + fields := []interface{}{data.Fields[0]} + fields = append(fields, data.Fields[2:]...) + return strings.ToUpper(name), fields, true + } + } + } + + // IMAP commands are formatted like this: + // [name] [...] + name, ok = data.Fields[0].(string) + if !ok { + return + } + return strings.ToUpper(name), data.Fields[1:], true +} |