Sync all IMAP folders
This commit is contained in:
parent
bb2bcb0576
commit
569b274b2c
4 changed files with 228 additions and 32 deletions
|
|
@ -66,7 +66,21 @@ type imapSource struct {
|
|||
}
|
||||
|
||||
func (s *imapSource) Folders() ([]Folder, error) {
|
||||
return []Folder{{Name: "INBOX", Delim: "/"}}, nil
|
||||
boxes, err := s.c.listMailboxes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
folders := make([]Folder, 0, len(boxes))
|
||||
for _, box := range boxes {
|
||||
if hasIMAPAttr(box.Attrs, `\Noselect`) {
|
||||
continue
|
||||
}
|
||||
folders = append(folders, Folder{Name: box.Name, Attrs: box.Attrs, Delim: box.Delim})
|
||||
}
|
||||
if len(folders) == 0 {
|
||||
folders = append(folders, Folder{Name: "INBOX", Delim: "/"})
|
||||
}
|
||||
return folders, nil
|
||||
}
|
||||
|
||||
func (s *imapSource) Fetch(folder string) ([]RawMessage, error) {
|
||||
|
|
@ -198,6 +212,110 @@ func (c *simpleIMAP) fetchAll() ([]RawMessage, error) {
|
|||
}
|
||||
}
|
||||
|
||||
type imapListMailbox struct {
|
||||
Name string
|
||||
Attrs []string
|
||||
Delim string
|
||||
}
|
||||
|
||||
func (c *simpleIMAP) listMailboxes() ([]imapListMailbox, error) {
|
||||
tag := c.nextTag()
|
||||
if _, err := fmt.Fprintf(c.w, "%s LIST \"\" \"*\"\r\n", tag); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.w.Flush(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out []imapListMailbox
|
||||
for {
|
||||
line, err := c.r.ReadString('\n')
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if strings.HasPrefix(line, tag+" ") {
|
||||
if strings.Contains(line, " OK") {
|
||||
return out, nil
|
||||
}
|
||||
return out, fmt.Errorf("imap list failed: %s", strings.TrimSpace(line))
|
||||
}
|
||||
if !strings.HasPrefix(line, "* LIST ") {
|
||||
continue
|
||||
}
|
||||
if box, ok := parseListMailbox(strings.TrimSpace(line)); ok {
|
||||
out = append(out, box)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseListMailbox(line string) (imapListMailbox, bool) {
|
||||
rest := strings.TrimSpace(strings.TrimPrefix(line, "* LIST "))
|
||||
if !strings.HasPrefix(rest, "(") {
|
||||
return imapListMailbox{}, false
|
||||
}
|
||||
endAttrs := strings.Index(rest, ")")
|
||||
if endAttrs < 0 {
|
||||
return imapListMailbox{}, false
|
||||
}
|
||||
attrs := strings.Fields(rest[1:endAttrs])
|
||||
rest = strings.TrimSpace(rest[endAttrs+1:])
|
||||
delim, next, ok := parseIMAPAtomOrQuoted(rest)
|
||||
if !ok {
|
||||
return imapListMailbox{}, false
|
||||
}
|
||||
if strings.EqualFold(delim, "NIL") {
|
||||
delim = "/"
|
||||
}
|
||||
rest = strings.TrimSpace(rest[next:])
|
||||
name, _, ok := parseIMAPAtomOrQuoted(rest)
|
||||
if !ok || name == "" {
|
||||
return imapListMailbox{}, false
|
||||
}
|
||||
return imapListMailbox{Name: name, Attrs: attrs, Delim: delim}, true
|
||||
}
|
||||
|
||||
func parseIMAPAtomOrQuoted(s string) (string, int, bool) {
|
||||
s = strings.TrimLeft(s, " \t")
|
||||
trimmed := len(s)
|
||||
if s == "" {
|
||||
return "", 0, false
|
||||
}
|
||||
if s[0] != '"' {
|
||||
end := strings.IndexAny(s, " \t\r\n")
|
||||
if end < 0 {
|
||||
end = len(s)
|
||||
}
|
||||
return s[:end], trimmed - len(s) + end, true
|
||||
}
|
||||
var b strings.Builder
|
||||
escaped := false
|
||||
for i := 1; i < len(s); i++ {
|
||||
ch := s[i]
|
||||
if escaped {
|
||||
b.WriteByte(ch)
|
||||
escaped = false
|
||||
continue
|
||||
}
|
||||
if ch == '\\' {
|
||||
escaped = true
|
||||
continue
|
||||
}
|
||||
if ch == '"' {
|
||||
return b.String(), trimmed - len(s) + i + 1, true
|
||||
}
|
||||
b.WriteByte(ch)
|
||||
}
|
||||
return "", 0, false
|
||||
}
|
||||
|
||||
func hasIMAPAttr(attrs []string, want string) bool {
|
||||
for _, attr := range attrs {
|
||||
if strings.EqualFold(attr, want) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *simpleIMAP) appendMessage(folder string, m RawMessage) error {
|
||||
tag := c.nextTag()
|
||||
flags := ""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue