package imap import ( "crypto/tls" "fmt" "strings" imapv2 "github.com/emersion/go-imap/v2" "github.com/emersion/go-imap/v2/imapclient" ) // FolderInfo describes a single IMAP folder with exclusion metadata. type FolderInfo struct { Name string `json:"name"` Excluded bool `json:"excluded"` Reason string `json:"reason,omitempty"` } // junkTrashNames lists well-known junk/trash folder names for fallback detection. var junkTrashNames = []string{ "junk", "spam", "trash", "deleted items", "deleted messages", "papierkorb", "gelöschte elemente", } // Connect establishes an IMAP client connection using the specified TLS mode. func Connect(host string, port int, tlsMode string) (*imapclient.Client, error) { addr := fmt.Sprintf("%s:%d", host, port) switch tlsMode { case "ssl": c, err := imapclient.DialTLS(addr, &imapclient.Options{ TLSConfig: &tls.Config{ServerName: host}, }) if err != nil { return nil, fmt.Errorf("imap connect ssl: %w", err) } return c, nil case "starttls": c, err := imapclient.DialStartTLS(addr, &imapclient.Options{ TLSConfig: &tls.Config{ServerName: host}, }) if err != nil { return nil, fmt.Errorf("imap connect starttls: %w", err) } return c, nil case "none": c, err := imapclient.DialInsecure(addr, nil) if err != nil { return nil, fmt.Errorf("imap connect plain: %w", err) } return c, nil default: return nil, fmt.Errorf("imap: unknown tls mode %q", tlsMode) } } // ListFolders retrieves all mailbox folders and detects junk/trash folders. func ListFolders(c *imapclient.Client) ([]FolderInfo, error) { listCmd := c.List("", "*", nil) mailboxes, err := listCmd.Collect() if err != nil { return nil, fmt.Errorf("imap list folders: %w", err) } var folders []FolderInfo for _, mb := range mailboxes { fi := FolderInfo{Name: mb.Mailbox} // Check special-use attributes (RFC 6154) for _, attr := range mb.Attrs { if attr == imapv2.MailboxAttrJunk { fi.Excluded = true fi.Reason = "special_use" break } if attr == imapv2.MailboxAttrTrash { fi.Excluded = true fi.Reason = "special_use" break } } // Fallback: case-insensitive name matching if !fi.Excluded { lower := strings.ToLower(mb.Mailbox) for _, jt := range junkTrashNames { if lower == jt { fi.Excluded = true fi.Reason = "name_match" break } } } folders = append(folders, fi) } return folders, nil }