From 6e2cb00008cbf09e556b00f87603797fcaa47e09 Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Mon, 16 Apr 2018 05:37:14 -0700 Subject: Depenancy upgrades and movign to dep. (#8630) --- vendor/github.com/mattermost/rsc/imap/imap.go | 1110 ------------------------- 1 file changed, 1110 deletions(-) delete mode 100644 vendor/github.com/mattermost/rsc/imap/imap.go (limited to 'vendor/github.com/mattermost/rsc/imap/imap.go') diff --git a/vendor/github.com/mattermost/rsc/imap/imap.go b/vendor/github.com/mattermost/rsc/imap/imap.go deleted file mode 100644 index 6555984d2..000000000 --- a/vendor/github.com/mattermost/rsc/imap/imap.go +++ /dev/null @@ -1,1110 +0,0 @@ -package imap - -import ( - "bufio" - "bytes" - "crypto/md5" - "crypto/tls" - "fmt" - "io" - "log" - "net" - "os" - "os/exec" - "strconv" - "strings" - "sync" -) - -var Debug = false - -const tag = "#" - -// A Mode specifies the IMAP connection mode. -type Mode int - -const ( - Unencrypted Mode = iota // unencrypted TCP connection - StartTLS // use IMAP STARTTLS command - unimplemented! - TLS // direct TLS connection - Command // exec shell command (server name) -) - -type lock struct { - locked bool - mu sync.Mutex -} - -func (l *lock) lock() { - l.mu.Lock() - l.locked = true -} - -func (l *lock) unlock() { - l.mustBeLocked() - l.locked = false - l.mu.Unlock() -} - -func (l *lock) mustBeLocked() { - if !l.locked { - panic("not locked") - } -} - -type Client struct { - server string - user string - passwd string - mode Mode - root string - - io lock - rw io.ReadWriteCloser // i/o to server - b *bufio.Reader // buffered rw - autoReconnect bool // reconnect on failure - connected bool // rw is active - - data lock - capability map[string]bool - flags Flags - boxByName map[string]*Box // all known boxes - allBox []*Box // all known boxes (do we need this?) - rootBox *Box // root of box tree - inbox *Box // inbox (special, not in tree) - box *Box // selected (current) box - nextBox *Box // next box to select (do we need this?) -} - -func NewClient(mode Mode, server, user, passwd string, root string) (*Client, error) { - c := &Client{ - server: server, - user: user, - passwd: passwd, - mode: mode, - root: root, - boxByName: map[string]*Box{}, - } - c.io.lock() - if err := c.reconnect(); err != nil { - return nil, err - } - c.autoReconnect = true - c.io.unlock() - - return c, nil -} - -func (c *Client) Close() error { - c.io.lock() - c.autoReconnect = false - c.connected = false - if c.rw != nil { - c.rw.Close() - c.rw = nil - } - c.io.unlock() - return nil -} - -func (c *Client) reconnect() error { - c.io.mustBeLocked() - c.autoReconnect = false - if c.rw != nil { - c.rw.Close() - c.rw = nil - } - - if Debug { - log.Printf("dial %s...", c.server) - } - rw, err := dial(c.server, c.mode) - if err != nil { - return err - } - - c.rw = rw - c.connected = true - c.capability = nil - c.box = nil - if Debug { - c.b = bufio.NewReader(&tee{rw, os.Stderr}) - } else { - c.b = bufio.NewReader(rw) - } - x, err := c.rdsx() - if x == nil { - err = fmt.Errorf("no greeting from %s: %v", c.server, err) - goto Error - } - if len(x.sx) < 2 || !x.sx[0].isAtom("*") || !x.sx[1].isAtom("PREAUTH") { - if !x.ok() { - err = fmt.Errorf("bad greeting - %s", x) - goto Error - } - if err = c.login(); err != nil { - goto Error - } - } - if c.capability == nil { - if err = c.cmd(nil, "CAPABILITY"); err != nil { - goto Error - } - if c.capability == nil { - err = fmt.Errorf("CAPABILITY command did not return capability list") - goto Error - } - } - if err := c.getBoxes(); err != nil { - goto Error - } - c.autoReconnect = true - return nil - -Error: - if c.rw != nil { - c.rw.Close() - c.rw = nil - } - c.autoReconnect = true - c.connected = false - return err -} - -var testDial func(string, Mode) (io.ReadWriteCloser, error) - -func dial(server string, mode Mode) (io.ReadWriteCloser, error) { - if testDial != nil { - return testDial(server, mode) - } - switch mode { - default: - // also case Unencrypted - return net.Dial("tcp", server+":143") - case StartTLS: - return nil, fmt.Errorf("StartTLS not supported") - case TLS: - return tls.Dial("tcp", server+":993", nil) - case Command: - cmd := exec.Command("sh", "-c", server) - cmd.Stderr = os.Stderr - r, err := cmd.StdoutPipe() - if err != nil { - return nil, err - } - w, err := cmd.StdinPipe() - if err != nil { - r.Close() - return nil, err - } - if err := cmd.Start(); err != nil { - r.Close() - w.Close() - return nil, err - } - return &pipe2{r, w}, nil - } - panic("not reached") -} - -type pipe2 struct { - io.ReadCloser - io.WriteCloser -} - -func (p *pipe2) Close() error { - p.ReadCloser.Close() - p.WriteCloser.Close() - return nil -} - -type tee struct { - r io.Reader - w io.Writer -} - -func (t tee) Read(p []byte) (n int, err error) { - n, err = t.r.Read(p) - if n > 0 { - t.w.Write(p[0:n]) - } - return -} - -func (c *Client) rdsx() (*sx, error) { - c.io.mustBeLocked() - return rdsx(c.b) -} - -type sxError struct { - x *sx -} - -func (e *sxError) Error() string { return e.x.String() } - -func (c *Client) cmd(b *Box, format string, args ...interface{}) error { - x, err := c.cmdsx(b, format, args...) - if err != nil { - return err - } - if !x.ok() { - return &sxError{x} - } - return nil -} - -// cmdsx0 runs a single command and return the sx. Does not redial. -func (c *Client) cmdsx0(format string, args ...interface{}) (*sx, error) { - c.io.mustBeLocked() - if c.rw == nil || !c.connected { - return nil, fmt.Errorf("not connected") - } - - cmd := fmt.Sprintf(format, args...) - if Debug { - fmt.Fprintf(os.Stderr, ">>> %s %s\n", tag, cmd) - } - if _, err := fmt.Fprintf(c.rw, "%s %s\r\n", tag, cmd); err != nil { - c.connected = false - return nil, err - } - return c.waitsx() -} - -// cmdsx runs a command on box b. It does redial. -func (c *Client) cmdsx(b *Box, format string, args ...interface{}) (*sx, error) { - c.io.mustBeLocked() - c.nextBox = b - -Trying: - for tries := 0; ; tries++ { - if c.rw == nil || !c.connected { - if !c.autoReconnect { - return nil, fmt.Errorf("not connected") - } - if err := c.reconnect(); err != nil { - return nil, err - } - if b != nil && c.nextBox == nil { - // box disappeared on reconnect - return nil, fmt.Errorf("box is gone") - } - } - - if b != nil && b != c.box { - if c.box != nil { - // TODO c.box.init = false - } - c.box = b - if _, err := c.cmdsx0("SELECT %s", iquote(b.Name)); err != nil { - c.box = nil - if tries++; tries == 1 && (c.rw == nil || !c.connected) { - continue Trying - } - return nil, err - } - } - - x, err := c.cmdsx0(format, args...) - if err != nil { - if tries++; tries == 1 && (c.rw == nil || !c.connected) { - continue Trying - } - return nil, err - } - return x, nil - } - panic("not reached") -} - -func (c *Client) waitsx() (*sx, error) { - c.io.mustBeLocked() - for { - x, err := c.rdsx() - if err != nil { - c.connected = false - return nil, err - } - if len(x.sx) >= 1 && x.sx[0].kind == sxAtom { - if x.sx[0].isAtom(tag) { - return x, nil - } - if x.sx[0].isAtom("*") { - c.unexpected(x) - } - } - if x.kind == sxList && len(x.sx) == 0 { - c.connected = false - return nil, fmt.Errorf("empty response") - } - } - panic("not reached") -} - -func iquote(s string) string { - if s == "" { - return `""` - } - - for i := 0; i < len(s); i++ { - if s[i] >= 0x80 || s[i] <= ' ' || s[i] == '\\' || s[i] == '"' { - goto Quote - } - } - return s - -Quote: - var b bytes.Buffer - b.WriteByte('"') - for i := 0; i < len(s); i++ { - if s[i] == '\\' || s[i] == '"' { - b.WriteByte('\\') - } - b.WriteByte(s[i]) - } - b.WriteByte('"') - return b.String() -} - -func (c *Client) login() error { - c.io.mustBeLocked() - x, err := c.cmdsx(nil, "LOGIN %s %s", iquote(c.user), iquote(c.passwd)) - if err != nil { - return err - } - if !x.ok() { - return fmt.Errorf("login rejected: %s", x) - } - return nil -} - -func (c *Client) getBoxes() error { - c.io.mustBeLocked() - for _, b := range c.allBox { - b.dead = true - // b.exists = 0 - // b.maxSeen = 0 - } - list := "LIST" - if c.capability["XLIST"] { // Gmail extension - list = "XLIST" - } - if err := c.cmd(nil, "%s %s *", list, iquote(c.root)); err != nil { - return err - } - if err := c.cmd(nil, "%s %s INBOX", list, iquote(c.root)); err != nil { - return err - } - if c.nextBox != nil && c.nextBox.dead { - c.nextBox = nil - } - for _, b := range c.allBox { - if b.dead { - delete(c.boxByName, b.Name) - } - b.firstNum = 0 - } - c.allBox = boxTrim(c.allBox) - for _, b := range c.allBox { - b.child = boxTrim(b.child) - } - return nil -} - -func boxTrim(list []*Box) []*Box { - w := 0 - for _, b := range list { - if !b.dead { - list[w] = b - w++ - } - } - return list[:w] -} - -const maxFetch = 1000 - -func (c *Client) setAutoReconnect(b bool) { - c.autoReconnect = b -} - -func (c *Client) check(b *Box) error { - c.io.mustBeLocked() - if b.dead { - return fmt.Errorf("box is gone") - } - - b.load = true - - // Update exists count. - if err := c.cmd(b, "NOOP"); err != nil { - return err - } - - // Have to get through this in one session. - // Caller can call again if we get disconnected - // and return an error. - c.autoReconnect = false - defer c.setAutoReconnect(true) - - // First load after reconnect: figure out what changed. - if b.firstNum == 0 && len(b.msgByUID) > 0 { - var lo, hi uint32 = 1<<32 - 1, 0 - for _, m := range b.msgByUID { - m.dead = true - uid := uint32(m.UID) - if lo > uid { - lo = uid - } - if hi < uid { - hi = uid - } - m.num = 0 - } - if err := c.cmd(b, "UID FETCH %d:%d FLAGS", lo, hi); err != nil { - return err - } - for _, m := range b.msgByUID { - if m.dead { - delete(b.msgByUID, m.UID) - } - } - } - - // First-ever load. - if b.firstNum == 0 { - if b.exists <= maxFetch { - b.firstNum = 1 - } else { - b.firstNum = b.exists - maxFetch + 1 - } - n := b.exists - b.firstNum + 1 - b.msgByNum = make([]*Msg, n) - return c.fetchBox(b, b.firstNum, 0) - } - - if b.exists <= b.maxSeen { - return nil - } - return c.fetchBox(b, b.maxSeen, 0) -} - -func (c *Client) fetchBox(b *Box, lo int, hi int) error { - c.io.mustBeLocked() - if b != c.box { - if err := c.cmd(b, "NOOP"); err != nil { - return err - } - } - extra := "" - if c.IsGmail() { - extra = " X-GM-MSGID X-GM-THRID X-GM-LABELS" - } - slo := fmt.Sprint(lo) - shi := "*" - if hi > 0 { - shi = fmt.Sprint(hi) - } - return c.cmd(b, "FETCH %s:%s (FLAGS UID INTERNALDATE RFC822.SIZE ENVELOPE BODY%s)", slo, shi, extra) -} - -func (c *Client) IsGmail() bool { - return c.capability["X-GM-EXT-1"] -} - -// Table-driven IMAP "unexpected response" parser. -// All the interesting data is in the unexpected responses. - -var unextab = []struct { - num int - name string - fmt string - fn func(*Client, *sx) -}{ - {0, "BYE", "", xbye}, - {0, "CAPABILITY", "", xcapability}, - {0, "FLAGS", "AAL", xflags}, - {0, "LIST", "AALSS", xlist}, - {0, "XLIST", "AALSS", xlist}, - {0, "OK", "", xok}, - // {0, "SEARCH", "AAN*", xsearch}, - {1, "EXISTS", "ANA", xexists}, - {1, "EXPUNGE", "ANA", xexpunge}, - {1, "FETCH", "ANAL", xfetch}, - // {1, "RECENT", "ANA", xrecent}, // why do we care? -} - -func (c *Client) unexpected(x *sx) { - c.io.mustBeLocked() - var num int - var name string - - if len(x.sx) >= 3 && x.sx[1].kind == sxNumber && x.sx[2].kind == sxAtom { - num = 1 - name = string(x.sx[2].data) - } else if len(x.sx) >= 2 && x.sx[1].kind == sxAtom { - num = 0 - name = string(x.sx[1].data) - } else { - return - } - - c.data.lock() - for _, t := range unextab { - if t.num == num && strings.EqualFold(t.name, name) { - if t.fmt != "" && !x.match(t.fmt) { - log.Printf("malformd %s: %s", name, x) - continue - } - t.fn(c, x) - } - } - c.data.unlock() -} - -func xbye(c *Client, x *sx) { - c.io.mustBeLocked() - c.rw.Close() - c.rw = nil - c.connected = false -} - -func xflags(c *Client, x *sx) { - c.data.mustBeLocked() - // This response contains in x.sx[2] the list of flags - // that can be validly attached to messages in c.box. - if b := c.box; b != nil { - c.flags = x.sx[2].parseFlags() - } -} - -func xcapability(c *Client, x *sx) { - c.data.mustBeLocked() - c.capability = make(map[string]bool) - for _, xx := range x.sx[2:] { - if xx.kind == sxAtom { - c.capability[string(xx.data)] = true - } - } -} - -func xlist(c *Client, x *sx) { - c.data.mustBeLocked() - s := string(x.sx[4].data) - t := string(x.sx[3].data) - - // INBOX is the special name for the main mailbox. - // All the other mailbox names have the root prefix removed, if applicable. - inbox := strings.EqualFold(s, "inbox") - if inbox { - s = "inbox" - } - - b := c.newBox(s, t, inbox) - if b == nil { - return - } - if inbox { - c.inbox = b - } - if s == c.root { - c.rootBox = b - } - b.dead = false - b.flags = x.sx[2].parseFlags() -} - -func xexists(c *Client, x *sx) { - c.data.mustBeLocked() - if b := c.box; b != nil { - b.exists = int(x.sx[1].number) - if b.exists < b.maxSeen { - b.maxSeen = b.exists - } - } -} - -func xexpunge(c *Client, x *sx) { - c.data.mustBeLocked() - if b := c.box; b != nil { - n := int(x.sx[1].number) - bynum := b.msgByNum - if bynum != nil { - if n < b.firstNum { - b.firstNum-- - } else if n < b.firstNum+len(bynum) { - copy(bynum[n-b.firstNum:], bynum[n-b.firstNum+1:]) - b.msgByNum = bynum[:len(bynum)-1] - } else { - log.Printf("expunge unexpected message %d %d %d", b.firstNum, b.exists, b.firstNum+len(bynum)) - } - } - if n <= b.exists { - b.exists-- - } - } -} - -// Table-driven OK info parser. - -var oktab = []struct { - name string - kind sxKind - fn func(*Client, *Box, *sx) -}{ - {"UIDVALIDITY", sxNumber, xokuidvalidity}, - {"PERMANENTFLAGS", sxList, xokpermflags}, - {"UNSEEN", sxNumber, xokunseen}, - {"READ-WRITE", 0, xokreadwrite}, - {"READ-ONLY", 0, xokreadonly}, -} - -func xok(c *Client, x *sx) { - c.data.mustBeLocked() - b := c.box - if b == nil { - return - } - if len(x.sx) >= 4 && x.sx[2].kind == sxAtom && x.sx[2].data[0] == '[' { - var arg *sx - if x.sx[3].kind == sxAtom && x.sx[3].data[0] == ']' { - arg = nil - } else if x.sx[4].kind == sxAtom && x.sx[4].data[0] == ']' { - arg = x.sx[3] - } else { - log.Printf("cannot parse OK: %s", x) - return - } - x.sx[2].data = x.sx[2].data[1:] - for _, t := range oktab { - if x.sx[2].isAtom(t.name) { - if t.kind != 0 && (arg == nil || arg.kind != t.kind) { - log.Printf("malformed %s: %s", t.name, arg) - continue - } - t.fn(c, b, arg) - } - } - } -} - -func xokuidvalidity(c *Client, b *Box, x *sx) { - c.data.mustBeLocked() - n := uint32(x.number) - if b.validity != n { - if b.msgByUID != nil { - log.Printf("imap: UID validity reset for %s", b.Name) - } - b.validity = n - b.maxSeen = 0 - b.firstNum = 0 - b.msgByNum = nil - b.msgByUID = nil - } -} - -func xokpermflags(c *Client, b *Box, x *sx) { - c.data.mustBeLocked() - b.permFlags = x.parseFlags() -} - -func xokunseen(c *Client, b *Box, x *sx) { - c.data.mustBeLocked() - b.unseen = int(x.number) -} - -func xokreadwrite(c *Client, b *Box, x *sx) { - c.data.mustBeLocked() - b.readOnly = false -} - -func xokreadonly(c *Client, b *Box, x *sx) { - c.data.mustBeLocked() - b.readOnly = true -} - -// Table-driven FETCH message info parser. - -var msgtab = []struct { - name string - fn func(*Msg, *sx, *sx) -}{ - {"FLAGS", xmsgflags}, - {"INTERNALDATE", xmsgdate}, - {"RFC822.SIZE", xmsgrfc822size}, - {"ENVELOPE", xmsgenvelope}, - {"X-GM-MSGID", xmsggmmsgid}, - {"X-GM-THRID", xmsggmthrid}, - {"BODY", xmsgbody}, - {"BODY[", xmsgbodydata}, -} - -func xfetch(c *Client, x *sx) { - c.data.mustBeLocked() - if c.box == nil { - log.Printf("FETCH but no open box: %s", x) - return - } - - // * 152 FETCH (UID 185 FLAGS() ...) - n := x.sx[1].number - xx := x.sx[3] - if len(xx.sx)%2 != 0 { - log.Printf("malformed FETCH: %s", x) - return - } - var uid uint64 - for i := 0; i < len(xx.sx); i += 2 { - if xx.sx[i].isAtom("UID") { - if xx.sx[i+1].kind == sxNumber { - uid = uint64(xx.sx[i+1].number) | uint64(c.box.validity)<<32 - goto HaveUID - } - } - } - // This happens; too bad. - // log.Printf("FETCH without UID: %s", x) - return - -HaveUID: - if m := c.box.msgByUID[uid]; m != nil && m.dead { - // FETCH during box garbage collection. - m.dead = false - m.num = int(n) - return - } - m := c.box.newMsg(uid, int(n)) - for i := 0; i < len(xx.sx); i += 2 { - k, v := xx.sx[i], xx.sx[i+1] - for _, t := range msgtab { - if k.isAtom(t.name) { - t.fn(m, k, v) - } - } - } -} - -func xmsggmmsgid(m *Msg, k, v *sx) { - m.GmailID = uint64(v.number) -} - -func xmsggmthrid(m *Msg, k, v *sx) { - m.GmailThread = uint64(v.number) -} - -func xmsgflags(m *Msg, k, v *sx) { - m.Flags = v.parseFlags() -} - -func xmsgrfc822size(m *Msg, k, v *sx) { - m.Bytes = v.number -} - -func xmsgdate(m *Msg, k, v *sx) { - m.Date = v.parseDate() -} - -func xmsgenvelope(m *Msg, k, v *sx) { - m.Hdr = parseEnvelope(v) -} - -func parseEnvelope(v *sx) *MsgHdr { - if v.kind != sxList || !v.match("SSLLLLLLSS") { - log.Printf("bad envelope: %s", v) - return nil - } - - hdr := &MsgHdr{ - Date: v.sx[0].nstring(), - Subject: unrfc2047(v.sx[1].nstring()), - From: parseAddrs(v.sx[2]), - Sender: parseAddrs(v.sx[3]), - ReplyTo: parseAddrs(v.sx[4]), - To: parseAddrs(v.sx[5]), - CC: parseAddrs(v.sx[6]), - BCC: parseAddrs(v.sx[7]), - InReplyTo: unrfc2047(v.sx[8].nstring()), - MessageID: unrfc2047(v.sx[9].nstring()), - } - - h := md5.New() - fmt.Fprintf(h, "date: %s\n", hdr.Date) - fmt.Fprintf(h, "subject: %s\n", hdr.Subject) - fmt.Fprintf(h, "from: %s\n", hdr.From) - fmt.Fprintf(h, "sender: %s\n", hdr.Sender) - fmt.Fprintf(h, "replyto: %s\n", hdr.ReplyTo) - fmt.Fprintf(h, "to: %s\n", hdr.To) - fmt.Fprintf(h, "cc: %s\n", hdr.CC) - fmt.Fprintf(h, "bcc: %s\n", hdr.BCC) - fmt.Fprintf(h, "inreplyto: %s\n", hdr.InReplyTo) - fmt.Fprintf(h, "messageid: %s\n", hdr.MessageID) - hdr.Digest = fmt.Sprintf("%x", h.Sum(nil)) - - return hdr -} - -func parseAddrs(x *sx) []Addr { - var addr []Addr - for _, xx := range x.sx { - if !xx.match("SSSS") { - log.Printf("bad address: %s", x) - continue - } - name := unrfc2047(xx.sx[0].nstring()) - // sx[1] is route - local := unrfc2047(xx.sx[2].nstring()) - host := unrfc2047(xx.sx[3].nstring()) - if local == "" || host == "" { - // rfc822 group syntax - addr = append(addr, Addr{name, ""}) - continue - } - addr = append(addr, Addr{name, local + "@" + host}) - } - return addr -} - -func xmsgbody(m *Msg, k, v *sx) { - if v.isNil() { - return - } - if v.kind != sxList { - log.Printf("bad body: %s", v) - } - - // To follow the structure exactly we should be doing this - // to m.NewPart(m.Part[0]) with type message/rfc822, - // but the extra layer is redundant - what else would be in - // a mailbox? - parseStructure(&m.Root, v) - n := m.num - if m.Box.maxSeen < n { - m.Box.maxSeen = n - } -} - -func parseStructure(p *MsgPart, x *sx) { - if x.isNil() { - return - } - if x.kind != sxList { - log.Printf("bad structure: %s", x) - return - } - if x.sx[0].isList() { - // multipart - var i int - for i = 0; i < len(x.sx) && x.sx[i].isList(); i++ { - parseStructure(p.newPart(), x.sx[i]) - } - if i != len(x.sx)-1 || !x.sx[i].isString() { - log.Printf("bad multipart structure: %s", x) - p.Type = "multipart/mixed" - return - } - s := strlwr(x.sx[i].nstring()) - p.Type = "multipart/" + s - return - } - - // single type - if len(x.sx) < 2 || !x.sx[0].isString() { - log.Printf("bad type structure: %s", x) - return - } - s := strlwr(x.sx[0].nstring()) - t := strlwr(x.sx[1].nstring()) - p.Type = s + "/" + t - if len(x.sx) < 7 || !x.sx[2].isList() || !x.sx[3].isString() || !x.sx[4].isString() || !x.sx[5].isString() || !x.sx[6].isNumber() { - log.Printf("bad part structure: %s", x) - return - } - parseParams(p, x.sx[2]) - p.ContentID = x.sx[3].nstring() - p.Desc = x.sx[4].nstring() - p.Encoding = x.sx[5].nstring() - p.Bytes = x.sx[6].number - if p.Type == "message/rfc822" { - if len(x.sx) < 10 || !x.sx[7].isList() || !x.sx[8].isList() || !x.sx[9].isNumber() { - log.Printf("bad rfc822 structure: %s", x) - return - } - p.Hdr = parseEnvelope(x.sx[7]) - parseStructure(p.newPart(), x.sx[8]) - p.Lines = x.sx[9].number - } - if s == "text" { - if len(x.sx) < 8 || !x.sx[7].isNumber() { - log.Printf("bad text structure: %s", x) - return - } - p.Lines = x.sx[7].number - } -} - -func parseParams(p *MsgPart, x *sx) { - if x.isNil() { - return - } - if len(x.sx)%2 != 0 { - log.Printf("bad message params: %s", x) - return - } - - for i := 0; i < len(x.sx); i += 2 { - k, v := x.sx[i].nstring(), x.sx[i+1].nstring() - k = strlwr(k) - switch strlwr(k) { - case "charset": - p.Charset = strlwr(v) - case "name": - p.Name = v - } - } -} - -func (c *Client) fetch(p *MsgPart, what string) { - c.io.mustBeLocked() - id := p.ID - if what != "" { - if id != "" { - id += "." - } - id += what - } - c.cmd(p.Msg.Box, "UID FETCH %d BODY[%s]", p.Msg.UID&(1<<32-1), id) -} - -func xmsgbodydata(m *Msg, k, v *sx) { - // k.data is []byte("BODY[...") - name := string(k.data[5:]) - if i := strings.Index(name, "]"); i >= 0 { - name = name[:i] - } - - p := &m.Root - for name != "" && '1' <= name[0] && name[0] <= '9' { - var num int - num, name = parseNum(name) - if num == 0 { - log.Printf("unexpected body name: %s", k.data) - return - } - num-- - if num >= len(p.Child) { - log.Printf("invalid body name: %s", k.data) - return - } - p = p.Child[num] - } - - switch strlwr(name) { - case "": - p.raw = v.nbytes() - case "mime": - p.mimeHeader = nocr(v.nbytes()) - case "header": - p.rawHeader = nocr(v.nbytes()) - case "text": - p.rawBody = nocr(v.nbytes()) - } -} - -func parseNum(name string) (int, string) { - rest := "" - i := strings.Index(name, ".") - if i >= 0 { - name, rest = name[:i], name[i+1:] - } - n, _ := strconv.Atoi(name) - return n, rest -} - -func nocr(b []byte) []byte { - w := 0 - for _, c := range b { - if c != '\r' { - b[w] = c - w++ - } - } - return b[:w] -} - -type uidList []*Msg - -func (l uidList) String() string { - var b bytes.Buffer - for i, m := range l { - if i > 0 { - b.WriteByte(',') - } - fmt.Fprintf(&b, "%d", m.UID&(1<<32-1)) - } - return b.String() -} - -func (c *Client) deleteList(msgs []*Msg) error { - if len(msgs) == 0 { - return nil - } - c.io.mustBeLocked() - - b := msgs[0].Box - for _, m := range msgs { - if m.Box != b { - return fmt.Errorf("messages span boxes: %q and %q", b.Name, m.Box.Name) - } - if uint32(m.UID>>32) != b.validity { - return fmt.Errorf("stale message") - } - } - - err := c.cmd(b, "UID STORE %s +FLAGS (\\Deleted)", uidList(msgs)) - if err == nil && c.box == b { - err = c.cmd(b, "EXPUNGE") - } - return err -} - -func (c *Client) copyList(dst, src *Box, msgs []*Msg) error { - if len(msgs) == 0 { - return nil - } - c.io.mustBeLocked() - - for _, m := range msgs { - if m.Box != src { - return fmt.Errorf("messages span boxes: %q and %q", src.Name, m.Box.Name) - } - if uint32(m.UID>>32) != src.validity { - return fmt.Errorf("stale message") - } - } - - var name string - if dst == c.inbox { - name = "INBOX" - } else { - name = iquote(dst.Name) - } - return c.cmd(src, "UID COPY %s %s", uidList(msgs), name) -} - -func (c *Client) muteList(src *Box, msgs []*Msg) error { - if len(msgs) == 0 { - return nil - } - c.io.mustBeLocked() - - for _, m := range msgs { - if m.Box != src { - return fmt.Errorf("messages span boxes: %q and %q", src.Name, m.Box.Name) - } - if uint32(m.UID>>32) != src.validity { - return fmt.Errorf("stale message") - } - } - - return c.cmd(src, "UID STORE %s +X-GM-LABELS (\\Muted)", uidList(msgs)) -} -- cgit v1.2.3-1-g7c22