package maildir import ( "bufio" "bytes" "fmt" "io" "io/ioutil" "github.com/emersion/go-maildir" "github.com/emersion/go-message" "github.com/emersion/go-message/textproto" "git.sr.ht/~sircmpwn/aerc/models" "git.sr.ht/~sircmpwn/aerc/worker/lib" ) // A Message is an individual email inside of a maildir.Dir. type Message struct { dir maildir.Dir uid uint32 key string } // NewReader reads a message into memory and returns an io.Reader for it. func (m Message) NewReader() (io.Reader, error) { f, err := m.dir.Open(m.key) if err != nil { return nil, err } defer f.Close() b, err := ioutil.ReadAll(f) if err != nil { return nil, err } return bytes.NewReader(b), nil } // Flags fetches the set of flags currently applied to the message. func (m Message) Flags() ([]maildir.Flag, error) { return m.dir.Flags(m.key) } // ModelFlags fetches the set of models.flags currently applied to the message. func (m Message) ModelFlags() ([]models.Flag, error) { flags, err := m.dir.Flags(m.key) if err != nil { return nil, err } return translateFlags(flags), nil } // SetFlags replaces the message's flags with a new set. func (m Message) SetFlags(flags []maildir.Flag) error { return m.dir.SetFlags(m.key, flags) } // MarkRead either adds or removes the maildir.FlagSeen flag from the message. func (m Message) MarkRead(seen bool) error { flags, err := m.Flags() if err != nil { return fmt.Errorf("could not read previous flags: %v", err) } if seen { flags = append(flags, maildir.FlagSeen) return m.SetFlags(flags) } var newFlags []maildir.Flag for _, flag := range flags { if flag != maildir.FlagSeen { newFlags = append(newFlags, flag) } } return m.SetFlags(newFlags) } // Remove deletes the email immediately. func (m Message) Remove() error { return m.dir.Remove(m.key) } // MessageInfo populates a models.MessageInfo struct for the message. func (m Message) MessageInfo() (*models.MessageInfo, error) { return lib.MessageInfo(m) } // NewBodyPartReader creates a new io.Reader for the requested body part(s) of // the message. func (m Message) NewBodyPartReader(requestedParts []int) (io.Reader, error) { f, err := m.dir.Open(m.key) if err != nil { return nil, err } defer f.Close() msg, err := message.Read(f) if err != nil { return nil, fmt.Errorf("could not read message: %v", err) } return lib.FetchEntityPartReader(msg, requestedParts) } var flagMap = map[maildir.Flag]models.Flag{ maildir.FlagReplied: models.AnsweredFlag, maildir.FlagSeen: models.SeenFlag, maildir.FlagTrashed: models.DeletedFlag, maildir.FlagFlagged: models.FlaggedFlag, // maildir.FlagDraft Flag = 'D' // maildir.FlagPassed Flag = 'P' } func translateFlags(maildirFlags []maildir.Flag) []models.Flag { var flags []models.Flag for _, maildirFlag := range maildirFlags { if flag, ok := flagMap[maildirFlag]; ok { flags = append(flags, flag) } } return flags } func (m Message) UID() uint32 { return m.uid } func (m Message) Headers() (*message.Header, error) { f, err := m.dir.Open(m.key) if err != nil { return nil, err } defer f.Close() br := bufio.NewReader(f) h, err := textproto.ReadHeader(br) if err != nil { return nil, err } mh := message.Header{h} return &mh, nil }