109 lines
3.9 KiB
Go
109 lines
3.9 KiB
Go
// 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)
|
|
}
|