fix: implement server-side folder filtering using IMAP LIST patterns
Replace client-side wildcard filtering with IMAP LIST pattern matching for improved efficiency and accuracy. This fixes the issue where patterns like "Work*" were not matching folders like "Work/Projects". Key improvements: - Use IMAP LIST with patterns for server-side filtering - Remove dependency on doublestar library - Add ListFilteredMailboxes() method with proper IMAP pattern support - Remove obsolete ShouldProcessMailbox() client-side filtering - Significantly reduce network traffic by filtering at server This ensures the Go implementation correctly processes folder patterns and achieves feature parity with the Rust implementation. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
589ea338e6
commit
84faf501f1
2 changed files with 83 additions and 52 deletions
120
go/mail/imap.go
120
go/mail/imap.go
|
|
@ -6,7 +6,6 @@ import (
|
|||
"io"
|
||||
"log"
|
||||
"mime"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -83,6 +82,84 @@ func (c *ImapClient) ListMailboxes() ([]string, error) {
|
|||
return mailboxes, nil
|
||||
}
|
||||
|
||||
// ListFilteredMailboxes lists mailboxes matching the given folder filters using IMAP LIST
|
||||
func (c *ImapClient) ListFilteredMailboxes(filter *config.FolderFilter) ([]string, error) {
|
||||
var allMailboxes []string
|
||||
|
||||
// If no include patterns, get all mailboxes
|
||||
if len(filter.Include) == 0 {
|
||||
return c.ListMailboxes()
|
||||
}
|
||||
|
||||
// Use IMAP LIST with each include pattern to let the server filter
|
||||
seen := make(map[string]bool)
|
||||
for _, pattern := range filter.Include {
|
||||
cmd := c.List("", pattern, nil)
|
||||
infos, err := cmd.Collect()
|
||||
if err != nil {
|
||||
log.Printf("Failed to list mailboxes with pattern '%s': %v", pattern, err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, info := range infos {
|
||||
if !seen[info.Mailbox] {
|
||||
allMailboxes = append(allMailboxes, info.Mailbox)
|
||||
seen[info.Mailbox] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply exclude filters client-side (IMAP LIST doesn't support exclusion)
|
||||
if len(filter.Exclude) == 0 {
|
||||
return allMailboxes, nil
|
||||
}
|
||||
|
||||
var filteredMailboxes []string
|
||||
for _, mailbox := range allMailboxes {
|
||||
excluded := false
|
||||
for _, excludePattern := range filter.Exclude {
|
||||
if matched := c.matchesImapPattern(excludePattern, mailbox); matched {
|
||||
excluded = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !excluded {
|
||||
filteredMailboxes = append(filteredMailboxes, mailbox)
|
||||
}
|
||||
}
|
||||
|
||||
return filteredMailboxes, nil
|
||||
}
|
||||
|
||||
// matchesImapPattern matches IMAP-style patterns (simple * wildcard matching)
|
||||
func (c *ImapClient) matchesImapPattern(pattern, name string) bool {
|
||||
// Handle exact match
|
||||
if pattern == name {
|
||||
return true
|
||||
}
|
||||
|
||||
// Handle simple prefix wildcard: "Work*" should match "Work/Projects"
|
||||
if strings.HasSuffix(pattern, "*") && !strings.Contains(pattern[:len(pattern)-1], "*") {
|
||||
prefix := strings.TrimSuffix(pattern, "*")
|
||||
return strings.HasPrefix(name, prefix)
|
||||
}
|
||||
|
||||
// Handle simple suffix wildcard: "*Temp" should match "Work/Temp"
|
||||
if strings.HasPrefix(pattern, "*") && !strings.Contains(pattern[1:], "*") {
|
||||
suffix := strings.TrimPrefix(pattern, "*")
|
||||
return strings.HasSuffix(name, suffix)
|
||||
}
|
||||
|
||||
// Handle contains wildcard: "*Temp*" should match "Work/Temp/Archive"
|
||||
if strings.HasPrefix(pattern, "*") && strings.HasSuffix(pattern, "*") {
|
||||
middle := strings.Trim(pattern, "*")
|
||||
return strings.Contains(name, middle)
|
||||
}
|
||||
|
||||
// For other patterns, fall back to basic string comparison
|
||||
return false
|
||||
}
|
||||
|
||||
// GetMessages retrieves messages from a specific mailbox with filtering support
|
||||
// Returns messages and a map of all current UIDs in the mailbox
|
||||
// maxMessages: 0 means no limit, > 0 limits the number of messages to fetch
|
||||
|
|
@ -381,47 +458,6 @@ func (c *ImapClient) parseMessagePart(entity *message.Entity, msg *Message) erro
|
|||
return nil
|
||||
}
|
||||
|
||||
// ShouldProcessMailbox checks if a mailbox should be processed based on filters with wildcard support
|
||||
func (c *ImapClient) ShouldProcessMailbox(mailbox string, filter *config.FolderFilter) bool {
|
||||
// If include list is specified, mailbox must match at least one pattern
|
||||
if len(filter.Include) > 0 {
|
||||
found := false
|
||||
for _, pattern := range filter.Include {
|
||||
// Handle special case: "*" means include all folders
|
||||
if pattern == "*" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
// Use filepath.Match for wildcard pattern matching
|
||||
if matched, err := filepath.Match(pattern, mailbox); err == nil && matched {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
// Also support exact string matching for backwards compatibility
|
||||
if mailbox == pattern {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// If exclude list is specified, mailbox must not match any exclude pattern
|
||||
for _, pattern := range filter.Exclude {
|
||||
// Use filepath.Match for wildcard pattern matching
|
||||
if matched, err := filepath.Match(pattern, mailbox); err == nil && matched {
|
||||
return false
|
||||
}
|
||||
// Also support exact string matching for backwards compatibility
|
||||
if mailbox == pattern {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ShouldProcessMessage checks if a message should be processed based on keyword filters
|
||||
func (c *ImapClient) ShouldProcessMessage(msg *Message, filter *config.MessageFilter) bool {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue