d360c9a5ba
- Systemauslastungs-Sektion wird immer gerendert (nicht nur bei Erfolg) - Fehlermeldung wenn /api/admin/system/stats nicht erreichbar ist - Feature-Status auf In Review gesetzt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
100 lines
2.4 KiB
Go
100 lines
2.4 KiB
Go
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
|
|
}
|