package memory import ( "io/ioutil" "time" "github.com/emersion/go-imap" "github.com/emersion/go-imap/backend" "github.com/emersion/go-imap/backend/backendutil" ) var Delimiter = "/" type Mailbox struct { Subscribed bool Messages []*Message name string user *User } func (mbox *Mailbox) Name() string { return mbox.name } func (mbox *Mailbox) Info() (*imap.MailboxInfo, error) { info := &imap.MailboxInfo{ Delimiter: Delimiter, Name: mbox.name, } return info, nil } func (mbox *Mailbox) uidNext() uint32 { var uid uint32 for _, msg := range mbox.Messages { if msg.Uid > uid { uid = msg.Uid } } uid++ return uid } func (mbox *Mailbox) flags() []string { flagsMap := make(map[string]bool) for _, msg := range mbox.Messages { for _, f := range msg.Flags { if !flagsMap[f] { flagsMap[f] = true } } } var flags []string for f := range flagsMap { flags = append(flags, f) } return flags } func (mbox *Mailbox) unseenSeqNum() uint32 { for i, msg := range mbox.Messages { seqNum := uint32(i + 1) seen := false for _, flag := range msg.Flags { if flag == imap.SeenFlag { seen = true break } } if !seen { return seqNum } } return 0 } func (mbox *Mailbox) Status(items []imap.StatusItem) (*imap.MailboxStatus, error) { status := imap.NewMailboxStatus(mbox.name, items) status.Flags = mbox.flags() status.PermanentFlags = []string{"\\*"} status.UnseenSeqNum = mbox.unseenSeqNum() for _, name := range items { switch name { case imap.StatusMessages: status.Messages = uint32(len(mbox.Messages)) case imap.StatusUidNext: status.UidNext = mbox.uidNext() case imap.StatusUidValidity: status.UidValidity = 1 case imap.StatusRecent: status.Recent = 0 // TODO case imap.StatusUnseen: status.Unseen = 0 // TODO } } return status, nil } func (mbox *Mailbox) SetSubscribed(subscribed bool) error { mbox.Subscribed = subscribed return nil } func (mbox *Mailbox) Check() error { return nil } func (mbox *Mailbox) ListMessages(uid bool, seqSet *imap.SeqSet, items []imap.FetchItem, ch chan<- *imap.Message) error { defer close(ch) for i, msg := range mbox.Messages { seqNum := uint32(i + 1) var id uint32 if uid { id = msg.Uid } else { id = seqNum } if !seqSet.Contains(id) { continue } m, err := msg.Fetch(seqNum, items) if err != nil { continue } ch <- m } return nil } func (mbox *Mailbox) SearchMessages(uid bool, criteria *imap.SearchCriteria) ([]uint32, error) { var ids []uint32 for i, msg := range mbox.Messages { seqNum := uint32(i + 1) ok, err := msg.Match(seqNum, criteria) if err != nil || !ok { continue } var id uint32 if uid { id = msg.Uid } else { id = seqNum } ids = append(ids, id) } return ids, nil } func (mbox *Mailbox) CreateMessage(flags []string, date time.Time, body imap.Literal) error { if date.IsZero() { date = time.Now() } b, err := ioutil.ReadAll(body) if err != nil { return err } mbox.Messages = append(mbox.Messages, &Message{ Uid: mbox.uidNext(), Date: date, Size: uint32(len(b)), Flags: flags, Body: b, }) return nil } func (mbox *Mailbox) UpdateMessagesFlags(uid bool, seqset *imap.SeqSet, op imap.FlagsOp, flags []string) error { for i, msg := range mbox.Messages { var id uint32 if uid { id = msg.Uid } else { id = uint32(i + 1) } if !seqset.Contains(id) { continue } msg.Flags = backendutil.UpdateFlags(msg.Flags, op, flags) } return nil } func (mbox *Mailbox) CopyMessages(uid bool, seqset *imap.SeqSet, destName string) error { dest, ok := mbox.user.mailboxes[destName] if !ok { return backend.ErrNoSuchMailbox } for i, msg := range mbox.Messages { var id uint32 if uid { id = msg.Uid } else { id = uint32(i + 1) } if !seqset.Contains(id) { continue } msgCopy := *msg msgCopy.Uid = dest.uidNext() dest.Messages = append(dest.Messages, &msgCopy) } return nil } func (mbox *Mailbox) Expunge() error { for i := len(mbox.Messages) - 1; i >= 0; i-- { msg := mbox.Messages[i] deleted := false for _, flag := range msg.Flags { if flag == imap.DeletedFlag { deleted = true break } } if deleted { mbox.Messages = append(mbox.Messages[:i], mbox.Messages[i+1:]...) } } return nil }