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 doesn’t 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 account’s 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 user’s 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 client’s 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 client’s 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 user’s 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 comparator’s 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, it’s 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 . // // 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 . // // 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 part’s 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 it’s 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, it’s 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 it’s 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 1–255 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 recipient’s 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 recipient’s 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, // it’s 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 submission’s 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; it’s 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 doesn’t 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; it’s 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; it’s 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 doesn’t 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 doesn’t 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 doesn’t 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 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 user’s 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 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{} }, }