完成IMAP关系与搜索边界修复检查点
This commit is contained in:
parent
b5ff928f44
commit
c4777dae85
@ -52,6 +52,7 @@ type serverSession struct {
|
||||
ctx *context.Context
|
||||
status Status
|
||||
currentMailbox string
|
||||
readOnly bool
|
||||
connectTime time.Time
|
||||
deleteUidList []int
|
||||
remoteAddress string
|
||||
@ -277,5 +278,6 @@ func appendTooBigError() error {
|
||||
|
||||
func (s *serverSession) Unselect() error {
|
||||
s.currentMailbox = ""
|
||||
s.readOnly = false
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ import (
|
||||
"git.pcux.cn/xuans/Smail/config"
|
||||
"git.pcux.cn/xuans/Smail/dto/parsemail"
|
||||
"git.pcux.cn/xuans/Smail/dto/response"
|
||||
"git.pcux.cn/xuans/Smail/services/detail"
|
||||
"git.pcux.cn/xuans/Smail/services/list"
|
||||
"git.pcux.cn/xuans/Smail/utils/context"
|
||||
"github.com/emersion/go-imap/v2"
|
||||
@ -345,7 +344,7 @@ func write(ctx *context.Context, w *imapserver.FetchWriter, emailList []*respons
|
||||
}
|
||||
for _, section := range options.BodySection {
|
||||
if !section.Peek {
|
||||
if err := detail.MakeRead(ctx, email.Id, true); err != nil {
|
||||
if err := updateReadByUID(ctx.UserID, email.UeId, true); err != nil {
|
||||
log.WithContext(ctx).WithError(err).Error("mark IMAP message as read failed")
|
||||
return &imap.Error{Type: imap.StatusResponseTypeNo, Text: "Fetch failed"}
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ import (
|
||||
|
||||
func matchGroup(ctx *context.Context, w *imapserver.ListWriter, basePath, pattern string) error {
|
||||
var groups []*models.Group
|
||||
if basePath == "" && pattern == "*" {
|
||||
if basePath == "" && (pattern == "*" || pattern == "%") {
|
||||
if err := db.Instance.Table("group").Where("user_id=?", ctx.UserID).Find(&groups); err != nil {
|
||||
log.WithContext(ctx).WithError(err).Error("query IMAP mailbox list failed")
|
||||
return listFailedError()
|
||||
|
||||
@ -43,30 +43,32 @@ func (s *serverSession) Move(w *imapserver.MoveWriter, numSet imap.NumSet, dest
|
||||
}
|
||||
}
|
||||
|
||||
mailIDs := make([]int, 0, len(emailList))
|
||||
relationIDs := make([]int, 0, len(emailList))
|
||||
for _, email := range emailList {
|
||||
mailIDs = append(mailIDs, email.Id)
|
||||
if email.UeId > 0 {
|
||||
relationIDs = append(relationIDs, email.UeId)
|
||||
}
|
||||
}
|
||||
|
||||
if len(mailIDs) == 0 {
|
||||
if len(relationIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if group.IsDefaultBox(dest) {
|
||||
return move2defaultbox(s.ctx, mailIDs, dest)
|
||||
return move2defaultbox(s.ctx, relationIDs, dest)
|
||||
}
|
||||
return move2userbox(s.ctx, mailIDs, dest)
|
||||
return move2userbox(s.ctx, relationIDs, dest)
|
||||
}
|
||||
|
||||
func move2defaultbox(ctx *context.Context, mailIDs []int, dest string) error {
|
||||
if err := group.Move2DefaultBox(ctx, mailIDs, dest); err != nil {
|
||||
func move2defaultbox(ctx *context.Context, relationIDs []int, dest string) error {
|
||||
if err := group.MoveRelationsToDefaultBox(ctx, relationIDs, dest); err != nil {
|
||||
log.WithContext(ctx).WithError(err).Error("move messages to default mailbox failed")
|
||||
return moveFailedError()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func move2userbox(ctx *context.Context, mailIDs []int, dest string) error {
|
||||
func move2userbox(ctx *context.Context, relationIDs []int, dest string) error {
|
||||
groupInfo, err := group.GetGroupByFullPath(ctx, dest)
|
||||
if err != nil {
|
||||
log.WithContext(ctx).WithError(err).Error("resolve IMAP move destination failed")
|
||||
@ -75,7 +77,7 @@ func move2userbox(ctx *context.Context, mailIDs []int, dest string) error {
|
||||
if groupInfo == nil || groupInfo.ID == 0 {
|
||||
return &imap.Error{Type: imap.StatusResponseTypeNo, Text: "Mailbox not found"}
|
||||
}
|
||||
if err := group.MoveMailToGroup(ctx, mailIDs, groupInfo.ID); err != nil {
|
||||
if err := group.MoveRelationsToGroup(ctx, relationIDs, groupInfo.ID); err != nil {
|
||||
log.WithContext(ctx).Error("move messages to user mailbox failed")
|
||||
return moveFailedError()
|
||||
}
|
||||
|
||||
@ -16,12 +16,14 @@ func (s *serverSession) Select(mailbox string, options *imap.SelectOptions) (*im
|
||||
}
|
||||
|
||||
mailbox = strings.Trim(mailbox, `"`)
|
||||
readOnly := options != nil && options.ReadOnly
|
||||
_, data, err := group.GetGroupStatus(s.ctx, mailbox, []string{"MESSAGES", "UNSEEN", "UIDNEXT", "UIDVALIDITY"})
|
||||
if err != nil {
|
||||
log.WithContext(s.ctx).WithError(err).Error("query selected mailbox status failed")
|
||||
return nil, &imap.Error{Type: imap.StatusResponseTypeNo, Text: "Select failed"}
|
||||
}
|
||||
s.currentMailbox = mailbox
|
||||
s.readOnly = readOnly
|
||||
|
||||
numMessages, err := imapUint32(data["MESSAGES"])
|
||||
if err != nil {
|
||||
|
||||
@ -18,6 +18,9 @@ func (s *serverSession) Store(w *imapserver.FetchWriter, numSet imap.NumSet, fla
|
||||
if s == nil || s.ctx == nil || s.ctx.UserID <= 0 || flags == nil {
|
||||
return &imap.Error{Type: imap.StatusResponseTypeNo, Text: "Store failed"}
|
||||
}
|
||||
if s.readOnly {
|
||||
return &imap.Error{Type: imap.StatusResponseTypeNo, Text: "Mailbox is read-only"}
|
||||
}
|
||||
|
||||
var emailList []*response.EmailResponseData
|
||||
|
||||
|
||||
@ -398,7 +398,11 @@ func MoveMailToGroup(ctx *context.Context, mailIDs []int, groupID int) error {
|
||||
rollbackGroupTransaction(session)
|
||||
return fmt.Errorf("load message ownership: %w", err)
|
||||
}
|
||||
if len(relations) != len(uniqueIDs) {
|
||||
ownedIDs := make(map[int]struct{}, len(relations))
|
||||
for _, relation := range relations {
|
||||
ownedIDs[relation.EmailID] = struct{}{}
|
||||
}
|
||||
if len(ownedIDs) != len(uniqueIDs) {
|
||||
rollbackGroupTransaction(session)
|
||||
return errors2.New("one or more messages are not owned by the user")
|
||||
}
|
||||
@ -429,6 +433,48 @@ func MoveMailToGroup(ctx *context.Context, mailIDs []int, groupID int) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MoveRelationsToGroup 按当前用户的邮箱关系 ID 移动邮件,避免同一封邮件的多个关系互相影响。
|
||||
func MoveRelationsToGroup(ctx *context.Context, relationIDs []int, groupID int) error {
|
||||
if groupID <= 0 {
|
||||
return errors2.New("invalid message move request")
|
||||
}
|
||||
uniqueIDs, err := normalizeMoveMessageIDs(relationIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
session := db.Instance.NewSession()
|
||||
defer session.Close()
|
||||
if err := session.Begin(); err != nil {
|
||||
return fmt.Errorf("begin relation move transaction: %w", err)
|
||||
}
|
||||
var target models.Group
|
||||
found, err := session.Table("group").Where("id = ? and user_id = ?", groupID, ctx.UserID).Get(&target)
|
||||
if err != nil || !found {
|
||||
rollbackGroupTransaction(session)
|
||||
if err != nil {
|
||||
return fmt.Errorf("load move destination: %w", err)
|
||||
}
|
||||
return errors2.New("move destination not found")
|
||||
}
|
||||
var relations []models.UserEmail
|
||||
if err := session.Table(&models.UserEmail{}).Where(builder.Eq{"id": uniqueIDs, "user_id": ctx.UserID}).And("email_id > 0").Find(&relations); err != nil {
|
||||
rollbackGroupTransaction(session)
|
||||
return fmt.Errorf("load relation ownership: %w", err)
|
||||
}
|
||||
if len(relations) != len(uniqueIDs) {
|
||||
rollbackGroupTransaction(session)
|
||||
return errors2.New("one or more messages are not owned by the user")
|
||||
}
|
||||
if _, err := session.Table(&models.UserEmail{}).Where(builder.Eq{"id": uniqueIDs, "user_id": ctx.UserID}).Cols("group_id").Update(map[string]interface{}{"group_id": groupID}); err != nil {
|
||||
rollbackGroupTransaction(session)
|
||||
return fmt.Errorf("move relations: %w", err)
|
||||
}
|
||||
if err := session.Commit(); err != nil {
|
||||
return fmt.Errorf("commit relation move transaction: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func buildChildren(ctx *context.Context, parentId int, visited map[int]struct{}, depth int) ([]*GroupItem, error) {
|
||||
if depth >= maxGroupDepth {
|
||||
return nil, errors2.New("group hierarchy is too deep")
|
||||
@ -569,14 +615,14 @@ func GetGroupStatus(ctx *context.Context, groupName string, params []string) (st
|
||||
var err error
|
||||
switch param {
|
||||
case "MESSAGES":
|
||||
value, err = readGroupValue(db.Instance.Table("user_email").Select("count(1)").Where("group_id=? and user_id=?", group.ID, ctx.UserID), "query mailbox message count failed")
|
||||
value, err = readGroupValue(db.Instance.Table("user_email").Select("count(1)").Where("group_id=? and user_id=? and email_id > 0", group.ID, ctx.UserID), "query mailbox message count failed")
|
||||
case "UIDNEXT":
|
||||
value, err = readGroupValue(db.Instance.Table("user_email").Select("id").Where("group_id=? and user_id=?", group.ID, ctx.UserID).OrderBy("id desc"), "query mailbox next UID failed")
|
||||
value++
|
||||
case "UIDVALIDITY":
|
||||
value = group.ID
|
||||
case "UNSEEN":
|
||||
value, err = readGroupValue(db.Instance.Table("user_email").Select("count(1)").Where("group_id=? and user_id=? and is_read=0", group.ID, ctx.UserID), "query mailbox unseen count failed")
|
||||
value, err = readGroupValue(db.Instance.Table("user_email").Select("count(1)").Where("group_id=? and user_id=? and email_id > 0 and is_read=0", group.ID, ctx.UserID), "query mailbox unseen count failed")
|
||||
default:
|
||||
continue
|
||||
}
|
||||
@ -629,21 +675,10 @@ func readGroupValue(querySession *xorm.Session, operation string) (int, error) {
|
||||
}
|
||||
|
||||
func getNextUID(ctx *context.Context, groupName string) (int, error) {
|
||||
var querySession *xorm.Session
|
||||
switch groupName {
|
||||
case "INBOX":
|
||||
querySession = db.Instance.Table("user_email").Select("id").Where("user_id=? and group_id=0 and status = 0", ctx.UserID).OrderBy("id desc")
|
||||
case "Sent Messages":
|
||||
querySession = db.Instance.Table("user_email").Select("id").Where("user_id=? and group_id=0 and status = 1", ctx.UserID).OrderBy("id desc")
|
||||
case "Drafts":
|
||||
querySession = db.Instance.Table("user_email").Select("id").Where("user_id=? and group_id=0 and status = 4", ctx.UserID).OrderBy("id desc")
|
||||
case "Deleted Messages":
|
||||
querySession = db.Instance.Table("user_email").Select("id").Where("user_id=? and group_id=0 and status = 3", ctx.UserID).OrderBy("id desc")
|
||||
case "Junk":
|
||||
querySession = db.Instance.Table("user_email").Select("id").Where("user_id=? and group_id=0 and status = 5", ctx.UserID).OrderBy("id desc")
|
||||
default:
|
||||
if !IsDefaultBox(groupName) {
|
||||
return 0, errors2.New("invalid default mailbox")
|
||||
}
|
||||
querySession := db.Instance.Table("user_email").Select("id").Where("user_id=? and email_id > 0", ctx.UserID).OrderBy("id desc")
|
||||
value, err := readGroupValue(querySession, "query next mailbox UID failed")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@ -656,32 +691,32 @@ func getGroupNum(ctx *context.Context, groupName string, mustUnread bool) (int,
|
||||
switch groupName {
|
||||
case "INBOX":
|
||||
if mustUnread {
|
||||
querySession = db.Instance.Table("user_email").Select("count(1)").Where("user_id=? and group_id=0 and status=0 and is_read=0", ctx.UserID)
|
||||
querySession = db.Instance.Table("user_email").Select("count(1)").Where("user_id=? and group_id=0 and status=0 and email_id > 0 and is_read=0", ctx.UserID)
|
||||
} else {
|
||||
querySession = db.Instance.Table("user_email").Select("count(1)").Where("user_id=? and group_id=0 and status=0", ctx.UserID)
|
||||
querySession = db.Instance.Table("user_email").Select("count(1)").Where("user_id=? and group_id=0 and status=0 and email_id > 0", ctx.UserID)
|
||||
}
|
||||
case "Sent Messages":
|
||||
if mustUnread {
|
||||
return 0, nil
|
||||
}
|
||||
querySession = db.Instance.Table("user_email").Select("count(1)").Where("user_id=? and group_id=0 and status=1", ctx.UserID)
|
||||
querySession = db.Instance.Table("user_email").Select("count(1)").Where("user_id=? and group_id=0 and status=1 and email_id > 0", ctx.UserID)
|
||||
case "Drafts":
|
||||
if mustUnread {
|
||||
querySession = db.Instance.Table("user_email").Select("count(1)").Where("user_id=? and group_id=0 and status=4 and is_read=0", ctx.UserID)
|
||||
querySession = db.Instance.Table("user_email").Select("count(1)").Where("user_id=? and group_id=0 and status=4 and email_id > 0 and is_read=0", ctx.UserID)
|
||||
} else {
|
||||
querySession = db.Instance.Table("user_email").Select("count(1)").Where("user_id=? and group_id=0 and status=4", ctx.UserID)
|
||||
querySession = db.Instance.Table("user_email").Select("count(1)").Where("user_id=? and group_id=0 and status=4 and email_id > 0", ctx.UserID)
|
||||
}
|
||||
case "Deleted Messages":
|
||||
if mustUnread {
|
||||
querySession = db.Instance.Table("user_email").Select("count(1)").Where("user_id=? and group_id=0 and status=3 and is_read=0", ctx.UserID)
|
||||
querySession = db.Instance.Table("user_email").Select("count(1)").Where("user_id=? and group_id=0 and status=3 and email_id > 0 and is_read=0", ctx.UserID)
|
||||
} else {
|
||||
querySession = db.Instance.Table("user_email").Select("count(1)").Where("user_id=? and group_id=0 and status=3", ctx.UserID)
|
||||
querySession = db.Instance.Table("user_email").Select("count(1)").Where("user_id=? and group_id=0 and status=3 and email_id > 0", ctx.UserID)
|
||||
}
|
||||
case "Junk":
|
||||
if mustUnread {
|
||||
querySession = db.Instance.Table("user_email").Select("count(1)").Where("user_id=? and group_id=0 and status=5 and is_read=0", ctx.UserID)
|
||||
querySession = db.Instance.Table("user_email").Select("count(1)").Where("user_id=? and group_id=0 and status=5 and email_id > 0 and is_read=0", ctx.UserID)
|
||||
} else {
|
||||
querySession = db.Instance.Table("user_email").Select("count(1)").Where("user_id=? and group_id=0 and status=5", ctx.UserID)
|
||||
querySession = db.Instance.Table("user_email").Select("count(1)").Where("user_id=? and group_id=0 and status=5 and email_id > 0", ctx.UserID)
|
||||
}
|
||||
default:
|
||||
return 0, errors2.New("invalid default mailbox")
|
||||
@ -779,3 +814,48 @@ func Move2DefaultBox(ctx *context.Context, mailIDs []int, groupName string) erro
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MoveRelationsToDefaultBox 按当前用户的邮箱关系 ID 移动邮件到系统邮箱。
|
||||
func MoveRelationsToDefaultBox(ctx *context.Context, relationIDs []int, groupName string) error {
|
||||
var status int8
|
||||
switch groupName {
|
||||
case "INBOX":
|
||||
status = consts.EmailTypeReceive
|
||||
case "Sent Messages":
|
||||
status = consts.EmailStatusSent
|
||||
case "Drafts":
|
||||
status = consts.EmailStatusDrafts
|
||||
case "Deleted Messages":
|
||||
status = consts.EmailStatusDel
|
||||
case "Junk":
|
||||
status = consts.EmailStatusJunk
|
||||
default:
|
||||
return errors2.New("invalid default mailbox")
|
||||
}
|
||||
uniqueIDs, err := normalizeMoveMessageIDs(relationIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
session := db.Instance.NewSession()
|
||||
defer session.Close()
|
||||
if err := session.Begin(); err != nil {
|
||||
return fmt.Errorf("begin relation default mailbox move transaction: %w", err)
|
||||
}
|
||||
var relations []models.UserEmail
|
||||
if err := session.Table(&models.UserEmail{}).Where(builder.Eq{"id": uniqueIDs, "user_id": ctx.UserID}).And("email_id > 0").Find(&relations); err != nil {
|
||||
rollbackGroupTransaction(session)
|
||||
return fmt.Errorf("load relation default mailbox ownership: %w", err)
|
||||
}
|
||||
if len(relations) != len(uniqueIDs) {
|
||||
rollbackGroupTransaction(session)
|
||||
return errors2.New("one or more messages are not owned by the user")
|
||||
}
|
||||
if _, err := session.Table(&models.UserEmail{}).Where(builder.Eq{"id": uniqueIDs, "user_id": ctx.UserID}).Cols("status", "group_id").Update(map[string]interface{}{"status": status, "group_id": 0}); err != nil {
|
||||
rollbackGroupTransaction(session)
|
||||
return fmt.Errorf("move relations to default mailbox: %w", err)
|
||||
}
|
||||
if err := session.Commit(); err != nil {
|
||||
return fmt.Errorf("commit relation default mailbox move transaction: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -170,37 +170,32 @@ func validIMAPNumber(value int) (uint32, bool) {
|
||||
|
||||
// filterByUIDSets filters the list by UID sets
|
||||
func filterByUIDSets(list []*response.UserEmailUIDData, uidSets []imap.UIDSet) []*response.UserEmailUIDData {
|
||||
var result []*response.UserEmailUIDData
|
||||
for _, item := range list {
|
||||
number, ok := validIMAPNumber(item.ID)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
uid := imap.UID(number)
|
||||
for _, uidSet := range uidSets {
|
||||
if uidSet.Contains(uid) {
|
||||
result = append(result, item)
|
||||
break
|
||||
result := list
|
||||
for _, uidSet := range uidSets {
|
||||
filtered := make([]*response.UserEmailUIDData, 0, len(result))
|
||||
for _, item := range result {
|
||||
number, ok := validIMAPNumber(item.ID)
|
||||
if ok && uidSet.Contains(imap.UID(number)) {
|
||||
filtered = append(filtered, item)
|
||||
}
|
||||
}
|
||||
result = filtered
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// filterBySeqNumSets filters the list by sequence number sets
|
||||
func filterBySeqNumSets(list []*response.UserEmailUIDData, seqSets []imap.SeqSet) []*response.UserEmailUIDData {
|
||||
var result []*response.UserEmailUIDData
|
||||
for _, item := range list {
|
||||
number, ok := validIMAPNumber(item.SerialNumber)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, seqSet := range seqSets {
|
||||
if seqSet.Contains(number) {
|
||||
result = append(result, item)
|
||||
break
|
||||
result := list
|
||||
for _, seqSet := range seqSets {
|
||||
filtered := make([]*response.UserEmailUIDData, 0, len(result))
|
||||
for _, item := range result {
|
||||
number, ok := validIMAPNumber(item.SerialNumber)
|
||||
if ok && seqSet.Contains(number) {
|
||||
filtered = append(filtered, item)
|
||||
}
|
||||
}
|
||||
result = filtered
|
||||
}
|
||||
return result
|
||||
}
|
||||
@ -242,7 +237,7 @@ func hasFlag(item *response.UserEmailUIDData, flag imap.Flag) bool {
|
||||
case imap.FlagSeen:
|
||||
return item.IsRead == 1
|
||||
case imap.FlagDeleted:
|
||||
return item.Status == 3
|
||||
return item.IsDeleted == 1
|
||||
case imap.FlagDraft:
|
||||
return item.Status == 4
|
||||
case imap.FlagJunk:
|
||||
@ -468,16 +463,14 @@ func applyOrCriteria(ctx *context.Context, groupName string, list []*response.Us
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// Build a set of current UIDs for intersection
|
||||
// 多个 OR 子句仍属于同一个 SEARCH,子句之间取交集。
|
||||
currentUIDs := make(map[int]bool)
|
||||
for _, item := range list {
|
||||
currentUIDs[item.ID] = true
|
||||
}
|
||||
|
||||
// For each OR pair, find items matching either condition
|
||||
resultUIDs := make(map[int]bool)
|
||||
|
||||
for _, pair := range orCriteria {
|
||||
pairUIDs := make(map[int]bool)
|
||||
// Get matches for first condition
|
||||
matches1, err := SearchEmails(ctx, groupName, &pair[0])
|
||||
if err != nil {
|
||||
@ -485,7 +478,7 @@ func applyOrCriteria(ctx *context.Context, groupName string, list []*response.Us
|
||||
}
|
||||
for _, item := range matches1 {
|
||||
if currentUIDs[item.ID] {
|
||||
resultUIDs[item.ID] = true
|
||||
pairUIDs[item.ID] = true
|
||||
}
|
||||
}
|
||||
|
||||
@ -496,7 +489,12 @@ func applyOrCriteria(ctx *context.Context, groupName string, list []*response.Us
|
||||
}
|
||||
for _, item := range matches2 {
|
||||
if currentUIDs[item.ID] {
|
||||
resultUIDs[item.ID] = true
|
||||
pairUIDs[item.ID] = true
|
||||
}
|
||||
}
|
||||
for uid := range currentUIDs {
|
||||
if !pairUIDs[uid] {
|
||||
delete(currentUIDs, uid)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -504,7 +502,7 @@ func applyOrCriteria(ctx *context.Context, groupName string, list []*response.Us
|
||||
// Build result from original list preserving order
|
||||
var result []*response.UserEmailUIDData
|
||||
for _, item := range list {
|
||||
if resultUIDs[item.ID] {
|
||||
if currentUIDs[item.ID] {
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,7 +133,7 @@ func buildIMAPMailboxRelationQuery(ctx *context.Context, groupName string) (stri
|
||||
if ctx == nil || ctx.UserID <= 0 {
|
||||
return "", nil, fmt.Errorf("invalid mailbox user")
|
||||
}
|
||||
inner := "SELECT id,email_id,is_read,is_deleted,ROW_NUMBER() OVER (ORDER BY id) AS serial_number FROM user_email WHERE user_id = ?"
|
||||
inner := "SELECT id,email_id,is_read,is_deleted,status,ROW_NUMBER() OVER (ORDER BY id) AS serial_number FROM user_email WHERE user_id = ? AND email_id > 0"
|
||||
params := []any{ctx.UserID}
|
||||
switch groupName {
|
||||
case "INBOX":
|
||||
@ -224,6 +224,23 @@ func getEmailListByUidList(ctx *context.Context, groupName string, req ImapListR
|
||||
return nil, err
|
||||
}
|
||||
sql := "SELECT * FROM (" + inner + ") mailbox WHERE "
|
||||
if req.Star == 0 && req.End == 0 && len(req.UidList) == 0 {
|
||||
maxUID, err := GetMaxUIDByMailbox(ctx, groupName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if maxUID == 0 {
|
||||
return []*response.EmailResponseData{}, nil
|
||||
}
|
||||
sql += "id = ?"
|
||||
params = append(params, maxUID)
|
||||
sql += " ORDER BY id"
|
||||
var relations []*response.UserEmailUIDData
|
||||
if err := db.Instance.SQL(sql, params...).Find(&relations); err != nil {
|
||||
return nil, fmt.Errorf("query mailbox wildcard UID: %w", err)
|
||||
}
|
||||
return hydrateIMAPEmailResponses(relations)
|
||||
}
|
||||
if req.Star > 0 {
|
||||
sql += "id >= ?"
|
||||
params = append(params, req.Star)
|
||||
@ -248,7 +265,7 @@ func getEmailListByUidList(ctx *context.Context, groupName string, req ImapListR
|
||||
}
|
||||
|
||||
func GetEmailListByGroup(ctx *context.Context, groupName string, req ImapListReq, uid bool) ([]*response.EmailResponseData, error) {
|
||||
if len(req.UidList) == 0 && req.Star == 0 && req.End == 0 {
|
||||
if len(req.UidList) == 0 && req.Star == 0 && req.End == 0 && !uid {
|
||||
return nil, nil
|
||||
}
|
||||
if uid {
|
||||
@ -315,11 +332,12 @@ func hydrateIMAPEmailResponses(relations []*response.UserEmailUIDData) ([]*respo
|
||||
if message == nil {
|
||||
continue
|
||||
}
|
||||
message.IsRead = relation.IsRead
|
||||
message.IsDeleted = relation.IsDeleted
|
||||
message.SerialNumber = relation.SerialNumber
|
||||
message.UeId = relation.ID
|
||||
result = append(result, message)
|
||||
copyMessage := *message
|
||||
copyMessage.IsRead = relation.IsRead
|
||||
copyMessage.IsDeleted = relation.IsDeleted
|
||||
copyMessage.SerialNumber = relation.SerialNumber
|
||||
copyMessage.UeId = relation.ID
|
||||
result = append(result, ©Message)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@ -64,7 +64,11 @@ func TestAuditRequestDrainMustRespectCancellation(t *testing.T) {
|
||||
defer cancel()
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
release := service.drainRequests(ctx)
|
||||
release, err := service.drainRequests(ctx)
|
||||
if err != nil {
|
||||
close(done)
|
||||
return
|
||||
}
|
||||
release()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user