Add comprehensive integration capabilities to Pop CLI: - Multi-account support with named profiles - Webhook management with signature verification - External PGP key management (import/export/encrypt/decrypt/sign/verify) - CLI plugin system for extensibility - Complete documentation in README.md All compilation errors fixed and build verified CLEAN. Security review delegated to FRE-5202. Co-Authored-By: Paperclip <noreply@paperclip.ing>
267 lines
7.3 KiB
Go
267 lines
7.3 KiB
Go
package mail
|
|
|
|
import "time"
|
|
|
|
type Folder int
|
|
|
|
const (
|
|
FolderInbox Folder = 0
|
|
FolderSent Folder = 3
|
|
FolderDraft Folder = 2
|
|
FolderTrash Folder = 4
|
|
FolderSpam Folder = 5
|
|
)
|
|
|
|
const (
|
|
MessageTypeRegular = "0"
|
|
MessageTypeDraft = "2"
|
|
)
|
|
|
|
func (f Folder) Name() string {
|
|
names := map[Folder]string{
|
|
FolderInbox: "Inbox",
|
|
FolderSent: "Sent",
|
|
FolderDraft: "Drafts",
|
|
FolderTrash: "Trash",
|
|
FolderSpam: "Spam",
|
|
}
|
|
if name, ok := names[f]; ok {
|
|
return name
|
|
}
|
|
return "Unknown"
|
|
}
|
|
|
|
type Message struct {
|
|
MessageID string `json:"MessageID"`
|
|
ConversationID string `json:"ConversationID"`
|
|
CreatedAt time.Time `json:"-"`
|
|
Created int64 `json:"Created"`
|
|
UpdatedAt time.Time `json:"-"`
|
|
Updated int64 `json:"Updated"`
|
|
Type int `json:"Type"`
|
|
Starred bool `json:"Starred"`
|
|
Read bool `json:"Read"`
|
|
Category int `json:"Category"`
|
|
Sender Recipient `json:"Sender"`
|
|
Recipients []Recipient `json:"Recipients"`
|
|
Subject string `json:"Subject"`
|
|
Body string `json:"Body"`
|
|
BodyEnc string `json:"BodyEnc,omitempty"`
|
|
Attachments []Attachment `json:"Attachments,omitempty"`
|
|
MimeMessageID string `json:"MimeMessageID,omitempty"`
|
|
InReplyTo string `json:"InReplyTo,omitempty"`
|
|
}
|
|
|
|
func (m *Message) Folder() Folder {
|
|
if m.Type == int(FolderDraft) {
|
|
return FolderDraft
|
|
}
|
|
if m.Type == int(FolderSent) {
|
|
return FolderSent
|
|
}
|
|
return FolderInbox
|
|
}
|
|
|
|
type Recipient struct {
|
|
Name string `json:"Name"`
|
|
Address string `json:"Address"`
|
|
Label int `json:"Label"`
|
|
AddressID string `json:"AddressID,omitempty"`
|
|
KeyID string `json:"KeyID,omitempty"`
|
|
Fingerprint string `json:"Fingerprint,omitempty"`
|
|
}
|
|
|
|
func (r Recipient) DisplayName() string {
|
|
if r.Name != "" {
|
|
return r.Name + " <" + r.Address + ">"
|
|
}
|
|
return r.Address
|
|
}
|
|
|
|
func (r Recipient) ToRecipient() Recipient {
|
|
return Recipient{
|
|
Name: r.Name,
|
|
Address: r.Address,
|
|
}
|
|
}
|
|
|
|
type Attachment struct {
|
|
AttachmentID string `json:"AttachmentID"`
|
|
Name string `json:"Name"`
|
|
ContentType string `json:"ContentType"`
|
|
Size int `json:"Size"`
|
|
DataEnc string `json:"DataEnc,omitempty"`
|
|
Keys []AttachmentKey `json:"Keys,omitempty"`
|
|
}
|
|
|
|
type AttachmentKey struct {
|
|
DataEnc string `json:"DataEnc"`
|
|
}
|
|
|
|
type Draft struct {
|
|
MessageID string `json:"MessageID"`
|
|
To []Recipient `json:"To"`
|
|
CC []Recipient `json:"CC,omitempty"`
|
|
BCC []Recipient `json:"BCC,omitempty"`
|
|
Subject string `json:"Subject"`
|
|
Body string `json:"Body"`
|
|
BodyEnc string `json:"BodyEnc,omitempty"`
|
|
Attachments []Attachment `json:"Attachments,omitempty"`
|
|
}
|
|
|
|
type ListMessagesRequest struct {
|
|
Folder Folder `json:"-"`
|
|
Page int `json:"Page"`
|
|
PageSize int `json:"PageSize"`
|
|
Passphrase string `json:"Passphrase"`
|
|
Starred *bool `json:"Starred,omitempty"`
|
|
Read *bool `json:"Read,omitempty"`
|
|
Since int64 `json:"Since,omitempty"`
|
|
}
|
|
|
|
type ListMessagesResponse struct {
|
|
Total int `json:"Total"`
|
|
Messages []Message `json:"Messages"`
|
|
}
|
|
|
|
type SendRequest struct {
|
|
To []Recipient `json:"To"`
|
|
CC []Recipient `json:"CC,omitempty"`
|
|
BCC []Recipient `json:"BCC,omitempty"`
|
|
Subject string `json:"Subject"`
|
|
Body string `json:"Body"`
|
|
HTML bool `json:"HTML,omitempty"`
|
|
ReplyTo []Recipient `json:"ReplyTo,omitempty"`
|
|
InReplyTo string `json:"InReplyTo,omitempty"`
|
|
References string `json:"References,omitempty"`
|
|
Attachments []Attachment `json:"Attachments,omitempty"`
|
|
Passphrase string `json:"Passphrase"`
|
|
}
|
|
|
|
type SearchRequest struct {
|
|
Query string `json:"Query"`
|
|
Page int `json:"Page"`
|
|
PageSize int `json:"PageSize"`
|
|
Passphrase string `json:"Passphrase"`
|
|
}
|
|
|
|
type SearchResponse struct {
|
|
Total int `json:"Total"`
|
|
Messages []Message `json:"Messages"`
|
|
}
|
|
|
|
// Conversation represents a threaded conversation (email thread)
|
|
type Conversation struct {
|
|
ConversationID string `json:"ConversationID"`
|
|
Subject string `json:"Subject"`
|
|
MessageCount int `json:"MessageCount"`
|
|
LastMessage *Message `json:"LastMessage"`
|
|
Participants []Recipient `json:"Participants"`
|
|
}
|
|
|
|
type ConversationResponse struct {
|
|
Total int `json:"Total"`
|
|
Conversations []Conversation `json:"Conversations"`
|
|
}
|
|
|
|
type GetConversationRequest struct {
|
|
ConversationID string `json:"ConversationID"`
|
|
Page int `json:"Page"`
|
|
PageSize int `json:"PageSize"`
|
|
Passphrase string `json:"Passphrase"`
|
|
}
|
|
|
|
type GetConversationResponse struct {
|
|
ConversationID string `json:"ConversationID"`
|
|
Subject string `json:"Subject"`
|
|
MessageCount int `json:"MessageCount"`
|
|
Messages []Message `json:"Messages"`
|
|
Participants []Recipient `json:"Participants"`
|
|
}
|
|
|
|
// BulkRequest represents a batch operation on multiple messages
|
|
type BulkRequest struct {
|
|
MessageIDs []string `json:"MessageIDs"`
|
|
Passphrase string `json:"Passphrase"`
|
|
}
|
|
|
|
type BulkResponse struct {
|
|
SuccessCount int `json:"SuccessCount"`
|
|
Total int `json:"Total"`
|
|
Errors []BulkError `json:"Errors,omitempty"`
|
|
}
|
|
|
|
type BulkError struct {
|
|
MessageID string `json:"MessageID"`
|
|
Error string `json:"Error"`
|
|
}
|
|
|
|
// ExportFormat represents the format for exporting messages
|
|
type ExportFormat int
|
|
|
|
const (
|
|
ExportFormatJSON ExportFormat = iota
|
|
ExportFormatMBOX
|
|
ExportFormatEMail
|
|
)
|
|
|
|
func (f ExportFormat) String() string {
|
|
names := map[ExportFormat]string{
|
|
ExportFormatJSON: "json",
|
|
ExportFormatMBOX: "mbox",
|
|
ExportFormatEMail: "eml",
|
|
}
|
|
if name, ok := names[f]; ok {
|
|
return name
|
|
}
|
|
return "json"
|
|
}
|
|
|
|
// ExportRequest represents a message export request
|
|
type ExportRequest struct {
|
|
MessageIDs []string `json:"MessageIDs,omitempty"`
|
|
Folder Folder `json:"Folder,omitempty"`
|
|
Format ExportFormat `json:"Format"`
|
|
Since int64 `json:"Since,omitempty"`
|
|
Before int64 `json:"Before,omitempty"`
|
|
Search string `json:"Search,omitempty"`
|
|
Passphrase string `json:"Passphrase"`
|
|
}
|
|
|
|
// ExportedMessage represents a message ready for export
|
|
type ExportedMessage struct {
|
|
MessageID string `json:"message_id"`
|
|
ConversationID string `json:"conversation_id"`
|
|
From Recipient `json:"from"`
|
|
To []Recipient `json:"to"`
|
|
CC []Recipient `json:"cc,omitempty"`
|
|
Subject string `json:"subject"`
|
|
Body string `json:"body"`
|
|
HTML bool `json:"html"`
|
|
Date string `json:"date"`
|
|
Starred bool `json:"starred"`
|
|
Read bool `json:"read"`
|
|
Attachments []Attachment `json:"attachments,omitempty"`
|
|
}
|
|
|
|
// ImportRequest represents a message import request
|
|
type ImportRequest struct {
|
|
FilePath string `json:"FilePath"`
|
|
Format ExportFormat `json:"Format"`
|
|
Folder Folder `json:"Folder,omitempty"`
|
|
Passphrase string `json:"Passphrase"`
|
|
}
|
|
|
|
type ImportResponse struct {
|
|
ImportedCount int `json:"ImportedCount"`
|
|
Total int `json:"Total"`
|
|
Errors []BulkError `json:"Errors,omitempty"`
|
|
}
|
|
|
|
// DraftAutoSaveConfig holds auto-save settings for drafts
|
|
type DraftAutoSaveConfig struct {
|
|
Enabled bool `json:"enabled"`
|
|
Interval int `json:"interval_seconds"`
|
|
LastSaved int64 `json:"last_saved_timestamp"`
|
|
}
|