// Package email helps manage emails stored in Maildir format. A good // write-up on the format is available at http://cr.yp.to/proto/maildir.html package maildir import ( "bytes" "fmt" "os" "path/filepath" "strings" ) // Info represents the info bits encoded as the suffix of a Maildir file. info // starting with "2,": Each character after the comma is an independent flag. // Flag "P" (passed): the user has resent/forwarded/bounced this message to someone else. // Flag "R" (replied): the user has replied to this message. // Flag "S" (seen): the user has viewed this message, though perhaps he didn't read all the way through it. // Flag "T" (trashed): the user has moved this message to the trash; the trash will be emptied by a later user action. // Flag "D" (draft): the user considers this message a draft; toggled at user discretion. // Flag "F" (flagged): user-defined flag; toggled at user discretion. // New flags may be defined later. Flags must be stored in ASCII order: e.g., "2,FRS". type Info struct { Base string // The portion of the filename before the ':'. Passed bool Replied bool Seen bool Trashed bool Draft bool Flagged bool } // NewInfo returns an Info containing the version 2 Maildir flags parsed from // fn's suffix. func NewInfo(fn string) (info Info) { info.Base = fn i := strings.LastIndex(fn, ":") if i != -1 { info.Base = fn[:i] infoStr := fn[i:] i = strings.Index(infoStr, ",") if i == -1 { return } flags := infoStr[i:] info.Passed = strings.Contains(flags, "P") info.Replied = strings.Contains(flags, "R") info.Seen = strings.Contains(flags, "S") info.Trashed = strings.Contains(flags, "T") info.Draft = strings.Contains(flags, "D") info.Flagged = strings.Contains(flags, "F") } return } func (i Info) String() string { if i.Passed || i.Replied || i.Seen || i.Trashed || i.Draft || i.Flagged { b := new(bytes.Buffer) fmt.Fprintf(b, "%s:2,", i.Base) if i.Passed { fmt.Fprint(b, "P") } if i.Replied { fmt.Fprint(b, "R") } if i.Seen { fmt.Fprint(b, "S") } if i.Trashed { fmt.Fprint(b, "T") } if i.Draft { fmt.Fprint(b, "D") } if i.Flagged { fmt.Fprint(b, "F") } return b.String() } return i.Base } func Walk(root string, walker func(path string) error) error { return filepath.Walk(root, func(path string, info os.FileInfo, err error) error { if !info.Mode().IsRegular() { return nil } okDirs := map[string]bool{ "cur": true, "tmp": true, "new": true, } dirPath := filepath.Dir(path) dir := filepath.Base(dirPath) if !okDirs[dir] { return nil } return walker(path) }) }