// Package accounts implements the MailProvider abstraction: one interface, // multiple backends (local GoMail account, generic IMAP/SMTP, and — Phase 10 — // Gmail/M365 native APIs). The webmail client (later phase) talks to every // linked account through this same interface regardless of where the mail // actually lives. package accounts import ( "context" "time" ) type Folder struct { ID string `json:"id"` // provider-native folder identifier (IMAP mailbox name, etc.) DisplayName string `json:"display_name"` Type string `json:"type"` // inbox|sent|drafts|trash|junk|custom UnreadCount int `json:"unread_count"` TotalCount int `json:"total_count"` } type MessageHeader struct { ID string `json:"id"` // provider-native message identifier (IMAP UID, etc.) FolderID string `json:"folder_id"` From string `json:"from"` To string `json:"to"` Subject string `json:"subject"` Date string `json:"date"` Flags []string `json:"flags"` SizeBytes int64 `json:"size_bytes"` } type FullMessage struct { MessageHeader Raw []byte `json:"raw"` // full RFC 5322 message; json.Marshal base64-encodes []byte automatically } type OutgoingMessage struct { From string `json:"from"` To []string `json:"to"` CC []string `json:"cc"` BCC []string `json:"bcc"` Subject string `json:"subject"` Body string `json:"body"` // plain text; HTML composer is a webmail-phase concern } type ListOpts struct { Limit int Offset int } type SyncResult struct { NewCursor string NewMessages []MessageHeader DeletedIDs []string FlagsChanged map[string][]string // messageID -> new flags } // MailProvider is implemented once per account type. Every method takes a // context so network-backed implementations (IMAP, and later Gmail/Graph // API) can be cancelled/timed-out uniformly with the local implementation. type MailProvider interface { ListFolders(ctx context.Context) ([]Folder, error) ListMessages(ctx context.Context, folderID string, opts ListOpts) ([]MessageHeader, error) GetMessage(ctx context.Context, folderID, messageID string) (*FullMessage, error) SendMessage(ctx context.Context, msg *OutgoingMessage) error SetFlags(ctx context.Context, folderID, messageID string, flags []string) error Move(ctx context.Context, folderID, messageID, destFolderID string) error Delete(ctx context.Context, folderID, messageID string) error Sync(ctx context.Context, since string) (*SyncResult, error) } // CalendarEvent and Contact are read-only views onto a linked account's // own calendar/contacts, native-API only (Gmail Calendar API + Google // People API, Microsoft Graph) — there is no IMAP-equivalent fallback for // these, unlike mail. Local calendars/contacts are unaffected: they keep // being served by internal/dav's CalDAV/CardDAV server, entirely separate // from this. type CalendarEvent struct { ID string `json:"id"` Summary string `json:"summary"` Location string `json:"location"` Description string `json:"description"` Start time.Time `json:"start"` End time.Time `json:"end"` AllDay bool `json:"all_day"` } type Contact struct { ID string `json:"id"` Name string `json:"name"` Emails []string `json:"emails"` Phones []string `json:"phones"` } // CalendarProvider and ContactProvider are deliberately separate from // MailProvider — GoMailProvider (local calendar/contacts already exist // via CalDAV/CardDAV) and IMAPProvider (generic IMAP has no calendar/ // contacts concept at all) don't implement either. Callers type-assert // (`p, ok := provider.(CalendarProvider)`) and report "not supported" // rather than a fake empty result — see internal/webmail/api.go's // calendarEvents/contacts handlers. type CalendarProvider interface { ListEvents(ctx context.Context, from, to time.Time) ([]CalendarEvent, error) } type ContactProvider interface { ListContacts(ctx context.Context) ([]Contact, error) }