Files
opencloud/pkg/jmap/jmap_model.go
Pascal Bleser 6224ded8b5 refactor(groupware): add max requests check
* move jmap.request() to jmap.Client.request() and pass the Session
   and a Logger to introduce checking the number of methodCalls within a
   request not exceeding the limit of the Session, as well as error
   handling and logging there instead of in each caller

 * a few bugfixes:
   - add a few missing Send() calls in logs
   - correct the response tag matching for
     GetMailboxChangesForMultipleAccounts
   - fix typo in Identity.ReplyTo json serialization rune
   - fix response tag in pkg/jmap/testdata/mailboxes1.json after
     changing them to be prefixed by the accountId
2025-12-09 09:15:37 +01:00

2694 lines
112 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package jmap
import (
"io"
"time"
)
const (
JmapCore = "urn:ietf:params:jmap:core"
JmapMail = "urn:ietf:params:jmap:mail"
JmapMDN = "urn:ietf:params:jmap:mdn" // https://datatracker.ietf.org/doc/rfc9007/
JmapSubmission = "urn:ietf:params:jmap:submission"
JmapVacationResponse = "urn:ietf:params:jmap:vacationresponse"
JmapCalendars = "urn:ietf:params:jmap:calendars"
JmapSieve = "urn:ietf:params:jmap:sieve"
JmapBlob = "urn:ietf:params:jmap:blob"
JmapQuota = "urn:ietf:params:jmap:quota"
JmapWebsocket = "urn:ietf:params:jmap:websocket"
JmapKeywordPrefix = "$"
JmapKeywordSeen = "$seen"
JmapKeywordDraft = "$draft"
JmapKeywordFlagged = "$flagged"
JmapKeywordAnswered = "$answered"
JmapKeywordForwarded = "$forwarded"
JmapKeywordPhishing = "$phising"
JmapKeywordJunk = "$junk"
JmapKeywordNotJunk = "$notjunk"
JmapKeywordMdnSent = "$mdnsent"
)
type SessionMailAccountCapabilities struct {
// The maximum number of Mailboxes that can be can assigned to a single Email object.
//
// This MUST be an integer >= 1, or null for no limit (or rather, the limit is always
// the number of Mailboxes in the account).
MaxMailboxesPerEmail int `json:"maxMailboxesPerEmail"`
// The maximum depth of the Mailbox hierarchy (i.e., one more than the maximum
// number of ancestors a Mailbox may have), or null for no limit.
MaxMailboxDepth int `json:"maxMailboxDepth"`
// The maximum length, in (UTF-8) octets, allowed for the name of a Mailbox.
//
// This MUST be at least 100, although it is recommended servers allow more.
MaxSizeMailboxName int `json:"maxSizeMailboxName"`
// The maximum total size of attachments, in octets, allowed for a single Email object.
//
// A server MAY still reject the import or creation of an Email with a lower attachment size
// total (for example, if the body includes several megabytes of text, causing the size of
// the encoded MIME structure to be over some server-defined limit).
//
// Note that this limit is for the sum of unencoded attachment sizes. Users are generally
// not knowledgeable about encoding overhead, etc., nor should they need to be, so marketing
// and help materials normally tell them the “max size attachments”.
//
// This is the unencoded size they see on their hard drive, so this capability matches that
// and allows the client to consistently enforce what the user understands as the limit.
//
// The server may separately have a limit for the total size of the message [RFC5322],
// created by combining the attachments (often base64 encoded) with the message headers and bodies.
//
// For example, suppose the server advertises maxSizeAttachmentsPerEmail: 50000000 (50 MB).
// The enforced server limit may be for a message size of 70000000 octets.
// Even with base64 encoding and a 2 MB HTML body, 50 MB attachments would fit under this limit.
//
// [RFC5322]: https://www.rfc-editor.org/rfc/rfc5322.html
MaxSizeAttachmentsPerEmail int `json:"maxSizeAttachmentsPerEmail"`
// A list of all the values the server supports for the “property” field of the Comparator
// object in an Email/query sort.
//
// This MAY include properties the client does not recognise (for example, custom properties
// specified in a vendor extension). Clients MUST ignore any unknown properties in the list.
EmailQuerySortOptions []string `json:"emailQuerySortOptions"`
// If true, the user may create a Mailbox in this account with a null parentId.
//
// (Permission for creating a child of an existing Mailbox is given by the myRights property
// on that Mailbox.)
MayCreateTopLevelMailbox bool `json:"mayCreateTopLevelMailbox"`
}
type SessionSubmissionAccountCapabilities struct {
// The number in seconds of the maximum delay the server supports in sending.
//
// This is 0 if the server does not support delayed send.
MaxDelayedSend int `json:"maxDelayedSend"`
// The set of SMTP submission extensions supported by the server, which the client may use
// when creating an EmailSubmission object.
//
// Each key in the object is the ehlo-name, and the value is a list of ehlo-args.
//
// A JMAP implementation that talks to a submission server [RFC6409] SHOULD have a configuration
// setting that allows an administrator to modify the set of submission EHLO capabilities it may
// expose on this property.
//
// This allows a JMAP server to easily add access to a new submission extension without code changes.
//
// By default, the JMAP server should hide EHLO capabilities that have to do with the transport
// mechanism and thus are only relevant to the JMAP server (for example, PIPELINING, CHUNKING, or STARTTLS).
//
// Examples of Submission extensions to include:
// - FUTURERELEASE [RFC4865]
// - SIZE [RFC1870]
// - DSN [RFC3461]
// - DELIVERYBY [RFC2852]
// - MT-PRIORITY [RFC6710]
//
// A JMAP server MAY advertise an extension and implement the semantics of that extension locally
// on the JMAP server even if a submission server used by JMAP doesnt implement it.
//
// The full IANA registry of submission extensions can be found at [iana.org].
//
// [RFC6409]: https://www.rfc-editor.org/rfc/rfc6409.html
// [RFC4865]: https://www.rfc-editor.org/rfc/rfc4865.html
// [RFC1870]: https://www.rfc-editor.org/rfc/rfc1870.html
// [RFC3461]: https://www.rfc-editor.org/rfc/rfc3461.html
// [RFC2852]: https://www.rfc-editor.org/rfc/rfc2852.html
// [RFC6710]: https://www.rfc-editor.org/rfc/rfc6710.html
// [iana.org]: https://www.iana.org/assignments/mail-parameters
SubmissionExtensions map[string][]string `json:"submissionExtensions"`
}
// This represents support for the VacationResponse data type and associated API methods.
//
// The value of this property is an empty object in both the JMAP session capabilities
// property and an accounts accountCapabilities property.
type SessionVacationResponseAccountCapabilities struct {
}
type SessionSieveAccountCapabilities struct {
// The maximum length, in octets, allowed for the name of a SieveScript.
//
// For compatibility with ManageSieve, this MUST be at least 512 (up to 128 Unicode characters).
MaxSizeScriptName int `json:"maxSizeScriptName"`
// The maximum size (in octets) of a Sieve script the server is willing to store for the user,
// or null for no limit.
MaxSizeScript int `json:"maxSizeScript"`
// The maximum number of Sieve scripts the server is willing to store for the user, or null for no limit.
MaxNumberScripts int `json:"maxNumberScripts"`
// The maximum number of Sieve "redirect" actions a script can perform during a single evaluation,
// or null for no limit.
//
// Note that this is different from the total number of "redirect" actions a script can contain.
MaxNumberRedirects int `json:"maxNumberRedirects"`
// A list of case-sensitive Sieve capability strings (as listed in the Sieve "require" action;
// see [RFC5228, Section 3.2]) indicating the extensions supported by the Sieve engine.
//
// [RFC5228, Section 3.2]: https://www.rfc-editor.org/rfc/rfc5228.html#section-3.2
SieveExtensions []string `json:"sieveExtensions"`
// A list of URI scheme parts [RFC3986] for notification methods supported by the Sieve "enotify"
// extension [RFC5435], or null if the extension is not supported by the Sieve engine.
//
// [RFC3986]: https://www.rfc-editor.org/rfc/rfc3986.html
// [RFC5435]: https://www.rfc-editor.org/rfc/rfc5435.html
NotificationMethods []string `json:"notificationMethods"`
// A list of URI scheme parts [RFC3986] for externally stored list types supported by the
// Sieve "extlists" extension [RFC6134], or null if the extension is not supported by the Sieve engine.
//
// [RFC3986]: https://www.rfc-editor.org/rfc/rfc3986.html
// [RFC6134]: https://www.rfc-editor.org/rfc/rfc6134.html
ExternalLists []string `json:"externalLists"`
}
type SessionBlobAccountCapabilities struct {
// The maximum size of the blob (in octets) that the server will allow to be created
// (including blobs created by concatenating multiple data sources together).
//
// Clients MUST NOT attempt to create blobs larger than this size.
//
// If this value is null, then clients are not required to limit the size of the blob
// they try to create, though servers can always reject creation of blobs regardless of
// size, e.g., due to lack of disk space or per-user rate limits.
MaxSizeBlobSet int `json:"maxSizeBlobSet"`
// The maximum number of DataSourceObjects allowed per creation in a Blob/upload.
//
// Servers MUST allow at least 64 DataSourceObjects per creation.
MaxDataSources int `json:"maxDataSources"`
// An array of data type names that are supported for Blob/lookup.
//
// If the server does not support lookups, then this will be the empty list.
//
// Note that the supportedTypeNames list may include private types that are not in the
// "JMAP Data Types" registry defined by this document.
//
// Clients MUST ignore type names they do not recognise.
SupportedTypeNames []string `json:"supportedTypeNames"`
// An array of supported digest algorithms that are supported for Blob/get.
//
// If the server does not support calculating blob digests, then this will be the empty
// list.
//
// Algorithms in this list MUST be present in the ["HTTP Digest Algorithm Values" registry]
// defined by [RFC3230]; however, in JMAP, they must be lowercased, e.g., "md5" rather than
// "MD5".
//
// Clients SHOULD prefer algorithms listed earlier in this list.
//
// ["HTTP Digest Algorithm Values" registry]: https://www.iana.org/assignments/http-dig-alg/http-dig-alg.xhtml
SupportedDigestAlgorithms []string `json:"supportedDigestAlgorithms"`
}
type SessionQuotaAccountCapabilities struct {
}
type SessionAccountCapabilities struct {
Mail SessionMailAccountCapabilities `json:"urn:ietf:params:jmap:mail"`
Submission SessionSubmissionAccountCapabilities `json:"urn:ietf:params:jmap:submission"`
VacationResponse SessionVacationResponseAccountCapabilities `json:"urn:ietf:params:jmap:vacationresponse"`
Sieve SessionSieveAccountCapabilities `json:"urn:ietf:params:jmap:sieve"`
Blob SessionBlobAccountCapabilities `json:"urn:ietf:params:jmap:blob"`
Quota SessionQuotaAccountCapabilities `json:"urn:ietf:params:jmap:quota"`
}
type SessionAccount struct {
// A user-friendly string to show when presenting content from this account, e.g., the email address representing the owner of the account.
Name string `json:"name,omitempty"`
// This is true if the account belongs to the authenticated user rather than a group account or a personal account of another user that has been shared with them.
IsPersonal bool `json:"isPersonal"`
// This is true if the entire account is read-only.
IsReadOnly bool `json:"isReadOnly"`
AccountCapabilities SessionAccountCapabilities `json:"accountCapabilities"`
}
type SessionCoreCapabilities struct {
// The maximum file size, in octets, that the server will accept for a single file upload (for any purpose)
MaxSizeUpload int `json:"maxSizeUpload"`
// The maximum number of concurrent requests the server will accept to the upload endpoint.
MaxConcurrentUpload int `json:"maxConcurrentUpload"`
// The maximum size, in octets, that the server will accept for a single request to the API endpoint.
MaxSizeRequest int `json:"maxSizeRequest"`
// The maximum number of concurrent requests the server will accept to the API endpoint.
MaxConcurrentRequests int `json:"maxConcurrentRequests"`
// The maximum number of method calls the server will accept in a single request to the API endpoint.
MaxCallsInRequest int `json:"maxCallsInRequest"`
// The maximum number of objects that the client may request in a single /get type method call.
MaxObjectsInGet int `json:"maxObjectsInGet"`
// The maximum number of objects the client may send to create, update, or destroy in a single /set type method call.
// This is the combined total, e.g., if the maximum is 10, you could not create 7 objects and destroy 6, as this would be 13 actions,
// which exceeds the limit.
MaxObjectsInSet int `json:"maxObjectsInSet"`
// A list of identifiers for algorithms registered in the collation registry, as defined in [@!RFC4790], that the server
// supports for sorting when querying records.
CollationAlgorithms []string `json:"collationAlgorithms"`
}
type SessionMailCapabilities struct {
}
type SessionSubmissionCapabilities struct {
}
type SessionVacationResponseCapabilities struct {
}
type SessionSieveCapabilities struct {
}
type SessionBlobCapabilities struct {
}
type SessionQuotaCapabilities struct {
}
type SessionWebsocketCapabilities struct {
// The wss-URI (see [Section 3 of RFC6455]) to use for initiating a JMAP-over-WebSocket
// handshake (the "WebSocket URL endpoint" colloquially).
//
// [Section 3 of RFC6455]: https://www.rfc-editor.org/rfc/rfc6455.html#section-3
Url string `json:"url"`
// This is true if the server supports push notifications over the WebSocket,
// as described in [Section 4.3.5 of RFC 8887].
//
// [Section 4.3.5 of RFC 8887]: https://www.rfc-editor.org/rfc/rfc8887.html#name-jmap-push-notifications
SupportsPush bool `json:"supportsPush"`
}
type SessionCapabilities struct {
Core SessionCoreCapabilities `json:"urn:ietf:params:jmap:core"`
Mail SessionMailCapabilities `json:"urn:ietf:params:jmap:mail"`
Submission SessionSubmissionCapabilities `json:"urn:ietf:params:jmap:submission"`
VacationResponse SessionVacationResponseCapabilities `json:"urn:ietf:params:jmap:vacationresponse"`
Sieve SessionSieveCapabilities `json:"urn:ietf:params:jmap:sieve"`
Blob SessionBlobCapabilities `json:"urn:ietf:params:jmap:blob"`
Quota SessionQuotaCapabilities `json:"urn:ietf:params:jmap:quota"`
Websocket SessionWebsocketCapabilities `json:"urn:ietf:params:jmap:websocket"`
}
type SessionPrimaryAccounts struct {
Core string `json:"urn:ietf:params:jmap:core"`
Mail string `json:"urn:ietf:params:jmap:mail"`
Submission string `json:"urn:ietf:params:jmap:submission"`
VacationResponse string `json:"urn:ietf:params:jmap:vacationresponse"`
Sieve string `json:"urn:ietf:params:jmap:sieve"`
Blob string `json:"urn:ietf:params:jmap:blob"`
Quota string `json:"urn:ietf:params:jmap:quota"`
Websocket string `json:"urn:ietf:params:jmap:websocket"`
}
type SessionState string
type State string
type SessionResponse struct {
Capabilities SessionCapabilities `json:"capabilities"`
Accounts map[string]SessionAccount `json:"accounts,omitempty"`
// A map of capability URIs (as found in accountCapabilities) to the account id that is considered to be the users main or default
// account for data pertaining to that capability.
// If no account being returned belongs to the user, or in any other way there is no appropriate way to determine a default account,
// there MAY be no entry for a particular URI, even though that capability is supported by the server (and in the capabilities object).
// urn:ietf:params:jmap:core SHOULD NOT be present.
PrimaryAccounts SessionPrimaryAccounts `json:"primaryAccounts"`
// The username associated with the given credentials, or the empty string if none.
Username string `json:"username,omitempty"`
// The URL to use for JMAP API requests.
ApiUrl string `json:"apiUrl,omitempty"`
// The URL endpoint to use when downloading files, in URI Template (level 1) format [@!RFC6570].
// The URL MUST contain variables called accountId, blobId, type, and name.
DownloadUrl string `json:"downloadUrl,omitempty"`
// The URL endpoint to use when uploading files, in URI Template (level 1) format [@!RFC6570].
// The URL MUST contain a variable called accountId.
UploadUrl string `json:"uploadUrl,omitempty"`
// The URL to connect to for push events, as described in Section 7.3, in URI Template (level 1) format [@!RFC6570].
// The URL MUST contain variables called types, closeafter, and ping.
EventSourceUrl string `json:"eventSourceUrl,omitempty"`
// A (preferably short) string representing the state of this object on the server.
// If the value of any other property on the Session object changes, this string will change.
// The current value is also returned on the API Response object (see Section 3.4), allowing clients to quickly
// determine if the session information has changed (e.g., an account has been added or removed),
// so they need to refetch the object.
State SessionState `json:"state,omitempty"`
}
// SetError type values.
const (
// The create/update/destroy would violate an ACL or other permissions policy.
//
// (create; update; destroy).
SetErrorTypeForbidden = "forbidden"
// The create would exceed a server-defined limit on the number or total size of objects of this type.
//
// (create; update).
SetErrorTypeOverQuota = "overQuota"
// The create/update would result in an object that exceeds a server-defined limit for the maximum
// size of a single object of this type.
//
// (create; update).
SetErrorTypeTooLarge = "tooLarge"
// Too many objects of this type have been created recently, and a server-defined rate limit has been reached.
// It may work if tried again later.
//
// (create).
SetErrorTypeRateLimit = "rateLimit"
// The id given to update/destroy cannot be found.
//
// (update; destroy).
SetErrorTypeNotFound = "notFound"
// The PatchObject given to update the record was not a valid patch (see the patch description).
//
// (update).
SetErrorTypeInvalidPatch = "invalidPatch"
// The client requested that an object be both updated and destroyed in the same /set request, and the server
// has decided to therefore ignore the update.
//
// (update).
SetErrorTypeWillDestroy = "willDestroy"
// The record given is invalid in some way. For example:
//
// - It contains properties that are invalid according to the type specification of this record type.
// - It contains a property that may only be set by the server (e.g., “id”) and is different to the current value.
// Note, to allow clients to pass whole objects back, it is not an error to include a server-set property in an
// update as long as the value is identical to the current value on the server.
// - There is a reference to another record (foreign key), and the given id does not correspond to a valid record.
//
// The SetError object SHOULD also have a property called properties of type String[] that lists all the properties
// that were invalid.
//
// Individual methods MAY specify more specific errors for certain conditions that would otherwise result in an
// invalidProperties error. If the condition of one of these is met, it MUST be returned instead of the invalidProperties error.
//
// (create; update).
SetErrorTypeInvalidProperties = "invalidProperties"
// This is a singleton type, so you cannot create another one or destroy the existing one.
//
// (create; destroy).
SetErrorTypeSingleton = "singleton"
// The total number of objects to create, update, or destroy exceeds the maximum number the server is
// willing to process in a single method call.
SetErrorTypeRequestTooLarge = "requestTooLarge"
// An ifInState argument was supplied, and it does not match the current state.
SetErrorTypeStateMismatch = "stateMismatch"
// The Email to be sent is invalid in some way.
//
// The SetError SHOULD contain a property called properties of type String[] that lists all the properties
// of the Email that were invalid.
SetErrorInvalidEmail = "invalidEmail"
// The envelope (supplied or generated) has more recipients than the server allows.
//
// A maxRecipients UnsignedInt property MUST also be present on the SetError specifying
// the maximum number of allowed recipients.
SetErrorTooManyRecipients = "tooManyRecipients"
// The envelope (supplied or generated) does not have any rcptTo email addresses.
SetErrorNoRecipients = "noRecipients"
// The rcptTo property of the envelope (supplied or generated) contains at least one rcptTo value which
// is not a valid email address for sending to.
//
// An invalidRecipients String[] property MUST also be present on the SetError, which is a list of the invalid addresses.
SetErrorInvalidRecipients = "invalidRecipients"
// The server does not permit the user to send a message with this envelope From address [RFC5321].
//
// [RFC5321]: https://datatracker.ietf.org/doc/html/rfc5321
SetErrorForbiddenMailFrom = "forbiddenMailFrom"
// The server does not permit the user to send a message with the From header field [RFC5322] of the message to be sent.
//
// [RFC5322]: https://datatracker.ietf.org/doc/html/rfc5322
SetErrorForbiddenFrom = "forbiddenFrom"
// The user does not have permission to send at all right now for some reason.
//
// A description String property MAY be present on the SetError object to display to the user why they are not permitted.
SetErrorForbiddenToSend = "forbiddenToSend"
)
type SetError struct {
// The type of error.
Type string `json:"type"`
// A description of the error to help with debugging that includes an explanation of what the problem was.
//
// This is a non-localised string and is not intended to be shown directly to end users.
Description string `json:"description,omitempty"`
// Lists all the properties of the Email that were invalid.
//
// Only set for the invalidEmail error after a failed EmailSubmission/set errors.
Properties []string `json:"properties,omitempty"`
// Specifies the maximum number of allowed recipients.
//
// Only set for the tooManyRecipients error after a failed EmailSubmission/set errors.
MaxRecipients int `json:"maxRecipients,omitzero"`
// List of invalid addresses.
//
// Only set for the invalidRecipients error after a failed EmailSubmission/set errors.
InvalidRecipients []string `json:"invalidRecipients,omitempty"`
}
type FilterOperatorTerm string
const (
And FilterOperatorTerm = "AND"
Or FilterOperatorTerm = "OR"
Not FilterOperatorTerm = "NOT"
)
type MailboxRights struct {
// If true, the user may use this Mailbox as part of a filter in an Email/query call,
// and the Mailbox may be included in the mailboxIds property of Email objects.
//
// Email objects may be fetched if they are in at least one Mailbox with this permission.
//
// If a sub-Mailbox is shared but not the parent Mailbox, this may be false.
//
// Corresponds to IMAP ACLs lr (if mapping from IMAP, both are required for this to be true).
MayReadItems bool `json:"mayReadItems"`
// The user may add mail to this Mailbox (by either creating a new Email or moving an existing one).
//
// Corresponds to IMAP ACL i.
MayAddItems bool `json:"mayAddItems"`
// The user may remove mail from this Mailbox (by either changing the Mailboxes of an Email or
// destroying the Email).
//
// Corresponds to IMAP ACLs te (if mapping from IMAP, both are required for this to be true).
MayRemoveItems bool `json:"mayRemoveItems"`
// The user may add or remove the $seen keyword to/from an Email.
//
// If an Email belongs to multiple Mailboxes, the user may only modify $seen if they have this
// permission for all of the Mailboxes.
//
// Corresponds to IMAP ACL s.
MaySetSeen bool `json:"maySetSeen"`
// The user may add or remove any keyword other than $seen to/from an Email.
//
// If an Email belongs to multiple Mailboxes, the user may only modify keywords if they have this
// permission for all of the Mailboxes.
//
// Corresponds to IMAP ACL w.
MaySetKeywords bool `json:"maySetKeywords"`
// The user may create a Mailbox with this Mailbox as its parent.
//
// Corresponds to IMAP ACL k.
MayCreateChild bool `json:"mayCreateChild"`
// The user may rename the Mailbox or make it a child of another Mailbox.
//
// Corresponds to IMAP ACL x (although this covers both rename and delete permissions).
MayRename bool `json:"mayRename"`
// The user may delete the Mailbox itself.
//
// Corresponds to IMAP ACL x (although this covers both rename and delete permissions).
MayDelete bool `json:"mayDelete"`
// Messages may be submitted directly to this Mailbox.
//
// Corresponds to IMAP ACL p.
MaySubmit bool `json:"maySubmit"`
}
type Mailbox struct {
// The id of the Mailbox.
Id string `json:"id,omitempty"`
// User-visible name for the Mailbox, e.g., “Inbox”.
//
// This MUST be a Net-Unicode string [@!RFC5198] of at least 1 character in length, subject to the maximum size
// given in the capability object.
//
// There MUST NOT be two sibling Mailboxes with both the same parent and the same name.
//
// Servers MAY reject names that violate server policy (e.g., names containing a slash (/) or control characters).
Name string `json:"name,omitempty"`
// The Mailbox id for the parent of this Mailbox, or null if this Mailbox is at the top level.
//
// Mailboxes form acyclic graphs (forests) directed by the child-to-parent relationship. There MUST NOT be a loop.
ParentId string `json:"parentId,omitempty"`
// Identifies Mailboxes that have a particular common purpose (e.g., the “inbox”), regardless of the name property
// (which may be localised).
//
// This value is shared with IMAP (exposed in IMAP via the SPECIAL-USE extension [RFC6154]).
// However, unlike in IMAP, a Mailbox MUST only have a single role, and there MUST NOT be two Mailboxes in the same
// account with the same role.
//
// Servers providing IMAP access to the same data are encouraged to enforce these extra restrictions in IMAP as well.
// Otherwise, modifying the IMAP attributes to ensure compliance when exposing the data over JMAP is implementation dependent.
//
// The value MUST be one of the Mailbox attribute names listed in the IANA IMAP Mailbox Name Attributes registry,
// as established in [RFC8457], converted to lowercase. New roles may be established here in the future.
//
// An account is not required to have Mailboxes with any particular roles.
//
// [RFC6154]: https://www.rfc-editor.org/rfc/rfc6154.html
// [RFC8457]: https://www.rfc-editor.org/rfc/rfc8457.html
Role string `json:"role,omitempty"`
// Defines the sort order of Mailboxes when presented in the clients UI, so it is consistent between devices.
//
// Default value: 0
//
// The number MUST be an integer in the range 0 <= sortOrder < 2^31.
//
// A Mailbox with a lower order should be displayed before a Mailbox with a higher order
// (that has the same parent) in any Mailbox listing in the clients UI.
// Mailboxes with equal order SHOULD be sorted in alphabetical order by name.
// The sorting should take into account locale-specific character order convention.
SortOrder int `json:"sortOrder,omitzero"`
// The number of Emails in this Mailbox.
TotalEmails int `json:"totalEmails"`
// The number of Emails in this Mailbox that have neither the $seen keyword nor the $draft keyword.
UnreadEmails int `json:"unreadEmails"`
// The number of Threads where at least one Email in the Thread is in this Mailbox.
TotalThreads int `json:"totalThreads"`
// An indication of the number of “unread” Threads in the Mailbox.
UnreadThreads int `json:"unreadThreads"`
// The set of rights (Access Control Lists (ACLs)) the user has in relation to this Mailbox.
//
// These are backwards compatible with IMAP ACLs, as defined in [RFC4314].
//
// [RFC4314]: https://www.rfc-editor.org/rfc/rfc4314.html
MyRights MailboxRights `json:"myRights,omitempty"`
// Has the user indicated they wish to see this Mailbox in their client?
//
// This SHOULD default to false for Mailboxes in shared accounts the user has access to and true
// for any new Mailboxes created by the user themself.
//
// This MUST be stored separately per user where multiple users have access to a shared Mailbox.
//
// A user may have permission to access a large number of shared accounts, or a shared account with a very
// large set of Mailboxes, but only be interested in the contents of a few of these.
//
// Clients may choose to only display Mailboxes where the isSubscribed property is set to true, and offer
// a separate UI to allow the user to see and subscribe/unsubscribe from the full set of Mailboxes.
//
// However, clients MAY choose to ignore this property, either entirely for ease of implementation or just
// for an account where isPersonal is true (indicating it is the users own rather than a shared account).
//
// This property corresponds to IMAP [RFC3501] Mailbox subscriptions.
//
// [RFC3501]: https://www.rfc-editor.org/rfc/rfc3501.html
IsSubscribed bool `json:"isSubscribed"`
}
type MailboxGetCommand struct {
AccountId string `json:"accountId"`
Ids []string `json:"ids,omitempty"`
}
type MailboxGetRefCommand struct {
AccountId string `json:"accountId"`
IdRef *ResultReference `json:"#ids,omitempty"`
}
type MailboxChangesCommand struct {
// The id of the account to use.
AccountId string `json:"accountId"`
// The current state of the client.
//
// This is the string that was returned as the state argument in the Mailbox/get response.
//
// The server will return the changes that have occurred since this state.
SinceState string `json:"sinceState,omitempty"`
// The maximum number of ids to return in the response.
//
// The server MAY choose to return fewer than this value but MUST NOT return more.
//
// If not given by the client, the server may choose how many to return.
//
// If supplied by the client, the value MUST be a positive integer greater than 0.
//
// If a value outside of this range is given, the server MUST reject the call with an invalidArguments error.
MaxChanges uint `json:"maxChanges,omitzero"`
}
type MailboxFilterElement interface {
_isAMailboxFilterElement() // marker method
}
type MailboxFilterCondition struct {
MailboxFilterElement
ParentId string `json:"parentId,omitempty"`
Name string `json:"name,omitempty"`
Role string `json:"role,omitempty"`
HasAnyRole *bool `json:"hasAnyRole,omitempty"`
IsSubscribed *bool `json:"isSubscribed,omitempty"`
}
var _ MailboxFilterElement = &MailboxFilterCondition{}
type MailboxFilterOperator struct {
MailboxFilterElement
Operator FilterOperatorTerm `json:"operator"`
Conditions []MailboxFilterElement `json:"conditions,omitempty"`
}
var _ MailboxFilterElement = &MailboxFilterOperator{}
type MailboxComparator struct {
Property string `json:"property"`
IsAscending bool `json:"isAscending,omitempty"`
Limit int `json:"limit,omitzero"`
CalculateTotal bool `json:"calculateTotal,omitempty"`
}
type MailboxQueryCommand struct {
AccountId string `json:"accountId"`
Filter MailboxFilterElement `json:"filter,omitempty"`
Sort []MailboxComparator `json:"sort,omitempty"`
SortAsTree bool `json:"sortAsTree,omitempty"`
FilterAsTree bool `json:"filterAsTree,omitempty"`
}
type EmailFilterElement interface {
_isAnEmailFilterElement() // marker method
IsNotEmpty() bool
}
type EmailFilterCondition struct {
// A Mailbox id.
//
// An Email must be in this Mailbox to match the condition.
InMailbox string `json:"inMailbox,omitempty"`
// A list of Mailbox ids.
//
// An Email must be in at least one Mailbox not in this list to match the condition.
//
// This is to allow messages solely in trash/spam to be easily excluded from a search.
InMailboxOtherThan []string `json:"inMailboxOtherThan,omitempty"`
// The receivedAt date-time of the Email must be before this date-time to match
// the condition.
Before time.Time `json:"before,omitzero"` // omitzero requires Go 1.24
// The receivedAt date-time of the Email must be the same or after this date-time
// to match the condition.
After time.Time `json:"after,omitzero"`
// The size property of the Email must be equal to or greater than this number to match
// the condition.
MinSize int `json:"minSize,omitempty"`
// The size property of the Email must be less than this number to match the condition.
MaxSize int `json:"maxSize,omitempty"`
// All Emails (including this one) in the same Thread as this Email must have the given
// keyword to match the condition.
AllInThreadHaveKeyword string `json:"allInThreadHaveKeyword,omitempty"`
// At least one Email (possibly this one) in the same Thread as this Email must have the
// given keyword to match the condition.
SomeInThreadHaveKeyword string `json:"someInThreadHaveKeyword,omitempty"`
// All Emails (including this one) in the same Thread as this Email must not have the
// given keyword to match the condition.
NoneInThreadHaveKeyword string `json:"noneInThreadHaveKeyword,omitempty"`
// This Email must have the given keyword to match the condition.
HasKeyword string `json:"hasKeyword,omitempty"`
// This Email must not have the given keyword to match the condition.
NotKeyword string `json:"notKeyword,omitempty"`
// The hasAttachment property of the Email must be identical to the value given to match
// the condition.
HasAttachment bool `json:"hasAttachment,omitempty"`
// Looks for the text in Emails.
//
// The server MUST look up text in the From, To, Cc, Bcc, and Subject header fields of the
// message and SHOULD look inside any text/* or other body parts that may be converted to
// text by the server.
//
// The server MAY extend the search to any additional textual property.
Text string `json:"text,omitempty"`
// Looks for the text in the From header field of the message.
From string `json:"from,omitempty"`
// Looks for the text in the To header field of the message.
To string `json:"to,omitempty"`
// Looks for the text in the Cc header field of the message.
Cc string `json:"cc,omitempty"`
// Looks for the text in the Bcc header field of the message.
Bcc string `json:"bcc,omitempty"`
// Looks for the text in the Subject header field of the message.
Subject string `json:"subject,omitempty"`
// Looks for the text in one of the body parts of the message.
//
// The server MAY exclude MIME body parts with content media types other than text/*
// and message/* from consideration in search matching.
//
// Care should be taken to match based on the text content actually presented to an end user
// by viewers for that media type or otherwise identified as appropriate for search indexing.
//
// Matching document metadata uninteresting to an end user (e.g., markup tag and attribute
// names) is undesirable.
Body string `json:"body,omitempty"`
// The array MUST contain either one or two elements.
//
// The first element is the name of the header field to match against.
//
// The second (optional) element is the text to look for in the header field value.
//
// If not supplied, the message matches simply if it has a header field of the given name.
Header []string `json:"header,omitempty"`
}
func (f EmailFilterCondition) _isAnEmailFilterElement() {
}
func (f EmailFilterCondition) IsNotEmpty() bool {
if !f.After.IsZero() {
return true
}
if f.AllInThreadHaveKeyword != "" {
return true
}
if len(f.Bcc) > 0 {
return true
}
if !f.Before.IsZero() {
return true
}
if f.Body != "" {
return true
}
if f.Cc != "" {
return true
}
if f.From != "" {
return true
}
if f.HasAttachment {
return true
}
if f.HasKeyword != "" {
return true
}
if len(f.Header) > 0 {
return true
}
if f.InMailbox != "" {
return true
}
if len(f.InMailboxOtherThan) > 0 {
return true
}
if f.MaxSize != 0 {
return true
}
if f.MinSize != 0 {
return true
}
if f.NoneInThreadHaveKeyword != "" {
return true
}
if f.NotKeyword != "" {
return true
}
if f.SomeInThreadHaveKeyword != "" {
return true
}
if f.Subject != "" {
return true
}
if f.Text != "" {
return true
}
if f.To != "" {
return true
}
return false
}
var _ EmailFilterElement = &EmailFilterCondition{}
type EmailFilterOperator struct {
Operator FilterOperatorTerm `json:"operator"`
Conditions []EmailFilterElement `json:"conditions,omitempty"`
}
func (o EmailFilterOperator) _isAnEmailFilterElement() {
}
func (o EmailFilterOperator) IsNotEmpty() bool {
return len(o.Conditions) > 0
}
var _ EmailFilterElement = &EmailFilterOperator{}
type EmailComparator struct {
// The name of the property on the objects to compare.
Property string `json:"property,omitempty"`
// If true, sort in ascending order.
//
// Optional; default value: true.
//
// If false, reverse the comparators results to sort in descending order.
IsAscending bool `json:"isAscending,omitempty"`
// The identifier, as registered in the collation registry defined in [RFC4790],
// for the algorithm to use when comparing the order of strings.
//
// Optional; default is server dependent.
//
// The algorithms the server supports are advertised in the capabilities object returned
// with the Session object.
//
// [RFC4790]: https://www.rfc-editor.org/rfc/rfc4790.html
Collation string `json:"collation,omitempty"`
// Email-specific: keyword that must be included in the Email object.
Keyword string `json:"keyword,omitempty"`
}
// If an anchor argument is given, the anchor is looked for in the results after filtering
// and sorting.
//
// If found, the anchorOffset is then added to its index. If the resulting index is now negative,
// it is clamped to 0. This index is now used exactly as though it were supplied as the position
// argument. If the anchor is not found, the call is rejected with an anchorNotFound error.
//
// If an anchor is specified, any position argument supplied by the client MUST be ignored.
// If no anchor is supplied, any anchorOffset argument MUST be ignored.
//
// A client can use anchor instead of position to find the index of an id within a large set of results.
type EmailQueryCommand struct {
// The id of the account to use.
AccountId string `json:"accountId"`
// Determines the set of Emails returned in the results.
//
// If null, all objects in the account of this type are included in the results.
Filter EmailFilterElement `json:"filter,omitempty"`
// Lists the names of properties to compare between two Email records, and how to compare
// them, to determine which comes first in the sort.
//
// If two Email records have an identical value for the first comparator, the next comparator
// will be considered, and so on. If all comparators are the same (this includes the case
// where an empty array or null is given as the sort argument), the sort order is server
// dependent, but it MUST be stable between calls to Email/query.
Sort []EmailComparator `json:"sort,omitempty"`
// If true, Emails in the same Thread as a previous Email in the list (given the
// filter and sort order) will be removed from the list.
//
// This means only one Email at most will be included in the list for any given Thread.
CollapseThreads bool `json:"collapseThreads,omitempty"`
// The zero-based index of the first id in the full list of results to return.
//
// If a negative value is given, it is an offset from the end of the list.
// Specifically, the negative value MUST be added to the total number of results given
// the filter, and if still negative, its clamped to 0. This is now the zero-based
// index of the first id to return.
//
// If the index is greater than or equal to the total number of objects in the results
// list, then the ids array in the response will be empty, but this is not an error.
Position uint `json:"position,omitempty"`
// An Email id.
//
// If supplied, the position argument is ignored.
// The index of this id in the results will be used in combination with the anchorOffset
// argument to determine the index of the first result to return.
Anchor string `json:"anchor,omitempty"`
// The index of the first result to return relative to the index of the anchor,
// if an anchor is given.
//
// Default: 0.
//
// This MAY be negative.
//
// For example, -1 means the Email immediately preceding the anchor is the first result in
// the list returned.
AnchorOffset int `json:"anchorOffset,omitzero"`
// The maximum number of results to return.
//
// If null, no limit presumed.
// The server MAY choose to enforce a maximum limit argument.
// In this case, if a greater value is given (or if it is null), the limit is clamped
// to the maximum; the new limit is returned with the response so the client is aware.
//
// If a negative value is given, the call MUST be rejected with an invalidArguments error.
Limit uint `json:"limit,omitempty"`
// Does the client wish to know the total number of results in the query?
//
// This may be slow and expensive for servers to calculate, particularly with complex filters,
// so clients should take care to only request the total when needed.
CalculateTotal bool `json:"calculateTotal,omitempty"`
}
type EmailGetCommand struct {
// The ids of the Email objects to return.
//
// If null, then all records of the data type are returned, if this is supported for that
// data type and the number of records does not exceed the maxObjectsInGet limit.
Ids []string `json:"ids,omitempty"`
// The id of the account to use.
AccountId string `json:"accountId"`
// If supplied, only the properties listed in the array are returned for each Email object.
//
// If null, the following properties are returned:
//
// [ "id", "blobId", "threadId", "mailboxIds", "keywords", "size",
// "receivedAt", "messageId", "inReplyTo", "references", "sender", "from",
// "to", "cc", "bcc", "replyTo", "subject", "sentAt", "hasAttachment",
// "preview", "bodyValues", "textBody", "htmlBody", "attachments" ]
//
// The id property of the object is always returned, even if not explicitly requested.
//
// If an invalid property is requested, the call MUST be rejected with an invalidArguments error.
Properties []string `json:"properties,omitempty"`
// A list of properties to fetch for each EmailBodyPart returned.
//
// If omitted, this defaults to:
//
// [ "partId", "blobId", "size", "name", "type", "charset", "disposition", "cid", "language", "location" ]
//
BodyProperties []string `json:"bodyProperties,omitempty"`
// (default: false) If true, the bodyValues property includes any text/* part in the textBody property.
FetchTextBodyValues bool `json:"fetchTextBodyValues,omitzero"`
// (default: false) If true, the bodyValues property includes any text/* part in the htmlBody property.
FetchHTMLBodyValues bool `json:"fetchHTMLBodyValues,omitzero"`
// (default: false) If true, the bodyValues property includes any text/* part in the bodyStructure property.
FetchAllBodyValues bool `json:"fetchAllBodyValues,omitzero"`
// If greater than zero, the value property of any EmailBodyValue object returned in bodyValues
// MUST be truncated if necessary so it does not exceed this number of octets in size.
//
// If 0 (the default), no truncation occurs.
//
// The server MUST ensure the truncation results in valid UTF-8 and does not occur mid-codepoint.
//
// If the part is of type text/html, the server SHOULD NOT truncate inside an HTML tag, e.g., in
// the middle of <a href="https://example.com">.
//
// There is no requirement for the truncated form to be a balanced tree or valid HTML (indeed, the original
// source may well be neither of these things).
MaxBodyValueBytes uint `json:"maxBodyValueBytes,omitempty"`
}
// Reference to Previous Method Results
//
// To allow clients to make more efficient use of the network and avoid round trips, an argument to one method
// can be taken from the result of a previous method call in the same request.
//
// To do this, the client prefixes the argument name with # (an [octothorpe]).
//
// When processing a method call, the server MUST first check the arguments object for any names beginning with #.
//
// If found, the result reference should be resolved and the value used as the “real” argument.
//
// The method is then processed as normal.
//
// If any result reference fails to resolve, the whole method MUST be rejected with an invalidResultReference error.
//
// If an arguments object contains the same argument name in normal and referenced form (e.g., foo and #foo),
// the method MUST return an invalidArguments error.
//
// To resolve:
//
// 1. Find the first response with a method call id identical to the resultOf property of the ResultReference
// in the methodResponses array from previously processed method calls in the same request.
// If none, evaluation fails.
// 2. If the response name is not identical to the name property of the ResultReference, evaluation fails.
// 3. Apply the path to the arguments object of the response (the second item in the response array)
// following the JSON Pointer algorithm [RFC6901], except with the following addition in “Evaluation” (see Section 4):
// 4. If the currently referenced value is a JSON array, the reference token may be exactly the single character *,
// making the new referenced value the result of applying the rest of the JSON Pointer tokens to every item in the
// array and returning the results in the same order in a new array.
// 5. If the result of applying the rest of the pointer tokens to each item was itself an array, the contents of this
// array are added to the output rather than the array itself (i.e., the result is flattened from an array of
// arrays to a single array).
//
// [octothorpe]; https://en.wiktionary.org/wiki/octothorpe
// [RFC6901]: https://datatracker.ietf.org/doc/html/rfc6901
type ResultReference struct {
// The method call id of a previous method call in the current request.
ResultOf string `json:"resultOf"`
// The required name of a response to that method call.
Name Command `json:"name"`
// A pointer into the arguments of the response selected via the name and resultOf properties.
//
// This is a JSON Pointer [RFC6901], except it also allows the use of * to map through an array.
//
// [RFC6901]: https://datatracker.ietf.org/doc/html/rfc6901
Path string `json:"path,omitempty"`
}
type EmailGetRefCommand struct {
// The ids of the Email objects to return.
//
// If null, then all records of the data type are returned, if this is supported for that
// data type and the number of records does not exceed the maxObjectsInGet limit.
IdRef *ResultReference `json:"#ids,omitempty"`
// The id of the account to use.
AccountId string `json:"accountId"`
// If supplied, only the properties listed in the array are returned for each Email object.
//
// If null, the following properties are returned:
//
// [ "id", "blobId", "threadId", "mailboxIds", "keywords", "size",
// "receivedAt", "messageId", "inReplyTo", "references", "sender", "from",
// "to", "cc", "bcc", "replyTo", "subject", "sentAt", "hasAttachment",
// "preview", "bodyValues", "textBody", "htmlBody", "attachments" ]
//
// The id property of the object is always returned, even if not explicitly requested.
//
// If an invalid property is requested, the call MUST be rejected with an invalidArguments error.
Properties []string `json:"properties,omitempty"`
// A list of properties to fetch for each EmailBodyPart returned.
//
// If omitted, this defaults to:
//
// [ "partId", "blobId", "size", "name", "type", "charset", "disposition", "cid", "language", "location" ]
//
BodyProperties []string `json:"bodyProperties,omitempty"`
// (default: false) If true, the bodyValues property includes any text/* part in the textBody property.
FetchTextBodyValues bool `json:"fetchTextBodyValues,omitzero"`
// (default: false) If true, the bodyValues property includes any text/* part in the htmlBody property.
FetchHTMLBodyValues bool `json:"fetchHTMLBodyValues,omitzero"`
// (default: false) If true, the bodyValues property includes any text/* part in the bodyStructure property.
FetchAllBodyValues bool `json:"fetchAllBodyValues,omitzero"`
// If greater than zero, the value property of any EmailBodyValue object returned in bodyValues
// MUST be truncated if necessary so it does not exceed this number of octets in size.
//
// If 0 (the default), no truncation occurs.
//
// The server MUST ensure the truncation results in valid UTF-8 and does not occur mid-codepoint.
//
// If the part is of type text/html, the server SHOULD NOT truncate inside an HTML tag, e.g., in
// the middle of <a href="https://example.com">.
//
// There is no requirement for the truncated form to be a balanced tree or valid HTML (indeed, the original
// source may well be neither of these things).
MaxBodyValueBytes uint `json:"maxBodyValueBytes,omitempty"`
}
type EmailChangesCommand struct {
// The id of the account to use.
AccountId string `json:"accountId"`
// The current state of the client.
//
// This is the string that was returned as the state argument in the Email/get response.
// The server will return the changes that have occurred since this state.
SinceState string `json:"sinceState,omitzero,omitempty"`
// The maximum number of ids to return in the response.
//
// The server MAY choose to return fewer than this value but MUST NOT return more.
// If not given by the client, the server may choose how many to return.
// If supplied by the client, the value MUST be a positive integer greater than 0.
MaxChanges uint `json:"maxChanges,omitzero"`
}
type EmailAddress struct {
// The display-name of the mailbox [RFC5322].
//
// If this is a quoted-string:
// 1. The surrounding DQUOTE characters are removed.
// 2. Any quoted-pair is decoded.
// 3. White space is unfolded, and then any leading and trailing white space is removed.
// If there is no display-name but there is a comment immediately following the addr-spec, the value of this
// SHOULD be used instead. Otherwise, this property is null.
//
// [RFC5322]: https://www.rfc-editor.org/rfc/rfc5322.html
Name string `json:"name,omitempty"`
// The addr-spec of the mailbox [RFC5322].
//
// Any syntactically correct encoded sections [RFC2047] with a known encoding MUST be decoded,
// following the same rules as for the Text form.
//
// Parsing SHOULD be best effort in the face of invalid structure to accommodate invalid messages and
// semi-complete drafts. EmailAddress objects MAY have an email property that does not conform to the
// addr-spec form (for example, may not contain an @ symbol).
//
// [RFC2047]: https://www.rfc-editor.org/rfc/rfc2047.html
// [RFC5322]: https://www.rfc-editor.org/rfc/rfc5322.html
Email string `json:"email,omitempty"`
}
type EmailAddressGroup struct {
// The display-name of the group [RFC5322], or null if the addresses are not part of a group.
//
// If this is a quoted-string, it is processed the same as the name in the EmailAddress type.
//
// [RFC5322]: https://www.rfc-editor.org/rfc/rfc5322.html
Name string `json:"name,omitempty"`
// The mailbox values that belong to this group, represented as EmailAddress objects.
Addresses []EmailAddress `json:"addresses,omitempty"`
}
type EmailHeader struct {
// The header field name as defined in [RFC5322], with the same capitalization that it has in the message.
//
// [RFC5322]: https://www.rfc-editor.org/rfc/rfc5322.html
Name string `json:"name"`
// The header field value as defined in [RFC5322], in Raw form.
//
// [RFC5322]: https://www.rfc-editor.org/rfc/rfc5322.html
Value string `json:"value"`
}
// Email body part.
//
// The client may specify a partId OR a blobId, but not both.
// If a partId is given, this partId MUST be present in the bodyValues property.
//
// The charset property MUST be omitted if a partId is given (the parts content is included
// in bodyValues, and the server may choose any appropriate encoding).
//
// The size property MUST be omitted if a partId is given. If a blobId is given, it may be
// included but is ignored by the server (the size is actually calculated from the blob content
// itself).
//
// A Content-Transfer-Encoding header field MUST NOT be given.
type EmailBodyPart struct {
// Identifies this part uniquely within the Email.
//
// This is scoped to the emailId and has no meaning outside of the JMAP Email object representation.
// This is null if, and only if, the part is of type multipart/*.
PartId string `json:"partId,omitempty"`
// The id representing the raw octets of the contents of the part, after decoding any known
// Content-Transfer-Encoding (as defined in [RFC2045]), or null if, and only if, the part is of type multipart/*.
//
// Note that two parts may be transfer-encoded differently but have the same blob id if their decoded octets are identical
// and the server is using a secure hash of the data for the blob id.
// If the transfer encoding is unknown, it is treated as though it had no transfer encoding.
//
// [RFC2045]: https://www.rfc-editor.org/rfc/rfc2045.html
BlobId string `json:"blobId,omitempty"`
// The size, in octets, of the raw data after content transfer decoding (as referenced by the blobId, i.e.,
// the number of octets in the file the user would download).
Size int `json:"size,omitempty"`
// This is a list of all header fields in the part, in the order they appear in the message.
//
// The values are in Raw form.
Headers []EmailHeader `json:"headers,omitempty"`
// This is the decoded filename parameter of the Content-Disposition header field per [RFC2231], or
// (for compatibility with existing systems).
//
// If not present, then its the decoded name parameter of the Content-Type header field per [RFC2047].
//
// [RFC2231]: https://www.rfc-editor.org/rfc/rfc2231.html
// [RFC2047]: https://www.rfc-editor.org/rfc/rfc2047.html
Name string `json:"name,omitempty"`
// The value of the Content-Type header field of the part, if present; otherwise, the implicit type as per
// the MIME standard (text/plain or message/rfc822 if inside a multipart/digest).
//
// CFWS is removed and any parameters are stripped.
Type string `json:"type,omitempty"`
// The value of the charset parameter of the Content-Type header field, if present, or null if the header
// field is present but not of type text/*.
//
// If there is no Content-Type header field, or it exists and is of type text/* but has no charset parameter,
// this is the implicit charset as per the MIME standard: us-ascii.
Charset string `json:"charset,omitempty"`
// The value of the Content-Disposition header field of the part, if present;
// otherwise, its null.
//
// CFWS is removed and any parameters are stripped.
Disposition string `json:"disposition,omitempty"`
// The value of the Content-Id header field of the part, if present; otherwise its null.
//
// CFWS and surrounding angle brackets (<>) are removed.
// This may be used to reference the content from within a text/html body part HTML using the cid: protocol, as defined in [RFC2392].
//
// [RFC2392]: https://www.rfc-editor.org/rfc/rfc2392.html
Cid string `json:"cid,omitempty"`
// The list of language tags, as defined in [RFC3282], in the Content-Language header field of the part, if present.
//
// [RFC3282]: https://www.rfc-editor.org/rfc/rfc3282.html
Language string `json:"language,omitempty"`
// The URI, as defined in [RFC2557], in the Content-Location header field of the part, if present.
//
// [RFC2557]: https://www.rfc-editor.org/rfc/rfc2557.html
Location string `json:"location,omitempty"`
// If the type is multipart/*, this contains the body parts of each child.
SubParts []EmailBodyPart `json:"subParts,omitempty"`
}
type EmailBodyValue struct {
// The value of the body part after decoding Content-Transfer-Encoding and the Content-Type charset,
// if both known to the server, and with any CRLF replaced with a single LF.
//
// The server MAY use heuristics to determine the charset to use for decoding if the charset is unknown,
// no charset is given, or it believes the charset given is incorrect.
//
// Decoding is best effort; the server SHOULD insert the unicode replacement character (U+FFFD) and continue
// when a malformed section is encountered.
//
// Note that due to the charset decoding and line ending normalisation, the length of this string will
// probably not be exactly the same as the size property on the corresponding EmailBodyPart.
Value string `json:"value,omitempty"`
// This is true if malformed sections were found while decoding the charset,
// or the charset was unknown, or the content-transfer-encoding was unknown.
//
// Default value is false.
IsEncodingProblem bool `json:"isEncodingProblem,omitzero"`
// This is true if the value has been truncated.
//
// Default value is false.
IsTruncated bool `json:"isTruncated,omitzero"`
}
// An Email.
//
// swagger:model
type Email struct {
// The id of the Email object.
//
// Note that this is the JMAP object id, NOT the Message-ID header field value of the message [RFC5322].
//
// [RFC5322]: https://www.rfc-editor.org/rfc/rfc5322.html
//
// required: true
// example: eaaaaab
Id string `json:"id,omitempty"`
// The id representing the raw octets of the message [RFC5322] for this Email.
//
// This may be used to download the raw original message or to attach it directly to another Email, etc.
//
// [RFC5322]: https://www.rfc-editor.org/rfc/rfc5322.html
//
// example: cbbrzak0jw3gmtovgtwd1nd1p7p0czjlxx0ejgqws9oucgpuyr9fsayaae
BlobId string `json:"blobId,omitempty"`
// The id of the Thread to which this Email belongs.
//
// example: b
ThreadId string `json:"threadId,omitempty"`
// The set of Mailbox ids this Email belongs to.
//
// An Email in the mail store MUST belong to one or more Mailboxes at all times (until it is destroyed).
// The set is represented as an object, with each key being a Mailbox id.
//
// The value for each key in the object MUST be true.
//
// example: {"a": true}
MailboxIds map[string]bool `json:"mailboxIds,omitempty"`
// A set of keywords that apply to the Email.
//
// The set is represented as an object, with the keys being the keywords.
//
// The value for each key in the object MUST be true.
//
// Keywords are shared with IMAP.
//
// The six system keywords from IMAP get special treatment.
//
// The following four keywords have their first character changed from \ in IMAP to $ in JMAP and have particular semantic meaning:
//
// - $draft: The Email is a draft the user is composing.
// - $seen: The Email has been read.
// - $flagged: The Email has been flagged for urgent/special attention.
// - $answered: The Email has been replied to.
//
// The IMAP \Recent keyword is not exposed via JMAP. The IMAP \Deleted keyword is also not present: IMAP uses a delete+expunge model,
// which JMAP does not. Any message with the \Deleted keyword MUST NOT be visible via JMAP (and so are not counted in the
// “totalEmails”, “unreadEmails”, “totalThreads”, and “unreadThreads” Mailbox properties).
//
// Users may add arbitrary keywords to an Email.
// For compatibility with IMAP, a keyword is a case-insensitive string of 1255 characters in the ASCII subset
// %x21%x7e (excludes control chars and space), and it MUST NOT include any of these characters:
//
// ( ) { ] % * " \
//
// Because JSON is case sensitive, servers MUST return keywords in lowercase.
//
// The [IMAP and JMAP Keywords] registry as established in [RFC5788] assigns semantic meaning to some other
// keywords in common use.
//
// New keywords may be established here in the future. In particular, note:
//
// - $forwarded: The Email has been forwarded.
// - $phishing: The Email is highly likely to be phishing.
// Clients SHOULD warn users to take care when viewing this Email and disable links and attachments.
// - $junk: The Email is definitely spam.
// Clients SHOULD set this flag when users report spam to help train automated spam-detection systems.
// - $notjunk: The Email is definitely not spam.
// Clients SHOULD set this flag when users indicate an Email is legitimate, to help train automated spam-detection systems.
//
// [IMAP and JMAP Keywords]: https://www.iana.org/assignments/imap-jmap-keywords/
// [RFC5788]: https://www.rfc-editor.org/rfc/rfc5788.html
Keywords map[string]bool `json:"keywords,omitempty"`
// The size, in octets, of the raw data for the message [RFC5322]
// (as referenced by the blobId, i.e., the number of octets in the file the user would download).
//
// [RFC5322]: https://www.rfc-editor.org/rfc/rfc5322.html
Size int `json:"size"`
// The date the Email was received by the message store.
//
// This is the internal date in IMAP [RFC3501].
//
// [RFC3501]: https://www.rfc-editor.org/rfc/rfc3501.html
ReceivedAt time.Time `json:"receivedAt,omitempty"`
// This is a list of all header fields [RFC5322], in the same order they appear in the message.
//
// [RFC5322]: https://www.rfc-editor.org/rfc/rfc5322.html
Headers []EmailHeader `json:"headers,omitempty"`
// The value is identical to the value of header:Message-ID:asMessageIds.
//
// For messages conforming to [RFC5322] this will be an array with a single entry.
//
// [RFC5322]: https://www.rfc-editor.org/rfc/rfc5322.html
MessageId []string `json:"messageId,omitempty"`
// The value is identical to the value of header:In-Reply-To:asMessageIds.
InReplyTo []string `json:"inReplyTo,omitempty"`
// The value is identical to the value of header:References:asMessageIds.
References []string `json:"references,omitempty"`
// The value is identical to the value of header:Sender:asAddresses.
Sender []EmailAddress `json:"sender,omitempty"`
// The value is identical to the value of header:From:asAddresses.
From []EmailAddress `json:"from,omitempty"`
// The value is identical to the value of header:To:asAddresses.
To []EmailAddress `json:"to,omitempty"`
// The value is identical to the value of header:Cc:asAddresses.
Cc []EmailAddress `json:"cc,omitempty"`
// The value is identical to the value of header:Bcc:asAddresses.
Bcc []EmailAddress `json:"bcc,omitempty"`
// The value is identical to the value of header:Reply-To:asAddresses.
ReplyTo []EmailAddress `json:"replyTo,omitempty"`
// The value is identical to the value of header:Subject:asText.
Subject string `json:"subject,omitempty"`
// The value is identical to the value of header:Date:asDate.
SentAt time.Time `json:"sentAt,omitempty"`
// This is the full MIME structure of the message body, without recursing into message/rfc822 or message/global parts.
//
// Note that EmailBodyParts may have subParts if they are of type multipart/*.
BodyStructure EmailBodyPart `json:"bodyStructure,omitzero"`
// This is a map of partId to an EmailBodyValue object for none, some, or all text/* parts.
//
// Which parts are included and whether the value is truncated is determined by various arguments to Email/get and Email/parse.
BodyValues map[string]EmailBodyValue `json:"bodyValues,omitempty"`
// A list of text/plain, text/html, image/*, audio/*, and/or video/* parts to display (sequentially) as the
// message body, with a preference for text/plain when alternative versions are available.
TextBody []EmailBodyPart `json:"textBody,omitempty"`
// A list of text/plain, text/html, image/*, audio/*, and/or video/* parts to display (sequentially) as the
// message body, with a preference for text/html when alternative versions are available.
HtmlBody []EmailBodyPart `json:"htmlBody,omitempty"`
// A list, traversing depth-first, of all parts in bodyStructure.
//
// They must satisfy either of the following conditions:
//
// - not of type multipart/* and not included in textBody or htmlBody
// - of type image/*, audio/*, or video/* and not in both textBody and htmlBody
//
// None of these parts include subParts, including message/* types.
//
// Attached messages may be fetched using the Email/parse method and the blobId.
//
// Note that a text/html body part HTML may reference image parts in attachments by using cid:
// links to reference the Content-Id, as defined in [RFC2392], or by referencing the Content-Location.
//
// [RFC2392]: https://www.rfc-editor.org/rfc/rfc2392.html
Attachments []EmailBodyPart `json:"attachments,omitempty"`
// This is true if there are one or more parts in the message that a client UI should offer as downloadable.
//
// A server SHOULD set hasAttachment to true if the attachments list contains at least one item that
// does not have Content-Disposition: inline.
//
// The server MAY ignore parts in this list that are processed automatically in some way or are referenced
// as embedded images in one of the text/html parts of the message.
//
// The server MAY set hasAttachment based on implementation-defined or site-configurable heuristics.
HasAttachment bool `json:"hasAttachment,omitempty"`
// A plaintext fragment of the message body.
//
// This is intended to be shown as a preview line when listing messages in the mail store and may be truncated
// when shown.
//
// The server may choose which part of the message to include in the preview; skipping quoted sections and
// salutations and collapsing white space can result in a more useful preview.
//
// This MUST NOT be more than 256 characters in length.
//
// As this is derived from the message content by the server, and the algorithm for doing so could change over
// time, fetching this for an Email a second time MAY return a different result.
// However, the previous value is not considered incorrect, and the change SHOULD NOT cause the Email object
// to be considered as changed by the server.
Preview string `json:"preview,omitempty"`
}
type Address struct {
// The email address being represented by the object.
//
// This is a “Mailbox” as used in the Reverse-path or Forward-path of the MAIL FROM or RCPT TO command in [RFC5321].
//
// [RFC5321]: https://datatracker.ietf.org/doc/html/rfc5321
Email string `json:"email,omitempty"`
// Any parameters to send with the email address (either mail-parameter or rcpt-parameter as appropriate,
// as specified in [RFC5321]).
//
// If supplied, each key in the object is a parameter name, and the value is either the parameter value (type String)
// or null if the parameter does not take a value.
//
// [RFC5321]: https://datatracker.ietf.org/doc/html/rfc5321
Parameters map[string]any `json:"parameters,omitempty"` // TODO RFC5321
}
// Information for use when sending via SMTP.
type Envelope struct {
// The email address to use as the return address in the SMTP submission,
// plus any parameters to pass with the MAIL FROM address.
MailFrom Address `json:"mailFrom"`
// The email addresses to send the message to, and any RCPT TO parameters to pass with the recipient.
RcptTo []Address `json:"rcptTo"`
}
type EmailSubmissionUndoStatus string
const (
// It may be possible to cancel this submission.
UndoStatusPending EmailSubmissionUndoStatus = "pending"
// The message has been relayed to at least one recipient in a manner that cannot be recalled.
// It is no longer possible to cancel this submission.
UndoStatusFinal EmailSubmissionUndoStatus = "final"
// The submission was canceled and will not be delivered to any recipient.
UndoStatusCanceled EmailSubmissionUndoStatus = "canceled"
)
type DeliveryStatusDelivered string
const (
// The message is in a local mail queue and status will change once it exits the local mail
// queues.
// The smtpReply property may still change.
DeliveredQueued DeliveryStatusDelivered = "queued"
// The message was successfully delivered to the mail store of the recipient.
// The smtpReply property is final.
DeliveredYes DeliveryStatusDelivered = "yes"
// Delivery to the recipient permanently failed.
// The smtpReply property is final.
DeliveredNo DeliveryStatusDelivered = "no"
// The final delivery status is unknown, (e.g., it was relayed to an external machine
// and no further information is available).
//
// The smtpReply property may still change if a DSN arrives.
DeliveredUnknown DeliveryStatusDelivered = "unknown"
)
type DeliveryStatusDisplayed string
const (
// The display status is unknown.
//
// This is the initial value.
DisplayedUnknown DeliveryStatusDisplayed = "unknown"
// The recipients system claims the message content has been displayed to the recipient.
//
// Note that there is no guarantee that the recipient has noticed, read, or understood the content.
DisplayedYes DeliveryStatusDisplayed = "yes"
)
type DeliveryStatus struct {
// The SMTP reply string returned for this recipient when the server last tried to
// relay the message, or in a later Delivery Status Notification (DSN, as defined in
// [RFC3464]) response for the message.
//
// This SHOULD be the response to the RCPT TO stage, unless this was accepted and the
// message as a whole was rejected at the end of the DATA stage, in which case the
// DATA stage reply SHOULD be used instead.
//
// [RFC3464]: https://datatracker.ietf.org/doc/html/rfc3464
SmtpReply string `json:"smtpReply"`
// Represents whether the message has been successfully delivered to the recipient.
//
// This MUST be one of the following values:
// - queued: The message is in a local mail queue and status will change once it exits
// the local mail queues. The smtpReply property may still change.
// - yes: The message was successfully delivered to the mail store of the recipient.
// The smtpReply property is final.
// - no: Delivery to the recipient permanently failed. The smtpReply property is final.
// - unknown: The final delivery status is unknown, (e.g., it was relayed to an external
// machine and no further information is available).
// The smtpReply property may still change if a DSN arrives.
Delivered DeliveryStatusDelivered `json:"delivered"`
// Represents whether the message has been displayed to the recipient.
//
// This MUST be one of the following values:
// - unknown: The display status is unknown. This is the initial value.
// - yes: The recipients system claims the message content has been displayed to the recipient.
// Note that there is no guarantee that the recipient has noticed, read, or understood the content.
Displayed DeliveryStatusDisplayed `json:"displayed"`
}
type EmailSubmission struct {
// The id of the EmailSubmission (server-set).
Id string `json:"id"`
// The id of the Identity to associate with this submission.
IdentityId string `json:"identityId"`
// The id of the Email to send.
//
// The Email being sent does not have to be a draft, for example, when “redirecting” an existing Email
// to a different address.
EmailId string `json:"emailId"`
// The Thread id of the Email to send (server-set).
//
// This is set by the server to the threadId property of the Email referenced by the emailId.
ThreadId string `json:"threadId"`
// Information for use when sending via SMTP.
//
// If the envelope property is null or omitted on creation, the server MUST generate this from the
// referenced Email as follows:
//
// - mailFrom: The email address in the Sender header field, if present; otherwise,
// its the email address in the From header field, if present.
// In either case, no parameters are added.
// - rcptTo: The deduplicated set of email addresses from the To, Cc, and Bcc header fields,
// if present, with no parameters for any of them.
Envelope *Envelope `json:"envelope,omitempty"`
// The date the submission was/will be released for delivery (server-set).
SendAt time.Time `json:"sendAt,omitzero"`
// This represents whether the submission may be canceled (server-set).
//
// This is server set on create and MUST be one of the following values:
//
// - pending: It may be possible to cancel this submission.
// - final: The message has been relayed to at least one recipient in a manner that cannot be
// recalled. It is no longer possible to cancel this submission.
// - canceled: The submission was canceled and will not be delivered to any recipient.
UndoStatus EmailSubmissionUndoStatus `json:"undoStatus"`
// This represents the delivery status for each of the submissions recipients, if known (server-set).
//
// This property MAY not be supported by all servers, in which case it will remain null.
//
// Servers that support it SHOULD update the EmailSubmission object each time the status of any of
// the recipients changes, even if some recipients are still being retried.
//
// This value is a map from the email address of each recipient to a DeliveryStatus object.
DeliveryStatus map[string]DeliveryStatus `json:"deliveryStatus"`
// A list of blob ids for DSNs [RFC3464] received for this submission,
// in order of receipt, oldest first (server-set) .
//
// The blob is the whole MIME message (with a top-level content-type of multipart/report), as received.
//
// [RFC3464]: https://datatracker.ietf.org/doc/html/rfc3464
DsnBlobIds []string `json:"dsnBlobIds,omitempty"`
// A list of blob ids for MDNs [RFC8098] received for this submission,
// in order of receipt, oldest first (server-set).
//
// The blob is the whole MIME message (with a top-level content-type of multipart/report), as received.
//
// [RFC8098]: https://datatracker.ietf.org/doc/html/rfc8098
MdnBlobIds []string `json:"mdnBlobIds,omitempty"`
}
type EmailSubmissionGetRefCommand struct {
// The id of the account to use.
AccountId string `json:"accountId"`
// The ids of the EmailSubmission objects to return.
//
// If null, then all records of the data type are returned, if this is supported for that data
// type and the number of records does not exceed the maxObjectsInGet limit.
IdRef *ResultReference `json:"#ids,omitempty"`
// If supplied, only the properties listed in the array are returned for each EmailSubmission object.
//
// If null, all properties of the object are returned. The id property of the object is always returned,
// even if not explicitly requested. If an invalid property is requested, the call MUST be rejected
// with an invalidArguments error.
Properties []string `json:"properties,omitempty"`
}
type EmailSubmissionGetResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
// A (preferably short) string representing the state on the server for all the data
// of this type in the account (not just the objects returned in this call).
//
// If the data changes, this string MUST change. If the EmailSubmission data is unchanged,
// servers SHOULD return the same state string on subsequent requests for this data type.
//
// When a client receives a response with a different state string to a previous call,
// it MUST either throw away all currently cached objects for the type or call
// EmailSubmission/changes to get the exact changes.
State State `json:"state"`
// An array of the EmailSubmission objects requested.
//
// This is the empty array if no objects were found or if the ids argument passed in
// was also an empty array.
//
// The results MAY be in a different order to the ids in the request arguments.
// If an identical id is included more than once in the request, the server MUST only
// include it once in either the list or the notFound argument of the response.
List []EmailSubmission `json:"list,omitempty"`
// This array contains the ids passed to the method for records that do not exist.
//
// The array is empty if all requested ids were found or if the ids argument passed in was
// either null or an empty array.
NotFound []string `json:"notFound,omitempty"`
}
// Patch Object.
//
// Example:
//
// - moves it from the drafts folder (which has Mailbox id “7cb4e8ee-df87-4757-b9c4-2ea1ca41b38e”)
// to the sent folder (which we presume has Mailbox id “73dbcb4b-bffc-48bd-8c2a-a2e91ca672f6”)
//
// - removes the $draft flag and
//
// {
// "mailboxIds/7cb4e8ee-df87-4757-b9c4-2ea1ca41b38e": null,
// "mailboxIds/73dbcb4b-bffc-48bd-8c2a-a2e91ca672f6": true,
// "keywords/$draft": null
// }
type PatchObject map[string]any
// same as EmailSubmission but without the server-set attributes
type EmailSubmissionCreate struct {
// The id of the Identity to associate with this submission.
IdentityId string `json:"identityId"`
// The id of the Email to send.
//
// The Email being sent does not have to be a draft, for example, when “redirecting” an existing
// Email to a different address.
EmailId string `json:"emailId"`
// Information for use when sending via SMTP.
Envelope *Envelope `json:"envelope,omitempty"`
}
type EmailSubmissionSetCommand struct {
AccountId string `json:"accountId"`
Create map[string]EmailSubmissionCreate `json:"create,omitempty"`
OldState State `json:"oldState,omitempty"`
NewState State `json:"newState,omitempty"`
// A map of EmailSubmission id to an object containing properties to update on the Email object
// referenced by the EmailSubmission if the create/update/destroy succeeds.
//
// (For references to EmailSubmissions created in the same “/set” invocation, this is equivalent
// to a creation-reference, so the id will be the creation id prefixed with a #.)
OnSuccessUpdateEmail map[string]PatchObject `json:"onSuccessUpdateEmail,omitempty"`
// A list of EmailSubmission ids for which the Email with the corresponding emailId should be destroyed
// if the create/update/destroy succeeds.
//
// (For references to EmailSubmission creations, this is equivalent to a creation-reference so the
// id will be the creation id prefixed with a #.)
OnSuccessDestroyEmail []string `json:"onSuccessDestroyEmail,omitempty"`
}
type CreatedEmailSubmission struct {
Id string `json:"id"`
}
type EmailSubmissionSetResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
// This is the sinceState argument echoed back; its the state from which the server is returning changes.
OldState State `json:"oldState"`
// This is the state the client will be in after applying the set of changes to the old state.
NewState State `json:"newState"`
// If true, the client may call EmailSubmission/changes again with the newState returned to get further
// updates.
//
// If false, newState is the current server state.
HasMoreChanges bool `json:"hasMoreChanges"`
// An array of ids for records that have been created since the old state.
Created map[string]CreatedEmailSubmission `json:"created,omitempty"`
// A map of the creation id to a SetError object for each record that failed to be created, or
// null if all successful.
NotCreated map[string]SetError `json:"notCreated,omitempty"`
// TODO(pbleser-oc) add updated and destroyed when they are needed
}
type ObjectType string
const (
VacationResponseType ObjectType = "VacationResponse"
EmailType ObjectType = "Email"
)
type Command string
type Invocation struct {
Command Command
Parameters any
Tag string
}
func invocation(command Command, parameters any, tag string) Invocation {
return Invocation{
Command: command,
Parameters: parameters,
Tag: tag,
}
}
type Request struct {
// The set of capabilities the client wishes to use.
//
// The client MAY include capability identifiers even if the method calls it makes do not utilise those capabilities.
// The server advertises the set of specifications it supports in the Session object (see [Section 2]), as keys on
// the capabilities property.
//
// [Section 2]: https://jmap.io/spec-core.html#the-jmap-session-resource
Using []string `json:"using"`
// An array of method calls to process on the server.
//
// The method calls MUST be processed sequentially, in order.
MethodCalls []Invocation `json:"methodCalls"`
// A map of a (client-specified) creation id to the id the server assigned when a record was successfully created (optional).
CreatedIds map[string]string `json:"createdIds,omitempty"`
}
type Response struct {
// An array of responses, in the same format as the methodCalls on the Request object.
// The output of the methods MUST be added to the methodResponses array in the same order that the methods are processed.
MethodResponses []Invocation `json:"methodResponses"`
// A map of a (client-specified) creation id to the id the server assigned when a record was successfully created.
//
// Optional; only returned if given in the request.
//
// This MUST include all creation ids passed in the original createdIds parameter of the Request object, as well as any
// additional ones added for newly created records.
CreatedIds map[string]string `json:"createdIds,omitempty"`
// The current value of the “state” string on the Session object, as described in [Section 2].
// Clients may use this to detect if this object has changed and needs to be refetched.
//
// [Section 2]: https://jmap.io/spec-core.html#the-jmap-session-resource
SessionState SessionState `json:"sessionState"`
}
type EmailQueryResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
// A string encoding the current state of the query on the server.
//
// This string MUST change if the results of the query (i.e., the matching ids and their sort order) have changed.
// The queryState string MAY change if something has changed on the server, which means the results may have changed
// but the server doesnt know for sure.
//
// The queryState string only represents the ordered list of ids that match the particular query (including its sort/filter).
// There is no requirement for it to change if a property on an object matching the query changes but the query results are unaffected
// (indeed, it is more efficient if the queryState string does not change in this case).
//
// The queryState string only has meaning when compared to future responses to a query with the same type/sort/filter or when used with
// /queryChanges to fetch changes.
//
// Should a client receive back a response with a different queryState string to a previous call, it MUST either throw away the currently
// cached query and fetch it again (note, this does not require fetching the records again, just the list of ids) or call
// Email/queryChanges to get the difference.
QueryState State `json:"queryState"`
// This is true if the server supports calling Email/queryChanges with these filter/sort parameters.
//
// Note, this does not guarantee that the Email/queryChanges call will succeed, as it may only be possible for a limited time
// afterwards due to server internal implementation details.
CanCalculateChanges bool `json:"canCalculateChanges"`
// The zero-based index of the first result in the ids array within the complete list of query results.
Position uint `json:"position"`
// The list of ids for each Email in the query results, starting at the index given by the position argument of this
// response and continuing until it hits the end of the results or reaches the limit number of ids.
//
// If position is >= total, this MUST be the empty list.
Ids []string `json:"ids"`
// The total number of Emails in the results (given the filter).
//
// Only if requested.
//
// This argument MUST be omitted if the calculateTotal request argument is not true.
Total uint `json:"total,omitempty,omitzero"`
// The limit enforced by the server on the maximum number of results to return (if set by the server).
//
// This is only returned if the server set a limit or used a different limit than that given in the request.
Limit uint `json:"limit,omitempty,omitzero"`
}
type EmailGetResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
// A (preferably short) string representing the state on the server for all the data of this type
// in the account (not just the objects returned in this call).
//
// If the data changes, this string MUST change.
// If the Email data is unchanged, servers SHOULD return the same state string on subsequent requests for this data type.
State State `json:"state"`
// An array of the Email objects requested.
//
// This is the empty array if no objects were found or if the ids argument passed in was also an empty array.
//
// The results MAY be in a different order to the ids in the request arguments.
//
// If an identical id is included more than once in the request, the server MUST only include it once in either
// the list or the notFound argument of the response.
List []Email `json:"list"`
// This array contains the ids passed to the method for records that do not exist.
//
// The array is empty if all requested ids were found or if the ids argument passed in was either null or an empty array.
NotFound []any `json:"notFound"`
}
type EmailChangesResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
// This is the sinceState argument echoed back; its the state from which the server is returning changes.
OldState State `json:"oldState"`
// This is the state the client will be in after applying the set of changes to the old state.
NewState State `json:"newState"`
// If true, the client may call Email/changes again with the newState returned to get further updates.
// If false, newState is the current server state.
HasMoreChanges bool `json:"hasMoreChanges"`
// An array of ids for records that have been created since the old state.
Created []string `json:"created,omitempty"`
// An array of ids for records that have been updated since the old state.
Updated []string `json:"updated,omitempty"`
// An array of ids for records that have been destroyed since the old state.
Destroyed []string `json:"destroyed,omitempty"`
}
type MailboxGetResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
// A (preferably short) string representing the state on the server for all the data of this type in the account
// (not just the objects returned in this call).
// If the data changes, this string MUST change.
// If the Mailbox data is unchanged, servers SHOULD return the same state string on subsequent requests for this data type.
// When a client receives a response with a different state string to a previous call, it MUST either throw away all currently
// cached objects for the type or call Foo/changes to get the exact changes.
State State `json:"state"`
// An array of the Mailbox objects requested.
// This is the empty array if no objects were found or if the ids argument passed in was also an empty array.
// The results MAY be in a different order to the ids in the request arguments.
// If an identical id is included more than once in the request, the server MUST only include it once in either
// the list or the notFound argument of the response.
List []Mailbox `json:"list"`
// This array contains the ids passed to the method for records that do not exist.
// The array is empty if all requested ids were found or if the ids argument passed in was either null or an empty array.
NotFound []any `json:"notFound"`
}
type MailboxChangesResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
// This is the sinceState argument echoed back; its the state from which the server is returning changes.
OldState State `json:"oldState"`
// This is the state the client will be in after applying the set of changes to the old state.
NewState State `json:"newState"`
// If true, the client may call Mailbox/changes again with the newState returned to get further updates.
//
// If false, newState is the current server state.
HasMoreChanges bool `json:"hasMoreChanges"`
// An array of ids for records that have been created since the old state.
Created []string `json:"created,omitempty"`
// An array of ids for records that have been updated since the old state.
Updated []string `json:"updated,omitempty"`
// An array of ids for records that have been destroyed since the old state.
Destroyed []string `json:"destroyed,omitempty"`
// If only the “totalEmails”, “unreadEmails”, “totalThreads”, and/or “unreadThreads” Mailbox properties have
// changed since the old state, this will be the list of properties that may have changed.
//
// If the server is unable to tell if only counts have changed, it MUST just be null.
UpdatedProperties []string `json:"updatedProperties,omitempty"`
}
type MailboxQueryResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
// A string encoding the current state of the query on the server.
//
// This string MUST change if the results of the query (i.e., the matching ids and their sort order) have changed.
// The queryState string MAY change if something has changed on the server, which means the results may have
// changed but the server doesnt know for sure.
//
// The queryState string only represents the ordered list of ids that match the particular query (including its
// sort/filter). There is no requirement for it to change if a property on an object matching the query changes
// but the query results are unaffected (indeed, it is more efficient if the queryState string does not change
// in this case). The queryState string only has meaning when compared to future responses to a query with the
// same type/sort/filter or when used with /queryChanges to fetch changes.
//
// Should a client receive back a response with a different queryState string to a previous call, it MUST either
// throw away the currently cached query and fetch it again (note, this does not require fetching the records
// again, just the list of ids) or call Mailbox/queryChanges to get the difference.
QueryState State `json:"queryState"`
// This is true if the server supports calling Mailbox/queryChanges with these filter/sort parameters.
//
// Note, this does not guarantee that the Mailbox/queryChanges call will succeed, as it may only be possible for
// a limited time afterwards due to server internal implementation details.
CanCalculateChanges bool `json:"canCalculateChanges"`
// The zero-based index of the first result in the ids array within the complete list of query results.
Position int `json:"position"`
// The list of ids for each Mailbox in the query results, starting at the index given by the position argument
// of this response and continuing until it hits the end of the results or reaches the limit number of ids.
//
// If position is >= total, this MUST be the empty list.
Ids []string `json:"ids"`
// The total number of Mailbox in the results (given the filter) (only if requested).
//
// This argument MUST be omitted if the calculateTotal request argument is not true.
Total int `json:"total,omitzero"`
// The limit enforced by the server on the maximum number of results to return (if set by the server).
//
// This is only returned if the server set a limit or used a different limit than that given in the request.
Limit int `json:"limit,omitzero"`
}
type EmailBodyStructure struct {
Type string `json:"type"`
PartId string `json:"partId"`
Other map[string]any `mapstructure:",remain"`
}
type EmailCreate struct {
// The set of Mailbox ids this Email belongs to.
//
// An Email in the mail store MUST belong to one or more Mailboxes at all times
// (until it is destroyed).
//
// The set is represented as an object, with each key being a Mailbox id.
// The value for each key in the object MUST be true.
MailboxIds map[string]bool `json:"mailboxIds,omitempty"`
// A set of keywords that apply to the Email.
//
// The set is represented as an object, with the keys being the keywords.
// The value for each key in the object MUST be true.
Keywords map[string]bool `json:"keywords,omitempty"`
// The ["From:" field] specifies the author(s) of the message, that is, the mailbox(es)
// of the person(s) or system(s) responsible for the writing of the message
//
// ["From:" field]: https://www.rfc-editor.org/rfc/rfc5322.html#section-3.6.2
From []EmailAddress `json:"from,omitempty"`
// The "Subject:" field contains a short string identifying the topic of the message.
Subject string `json:"subject,omitempty"`
// The date the Email was received by the message store.
//
// (default: time of most recent Received header, or time of import on server if none).
ReceivedAt time.Time `json:"receivedAt,omitzero"`
// The origination date specifies the date and time at which the creator of the message indicated that
// the message was complete and ready to enter the mail delivery system.
//
// For instance, this might be the time that a user pushes the "send" or "submit" button in an
// application program.
//
// In any case, it is specifically not intended to convey the time that the message is actually transported,
// but rather the time at which the human or other creator of the message has put the message into its final
// form, ready for transport.
//
// (For example, a portable computer user who is not connected to a network might queue a message for delivery.
// The origination date is intended to contain the date and time that the user queued the message, not the time
// when the user connected to the network to send the message.)
SentAt time.Time `json:"sentAt,omitzero"`
// This is the full MIME structure of the message body, without recursing into message/rfc822 or message/global parts.
//
// Note that EmailBodyParts may have subParts if they are of type multipart/*.
BodyStructure EmailBodyStructure `json:"bodyStructure"`
// This is a map of partId to an EmailBodyValue object for none, some, or all text/* parts.
BodyValues map[string]EmailBodyValue `json:"bodyValues,omitempty"`
}
type EmailUpdate map[string]any
type EmailSetCommand struct {
AccountId string `json:"accountId"`
Create map[string]EmailCreate `json:"create,omitempty"`
Update map[string]EmailUpdate `json:"update,omitempty"`
Destroy []string `json:"destroy,omitempty"`
}
type EmailSetResponse struct {
// The id of the account used for the call.
AccountId string `json:"accountId"`
// The state string that would have been returned by Email/get before making the
// requested changes, or null if the server doesnt know what the previous state
// string was.
OldState State `json:"oldState,omitempty"`
// The state string that will now be returned by Email/get.
NewState State `json:"newState"`
// A map of the creation id to an object containing any properties of the created Email object
// that were not sent by the client.
//
// This includes all server-set properties (such as the id in most object types) and any properties
// that were omitted by the client and thus set to a default by the server.
//
// This argument is null if no Email objects were successfully created.
Created map[string]Email `json:"created,omitempty"`
// The keys in this map are the ids of all Emails that were successfully updated.
//
// The value for each id is an Email object containing any property that changed in a way not
// explicitly requested by the PatchObject sent to the server, or null if none.
//
// This lets the client know of any changes to server-set or computed properties.
//
// This argument is null if no Email objects were successfully updated.
Updated map[string]Email `json:"updated,omitempty"`
// A list of Email ids for records that were successfully destroyed, or null if none.
Destroyed []string `json:"destroyed,omitempty"`
// A map of the creation id to a SetError object for each record that failed to be created,
// or null if all successful.
NotCreated map[string]SetError `json:"notCreated,omitempty"`
// A map of the Email id to a SetError object for each record that failed to be updated,
// or null if all successful.
NotUpdated map[string]SetError `json:"notUpdated,omitempty"`
// A map of the Email id to a SetError object for each record that failed to be destroyed,
// or null if all successful.
NotDestroyed map[string]SetError `json:"notDestroyed,omitempty"`
}
const (
EmailMimeType = "message/rfc822"
)
type EmailImport struct {
// The id of the blob containing the raw message [RFC5322].
//
// [RFC5322]: https://www.rfc-editor.org/rfc/rfc5322.html
BlobId string `json:"blobId"`
// The ids of the Mailboxes to assign this Email to.
//
// At least one Mailbox MUST be given.
MailboxIds map[string]bool `json:"mailboxIds"`
// The keywords to apply to the Email.
Keywords map[string]bool `json:"keywords"`
// (default: time of most recent Received header, or time of import
// on server if none) The receivedAt date to set on the Email.
ReceivedAt time.Time `json:"receivedAt"`
}
type EmailImportCommand struct {
AccountId string `json:"accountId"`
// This is a state string as returned by the Email/get method.
//
// If supplied, the string must match the current state of the account referenced
// by the accountId; otherwise, the method will be aborted and a stateMismatch
// error returned.
//
// If null, any changes will be applied to the current state.
IfInState string `json:"ifInState,omitempty"`
// A map of creation id (client specified) to EmailImport objects.
Emails map[string]EmailImport `json:"emails"`
}
// Successfully imported Email.
type ImportedEmail struct {
// Id of the successfully imported Email.
Id string `json:"id"`
// Blob id of the successfully imported Email.
BlobId string `json:"blobId"`
// Thread id of the successfully imported Email.
ThreadId string `json:"threadId"`
// Size of the successfully imported Email.
Size int `json:"size"`
}
type EmailImportResponse struct {
// The id of the account used for this call.
AccountId string `json:"accountId"`
// The state string that would have been returned by Email/get on this account
// before making the requested changes, or null if the server doesnt know
// what the previous state string was.
OldState State `json:"oldState"`
// The state string that will now be returned by Email/get on this account.
NewState State `json:"newState"`
// A map of the creation id to an object containing the id, blobId, threadId,
// and size properties for each successfully imported Email, or null if none.
Created map[string]ImportedEmail `json:"created"`
// A map of the creation id to a SetError object for each Email that failed to
// be created, or null if all successful.
NotCreated map[string]SetError `json:"notCreated"`
}
// Replies are grouped together with the original message to form a Thread.
//
// In JMAP, a Thread is simply a flat list of Emails, ordered by date.
//
// Every Email MUST belong to a Thread, even if it is the only Email in the Thread.
type Thread struct {
// The id of the Thread.
Id string
// The ids of the Emails in the Thread, sorted by the receivedAt date of the Email,
// oldest first.
//
// If two Emails have an identical date, the sort is server dependent but MUST be
// stable (sorting by id is recommended).
EmailIds []string
}
type ThreadGetCommand struct {
AccountId string `json:"accountId"`
Ids []string `json:"ids,omitempty"`
}
type ThreadGetResponse struct {
AccountId string
State State
List []Thread
NotFound []any
}
type IdentityGetCommand struct {
AccountId string `json:"accountId"`
Ids []string `json:"ids,omitempty"`
}
type Identity struct {
// The id of the Identity.
Id string `json:"id"`
// The “From” name the client SHOULD use when creating a new Email from this Identity.
Name string `json:"name,omitempty"`
// The “From” email address the client MUST use when creating a new Email from this Identity.
//
// If the mailbox part of the address (the section before the “@”) is the single character
// * (e.g., *@example.com) then the client may use any valid address ending in that domain
// (e.g., foo@example.com).
Email string `json:"email,omitempty"`
// The Reply-To value the client SHOULD set when creating a new Email from this Identity.
ReplyTo string `json:"replyTo,omitempty"`
// The Bcc value the client SHOULD set when creating a new Email from this Identity.
Bcc []EmailAddress `json:"bcc,omitempty"`
// A signature the client SHOULD insert into new plaintext messages that will be sent from
// this Identity.
//
// Clients MAY ignore this and/or combine this with a client-specific signature preference.
TextSignature string `json:"textSignature,omitempty"`
// A signature the client SHOULD insert into new HTML messages that will be sent from this
// Identity.
//
// This text MUST be an HTML snippet to be inserted into the <body></body> section of the HTML.
//
// Clients MAY ignore this and/or combine this with a client-specific signature preference.
HtmlSignature string `json:"htmlSignature,omitempty"`
// Is the user allowed to delete this Identity?
//
// Servers may wish to set this to false for the users username or other default address.
//
// Attempts to destroy an Identity with mayDelete: false will be rejected with a standard
// forbidden SetError.
MayDelete bool `json:"mayDelete"`
}
type IdentityGetResponse struct {
AccountId string `json:"accountId"`
State State `json:"state"`
List []Identity `json:"list,omitempty"`
NotFound []string `json:"notFound,omitempty"`
}
type VacationResponseGetCommand struct {
AccountId string `json:"accountId"`
}
// Vacation Response
//
// A vacation response sends an automatic reply when a message is delivered to the mail store,
// informing the original sender that their message may not be read for some time.
//
// Automated message sending can produce undesirable behaviour.
// To avoid this, implementors MUST follow the recommendations set forth in [RFC3834].
//
// The VacationResponse object represents the state of vacation-response-related settings for an account.
//
// [RFC3834]: https://www.rfc-editor.org/rfc/rfc3834.html
type VacationResponse struct {
// The id of the object.
// There is only ever one VacationResponse object, and its id is "singleton"
Id string `json:"id,omitempty"`
// Should a vacation response be sent if a message arrives between the "fromDate" and "toDate"?
IsEnabled bool `json:"isEnabled"`
// If "isEnabled" is true, messages that arrive on or after this date-time (but before the "toDate" if defined) should receive the
// user's vacation response. If null, the vacation response is effective immediately.
FromDate time.Time `json:"fromDate,omitzero"`
// If "isEnabled" is true, messages that arrive before this date-time but on or after the "fromDate" if defined) should receive the
// user's vacation response. If null, the vacation response is effective indefinitely.
ToDate time.Time `json:"toDate,omitzero"`
// The subject that will be used by the message sent in response to messages when the vacation response is enabled.
// If null, an appropriate subject SHOULD be set by the server.
Subject string `json:"subject,omitempty"`
// The plaintext body to send in response to messages when the vacation response is enabled.
// If this is null, the server SHOULD generate a plaintext body part from the "htmlBody" when sending vacation responses
// but MAY choose to send the response as HTML only. If both "textBody" and "htmlBody" are null, an appropriate default
// body SHOULD be generated for responses by the server.
TextBody string `json:"textBody,omitempty"`
// The HTML body to send in response to messages when the vacation response is enabled.
// If this is null, the server MAY choose to generate an HTML body part from the "textBody" when sending vacation responses
// or MAY choose to send the response as plaintext only.
HtmlBody string `json:"htmlBody,omitempty"`
}
type VacationResponseGetResponse struct {
// The identifier of the account this response pertains to.
AccountId string `json:"accountId"`
// A string representing the state on the server for all the data of this type in the account
// (not just the objects returned in this call).
//
// If the data changes, this string MUST change. If the data is unchanged, servers SHOULD return the same state string
// on subsequent requests for this data type.
State State `json:"state,omitempty"`
// An array of VacationResponse objects.
List []VacationResponse `json:"list,omitempty"`
// Contains identifiers of requested objects that were not found.
NotFound []any `json:"notFound,omitempty"`
}
type VacationResponseSetCommand struct {
AccountId string `json:"accountId"`
IfInState string `json:"ifInState,omitempty"`
Create map[string]VacationResponse `json:"create,omitempty"`
Update map[string]PatchObject `json:"update,omitempty"`
Destroy []string `json:"destroy,omitempty"`
}
type VacationResponseSetResponse struct {
AccountId string `json:"accountId"`
OldState State `json:"oldState,omitempty"`
NewState State `json:"newState,omitempty"`
Created map[string]VacationResponse `json:"created,omitempty"`
Updated map[string]VacationResponse `json:"updated,omitempty"`
Destroyed []string `json:"destroyed,omitempty"`
NotCreated map[string]SetError `json:"notCreated,omitempty"`
NotUpdated map[string]SetError `json:"notUpdated,omitempty"`
NotDestroyed map[string]SetError `json:"notDestroyed,omitempty"`
}
// One of these attributes must be set, but not both.
type DataSourceObject struct {
DataAsText string `json:"data:asText,omitempty"`
DataAsBase64 string `json:"data:asBase64,omitempty"`
}
type UploadObject struct {
Data []DataSourceObject `json:"data"`
Type string `json:"type,omitempty"`
}
type BlobUploadCommand struct {
AccountId string `json:"accountId"`
Create map[string]UploadObject `json:"create"`
}
type BlobUploadCreateResult struct {
Id string `json:"id"`
Type string `json:"type,omitempty"`
Size int `json:"size"`
}
type BlobUploadResponse struct {
AccountId string `json:"accountId"`
Created map[string]BlobUploadCreateResult `json:"created"`
}
const (
BlobPropertyDataAsText = "data:asText"
BlobPropertyDataAsBase64 = "data:asBase64"
BlobPropertyData = "data"
BlobPropertySize = "size"
// https://www.iana.org/assignments/http-digest-hash-alg/http-digest-hash-alg.xhtml
BlobPropertyDigestSha256 = "digest:sha256"
// https://www.iana.org/assignments/http-digest-hash-alg/http-digest-hash-alg.xhtml
BlobPropertyDigestSha512 = "digest:sha512"
)
type BlobGetCommand struct {
AccountId string `json:"accountId"`
Ids []string `json:"ids,omitempty"`
Properties []string `json:"properties,omitempty"`
Offset int `json:"offset,omitzero"`
Length int `json:"length,omitzero"`
}
type BlobGetRefCommand struct {
AccountId string `json:"accountId"`
IdRef *ResultReference `json:"#ids,omitempty"`
Properties []string `json:"properties,omitempty"`
Offset int `json:"offset,omitzero"`
Length int `json:"length,omitzero"`
}
type Blob struct {
// The unique identifier of the blob.
Id string `json:"id"`
// (raw octets, must be UTF-8)
DataAsText string `json:"data:asText,omitempty"`
// (base64 representation of octets)
DataAsBase64 string `json:"data:asBase64,omitempty"`
// The base64 encoding of the digest of the octets in the selected range,
// calculated using the SHA-256 algorithm.
DigestSha256 string `json:"digest:sha256,omitempty"`
// The base64 encoding of the digest of the octets in the selected range,
// calculated using the SHA-512 algorithm.
DigestSha512 string `json:"digest:sha512,omitempty"`
// If an encoding problem occured.
IsEncodingProblem bool `json:"isEncodingProblem,omitzero"`
// When requesting a range: the isTruncated property in the result MUST be
// set to true to tell the client that the requested range could not be fully satisfied.
IsTruncated bool `json:"isTruncated,omitzero"`
// The number of octets in the entire blob.
Size int `json:"size"`
}
// Picks the best digest if available, or ""
func (b *Blob) Digest() string {
if b.DigestSha512 != "" {
return b.DigestSha512
} else if b.DigestSha256 != "" {
return b.DigestSha256
} else {
return ""
}
}
type BlobGetResponse struct {
AccountId string `json:"accountId"`
State State `json:"state,omitempty"`
List []Blob `json:"list,omitempty"`
NotFound []any `json:"notFound,omitempty"`
}
type BlobDownload struct {
Body io.ReadCloser
Size int
Type string
ContentDisposition string
CacheControl string
}
// When doing a search on a String property, the client may wish to show the relevant
// section of the body that matches the search as a preview and to highlight any
// matching terms in both this and the subject of the Email.
//
// Search snippets represent this data.
//
// What is a relevant section of the body for preview is server defined. If the server is
// unable to determine search snippets, it MUST return null for both the subject and preview
// properties.
//
// Note that unlike most data types, a SearchSnippet DOES NOT have a property called id.
type SearchSnippet struct {
// The Email id the snippet applies to.
EmailId string `json:"emailId"`
// If text from the filter matches the subject, this is the subject of the Email
// with the following transformations:
//
// 1. Any instance of the following three characters MUST be replaced by an
// appropriate HTML entity: & (ampersand), < (less-than sign), and > (greater-than sign)
// HTML. Other characters MAY also be replaced with an HTML entity form.
// 2. The matching words/phrases from the filter are wrapped in HTML <mark></mark> tags.
//
// If the subject does not match text from the filter, this property is null.
Subject string `json:"subject,omitempty"`
// If text from the filter matches the plaintext or HTML body, this is the
// relevant section of the body (converted to plaintext if originally HTML),
// with the same transformations as the subject property.
//
// It MUST NOT be bigger than 255 octets in size.
//
// If the body does not contain a match for the text from the filter, this property is null.
Preview string `json:"preview,omitempty"`
}
type SearchSnippetGetRefCommand struct {
// The id of the account to use.
AccountId string `json:"accountId"`
// The same filter as passed to Email/query.
Filter EmailFilterElement `json:"filter,omitempty"`
// The ids of the Emails to fetch snippets for.
EmailIdRef *ResultReference `json:"#emailIds,omitempty"`
}
type SearchSnippetGetResponse struct {
AccountId string `json:"accountId"`
List []SearchSnippet `json:"list,omitempty"`
NotFound []string `json:"notFound,omitempty"`
}
const (
CommandBlobGet Command = "Blob/get"
CommandBlobUpload Command = "Blob/upload"
CommandEmailGet Command = "Email/get"
CommandEmailQuery Command = "Email/query"
CommandEmailChanges Command = "Email/changes"
CommandEmailSet Command = "Email/set"
CommandEmailImport Command = "Email/import"
CommandEmailSubmissionGet Command = "EmailSubmission/get"
CommandEmailSubmissionSet Command = "EmailSubmission/set"
CommandThreadGet Command = "Thread/get"
CommandMailboxGet Command = "Mailbox/get"
CommandMailboxQuery Command = "Mailbox/query"
CommandMailboxChanges Command = "Mailbox/changes"
CommandIdentityGet Command = "Identity/get"
CommandVacationResponseGet Command = "VacationResponse/get"
CommandVacationResponseSet Command = "VacationResponse/set"
CommandSearchSnippetGet Command = "SearchSnippet/get"
)
var CommandResponseTypeMap = map[Command]func() any{
CommandBlobGet: func() any { return BlobGetResponse{} },
CommandBlobUpload: func() any { return BlobUploadResponse{} },
CommandMailboxQuery: func() any { return MailboxQueryResponse{} },
CommandMailboxGet: func() any { return MailboxGetResponse{} },
CommandMailboxChanges: func() any { return MailboxChangesResponse{} },
CommandEmailQuery: func() any { return EmailQueryResponse{} },
CommandEmailChanges: func() any { return EmailChangesResponse{} },
CommandEmailGet: func() any { return EmailGetResponse{} },
CommandEmailSubmissionGet: func() any { return EmailSubmissionGetResponse{} },
CommandEmailSubmissionSet: func() any { return EmailSubmissionSetResponse{} },
CommandThreadGet: func() any { return ThreadGetResponse{} },
CommandIdentityGet: func() any { return IdentityGetResponse{} },
CommandVacationResponseGet: func() any { return VacationResponseGetResponse{} },
CommandVacationResponseSet: func() any { return VacationResponseSetResponse{} },
CommandSearchSnippetGet: func() any { return SearchSnippetGetResponse{} },
}