package main import ( "github.com/emersion/go-imap/v2" "github.com/emersion/go-imap/v2/imapserver" "io" ) type ( user = User mailbox = MailboxView ) // UserSession represents a session tied to a specific user. // // UserSession implements imapserver.Session. Typically, a UserSession pointer // is embedded into a larger struct which overrides Login. type UserSession struct { *user // immutable *mailbox // may be nil } var _ imapserver.SessionIMAP4rev2 = (*UserSession)(nil) // NewUserSession creates a new user session. func NewUserSession(user *User) *UserSession { return &UserSession{user: user} } type ByteLiteral struct { io.Reader size int64 } func (l *ByteLiteral) Size() int64 { return l.size } func (sess *UserSession) Append(mboxName string, r imap.LiteralReader, options *imap.AppendOptions) (*imap.AppendData, error) { mbox, err := sess.user.mailbox(mboxName) if err != nil { return nil, err } view := mbox.NewView() defer func() { err := view.Close() if err != nil { log("Failed to close view: "+err.Error()+", resource leaks may occur", 1) } }() println("INITIAL APPEND ENTRYPOINT SUCCESS") return view.appendLiteral(&ByteLiteral{Reader: r, size: r.Size()}, options) } func (sess *UserSession) Close() error { if sess != nil && sess.mailbox != nil { return sess.mailbox.Close() } return nil } func (sess *UserSession) Select(name string, _ *imap.SelectOptions) (*imap.SelectData, error) { mbox, err := sess.user.mailbox(name) if err != nil { return nil, err } sess.mailbox = mbox.NewView() return mbox.selectData() } func (sess *UserSession) Unselect() error { err := sess.mailbox.Close() if err != nil { return err } sess.mailbox = nil return nil } func (sess *UserSession) Copy(numSet imap.NumSet, destName string) (data *imap.CopyData, fErr error) { dest, err := sess.user.mailbox(destName) if err != nil { return nil, &imap.Error{ Type: imap.StatusResponseTypeNo, Code: imap.ResponseCodeTryCreate, Text: "No such mailbox", } } else if sess.mailbox != nil && dest == sess.mailbox.Mailbox { return nil, &imap.Error{ Type: imap.StatusResponseTypeNo, Text: "Source and destination mailboxes are identical", } } destView := dest.NewView() defer func() { err := destView.Close() if err != nil { log("Failed to close view: "+err.Error()+", resource leaks may occur", 1) } }() var sourceUIDs, destUIDs imap.UIDSet err = sess.mailbox.forEach(numSet, func(seqNum uint32, msg *message) { appendData, err := destView.copyMsg(msg) if err != nil { fErr = err return } sourceUIDs.AddNum(msg.uid) destUIDs.AddNum(appendData.UID) }) if err != nil { return nil, err } if fErr != nil { return nil, fErr } return &imap.CopyData{ UIDValidity: dest.UIDValidity, SourceUIDs: sourceUIDs, DestUIDs: destUIDs, }, nil } func (sess *UserSession) Move(w *imapserver.MoveWriter, numSet imap.NumSet, destName string) (fErr error) { dest, err := sess.user.mailbox(destName) if err != nil { return &imap.Error{ Type: imap.StatusResponseTypeNo, Code: imap.ResponseCodeTryCreate, Text: "No such mailbox", } } else if sess.mailbox != nil && dest == sess.mailbox.Mailbox { return &imap.Error{ Type: imap.StatusResponseTypeNo, Text: "Source and destination mailboxes are identical", } } destView := dest.NewView() defer func() { err := destView.Close() if err != nil { log("Failed to close view: "+err.Error()+", resource leaks may occur", 1) } }() var sourceUIDs, destUIDs imap.UIDSet err = sess.mailbox.forEach(numSet, func(seqNum uint32, msg *message) { var appendData *imap.AppendData appendData, fErr = destView.copyMsg(msg) if fErr != nil { return } sourceUIDs.AddNum(msg.uid) destUIDs.AddNum(appendData.UID) }) if err != nil { return err } if fErr != nil { return fErr } err = w.WriteCopyData(&imap.CopyData{ UIDValidity: dest.UIDValidity, SourceUIDs: sourceUIDs, DestUIDs: destUIDs, }) if err != nil { return err } println(sourceUIDs.String()) err = sess.mailbox.Expunge(nil, &sourceUIDs) if err != nil { return err } return nil } func (sess *UserSession) Poll(w *imapserver.UpdateWriter, allowExpunge bool) error { if sess.mailbox == nil { return nil } return sess.mailbox.Poll(w, allowExpunge) } func (sess *UserSession) Idle(w *imapserver.UpdateWriter, stop <-chan struct{}) error { if sess.mailbox == nil { return nil // TODO } return sess.mailbox.Idle(w, stop) }