Testing a Gemini codegen run

wip

+12
.gitignore
···
+
# Python-generated files
+
__pycache__/
+
*.py[oc]
+
build/
+
dist/
+
wheels/
+
*.egg-info
+
+
# Virtual environments
+
.venv
+
api-key
+
output/_build
+1
.python-version
···
+
3.13
+1
README.md
···
+
Testing a codegen run with Gemini.
+6
main.py
···
+
def main():
+
print("Hello from gemini!")
+
+
+
if __name__ == "__main__":
+
main()
+24
output/dune
···
+
(library
+
(name jmap)
+
(public_name jmap)
+
(libraries yojson uri)
+
(modules_without_implementation
+
jmap
+
jmap_types
+
jmap_error
+
jmap_wire
+
jmap_session
+
jmap_methods
+
jmap_binary
+
jmap_push
+
jmap_email_types
+
jmap_mailbox
+
jmap_thread
+
jmap_email
+
jmap_search_snippet
+
jmap_identity
+
jmap_submission
+
jmap_vacation
+
jmap_email
+
jmap_unix
+
))
+1
output/dune-project
···
+
(lang dune 3.17)
+82
output/full_response.json
···
+
[
+
{
+
"filename": "dune-project",
+
"contents": "(lang dune 3.17)"
+
},
+
{
+
"filename": "dune",
+
"contents": "(library\n (name jmap)\n (public_name jmap)\n (libraries yojson uri)\n (modules_without_implementation\n jmap\n jmap_types\n jmap_error\n jmap_wire\n jmap_session\n jmap_methods\n jmap_binary\n jmap_push\n jmap_email_types\n jmap_mailbox\n jmap_thread\n jmap_email\n jmap_search_snippet\n jmap_identity\n jmap_submission\n jmap_vacation\n jmap_email\n jmap_unix\n ))"
+
},
+
{
+
"filename": "jmap_types.mli",
+
"contents": "(** Basic JMAP types as defined in RFC 8620. *)\n\n(** The Id data type.\n A string of 1 to 255 octets, using URL-safe base64 characters.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-1.2> RFC 8620, Section 1.2 *)\ntype id = string\n\n(** The Int data type.\n An integer in the range [-2^53+1, 2^53-1]. Represented as OCaml's standard [int].\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-1.3> RFC 8620, Section 1.3 *)\ntype jint = int\n\n(** The UnsignedInt data type.\n An integer in the range [0, 2^53-1]. Represented as OCaml's standard [int].\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-1.3> RFC 8620, Section 1.3 *)\ntype uint = int\n\n(** The Date data type.\n A string in RFC 3339 \"date-time\" format.\n Represented as a float using Unix time.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-1.4> RFC 8620, Section 1.4 *)\ntype date = float\n\n(** The UTCDate data type.\n A string in RFC 3339 \"date-time\" format, restricted to UTC (Z timezone).\n Represented as a float using Unix time.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-1.4> RFC 8620, Section 1.4 *)\ntype utc_date = float\n\n(** Represents a JSON object used as a map String -> V. *)\ntype 'v string_map = (string, 'v) Hashtbl.t\n\n(** Represents a JSON object used as a map Id -> V. *)\ntype 'v id_map = (id, 'v) Hashtbl.t\n\n(** Represents a JSON Pointer path with JMAP extensions.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.7> RFC 8620, Section 3.7 *)\ntype json_pointer = string"
+
},
+
{
+
"filename": "jmap_error.mli",
+
"contents": "(** JMAP Error Types. *)\n\nopen Jmap_types\n\n(** Problem details object for HTTP-level errors.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.6.1> RFC 8620, Section 3.6.1\n @see <https://www.rfc-editor.org/rfc/rfc7807.html> RFC 7807 *)\ntype problem_details = {\n problem_type : string; (** The \"type\" field from RFC 7807. *)\n status : int option; (** The \"status\" field. *)\n detail : string option; (** The \"detail\" field. *)\n limit : string option; (** For \"urn:ietf:params:jmap:error:limit\" *)\n other_fields : Yojson.Safe.t string_map; (** Catch-all for extra fields *)\n}\n\n(** Standard Method-level error types.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.6.2> RFC 8620, Section 3.6.2 *)\ntype method_error_type = [\n | `ServerUnavailable\n | `ServerFail\n | `ServerPartialFail\n | `UnknownMethod\n | `InvalidArguments\n | `InvalidResultReference\n | `Forbidden\n | `AccountNotFound\n | `AccountNotSupportedByMethod\n | `AccountReadOnly\n | `RequestTooLarge (* From /get *)\n | `CannotCalculateChanges (* From /changes, /queryChanges *)\n | `StateMismatch (* From /set, /copy *)\n | `AnchorNotFound (* From /query *)\n | `UnsupportedSort (* From /query *)\n | `UnsupportedFilter (* From /query *)\n | `TooManyChanges (* From /queryChanges *)\n | `FromAccountNotFound (* From /copy, Blob/copy *)\n | `FromAccountNotSupportedByMethod (* From /copy *)\n | `Other_method_error of string (* For future or custom errors *)\n]\n\n(** Description for method errors. May contain additional details.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.6.2> RFC 8620, Section 3.6.2 *)\ntype method_error_description = {\n description : string option;\n}\n\n(** Represents a method-level error response invocation part.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.6.2> RFC 8620, Section 3.6.2 *)\ntype method_error = {\n err_type : method_error_type;\n err_description : method_error_description option;\n}\n\n(** Standard SetError types.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.3> RFC 8620, Section 5.3 *)\ntype set_error_type = [\n | `Forbidden\n | `OverQuota\n | `TooLarge\n | `RateLimit\n | `NotFound\n | `InvalidPatch\n | `WillDestroy\n | `InvalidProperties\n | `Singleton\n | `AlreadyExists (* From /copy *)\n | `MailboxHasChild (* RFC 8621 *)\n | `MailboxHasEmail (* RFC 8621 *)\n | `BlobNotFound (* RFC 8621 *)\n | `TooManyKeywords (* RFC 8621 *)\n | `TooManyMailboxes (* RFC 8621 *)\n | `InvalidEmail (* RFC 8621 *)\n | `TooManyRecipients (* RFC 8621 *)\n | `NoRecipients (* RFC 8621 *)\n | `InvalidRecipients (* RFC 8621 *)\n | `ForbiddenMailFrom (* RFC 8621 *)\n | `ForbiddenFrom (* RFC 8621 *)\n | `ForbiddenToSend (* RFC 8621 *)\n | `CannotUnsend (* RFC 8621 *)\n | `Other_set_error of string (* For future or custom errors *)\n]\n\n(** SetError object.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.3> RFC 8620, Section 5.3 *)\ntype set_error = {\n set_err_type : set_error_type;\n set_err_description : string option;\n set_err_properties : string list option; (** For InvalidProperties *)\n set_err_existing_id : id option; (** For AlreadyExists *)\n set_err_max_recipients : uint option; (** For TooManyRecipients *)\n set_err_invalid_recipients : string list option; (** For InvalidRecipients *)\n set_err_max_size : uint option; (** For TooLarge *)\n set_err_not_found_blob_ids : id list option; (** For BlobNotFound *)\n}"
+
},
+
{
+
"filename": "jmap_wire.mli",
+
"contents": "(** JMAP Wire Protocol Structures (Request/Response). *)\n\nopen Jmap_types\nopen Jmap_error\n\n(** An invocation tuple within a request or response.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.2> RFC 8620, Section 3.2 *)\ntype invocation = {\n method_name : string;\n arguments : Yojson.Safe.t;\n method_call_id : string;\n}\n\n(** A reference to a previous method call's result.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.7> RFC 8620, Section 3.7 *)\ntype result_reference = {\n result_of : string; (** The method call id of the previous call *)\n name : string; (** The required response name *)\n path : json_pointer; (** JSON Pointer path *)\n}\n\n(** The Request object.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.3> RFC 8620, Section 3.3 *)\ntype request = {\n using : string list;\n method_calls : invocation list;\n created_ids : id id_map option;\n}\n\n(** A response invocation part, which can be a standard response or an error.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.4> RFC 8620, Section 3.4\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.6.2> RFC 8620, Section 3.6.2 *)\ntype response_invocation =\n | Response of invocation\n | Error of method_error * string (* string is the method_call_id *)\n\n(** The Response object.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.4> RFC 8620, Section 3.4 *)\ntype response = {\n method_responses : response_invocation list;\n created_ids : id id_map option;\n session_state : string;\n}"
+
},
+
{
+
"filename": "jmap_session.mli",
+
"contents": "(** JMAP Session Resource.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-2> RFC 8620, Section 2 *)\n\nopen Jmap_types\n\n(** Core capability information.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-2> RFC 8620, Section 2 *)\ntype core_capability = {\n max_size_upload : uint;\n max_concurrent_upload : uint;\n max_size_request : uint;\n max_concurrent_requests : uint;\n max_calls_in_request : uint;\n max_objects_in_get : uint;\n max_objects_in_set : uint;\n collation_algorithms : string list;\n}\n\n(** Account capability information.\n The value is capability-specific.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-2> RFC 8620, Section 2 *)\ntype account_capability_value = Yojson.Safe.t\n\n(** An Account object.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-2> RFC 8620, Section 2 *)\ntype account = {\n acc_name : string;\n is_personal : bool;\n is_read_only : bool;\n account_capabilities : account_capability_value string_map;\n}\n\n(** Server capability information.\n The value is capability-specific.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-2> RFC 8620, Section 2 *)\ntype server_capability_value = Yojson.Safe.t\n\n(** The Session object.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-2> RFC 8620, Section 2 *)\ntype session = {\n capabilities : server_capability_value string_map;\n accounts : account id_map;\n primary_accounts : id string_map;\n username : string;\n api_url : Uri.t;\n download_url : Uri.t; (** Must be a Level 1 URI Template *) \n upload_url : Uri.t; (** Must be a Level 1 URI Template *)\n event_source_url : Uri.t; (** Must be a Level 1 URI Template *)\n state : string;\n}\n\n(** Function to perform service autodiscovery.\n Returns the session URL if found.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-2.2> RFC 8620, Section 2.2 *)\nval discover : domain:string -> Uri.t option Lwt.t\n\n(** Function to fetch the session object from a given URL.\n Requires authentication handling (details TBD/outside this signature). *)\nval get_session : url:Uri.t -> session Lwt.t"
+
},
+
{
+
"filename": "jmap_methods.mli",
+
"contents": "(** Standard JMAP Methods and Core/echo.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-4> RFC 8620, Section 4\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5> RFC 8620, Section 5 *)\n\nopen Jmap_types\nopen Jmap_error\n\n(** Generic representation of a record type. Actual types defined elsewhere. *)\ntype generic_record\n\n(** Arguments for /get methods.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.1> RFC 8620, Section 5.1 *)\ntype 'record get_args = {\n get_account_id : id;\n get_ids : id list option;\n get_properties : string list option;\n}\n\n(** Response for /get methods.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.1> RFC 8620, Section 5.1 *)\ntype 'record get_response = {\n get_resp_account_id : id;\n get_resp_state : string;\n get_resp_list : 'record list;\n get_resp_not_found : id list;\n}\n\n(** Arguments for /changes methods.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.2> RFC 8620, Section 5.2 *)\ntype changes_args = {\n changes_account_id : id;\n changes_since_state : string;\n changes_max_changes : uint option;\n}\n\n(** Response for /changes methods.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.2> RFC 8620, Section 5.2 *)\ntype changes_response = {\n changes_resp_account_id : id;\n changes_resp_old_state : string;\n changes_resp_new_state : string;\n changes_resp_has_more_changes : bool;\n changes_resp_created : id list;\n changes_resp_updated : id list;\n changes_resp_destroyed : id list;\n changes_resp_updated_properties : string list option; (* Mailbox/changes specific *)\n}\n\n(** Patch object for /set update.\n A list of (JSON Pointer path, value) pairs.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.3> RFC 8620, Section 5.3 *)\ntype patch_object = (json_pointer * Yojson.Safe.t) list\n\n(** Arguments for /set methods.\n ['create_record] is the record type without server-set/immutable fields.\n ['update_record] is the patch object type (usually [patch_object]).\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.3> RFC 8620, Section 5.3 *)\ntype ('create_record, 'update_record) set_args = {\n set_account_id : id;\n set_if_in_state : string option;\n set_create : 'create_record id_map option;\n set_update : 'update_record id_map option;\n set_destroy : id list option;\n set_on_success_destroy_original : bool option; (* For /copy *)\n set_destroy_from_if_in_state : string option; (* For /copy *)\n set_on_destroy_remove_emails : bool option; (* For Mailbox/set *)\n}\n\n(** Response for /set methods.\n ['created_record_info] is the server-set info for created records.\n ['updated_record_info] is the server-set/computed info for updated records.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.3> RFC 8620, Section 5.3 *)\ntype ('created_record_info, 'updated_record_info) set_response = {\n set_resp_account_id : id;\n set_resp_old_state : string option;\n set_resp_new_state : string;\n set_resp_created : 'created_record_info id_map option;\n set_resp_updated : 'updated_record_info option id_map option;\n set_resp_destroyed : id list option;\n set_resp_not_created : set_error id_map option;\n set_resp_not_updated : set_error id_map option;\n set_resp_not_destroyed : set_error id_map option;\n}\n\n(** Arguments for /copy methods.\n ['copy_record_override] contains the record id and override properties.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.4> RFC 8620, Section 5.4 *)\ntype 'copy_record_override copy_args = {\n copy_from_account_id : id;\n copy_if_from_in_state : string option;\n copy_account_id : id;\n copy_if_in_state : string option;\n copy_create : 'copy_record_override id_map; (* Map from creation id *)\n copy_on_success_destroy_original : bool; (* default: false *)\n copy_destroy_from_if_in_state : string option;\n}\n\n(** Response for /copy methods.\n ['created_record_info] is the server-set info for the created copy.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.4> RFC 8620, Section 5.4 *)\ntype 'created_record_info copy_response = {\n copy_resp_from_account_id : id;\n copy_resp_account_id : id;\n copy_resp_old_state : string option;\n copy_resp_new_state : string;\n copy_resp_created : 'created_record_info id_map option;\n copy_resp_not_created : set_error id_map option;\n}\n\n(** Base filter operator type.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.5> RFC 8620, Section 5.5 *)\ntype filter_operator = {\n operator : [ `AND | `OR | `NOT ];\n conditions : filter list;\n}\n(** Represents either a filter operator or a filter condition.\n ['condition] is the type of the specific FilterCondition object.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.5> RFC 8620, Section 5.5 *)\nand filter = \n | Filter_operator of filter_operator\n | Filter_condition of Yojson.Safe.t (* Placeholder for specific condition type *)\n\n(** Comparator for sorting.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.5> RFC 8620, Section 5.5 *)\ntype comparator = {\n property : string;\n is_ascending : bool option; (* default: true *)\n collation : string option;\n keyword : string option; (* For Email/query keyword sorts *)\n other_fields : Yojson.Safe.t string_map; (** Catch-all for extra fields *)\n}\n\n(** Arguments for /query methods.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.5> RFC 8620, Section 5.5 *)\ntype query_args = {\n query_account_id : id;\n query_filter : filter option;\n query_sort : comparator list option;\n query_position : jint option; (* default: 0 *)\n query_anchor : id option;\n query_anchor_offset : jint option; (* default: 0 *)\n query_limit : uint option;\n query_calculate_total : bool option; (* default: false *)\n query_collapse_threads : bool option; (* For Email/query *)\n query_sort_as_tree : bool option; (* For Mailbox/query *)\n query_filter_as_tree : bool option; (* For Mailbox/query *)\n}\n\n(** Response for /query methods.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.5> RFC 8620, Section 5.5 *)\ntype query_response = {\n query_resp_account_id : id;\n query_resp_query_state : string;\n query_resp_can_calculate_changes : bool;\n query_resp_position : uint;\n query_resp_ids : id list;\n query_resp_total : uint option;\n query_resp_limit : uint option;\n}\n\n(** Item indicating an added record in /queryChanges.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.6> RFC 8620, Section 5.6 *)\ntype added_item = {\n added_item_id : id;\n added_item_index : uint;\n}\n\n(** Arguments for /queryChanges methods.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.6> RFC 8620, Section 5.6 *)\ntype query_changes_args = {\n query_changes_account_id : id;\n query_changes_filter : filter option;\n query_changes_sort : comparator list option;\n query_changes_since_query_state : string;\n query_changes_max_changes : uint option;\n query_changes_up_to_id : id option;\n query_changes_calculate_total : bool option; (* default: false *)\n query_changes_collapse_threads : bool option; (* For Email/queryChanges *)\n}\n\n(** Response for /queryChanges methods.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.6> RFC 8620, Section 5.6 *)\ntype query_changes_response = {\n query_changes_resp_account_id : id;\n query_changes_resp_old_query_state : string;\n query_changes_resp_new_query_state : string;\n query_changes_resp_total : uint option;\n query_changes_resp_removed : id list;\n query_changes_resp_added : added_item list;\n}\n\n(** Core/echo method: Arguments are mirrored in the response.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-4> RFC 8620, Section 4 *)\ntype core_echo_args = Yojson.Safe.t\ntype core_echo_response = Yojson.Safe.t"
+
},
+
{
+
"filename": "jmap_binary.mli",
+
"contents": "(** JMAP Binary Data Handling.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-6> RFC 8620, Section 6 *)\n\nopen Jmap_types\nopen Jmap_error\n\n(** Response from uploading binary data.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-6.1> RFC 8620, Section 6.1 *)\ntype upload_response = {\n upload_resp_account_id : id;\n upload_resp_blob_id : id;\n upload_resp_type : string;\n upload_resp_size : uint;\n}\n\n(** Arguments for Blob/copy.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-6.3> RFC 8620, Section 6.3 *)\ntype blob_copy_args = {\n blob_copy_from_account_id : id;\n blob_copy_account_id : id;\n blob_copy_blob_ids : id list;\n}\n\n(** Response for Blob/copy.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-6.3> RFC 8620, Section 6.3 *)\ntype blob_copy_response = {\n blob_copy_resp_from_account_id : id;\n blob_copy_resp_account_id : id;\n blob_copy_resp_copied : id id_map option;\n blob_copy_resp_not_copied : set_error id_map option;\n}"
+
},
+
{
+
"filename": "jmap_push.mli",
+
"contents": "(** JMAP Push Notifications.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7> RFC 8620, Section 7 *)\n\nopen Jmap_types\nopen Jmap_methods\n\n(** TypeState object map (TypeName -> StateString).\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.1> RFC 8620, Section 7.1 *)\ntype type_state = string string_map\n\n(** StateChange object.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.1> RFC 8620, Section 7.1 *)\ntype state_change = {\n (* type field is implicitly \"StateChange\" *)\n changed : type_state id_map;\n}\n\n(** PushSubscription encryption keys.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2> RFC 8620, Section 7.2 *)\ntype push_encryption_keys = {\n p256dh : string; (** P-256 ECDH public key (URL-safe base64) *)\n auth : string; (** Authentication secret (URL-safe base64) *)\n}\n\n(** PushSubscription object.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2> RFC 8620, Section 7.2 *)\ntype push_subscription = {\n push_sub_id : id; (** server-set, immutable *)\n device_client_id : string; (** immutable *)\n url : Uri.t; (** immutable *)\n keys : push_encryption_keys option; (** immutable *)\n verification_code : string option;\n expires : utc_date option;\n types : string list option;\n}\n\n(** PushSubscription object for creation (omits server-set fields).\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2> RFC 8620, Section 7.2 *)\ntype push_subscription_create = {\n push_sub_create_device_client_id : string;\n push_sub_create_url : Uri.t;\n push_sub_create_keys : push_encryption_keys option;\n push_sub_create_expires : utc_date option;\n push_sub_create_types : string list option;\n}\n\n(** PushSubscription object for update patch.\n Only verification_code and expires can be updated.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2> RFC 8620, Section 7.2\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2.2> RFC 8620, Section 7.2.2 *)\ntype push_subscription_update = patch_object\n\n(** Arguments for PushSubscription/get.\n Extends standard /get args.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2.1> RFC 8620, Section 7.2.1 *)\ntype push_subscription_get_args = {\n push_sub_get_ids : id list option;\n push_sub_get_properties : string list option;\n}\n\n(** Response for PushSubscription/get.\n Extends standard /get response.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2.1> RFC 8620, Section 7.2.1 *)\ntype push_subscription_get_response = {\n push_sub_get_resp_list : push_subscription list;\n push_sub_get_resp_not_found : id list;\n}\n\n(** Arguments for PushSubscription/set.\n Extends standard /set args.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2.2> RFC 8620, Section 7.2.2 *)\ntype push_subscription_set_args = {\n push_sub_set_create : push_subscription_create id_map option;\n push_sub_set_update : push_subscription_update id_map option;\n push_sub_set_destroy : id list option;\n}\n\n(** Server-set information for created PushSubscription.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2.2> RFC 8620, Section 7.2.2 *)\ntype push_subscription_created_info = {\n push_sub_created_id : id;\n push_sub_created_expires : utc_date option;\n}\n\n(** Server-set information for updated PushSubscription.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2.2> RFC 8620, Section 7.2.2 *)\ntype push_subscription_updated_info = {\n push_sub_updated_expires : utc_date option;\n}\n\n(** Response for PushSubscription/set.\n Extends standard /set response.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2.2> RFC 8620, Section 7.2.2 *)\ntype push_subscription_set_response = {\n push_sub_set_resp_created : push_subscription_created_info id_map option;\n push_sub_set_resp_updated : push_subscription_updated_info option id_map option;\n push_sub_set_resp_destroyed : id list option;\n push_sub_set_resp_not_created : set_error id_map option;\n push_sub_set_resp_not_updated : set_error id_map option;\n push_sub_set_resp_not_destroyed : set_error id_map option;\n}\n\n(** PushVerification object.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2.2> RFC 8620, Section 7.2.2 *)\ntype push_verification = {\n (* type field is implicitly \"PushVerification\" *)\n push_verification_push_subscription_id : id;\n push_verification_verification_code : string;\n}\n\n(** Data for EventSource ping event.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.3> RFC 8620, Section 7.3 *)\ntype event_source_ping_data = {\n interval : uint;\n}"
+
},
+
{
+
"filename": "jmap_email_types.mli",
+
"contents": "(** Common types for JMAP Mail (RFC 8621). *)\n\nopen Jmap_types\n\n(** Represents an email address with an optional name.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.1.2.3> RFC 8621, Section 4.1.2.3 *)\ntype email_address = {\n email_addr_name : string option;\n email_addr_email : string;\n}\n\n(** Represents a group of email addresses.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.1.2.4> RFC 8621, Section 4.1.2.4 *)\ntype email_address_group = {\n email_group_name : string option;\n email_group_addresses : email_address list;\n}\n\n(** Represents a header field (name and raw value).\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.1.3> RFC 8621, Section 4.1.3 *)\ntype email_header = {\n header_name : string;\n header_value : string; (* Raw form *)\n}\n\n(** Represents a body part within an Email's MIME structure.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.1.4> RFC 8621, Section 4.1.4 *)\ntype email_body_part = {\n part_id : string option; (** null only for multipart/* *)\n part_blob_id : id option; (** null only for multipart/* *)\n part_size : uint;\n part_headers : email_header list;\n part_name : string option;\n part_type : string;\n part_charset : string option;\n part_disposition : string option;\n part_cid : string option;\n part_language : string list option;\n part_location : string option;\n part_sub_parts : email_body_part list option; (** only for multipart/* *)\n part_other_headers : Yojson.Safe.t string_map; (** Requested header:* properties *)\n}\n\n(** Represents the decoded value of a text body part.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.1.4> RFC 8621, Section 4.1.4 *)\ntype email_body_value = {\n body_value : string;\n is_encoding_problem : bool; (* default: false *)\n is_truncated : bool; (* default: false *)\n}"
+
},
+
{
+
"filename": "jmap_mailbox.mli",
+
"contents": "(** JMAP Mailbox.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-2> RFC 8621, Section 2 *)\n\nopen Jmap_types\nopen Jmap_methods\n\n(** Mailbox access rights.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-2> RFC 8621, Section 2 *)\ntype mailbox_rights = {\n may_read_items : bool;\n may_add_items : bool;\n may_remove_items : bool;\n may_set_seen : bool;\n may_set_keywords : bool;\n may_create_child : bool;\n may_rename : bool;\n may_delete : bool;\n may_submit : bool;\n}\n\n(** Mailbox object.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-2> RFC 8621, Section 2 *)\ntype mailbox = {\n mailbox_id : id; (** immutable, server-set *)\n name : string;\n parent_id : id option;\n role : string option;\n sort_order : uint; (* default: 0 *)\n total_emails : uint; (** server-set *)\n unread_emails : uint; (** server-set *)\n total_threads : uint; (** server-set *)\n unread_threads : uint; (** server-set *)\n my_rights : mailbox_rights; (** server-set *)\n is_subscribed : bool;\n}\n\n(** Mailbox object for creation.\n Excludes server-set fields.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-2> RFC 8621, Section 2 *)\ntype mailbox_create = {\n mailbox_create_name : string;\n mailbox_create_parent_id : id option;\n mailbox_create_role : string option;\n mailbox_create_sort_order : uint option;\n mailbox_create_is_subscribed : bool option;\n}\n\n(** Mailbox object for update.\n Patch object, specific structure not enforced here.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-2.5> RFC 8621, Section 2.5 *)\ntype mailbox_update = patch_object\n\n(** Server-set info for created mailbox.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-2.5> RFC 8621, Section 2.5 *)\ntype mailbox_created_info = {\n mailbox_created_id : id;\n mailbox_created_role : string option; (** If default used *)\n mailbox_created_sort_order : uint; (** If default used *)\n mailbox_created_total_emails : uint;\n mailbox_created_unread_emails : uint;\n mailbox_created_total_threads : uint;\n mailbox_created_unread_threads : uint;\n mailbox_created_my_rights : mailbox_rights;\n mailbox_created_is_subscribed : bool; (** If default used *)\n}\n\n(** Server-set/computed info for updated mailbox.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-2.5> RFC 8621, Section 2.5 *)\ntype mailbox_updated_info = mailbox (* Contains only changed server-set props *)\n\n(** FilterCondition for Mailbox/query.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-2.3> RFC 8621, Section 2.3 *)\ntype mailbox_filter_condition = {\n filter_parent_id : id option option; (* Use option option for explicit null *)\n filter_name : string option;\n filter_role : string option option; (* Use option option for explicit null *)\n filter_has_any_role : bool option;\n filter_is_subscribed : bool option;\n}\n\n(** Mailbox/get: Args type (specialized from ['record get_args]). *)\ntype mailbox_get_args = mailbox get_args\n\n(** Mailbox/get: Response type (specialized from ['record get_response]). *)\ntype mailbox_get_response = mailbox get_response\n\n(** Mailbox/changes: Args type (specialized from [changes_args]). *)\ntype mailbox_changes_args = changes_args\n\n(** Mailbox/changes: Response type (specialized from [changes_response]). *)\ntype mailbox_changes_response = changes_response\n\n(** Mailbox/query: Args type (specialized from [query_args]). *)\ntype mailbox_query_args = query_args\n\n(** Mailbox/query: Response type (specialized from [query_response]). *)\ntype mailbox_query_response = query_response\n\n(** Mailbox/queryChanges: Args type (specialized from [query_changes_args]). *)\ntype mailbox_query_changes_args = query_changes_args\n\n(** Mailbox/queryChanges: Response type (specialized from [query_changes_response]). *)\ntype mailbox_query_changes_response = query_changes_response\n\n(** Mailbox/set: Args type (specialized from [('c, 'u) set_args]). *)\ntype mailbox_set_args = (mailbox_create, mailbox_update) set_args\n\n(** Mailbox/set: Response type (specialized from [('c, 'u) set_response]). *)\ntype mailbox_set_response = (mailbox_created_info, mailbox_updated_info) set_response"
+
},
+
{
+
"filename": "jmap_thread.mli",
+
"contents": "(** JMAP Thread.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3> RFC 8621, Section 3 *)\n\nopen Jmap_types\nopen Jmap_methods\n\n(** Thread object.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3> RFC 8621, Section 3 *)\ntype thread = {\n thread_id : id; (** immutable, server-set *)\n email_ids : id list; (** server-set *)\n}\n\n(** Thread/get: Args type (specialized from ['record get_args]). *)\ntype thread_get_args = thread get_args\n\n(** Thread/get: Response type (specialized from ['record get_response]). *)\ntype thread_get_response = thread get_response\n\n(** Thread/changes: Args type (specialized from [changes_args]). *)\ntype thread_changes_args = changes_args\n\n(** Thread/changes: Response type (specialized from [changes_response]). *)\ntype thread_changes_response = changes_response"
+
},
+
{
+
"filename": "jmap_email.mli",
+
"contents": "(** JMAP Email.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4> RFC 8621, Section 4 *)\n\nopen Jmap_types\nopen Jmap_email_types\nopen Jmap_methods\n\n(** Email object.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.1> RFC 8621, Section 4.1 *)\ntype email = {\n (* Metadata *)\n email_id : id; (** immutable, server-set *)\n blob_id : id; (** immutable, server-set *)\n thread_id : id; (** immutable, server-set *)\n mailbox_ids : bool id_map;\n keywords : bool string_map; (* default: {} *)\n size : uint; (** immutable, server-set *)\n received_at : utc_date; (** immutable, server-set *)\n\n (* Header Fields (Convenience properties) *)\n message_id : string list option; (** immutable *)\n in_reply_to : string list option; (** immutable *)\n references : string list option; (** immutable *)\n sender : email_address list option; (** immutable *)\n from : email_address list option; (** immutable *)\n to_ : email_address list option; (** immutable *)\n cc : email_address list option; (** immutable *)\n bcc : email_address list option; (** immutable *)\n reply_to : email_address list option; (** immutable *)\n subject : string option; (** immutable *)\n sent_at : date option; (** immutable *)\n\n (* Header Fields (Low-level and custom) *)\n headers : email_header list; (** immutable *)\n header_fields : Yojson.Safe.t string_map; (** Requested header:* properties *)\n\n (* Body Parts *)\n body_structure : email_body_part; (** immutable *)\n body_values : email_body_value string_map; (** immutable *)\n text_body : email_body_part list; (** immutable *)\n html_body : email_body_part list; (** immutable *)\n attachments : email_body_part list; (** immutable *)\n has_attachment : bool; (** immutable, server-set *)\n preview : string; (** immutable, server-set *)\n}\n\n(** Email object for creation.\n Excludes server-set fields and requires specific structure for bodies/headers.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.6> RFC 8621, Section 4.6 *)\ntype email_create = {\n email_create_mailbox_ids : bool id_map;\n email_create_keywords : bool string_map option;\n email_create_received_at : utc_date option; (* Only for drafts *)\n\n (* Header fields must be set individually using header:* syntax or convenience props *)\n email_create_header_fields : Yojson.Safe.t string_map;\n\n (* Body: MUST provide exactly one of these body specifications *)\n email_create_body_structure : email_body_part option;\n email_create_body_values : email_body_value string_map option;\n email_create_text_body : email_body_part option;\n email_create_html_body : email_body_part option;\n email_create_attachments : email_body_part list option;\n\n (* If body_structure not given, bodyValues MUST correspond to partIds in\n text/html/attachments *) \n}\n\n(** Email object for update.\n Patch object, specific structure not enforced here.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.6> RFC 8621, Section 4.6 *)\ntype email_update = patch_object\n\n(** Server-set info for created email.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.6> RFC 8621, Section 4.6 *)\ntype email_created_info = {\n email_created_id : id;\n email_created_blob_id : id;\n email_created_thread_id : id;\n email_created_size : uint;\n}\n\n(** Server-set/computed info for updated email.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.6> RFC 8621, Section 4.6 *)\ntype email_updated_info = email (* Contains only changed server-set props *)\n\n(** FilterCondition for Email/query.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.4.1> RFC 8621, Section 4.4.1 *)\ntype email_filter_condition = {\n filter_in_mailbox : id option;\n filter_in_mailbox_other_than : id list option;\n filter_before : utc_date option;\n filter_after : utc_date option;\n filter_min_size : uint option;\n filter_max_size : uint option;\n filter_all_in_thread_have_keyword : string option;\n filter_some_in_thread_have_keyword : string option;\n filter_none_in_thread_have_keyword : string option;\n filter_has_keyword : string option;\n filter_not_keyword : string option;\n filter_has_attachment : bool option;\n filter_text : string option;\n filter_from : string option;\n filter_to : string option;\n filter_cc : string option;\n filter_bcc : string option;\n filter_subject : string option;\n filter_body : string option;\n filter_header : string list option; (* [ name ] or [ name, value ] *)\n}\n\n(** Override properties for Email/copy.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.7> RFC 8621, Section 4.7 *)\ntype email_copy_override = {\n email_copy_override_id : id; (* id of email to copy *)\n email_copy_override_mailbox_ids : bool id_map option;\n email_copy_override_keywords : bool string_map option;\n email_copy_override_received_at : utc_date option;\n}\n\n(** EmailImport object for Email/import.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.8> RFC 8621, Section 4.8 *)\ntype email_import = {\n email_import_blob_id : id;\n email_import_mailbox_ids : bool id_map;\n email_import_keywords : bool string_map option;\n email_import_received_at : utc_date option;\n}\n\n(** Email/get: Args type (specialized from ['record get_args]).\n Adds body fetching options.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.2> RFC 8621, Section 4.2 *)\ntype email_get_args = {\n get_account_id : id;\n get_ids : id list option;\n get_properties : string list option;\n get_body_properties : string list option;\n get_fetch_text_body_values : bool option; (* default: false *)\n get_fetch_html_body_values : bool option; (* default: false *)\n get_fetch_all_body_values : bool option; (* default: false *)\n get_max_body_value_bytes : uint option; (* default: 0 *)\n}\n\n(** Email/get: Response type (specialized from ['record get_response]). *)\ntype email_get_response = email get_response\n\n(** Email/changes: Args type (specialized from [changes_args]). *)\ntype email_changes_args = changes_args\n\n(** Email/changes: Response type (specialized from [changes_response]). *)\ntype email_changes_response = changes_response\n\n(** Email/query: Args type (specialized from [query_args]). *)\ntype email_query_args = query_args\n\n(** Email/query: Response type (specialized from [query_response]). *)\ntype email_query_response = query_response\n\n(** Email/queryChanges: Args type (specialized from [query_changes_args]). *)\ntype email_query_changes_args = query_changes_args\n\n(** Email/queryChanges: Response type (specialized from [query_changes_response]). *)\ntype email_query_changes_response = query_changes_response\n\n(** Email/set: Args type (specialized from [('c, 'u) set_args]). *)\ntype email_set_args = (email_create, email_update) set_args\n\n(** Email/set: Response type (specialized from [('c, 'u) set_response]). *)\ntype email_set_response = (email_created_info, email_updated_info) set_response\n\n(** Email/copy: Args type (specialized from ['override copy_args]). *)\ntype email_copy_args = email_copy_override copy_args\n\n(** Email/copy: Response type (specialized from ['created copy_response]). *)\ntype email_copy_response = email_created_info copy_response\n\n(** Email/import: Args type.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.8> RFC 8621, Section 4.8 *)\ntype email_import_args = {\n import_account_id : id;\n import_if_in_state : string option;\n import_emails : email_import id_map;\n}\n\n(** Email/import: Response type.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.8> RFC 8621, Section 4.8 *)\ntype email_import_response = {\n import_resp_account_id : id;\n import_resp_old_state : string option;\n import_resp_new_state : string;\n import_resp_created : email_created_info id_map option;\n import_resp_not_created : set_error id_map option;\n}\n\n(** Email/parse: Args type.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.9> RFC 8621, Section 4.9 *)\ntype email_parse_args = {\n parse_account_id : id;\n parse_blob_ids : id list;\n parse_properties : string list option;\n parse_body_properties : string list option;\n parse_fetch_text_body_values : bool option; (* default: false *)\n parse_fetch_html_body_values : bool option; (* default: false *)\n parse_fetch_all_body_values : bool option; (* default: false *)\n parse_max_body_value_bytes : uint option; (* default: 0 *)\n}\n\n(** Email/parse: Response type.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.9> RFC 8621, Section 4.9 *)\ntype email_parse_response = {\n parse_resp_account_id : id;\n parse_resp_parsed : email id_map option;\n parse_resp_not_parsable : id list option;\n parse_resp_not_found : id list option;\n}"
+
},
+
{
+
"filename": "jmap_search_snippet.mli",
+
"contents": "(** JMAP Search Snippet.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-5> RFC 8621, Section 5 *)\n\nopen Jmap_types\nopen Jmap_methods\n\n(** SearchSnippet object.\n Note: Does not have an 'id' property.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-5> RFC 8621, Section 5 *)\ntype search_snippet = {\n snippet_email_id : id;\n snippet_subject : string option;\n snippet_preview : string option;\n}\n\n(** SearchSnippet/get: Args type.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-5.1> RFC 8621, Section 5.1 *)\ntype search_snippet_get_args = {\n snippet_get_account_id : id;\n snippet_get_filter : filter option;\n snippet_get_email_ids : id list;\n}\n\n(** SearchSnippet/get: Response type.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-5.1> RFC 8621, Section 5.1 *)\ntype search_snippet_get_response = {\n snippet_get_resp_account_id : id;\n snippet_get_resp_list : search_snippet list;\n snippet_get_resp_not_found : id list option;\n}"
+
},
+
{
+
"filename": "jmap_identity.mli",
+
"contents": "(** JMAP Identity.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-6> RFC 8621, Section 6 *)\n\nopen Jmap_types\nopen Jmap_email_types\nopen Jmap_methods\n\n(** Identity object.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-6> RFC 8621, Section 6 *)\ntype identity = {\n identity_id : id; (** immutable, server-set *)\n name : string; (* default: \"\" *)\n email : string; (** immutable *)\n reply_to : email_address list option;\n bcc : email_address list option;\n text_signature : string; (* default: \"\" *)\n html_signature : string; (* default: \"\" *)\n may_delete : bool; (** server-set *)\n}\n\n(** Identity object for creation.\n Excludes server-set fields.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-6> RFC 8621, Section 6 *)\ntype identity_create = {\n identity_create_name : string option;\n identity_create_email : string;\n identity_create_reply_to : email_address list option;\n identity_create_bcc : email_address list option;\n identity_create_text_signature : string option;\n identity_create_html_signature : string option;\n}\n\n(** Identity object for update.\n Patch object, specific structure not enforced here.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-6.3> RFC 8621, Section 6.3 *)\ntype identity_update = patch_object\n\n(** Server-set info for created identity.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-6.3> RFC 8621, Section 6.3 *)\ntype identity_created_info = {\n identity_created_id : id;\n identity_created_may_delete : bool;\n}\n\n(** Server-set/computed info for updated identity.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-6.3> RFC 8621, Section 6.3 *)\ntype identity_updated_info = identity (* Contains only changed server-set props *)\n\n(** Identity/get: Args type (specialized from ['record get_args]). *)\ntype identity_get_args = identity get_args\n\n(** Identity/get: Response type (specialized from ['record get_response]). *)\ntype identity_get_response = identity get_response\n\n(** Identity/changes: Args type (specialized from [changes_args]). *)\ntype identity_changes_args = changes_args\n\n(** Identity/changes: Response type (specialized from [changes_response]). *)\ntype identity_changes_response = changes_response\n\n(** Identity/set: Args type (specialized from [('c, 'u) set_args]). *)\ntype identity_set_args = (identity_create, identity_update) set_args\n\n(** Identity/set: Response type (specialized from [('c, 'u) set_response]). *)\ntype identity_set_response = (identity_created_info, identity_updated_info) set_response"
+
},
+
{
+
"filename": "jmap_submission.mli",
+
"contents": "(** JMAP Email Submission.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7> RFC 8621, Section 7 *)\n\nopen Jmap_types\nopen Jmap_methods\n\n(** Address object for Envelope.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7> RFC 8621, Section 7 *)\ntype envelope_address = {\n env_addr_email : string;\n env_addr_parameters : Yojson.Safe.t string_map option;\n}\n\n(** Envelope object.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7> RFC 8621, Section 7 *)\ntype envelope = {\n env_mail_from : envelope_address;\n env_rcpt_to : envelope_address list;\n}\n\n(** Delivery status for a recipient.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7> RFC 8621, Section 7 *)\ntype delivery_status = {\n delivery_smtp_reply : string;\n delivery_delivered : [ `Queued | `Yes | `No | `Unknown ];\n delivery_displayed : [ `Yes | `Unknown ];\n}\n\n(** EmailSubmission object.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7> RFC 8621, Section 7 *)\ntype email_submission = {\n email_sub_id : id; (** immutable, server-set *)\n identity_id : id; (** immutable *)\n email_id : id; (** immutable *)\n thread_id : id; (** immutable, server-set *)\n envelope : envelope option; (** immutable *)\n send_at : utc_date; (** immutable, server-set *)\n undo_status : [ `Pending | `Final | `Canceled ];\n delivery_status : delivery_status string_map option; (** server-set *)\n dsn_blob_ids : id list; (** server-set *)\n mdn_blob_ids : id list; (** server-set *)\n}\n\n(** EmailSubmission object for creation.\n Excludes server-set fields.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7> RFC 8621, Section 7 *)\ntype email_submission_create = {\n email_sub_create_identity_id : id;\n email_sub_create_email_id : id;\n email_sub_create_envelope : envelope option;\n}\n\n(** EmailSubmission object for update.\n Only undoStatus can be updated (to 'canceled').\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7> RFC 8621, Section 7 *)\ntype email_submission_update = patch_object\n\n(** Server-set info for created email submission.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7.5> RFC 8621, Section 7.5 *)\ntype email_submission_created_info = {\n email_sub_created_id : id;\n email_sub_created_thread_id : id;\n email_sub_created_send_at : utc_date;\n}\n\n(** Server-set/computed info for updated email submission.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7.5> RFC 8621, Section 7.5 *)\ntype email_submission_updated_info = email_submission (* Contains only changed server-set props *)\n\n(** FilterCondition for EmailSubmission/query.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7.3> RFC 8621, Section 7.3 *)\ntype email_submission_filter_condition = {\n filter_identity_ids : id list option;\n filter_email_ids : id list option;\n filter_thread_ids : id list option;\n filter_undo_status : [ `Pending | `Final | `Canceled ] option;\n filter_before : utc_date option;\n filter_after : utc_date option;\n}\n\n(** EmailSubmission/get: Args type (specialized from ['record get_args]). *)\ntype email_submission_get_args = email_submission get_args\n\n(** EmailSubmission/get: Response type (specialized from ['record get_response]). *)\ntype email_submission_get_response = email_submission get_response\n\n(** EmailSubmission/changes: Args type (specialized from [changes_args]). *)\ntype email_submission_changes_args = changes_args\n\n(** EmailSubmission/changes: Response type (specialized from [changes_response]). *)\ntype email_submission_changes_response = changes_response\n\n(** EmailSubmission/query: Args type (specialized from [query_args]). *)\ntype email_submission_query_args = query_args\n\n(** EmailSubmission/query: Response type (specialized from [query_response]). *)\ntype email_submission_query_response = query_response\n\n(** EmailSubmission/queryChanges: Args type (specialized from [query_changes_args]). *)\ntype email_submission_query_changes_args = query_changes_args\n\n(** EmailSubmission/queryChanges: Response type (specialized from [query_changes_response]). *)\ntype email_submission_query_changes_response = query_changes_response\n\n(** EmailSubmission/set: Args type (specialized from [('c, 'u) set_args]).\n Includes onSuccess arguments.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7.5> RFC 8621, Section 7.5 *)\ntype email_submission_set_args = {\n set_account_id : id;\n set_if_in_state : string option;\n set_create : email_submission_create id_map option;\n set_update : email_submission_update id_map option;\n set_destroy : id list option;\n set_on_success_update_email : Jmap_email.email_update id_map option;\n set_on_success_destroy_email : id list option;\n}\n\n(** EmailSubmission/set: Response type (specialized from [('c, 'u) set_response]). *)\ntype email_submission_set_response = (email_submission_created_info, email_submission_updated_info) set_response"
+
},
+
{
+
"filename": "jmap_vacation.mli",
+
"contents": "(** JMAP Vacation Response.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-8> RFC 8621, Section 8 *)\n\nopen Jmap_types\nopen Jmap_methods\n\n(** VacationResponse object.\n Note: id is always \"singleton\".\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-8> RFC 8621, Section 8 *)\ntype vacation_response_obj = {\n vacation_id : id; (** immutable, server-set, MUST be \"singleton\" *)\n is_enabled : bool;\n from_date : utc_date option;\n to_date : utc_date option;\n subject : string option;\n text_body : string option;\n html_body : string option;\n}\n\n(** VacationResponse object for update.\n Patch object, specific structure not enforced here.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-8.2> RFC 8621, Section 8.2 *)\ntype vacation_response_update = patch_object\n\n(** VacationResponse/get: Args type (specialized from ['record get_args]). *)\ntype vacation_response_get_args = vacation_response_obj get_args\n\n(** VacationResponse/get: Response type (specialized from ['record get_response]). *)\ntype vacation_response_get_response = vacation_response_obj get_response\n\n(** VacationResponse/set: Args type.\n Only allows update, id must be \"singleton\".\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-8.2> RFC 8621, Section 8.2 *)\ntype vacation_response_set_args = {\n set_account_id : id;\n set_if_in_state : string option;\n set_update : vacation_response_update id_map option;\n}\n\n(** VacationResponse/set: Response type.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-8.2> RFC 8621, Section 8.2 *)\ntype vacation_response_set_response = {\n set_resp_account_id : id;\n set_resp_old_state : string option;\n set_resp_new_state : string;\n set_resp_updated : vacation_response_obj option id_map option;\n set_resp_not_updated : set_error id_map option;\n}"
+
},
+
{
+
"filename": "jmap_email.mli",
+
"contents": "(** JMAP Mail Extension (RFC 8621).\n\n This module provides types and signatures for interacting with JMAP Mail\n data types: Mailbox, Thread, Email, SearchSnippet, Identity, EmailSubmission,\n and VacationResponse.\n*)\n\n(** {1 Core Types} *)\nmodule Types = Jmap_email_types\n\n(** {1 Mailbox} \n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-2> RFC 8621, Section 2 *)\nmodule Mailbox : sig\n include module type of Jmap_mailbox\nend\n\n(** {1 Thread}\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3> RFC 8621, Section 3 *)\nmodule Thread : sig\n include module type of Jmap_thread\nend\n\n(** {1 Email}\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4> RFC 8621, Section 4 *)\nmodule Email : sig\n include module type of Jmap_email\nend\n\n(** {1 Search Snippet}\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-5> RFC 8621, Section 5 *)\nmodule SearchSnippet : sig\n include module type of Jmap_search_snippet\nend\n\n(** {1 Identity}\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-6> RFC 8621, Section 6 *)\nmodule Identity : sig\n include module type of Jmap_identity\nend\n\n(** {1 Email Submission}\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7> RFC 8621, Section 7 *)\nmodule Submission : sig\n include module type of Jmap_submission\nend\n\n(** {1 Vacation Response}\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-8> RFC 8621, Section 8 *)\nmodule Vacation : sig\n include module type of Jmap_vacation\nend\n\n(** Capability URI for JMAP Mail.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-1.3.1> RFC 8621, Section 1.3.1 *)\nval capability_mail : string\n\n(** Capability URI for JMAP Submission.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-1.3.2> RFC 8621, Section 1.3.2 *)\nval capability_submission : string\n\n(** Capability URI for JMAP Vacation Response.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-1.3.3> RFC 8621, Section 1.3.3 *)\nval capability_vacationresponse : string\n\n(** Type name for EmailDelivery push notifications.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-1.5> RFC 8621, Section 1.5 *)\nval push_event_type_email_delivery : string\n\n(** JMAP keywords corresponding to IMAP system flags.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.1.1> RFC 8621, Section 4.1.1 *)\nval keyword_draft : string\nval keyword_seen : string\nval keyword_flagged : string\nval keyword_answered : string\n\n(** Common JMAP keywords from RFC 5788.\n @see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.1.1> RFC 8621, Section 4.1.1 *)\nval keyword_forwarded : string\nval keyword_phishing : string\nval keyword_junk : string\nval keyword_notjunk : string"
+
},
+
{
+
"filename": "jmap_unix.mli",
+
"contents": "(** Unix-specific JMAP client implementation interface.\n\n This module provides functions to interact with a JMAP server using\n Unix sockets for network communication.\n*)\n\nopen Jmap_types\nopen Jmap_wire\nopen Jmap_session\nopen Jmap_push\n\n(** Represents an active JMAP connection context. Opaque type. *)\ntype context\n\n(** Represents an active EventSource connection. Opaque type. *)\ntype event_source_connection\n\n(** Exception raised for JMAP communication errors. *)\nexception Jmap_error of Jmap_error.problem_details\n\n(** Connect to a JMAP server and retrieve the session.\n This handles discovery (if needed) and authentication (mechanism TBD).\n [host] is the server hostname.\n [port] is the server port (default 443 if TLS enabled, TBD if not).\n [credentials] is an opaque type representing authentication credentials.\n @param ?session_url Optional direct URL to the Session resource.\n @param ?username Optional username (e.g., email address) for discovery.\n*)\nval connect : \n ?session_url:Uri.t ->\n ?username:string ->\n host:string -> \n ?port:int -> \n credentials:'a -> \n (context * session) Lwt.t\n\n(** Perform a JMAP API request.\n @param ctx The connection context.\n @param request The JMAP request object.\n*)\nval request : context -> request -> response Lwt.t\n\n(** Upload binary data.\n @param ctx The connection context.\n @param account_id The target account ID.\n @param content_type The MIME type of the data.\n @param data_stream A stream providing the binary data chunks.\n*)\nval upload : \n context -> \n account_id:id -> \n content_type:string -> \n data_stream:string Lwt_stream.t -> \n Jmap_binary.upload_response Lwt.t\n\n(** Download binary data.\n @param ctx The connection context.\n @param account_id The account ID.\n @param blob_id The blob ID to download.\n @param content_type The desired Content-Type for the download response.\n @param name The desired filename for the download response.\n @return A stream producing the binary data chunks.\n*)\nval download : \n context -> \n account_id:id -> \n blob_id:id -> \n content_type:string -> \n name:string -> \n string Lwt_stream.t Lwt.t\n\n(** Connect to the EventSource for push notifications.\n @param ctx The connection context.\n @param ?types List of types to subscribe to (default \"*\").\n @param ?close_after Request server to close after first state event.\n @param ?ping Request ping interval in seconds (default 0).\n @return A tuple of the connection handle and a stream of push events.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.3> RFC 8620, Section 7.3 *)\nval connect_event_source : \n context -> \n ?types:string list -> \n ?close_after:[`State | `No] -> \n ?ping:uint ->\n (event_source_connection * \n ([`State of state_change | `Ping of event_source_ping_data ] Lwt_stream.t)) Lwt.t\n\n(** Close an EventSource connection.\n @param conn The EventSource connection handle.\n*)\nval close_event_source : event_source_connection -> unit Lwt.t\n\n(** Close the JMAP connection context. *)\nval close : context -> unit Lwt.t"
+
},
+
{
+
"filename": "jmap.mli",
+
"contents": "(** JMAP Client Library Interface (RFC 8620 & RFC 8621)\n\n This library provides OCaml types and function signatures for interacting\n with a JMAP server.\n\n Modules:\n - {!Jmap_types}: Basic data types (Id, Date, etc.).\n - {!Jmap_error}: Error types (ProblemDetails, MethodError, SetError).\n - {!Jmap_wire}: Request and Response structures.\n - {!Jmap_session}: Session object and discovery.\n - {!Jmap_methods}: Standard method patterns (/get, /set, etc.) and Core/echo.\n - {!Jmap_binary}: Binary data upload/download types.\n - {!Jmap_push}: Push notification types (StateChange, PushSubscription).\n - {!Jmap_email}: Mail extension (RFC 8621) types and methods.\n - {!Jmap_unix}: Example interface for a Unix-based implementation.\n\n @see <https://www.rfc-editor.org/rfc/rfc8620.html> RFC 8620: Core JMAP\n @see <https://www.rfc-editor.org/rfc/rfc8621.html> RFC 8621: JMAP for Mail\n*)\n\n(** {1 Core JMAP Types and Modules} *)\n\nmodule Types = Jmap_types\nmodule Error = Jmap_error\nmodule Wire = Jmap_wire\nmodule Session = Jmap_session\nmodule Methods = Jmap_methods\nmodule Binary = Jmap_binary\nmodule Push = Jmap_push\n\n(** {1 Mail Extension (RFC 8621)} *)\n\nmodule Email = Jmap_email\n\n(** {1 Unix Implementation Interface} *)\n\nmodule Unix : sig\n include module type of Jmap_unix\nend\n\n(** {1 Example Usage Sketch}\n\n{[ \n (* OCaml 5.1 required for Lwt let operators *) \n open Lwt.Syntax \n open Jmap \n open Jmap.Types \n open Jmap.Wire \n open Jmap.Methods \n open Jmap.Email \n\n let list_unread_from_sender ctx session sender_email = \n (* Find the primary mail account *) \n let primary_mail_account_id = \n Hashtbl.find session.primary_accounts capability_mail \n in \n (* Construct the filter *) \n let filter : filter = \n Filter_operator {\n operator = `AND;\n conditions = [\n Filter_condition (Yojson.Safe.to_basic (`Assoc [\n (\"from\", `String sender_email);\n ]));\n Filter_condition (Yojson.Safe.to_basic (`Assoc [\n (\"hasKeyword\", `String keyword_seen);\n (\"value\", `Bool false);\n ]));\n ]\n }\n in\n (* Prepare the Email/query invocation *) \n let query_args : email_query_args = { \n query_account_id = primary_mail_account_id;\n query_filter = Some filter;\n query_sort = Some [ { property = \"receivedAt\"; is_ascending = Some false; collation = None; keyword = None; other_fields = Hashtbl.create 0 } ];\n query_position = Some 0;\n query_anchor = None;\n query_anchor_offset = None;\n query_limit = Some 20; (* Get latest 20 *)\n query_calculate_total = Some false;\n query_collapse_threads = Some false;\n query_sort_as_tree = None;\n query_filter_as_tree = None;\n } in\n let query_invocation : invocation = {\n method_name = \"Email/query\";\n arguments = (* Yojson conversion of query_args needed here *);\n method_call_id = \"q1\";\n } in\n\n (* Prepare the Email/get invocation using a back-reference *) \n let get_args : email_get_args = {\n get_account_id = primary_mail_account_id;\n get_ids = None; (* Use back-reference instead *)\n get_properties = Some [\"id\"; \"subject\"; \"receivedAt\"; \"from\"];\n get_body_properties = None;\n get_fetch_text_body_values = None;\n get_fetch_html_body_values = None;\n get_fetch_all_body_values = None;\n get_max_body_value_bytes = None;\n } in\n let get_invocation : invocation = {\n method_name = \"Email/get\";\n arguments = (* Yojson conversion of get_args, with ids replaced by a ResultReference to q1 needed here *);\n method_call_id = \"g1\";\n } in\n\n (* Prepare the JMAP request *) \n let request : request = { \n using = [ Jmap.Session.capability_core; Email.capability_mail ];\n method_calls = [ query_invocation; get_invocation ];\n created_ids = None;\n } in\n\n (* Send the request *) \n let* response = Jmap.Unix.request ctx request in\n\n (* Process the response (extract Email/get results) *) \n (* ... Omitted: find the Email/get response in response.method_responses ... *) \n Lwt.return_unit \n\n let main () = \n (* Authentication details are placeholder *) \n let credentials = \"my_auth_token\" in \n let* (ctx, session) = Jmap.Unix.connect ~host:\"jmap.example.com\" ~credentials in \n let* () = list_unread_from_sender ctx session \"boss@example.com\" in \n Jmap.Unix.close ctx \n\n (* Lwt_main.run (main ()) *) \n]} *)\n\n(** Capability URI for JMAP Core.\n @see <https://www.rfc-editor.org/rfc/rfc8620.html#section-2> RFC 8620, Section 2 *)\nval capability_core : string"
+
}
+
]
+133
output/jmap.mli
···
+
(** JMAP Client Library Interface (RFC 8620 & RFC 8621)
+
+
This library provides OCaml types and function signatures for interacting
+
with a JMAP server.
+
+
Modules:
+
- {!Jmap_types}: Basic data types (Id, Date, etc.).
+
- {!Jmap_error}: Error types (ProblemDetails, MethodError, SetError).
+
- {!Jmap_wire}: Request and Response structures.
+
- {!Jmap_session}: Session object and discovery.
+
- {!Jmap_methods}: Standard method patterns (/get, /set, etc.) and Core/echo.
+
- {!Jmap_binary}: Binary data upload/download types.
+
- {!Jmap_push}: Push notification types (StateChange, PushSubscription).
+
- {!Jmap_email}: Mail extension (RFC 8621) types and methods.
+
- {!Jmap_unix}: Example interface for a Unix-based implementation.
+
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html> RFC 8620: Core JMAP
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html> RFC 8621: JMAP for Mail
+
*)
+
+
(** {1 Core JMAP Types and Modules} *)
+
+
module Types = Jmap_types
+
module Error = Jmap_error
+
module Wire = Jmap_wire
+
module Session = Jmap_session
+
module Methods = Jmap_methods
+
module Binary = Jmap_binary
+
module Push = Jmap_push
+
+
(** {1 Mail Extension (RFC 8621)} *)
+
+
module Email = Jmap_email
+
+
(** {1 Unix Implementation Interface} *)
+
+
module Unix = Jmap_unix
+
+
(** {1 Example Usage Sketch}
+
+
{[
+
(* OCaml 5.1 required for Lwt let operators *)
+
open Lwt.Syntax
+
open Jmap
+
open Jmap.Types
+
open Jmap.Wire
+
open Jmap.Methods
+
open Jmap.Email
+
+
let list_unread_from_sender ctx session sender_email =
+
(* Find the primary mail account *)
+
let primary_mail_account_id =
+
Hashtbl.find session.primary_accounts capability_mail
+
in
+
(* Construct the filter *)
+
let filter : filter =
+
Filter_operator {
+
operator = `AND;
+
conditions = [
+
Filter_condition (Yojson.Safe.to_basic (`Assoc [
+
("from", `String sender_email);
+
]));
+
Filter_condition (Yojson.Safe.to_basic (`Assoc [
+
("hasKeyword", `String keyword_seen);
+
("value", `Bool false);
+
]));
+
]
+
}
+
in
+
(* Prepare the Email/query invocation *)
+
let query_args : email_query_args = {
+
query_account_id = primary_mail_account_id;
+
query_filter = Some filter;
+
query_sort = Some [ { property = "receivedAt"; is_ascending = Some false; collation = None; keyword = None; other_fields = Hashtbl.create 0 } ];
+
query_position = Some 0;
+
query_anchor = None;
+
query_anchor_offset = None;
+
query_limit = Some 20; (* Get latest 20 *)
+
query_calculate_total = Some false;
+
query_collapse_threads = Some false;
+
query_sort_as_tree = None;
+
query_filter_as_tree = None;
+
} in
+
let query_invocation : invocation = {
+
method_name = "Email/query";
+
arguments = (* Yojson conversion of query_args needed here *);
+
method_call_id = "q1";
+
} in
+
+
(* Prepare the Email/get invocation using a back-reference *)
+
let get_args : email_get_args = {
+
get_account_id = primary_mail_account_id;
+
get_ids = None; (* Use back-reference instead *)
+
get_properties = Some ["id"; "subject"; "receivedAt"; "from"];
+
get_body_properties = None;
+
get_fetch_text_body_values = None;
+
get_fetch_html_body_values = None;
+
get_fetch_all_body_values = None;
+
get_max_body_value_bytes = None;
+
} in
+
let get_invocation : invocation = {
+
method_name = "Email/get";
+
arguments = (* Yojson conversion of get_args, with ids replaced by a ResultReference to q1 needed here *);
+
method_call_id = "g1";
+
} in
+
+
(* Prepare the JMAP request *)
+
let request : request = {
+
using = [ Jmap.Session.capability_core; Email.capability_mail ];
+
method_calls = [ query_invocation; get_invocation ];
+
created_ids = None;
+
} in
+
+
(* Send the request *)
+
let* response = Jmap.Unix.request ctx request in
+
+
(* Process the response (extract Email/get results) *)
+
(* ... Omitted: find the Email/get response in response.method_responses ... *)
+
Lwt.return_unit
+
+
let main () =
+
(* Authentication details are placeholder *)
+
let credentials = "my_auth_token" in
+
let* (ctx, session) = Jmap.Unix.connect ~host:"jmap.example.com" ~credentials in
+
let* () = list_unread_from_sender ctx session "boss@example.com" in
+
Jmap.Unix.close ctx
+
+
(* Lwt_main.run (main ()) *)
+
]} *)
+
+
(** Capability URI for JMAP Core.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-2> RFC 8620, Section 2 *)
+
val capability_core : string
output/jmap.opam

This is a binary file and will not be displayed.

+31
output/jmap_binary.mli
···
+
(** JMAP Binary Data Handling.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-6> RFC 8620, Section 6 *)
+
+
open Jmap_types
+
open Jmap_error
+
+
(** Response from uploading binary data.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-6.1> RFC 8620, Section 6.1 *)
+
type upload_response = {
+
upload_resp_account_id : id;
+
upload_resp_blob_id : id;
+
upload_resp_type : string;
+
upload_resp_size : uint;
+
}
+
+
(** Arguments for Blob/copy.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-6.3> RFC 8620, Section 6.3 *)
+
type blob_copy_args = {
+
blob_copy_from_account_id : id;
+
blob_copy_account_id : id;
+
blob_copy_blob_ids : id list;
+
}
+
+
(** Response for Blob/copy.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-6.3> RFC 8620, Section 6.3 *)
+
type blob_copy_response = {
+
blob_copy_resp_from_account_id : id;
+
blob_copy_resp_account_id : id;
+
blob_copy_resp_copied : id id_map option;
+
blob_copy_resp_not_copied : set_error id_map option;
+
}
+67
output/jmap_email.mli
···
+
(** JMAP Mail Extension (RFC 8621).
+
+
This module provides types and signatures for interacting with JMAP Mail
+
data types: Mailbox, Thread, Email, SearchSnippet, Identity, EmailSubmission,
+
and VacationResponse.
+
*)
+
+
(** {1 Core Types} *)
+
module Types = Jmap_email_types
+
+
(** {1 Mailbox}
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-2> RFC 8621, Section 2 *)
+
module Mailbox = Jmap_mailbox
+
+
(** {1 Thread}
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3> RFC 8621, Section 3 *)
+
module Thread = Jmap_thread
+
+
(** {1 Email}
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4> RFC 8621, Section 4 *)
+
module Email = Jmap_email
+
+
(** {1 Search Snippet}
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-5> RFC 8621, Section 5 *)
+
module SearchSnippet = Jmap_search_snippet
+
+
(** {1 Identity}
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-6> RFC 8621, Section 6 *)
+
module Identity = Jmap_identity
+
+
(** {1 Email Submission}
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7> RFC 8621, Section 7 *)
+
module Submission = Jmap_submission
+
+
(** {1 Vacation Response}
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-8> RFC 8621, Section 8 *)
+
module Vacation = Jmap_vacation
+
+
(** Capability URI for JMAP Mail.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-1.3.1> RFC 8621, Section 1.3.1 *)
+
val capability_mail : string
+
+
(** Capability URI for JMAP Submission.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-1.3.2> RFC 8621, Section 1.3.2 *)
+
val capability_submission : string
+
+
(** Capability URI for JMAP Vacation Response.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-1.3.3> RFC 8621, Section 1.3.3 *)
+
val capability_vacationresponse : string
+
+
(** Type name for EmailDelivery push notifications.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-1.5> RFC 8621, Section 1.5 *)
+
val push_event_type_email_delivery : string
+
+
(** JMAP keywords corresponding to IMAP system flags.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.1.1> RFC 8621, Section 4.1.1 *)
+
val keyword_draft : string
+
val keyword_seen : string
+
val keyword_flagged : string
+
val keyword_answered : string
+
+
(** Common JMAP keywords from RFC 5788.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.1.1> RFC 8621, Section 4.1.1 *)
+
val keyword_forwarded : string
+
val keyword_phishing : string
+
val keyword_junk : string
+
val keyword_notjunk : string
+50
output/jmap_email_types.mli
···
+
(** Common types for JMAP Mail (RFC 8621). *)
+
+
open Jmap_types
+
+
(** Represents an email address with an optional name.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.1.2.3> RFC 8621, Section 4.1.2.3 *)
+
type email_address = {
+
email_addr_name : string option;
+
email_addr_email : string;
+
}
+
+
(** Represents a group of email addresses.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.1.2.4> RFC 8621, Section 4.1.2.4 *)
+
type email_address_group = {
+
email_group_name : string option;
+
email_group_addresses : email_address list;
+
}
+
+
(** Represents a header field (name and raw value).
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.1.3> RFC 8621, Section 4.1.3 *)
+
type email_header = {
+
header_name : string;
+
header_value : string; (* Raw form *)
+
}
+
+
(** Represents a body part within an Email's MIME structure.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.1.4> RFC 8621, Section 4.1.4 *)
+
type email_body_part = {
+
part_id : string option; (** null only for multipart/* *)
+
part_blob_id : id option; (** null only for multipart/* *)
+
part_size : uint;
+
part_headers : email_header list;
+
part_name : string option;
+
part_type : string;
+
part_charset : string option;
+
part_disposition : string option;
+
part_cid : string option;
+
part_language : string list option;
+
part_location : string option;
+
part_sub_parts : email_body_part list option; (** only for multipart/* *)
+
part_other_headers : Yojson.Safe.t string_map; (** Requested header:* properties *)
+
}
+
+
(** Represents the decoded value of a text body part.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-4.1.4> RFC 8621, Section 4.1.4 *)
+
type email_body_value = {
+
body_value : string;
+
is_encoding_problem : bool; (* default: false *)
+
is_truncated : bool; (* default: false *)
+
}
+94
output/jmap_error.mli
···
+
(** JMAP Error Types. *)
+
+
open Jmap_types
+
+
(** Problem details object for HTTP-level errors.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.6.1> RFC 8620, Section 3.6.1
+
@see <https://www.rfc-editor.org/rfc/rfc7807.html> RFC 7807 *)
+
type problem_details = {
+
problem_type : string; (** The "type" field from RFC 7807. *)
+
status : int option; (** The "status" field. *)
+
detail : string option; (** The "detail" field. *)
+
limit : string option; (** For "urn:ietf:params:jmap:error:limit" *)
+
other_fields : Yojson.Safe.t string_map; (** Catch-all for extra fields *)
+
}
+
+
(** Standard Method-level error types.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.6.2> RFC 8620, Section 3.6.2 *)
+
type method_error_type = [
+
| `ServerUnavailable
+
| `ServerFail
+
| `ServerPartialFail
+
| `UnknownMethod
+
| `InvalidArguments
+
| `InvalidResultReference
+
| `Forbidden
+
| `AccountNotFound
+
| `AccountNotSupportedByMethod
+
| `AccountReadOnly
+
| `RequestTooLarge (* From /get *)
+
| `CannotCalculateChanges (* From /changes, /queryChanges *)
+
| `StateMismatch (* From /set, /copy *)
+
| `AnchorNotFound (* From /query *)
+
| `UnsupportedSort (* From /query *)
+
| `UnsupportedFilter (* From /query *)
+
| `TooManyChanges (* From /queryChanges *)
+
| `FromAccountNotFound (* From /copy, Blob/copy *)
+
| `FromAccountNotSupportedByMethod (* From /copy *)
+
| `Other_method_error of string (* For future or custom errors *)
+
]
+
+
(** Description for method errors. May contain additional details.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.6.2> RFC 8620, Section 3.6.2 *)
+
type method_error_description = {
+
description : string option;
+
}
+
+
(** Represents a method-level error response invocation part.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.6.2> RFC 8620, Section 3.6.2 *)
+
type method_error = {
+
err_type : method_error_type;
+
err_description : method_error_description option;
+
}
+
+
(** Standard SetError types.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.3> RFC 8620, Section 5.3 *)
+
type set_error_type = [
+
| `Forbidden
+
| `OverQuota
+
| `TooLarge
+
| `RateLimit
+
| `NotFound
+
| `InvalidPatch
+
| `WillDestroy
+
| `InvalidProperties
+
| `Singleton
+
| `AlreadyExists (* From /copy *)
+
| `MailboxHasChild (* RFC 8621 *)
+
| `MailboxHasEmail (* RFC 8621 *)
+
| `BlobNotFound (* RFC 8621 *)
+
| `TooManyKeywords (* RFC 8621 *)
+
| `TooManyMailboxes (* RFC 8621 *)
+
| `InvalidEmail (* RFC 8621 *)
+
| `TooManyRecipients (* RFC 8621 *)
+
| `NoRecipients (* RFC 8621 *)
+
| `InvalidRecipients (* RFC 8621 *)
+
| `ForbiddenMailFrom (* RFC 8621 *)
+
| `ForbiddenFrom (* RFC 8621 *)
+
| `ForbiddenToSend (* RFC 8621 *)
+
| `CannotUnsend (* RFC 8621 *)
+
| `Other_set_error of string (* For future or custom errors *)
+
]
+
+
(** SetError object.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.3> RFC 8620, Section 5.3 *)
+
type set_error = {
+
set_err_type : set_error_type;
+
set_err_description : string option;
+
set_err_properties : string list option; (** For InvalidProperties *)
+
set_err_existing_id : id option; (** For AlreadyExists *)
+
set_err_max_recipients : uint option; (** For TooManyRecipients *)
+
set_err_invalid_recipients : string list option; (** For InvalidRecipients *)
+
set_err_max_size : uint option; (** For TooLarge *)
+
set_err_not_found_blob_ids : id list option; (** For BlobNotFound *)
+
}
+65
output/jmap_identity.mli
···
+
(** JMAP Identity.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-6> RFC 8621, Section 6 *)
+
+
open Jmap_types
+
open Jmap_email_types
+
open Jmap_methods
+
+
(** Identity object.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-6> RFC 8621, Section 6 *)
+
type identity = {
+
identity_id : id; (** immutable, server-set *)
+
name : string; (* default: "" *)
+
email : string; (** immutable *)
+
reply_to : email_address list option;
+
bcc : email_address list option;
+
text_signature : string; (* default: "" *)
+
html_signature : string; (* default: "" *)
+
may_delete : bool; (** server-set *)
+
}
+
+
(** Identity object for creation.
+
Excludes server-set fields.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-6> RFC 8621, Section 6 *)
+
type identity_create = {
+
identity_create_name : string option;
+
identity_create_email : string;
+
identity_create_reply_to : email_address list option;
+
identity_create_bcc : email_address list option;
+
identity_create_text_signature : string option;
+
identity_create_html_signature : string option;
+
}
+
+
(** Identity object for update.
+
Patch object, specific structure not enforced here.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-6.3> RFC 8621, Section 6.3 *)
+
type identity_update = patch_object
+
+
(** Server-set info for created identity.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-6.3> RFC 8621, Section 6.3 *)
+
type identity_created_info = {
+
identity_created_id : id;
+
identity_created_may_delete : bool;
+
}
+
+
(** Server-set/computed info for updated identity.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-6.3> RFC 8621, Section 6.3 *)
+
type identity_updated_info = identity (* Contains only changed server-set props *)
+
+
(** Identity/get: Args type (specialized from ['record get_args]). *)
+
type identity_get_args = identity get_args
+
+
(** Identity/get: Response type (specialized from ['record get_response]). *)
+
type identity_get_response = identity get_response
+
+
(** Identity/changes: Args type (specialized from [changes_args]). *)
+
type identity_changes_args = changes_args
+
+
(** Identity/changes: Response type (specialized from [changes_response]). *)
+
type identity_changes_response = changes_response
+
+
(** Identity/set: Args type (specialized from [('c, 'u) set_args]). *)
+
type identity_set_args = (identity_create, identity_update) set_args
+
+
(** Identity/set: Response type (specialized from [('c, 'u) set_response]). *)
+
type identity_set_response = (identity_created_info, identity_updated_info) set_response
+109
output/jmap_mailbox.mli
···
+
(** JMAP Mailbox.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-2> RFC 8621, Section 2 *)
+
+
open Jmap_types
+
open Jmap_methods
+
+
(** Mailbox access rights.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-2> RFC 8621, Section 2 *)
+
type mailbox_rights = {
+
may_read_items : bool;
+
may_add_items : bool;
+
may_remove_items : bool;
+
may_set_seen : bool;
+
may_set_keywords : bool;
+
may_create_child : bool;
+
may_rename : bool;
+
may_delete : bool;
+
may_submit : bool;
+
}
+
+
(** Mailbox object.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-2> RFC 8621, Section 2 *)
+
type mailbox = {
+
mailbox_id : id; (** immutable, server-set *)
+
name : string;
+
parent_id : id option;
+
role : string option;
+
sort_order : uint; (* default: 0 *)
+
total_emails : uint; (** server-set *)
+
unread_emails : uint; (** server-set *)
+
total_threads : uint; (** server-set *)
+
unread_threads : uint; (** server-set *)
+
my_rights : mailbox_rights; (** server-set *)
+
is_subscribed : bool;
+
}
+
+
(** Mailbox object for creation.
+
Excludes server-set fields.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-2> RFC 8621, Section 2 *)
+
type mailbox_create = {
+
mailbox_create_name : string;
+
mailbox_create_parent_id : id option;
+
mailbox_create_role : string option;
+
mailbox_create_sort_order : uint option;
+
mailbox_create_is_subscribed : bool option;
+
}
+
+
(** Mailbox object for update.
+
Patch object, specific structure not enforced here.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-2.5> RFC 8621, Section 2.5 *)
+
type mailbox_update = patch_object
+
+
(** Server-set info for created mailbox.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-2.5> RFC 8621, Section 2.5 *)
+
type mailbox_created_info = {
+
mailbox_created_id : id;
+
mailbox_created_role : string option; (** If default used *)
+
mailbox_created_sort_order : uint; (** If default used *)
+
mailbox_created_total_emails : uint;
+
mailbox_created_unread_emails : uint;
+
mailbox_created_total_threads : uint;
+
mailbox_created_unread_threads : uint;
+
mailbox_created_my_rights : mailbox_rights;
+
mailbox_created_is_subscribed : bool; (** If default used *)
+
}
+
+
(** Server-set/computed info for updated mailbox.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-2.5> RFC 8621, Section 2.5 *)
+
type mailbox_updated_info = mailbox (* Contains only changed server-set props *)
+
+
(** FilterCondition for Mailbox/query.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-2.3> RFC 8621, Section 2.3 *)
+
type mailbox_filter_condition = {
+
filter_parent_id : id option option; (* Use option option for explicit null *)
+
filter_name : string option;
+
filter_role : string option option; (* Use option option for explicit null *)
+
filter_has_any_role : bool option;
+
filter_is_subscribed : bool option;
+
}
+
+
(** Mailbox/get: Args type (specialized from ['record get_args]). *)
+
type mailbox_get_args = mailbox get_args
+
+
(** Mailbox/get: Response type (specialized from ['record get_response]). *)
+
type mailbox_get_response = mailbox get_response
+
+
(** Mailbox/changes: Args type (specialized from [changes_args]). *)
+
type mailbox_changes_args = changes_args
+
+
(** Mailbox/changes: Response type (specialized from [changes_response]). *)
+
type mailbox_changes_response = changes_response
+
+
(** Mailbox/query: Args type (specialized from [query_args]). *)
+
type mailbox_query_args = query_args
+
+
(** Mailbox/query: Response type (specialized from [query_response]). *)
+
type mailbox_query_response = query_response
+
+
(** Mailbox/queryChanges: Args type (specialized from [query_changes_args]). *)
+
type mailbox_query_changes_args = query_changes_args
+
+
(** Mailbox/queryChanges: Response type (specialized from [query_changes_response]). *)
+
type mailbox_query_changes_response = query_changes_response
+
+
(** Mailbox/set: Args type (specialized from [('c, 'u) set_args]). *)
+
type mailbox_set_args = (mailbox_create, mailbox_update) set_args
+
+
(** Mailbox/set: Response type (specialized from [('c, 'u) set_response]). *)
+
type mailbox_set_response = (mailbox_created_info, mailbox_updated_info) set_response
+196
output/jmap_methods.mli
···
+
(** Standard JMAP Methods and Core/echo.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-4> RFC 8620, Section 4
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5> RFC 8620, Section 5 *)
+
+
open Jmap_types
+
open Jmap_error
+
+
(** Generic representation of a record type. Actual types defined elsewhere. *)
+
type generic_record
+
+
(** Arguments for /get methods.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.1> RFC 8620, Section 5.1 *)
+
type 'record get_args = {
+
get_account_id : id;
+
get_ids : id list option;
+
get_properties : string list option;
+
}
+
+
(** Response for /get methods.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.1> RFC 8620, Section 5.1 *)
+
type 'record get_response = {
+
get_resp_account_id : id;
+
get_resp_state : string;
+
get_resp_list : 'record list;
+
get_resp_not_found : id list;
+
}
+
+
(** Arguments for /changes methods.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.2> RFC 8620, Section 5.2 *)
+
type changes_args = {
+
changes_account_id : id;
+
changes_since_state : string;
+
changes_max_changes : uint option;
+
}
+
+
(** Response for /changes methods.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.2> RFC 8620, Section 5.2 *)
+
type changes_response = {
+
changes_resp_account_id : id;
+
changes_resp_old_state : string;
+
changes_resp_new_state : string;
+
changes_resp_has_more_changes : bool;
+
changes_resp_created : id list;
+
changes_resp_updated : id list;
+
changes_resp_destroyed : id list;
+
changes_resp_updated_properties : string list option; (* Mailbox/changes specific *)
+
}
+
+
(** Patch object for /set update.
+
A list of (JSON Pointer path, value) pairs.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.3> RFC 8620, Section 5.3 *)
+
type patch_object = (json_pointer * Yojson.Safe.t) list
+
+
(** Arguments for /set methods.
+
['create_record] is the record type without server-set/immutable fields.
+
['update_record] is the patch object type (usually [patch_object]).
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.3> RFC 8620, Section 5.3 *)
+
type ('create_record, 'update_record) set_args = {
+
set_account_id : id;
+
set_if_in_state : string option;
+
set_create : 'create_record id_map option;
+
set_update : 'update_record id_map option;
+
set_destroy : id list option;
+
set_on_success_destroy_original : bool option; (* For /copy *)
+
set_destroy_from_if_in_state : string option; (* For /copy *)
+
set_on_destroy_remove_emails : bool option; (* For Mailbox/set *)
+
}
+
+
(** Response for /set methods.
+
['created_record_info] is the server-set info for created records.
+
['updated_record_info] is the server-set/computed info for updated records.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.3> RFC 8620, Section 5.3 *)
+
type ('created_record_info, 'updated_record_info) set_response = {
+
set_resp_account_id : id;
+
set_resp_old_state : string option;
+
set_resp_new_state : string;
+
set_resp_created : 'created_record_info id_map option;
+
set_resp_updated : 'updated_record_info option id_map option;
+
set_resp_destroyed : id list option;
+
set_resp_not_created : set_error id_map option;
+
set_resp_not_updated : set_error id_map option;
+
set_resp_not_destroyed : set_error id_map option;
+
}
+
+
(** Arguments for /copy methods.
+
['copy_record_override] contains the record id and override properties.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.4> RFC 8620, Section 5.4 *)
+
type 'copy_record_override copy_args = {
+
copy_from_account_id : id;
+
copy_if_from_in_state : string option;
+
copy_account_id : id;
+
copy_if_in_state : string option;
+
copy_create : 'copy_record_override id_map; (* Map from creation id *)
+
copy_on_success_destroy_original : bool; (* default: false *)
+
copy_destroy_from_if_in_state : string option;
+
}
+
+
(** Response for /copy methods.
+
['created_record_info] is the server-set info for the created copy.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.4> RFC 8620, Section 5.4 *)
+
type 'created_record_info copy_response = {
+
copy_resp_from_account_id : id;
+
copy_resp_account_id : id;
+
copy_resp_old_state : string option;
+
copy_resp_new_state : string;
+
copy_resp_created : 'created_record_info id_map option;
+
copy_resp_not_created : set_error id_map option;
+
}
+
+
(** Base filter operator type.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.5> RFC 8620, Section 5.5 *)
+
type filter_operator = {
+
operator : [ `AND | `OR | `NOT ];
+
conditions : filter list;
+
}
+
+
(** Represents either a filter operator or a filter condition.
+
['condition] is the type of the specific FilterCondition object.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.5> RFC 8620, Section 5.5 *)
+
and filter =
+
| Filter_operator of filter_operator
+
| Filter_condition of Yojson.Safe.t (* Placeholder for specific condition type *)
+
+
(** Comparator for sorting.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.5> RFC 8620, Section 5.5 *)
+
type comparator = {
+
property : string;
+
is_ascending : bool option; (* default: true *)
+
collation : string option;
+
keyword : string option; (* For Email/query keyword sorts *)
+
other_fields : Yojson.Safe.t string_map; (** Catch-all for extra fields *)
+
}
+
+
(** Arguments for /query methods.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.5> RFC 8620, Section 5.5 *)
+
type query_args = {
+
query_account_id : id;
+
query_filter : filter option;
+
query_sort : comparator list option;
+
query_position : jint option; (* default: 0 *)
+
query_anchor : id option;
+
query_anchor_offset : jint option; (* default: 0 *)
+
query_limit : uint option;
+
query_calculate_total : bool option; (* default: false *)
+
query_collapse_threads : bool option; (* For Email/query *)
+
query_sort_as_tree : bool option; (* For Mailbox/query *)
+
query_filter_as_tree : bool option; (* For Mailbox/query *)
+
}
+
+
(** Response for /query methods.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.5> RFC 8620, Section 5.5 *)
+
type query_response = {
+
query_resp_account_id : id;
+
query_resp_query_state : string;
+
query_resp_can_calculate_changes : bool;
+
query_resp_position : uint;
+
query_resp_ids : id list;
+
query_resp_total : uint option;
+
query_resp_limit : uint option;
+
}
+
+
(** Item indicating an added record in /queryChanges.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.6> RFC 8620, Section 5.6 *)
+
type added_item = {
+
added_item_id : id;
+
added_item_index : uint;
+
}
+
+
(** Arguments for /queryChanges methods.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.6> RFC 8620, Section 5.6 *)
+
type query_changes_args = {
+
query_changes_account_id : id;
+
query_changes_filter : filter option;
+
query_changes_sort : comparator list option;
+
query_changes_since_query_state : string;
+
query_changes_max_changes : uint option;
+
query_changes_up_to_id : id option;
+
query_changes_calculate_total : bool option; (* default: false *)
+
query_changes_collapse_threads : bool option; (* For Email/queryChanges *)
+
}
+
+
(** Response for /queryChanges methods.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-5.6> RFC 8620, Section 5.6 *)
+
type query_changes_response = {
+
query_changes_resp_account_id : id;
+
query_changes_resp_old_query_state : string;
+
query_changes_resp_new_query_state : string;
+
query_changes_resp_total : uint option;
+
query_changes_resp_removed : id list;
+
query_changes_resp_added : added_item list;
+
}
+
+
(** Core/echo method: Arguments are mirrored in the response.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-4> RFC 8620, Section 4 *)
+
type core_echo_args = Yojson.Safe.t
+
type core_echo_response = Yojson.Safe.t
+116
output/jmap_push.mli
···
+
(** JMAP Push Notifications.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7> RFC 8620, Section 7 *)
+
+
open Jmap_types
+
open Jmap_methods
+
open Jmap_error
+
+
(** TypeState object map (TypeName -> StateString).
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.1> RFC 8620, Section 7.1 *)
+
type type_state = string string_map
+
+
(** StateChange object.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.1> RFC 8620, Section 7.1 *)
+
type state_change = {
+
(* type field is implicitly "StateChange" *)
+
changed : type_state id_map;
+
}
+
+
(** PushSubscription encryption keys.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2> RFC 8620, Section 7.2 *)
+
type push_encryption_keys = {
+
p256dh : string; (** P-256 ECDH public key (URL-safe base64) *)
+
auth : string; (** Authentication secret (URL-safe base64) *)
+
}
+
+
(** PushSubscription object.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2> RFC 8620, Section 7.2 *)
+
type push_subscription = {
+
push_sub_id : id; (** server-set, immutable *)
+
device_client_id : string; (** immutable *)
+
url : Uri.t; (** immutable *)
+
keys : push_encryption_keys option; (** immutable *)
+
verification_code : string option;
+
expires : utc_date option;
+
types : string list option;
+
}
+
+
(** PushSubscription object for creation (omits server-set fields).
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2> RFC 8620, Section 7.2 *)
+
type push_subscription_create = {
+
push_sub_create_device_client_id : string;
+
push_sub_create_url : Uri.t;
+
push_sub_create_keys : push_encryption_keys option;
+
push_sub_create_expires : utc_date option;
+
push_sub_create_types : string list option;
+
}
+
+
(** PushSubscription object for update patch.
+
Only verification_code and expires can be updated.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2> RFC 8620, Section 7.2
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2.2> RFC 8620, Section 7.2.2 *)
+
type push_subscription_update = patch_object
+
+
(** Arguments for PushSubscription/get.
+
Extends standard /get args.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2.1> RFC 8620, Section 7.2.1 *)
+
type push_subscription_get_args = {
+
push_sub_get_ids : id list option;
+
push_sub_get_properties : string list option;
+
}
+
+
(** Response for PushSubscription/get.
+
Extends standard /get response.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2.1> RFC 8620, Section 7.2.1 *)
+
type push_subscription_get_response = {
+
push_sub_get_resp_list : push_subscription list;
+
push_sub_get_resp_not_found : id list;
+
}
+
+
(** Arguments for PushSubscription/set.
+
Extends standard /set args.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2.2> RFC 8620, Section 7.2.2 *)
+
type push_subscription_set_args = {
+
push_sub_set_create : push_subscription_create id_map option;
+
push_sub_set_update : push_subscription_update id_map option;
+
push_sub_set_destroy : id list option;
+
}
+
+
(** Server-set information for created PushSubscription.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2.2> RFC 8620, Section 7.2.2 *)
+
type push_subscription_created_info = {
+
push_sub_created_id : id;
+
push_sub_created_expires : utc_date option;
+
}
+
+
(** Server-set information for updated PushSubscription.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2.2> RFC 8620, Section 7.2.2 *)
+
type push_subscription_updated_info = {
+
push_sub_updated_expires : utc_date option;
+
}
+
+
(** Response for PushSubscription/set.
+
Extends standard /set response.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2.2> RFC 8620, Section 7.2.2 *)
+
type push_subscription_set_response = {
+
push_sub_set_resp_created : push_subscription_created_info id_map option;
+
push_sub_set_resp_updated : push_subscription_updated_info option id_map option;
+
push_sub_set_resp_destroyed : id list option;
+
push_sub_set_resp_not_created : set_error id_map option;
+
push_sub_set_resp_not_updated : set_error id_map option;
+
push_sub_set_resp_not_destroyed : set_error id_map option;
+
}
+
+
(** PushVerification object.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.2.2> RFC 8620, Section 7.2.2 *)
+
type push_verification = {
+
(* type field is implicitly "PushVerification" *)
+
push_verification_push_subscription_id : id;
+
push_verification_verification_code : string;
+
}
+
+
(** Data for EventSource ping event.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.3> RFC 8620, Section 7.3 *)
+
type event_source_ping_data = {
+
interval : uint;
+
}
+30
output/jmap_search_snippet.mli
···
+
(** JMAP Search Snippet.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-5> RFC 8621, Section 5 *)
+
+
open Jmap_types
+
open Jmap_methods
+
+
(** SearchSnippet object.
+
Note: Does not have an 'id' property.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-5> RFC 8621, Section 5 *)
+
type search_snippet = {
+
snippet_email_id : id;
+
snippet_subject : string option;
+
snippet_preview : string option;
+
}
+
+
(** SearchSnippet/get: Args type.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-5.1> RFC 8621, Section 5.1 *)
+
type search_snippet_get_args = {
+
snippet_get_account_id : id;
+
snippet_get_filter : filter option;
+
snippet_get_email_ids : id list;
+
}
+
+
(** SearchSnippet/get: Response type.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-5.1> RFC 8621, Section 5.1 *)
+
type search_snippet_get_response = {
+
snippet_get_resp_account_id : id;
+
snippet_get_resp_list : search_snippet list;
+
snippet_get_resp_not_found : id list option;
+
}
+59
output/jmap_session.mli
···
+
(** JMAP Session Resource.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-2> RFC 8620, Section 2 *)
+
+
open Jmap_types
+
+
(** Core capability information.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-2> RFC 8620, Section 2 *)
+
type core_capability = {
+
max_size_upload : uint;
+
max_concurrent_upload : uint;
+
max_size_request : uint;
+
max_concurrent_requests : uint;
+
max_calls_in_request : uint;
+
max_objects_in_get : uint;
+
max_objects_in_set : uint;
+
collation_algorithms : string list;
+
}
+
+
(** Account capability information.
+
The value is capability-specific.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-2> RFC 8620, Section 2 *)
+
type account_capability_value = Yojson.Safe.t
+
+
(** An Account object.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-2> RFC 8620, Section 2 *)
+
type account = {
+
acc_name : string;
+
is_personal : bool;
+
is_read_only : bool;
+
account_capabilities : account_capability_value string_map;
+
}
+
+
(** Server capability information.
+
The value is capability-specific.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-2> RFC 8620, Section 2 *)
+
type server_capability_value = Yojson.Safe.t
+
+
(** The Session object.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-2> RFC 8620, Section 2 *)
+
type session = {
+
capabilities : server_capability_value string_map;
+
accounts : account id_map;
+
primary_accounts : id string_map;
+
username : string;
+
api_url : Uri.t;
+
download_url : Uri.t; (** Must be a Level 1 URI Template *)
+
upload_url : Uri.t; (** Must be a Level 1 URI Template *)
+
event_source_url : Uri.t; (** Must be a Level 1 URI Template *)
+
state : string;
+
}
+
+
(** Function to perform service autodiscovery.
+
Returns the session URL if found.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-2.2> RFC 8620, Section 2.2 *)
+
val discover : domain:string -> Uri.t option
+
+
(** Function to fetch the session object from a given URL.
+
Requires authentication handling (details TBD/outside this signature). *)
+
val get_session : url:Uri.t -> session
+118
output/jmap_submission.mli
···
+
(** JMAP Email Submission.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7> RFC 8621, Section 7 *)
+
+
open Jmap_types
+
open Jmap_methods
+
+
(** Address object for Envelope.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7> RFC 8621, Section 7 *)
+
type envelope_address = {
+
env_addr_email : string;
+
env_addr_parameters : Yojson.Safe.t string_map option;
+
}
+
+
(** Envelope object.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7> RFC 8621, Section 7 *)
+
type envelope = {
+
env_mail_from : envelope_address;
+
env_rcpt_to : envelope_address list;
+
}
+
+
(** Delivery status for a recipient.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7> RFC 8621, Section 7 *)
+
type delivery_status = {
+
delivery_smtp_reply : string;
+
delivery_delivered : [ `Queued | `Yes | `No | `Unknown ];
+
delivery_displayed : [ `Yes | `Unknown ];
+
}
+
+
(** EmailSubmission object.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7> RFC 8621, Section 7 *)
+
type email_submission = {
+
email_sub_id : id; (** immutable, server-set *)
+
identity_id : id; (** immutable *)
+
email_id : id; (** immutable *)
+
thread_id : id; (** immutable, server-set *)
+
envelope : envelope option; (** immutable *)
+
send_at : utc_date; (** immutable, server-set *)
+
undo_status : [ `Pending | `Final | `Canceled ];
+
delivery_status : delivery_status string_map option; (** server-set *)
+
dsn_blob_ids : id list; (** server-set *)
+
mdn_blob_ids : id list; (** server-set *)
+
}
+
+
(** EmailSubmission object for creation.
+
Excludes server-set fields.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7> RFC 8621, Section 7 *)
+
type email_submission_create = {
+
email_sub_create_identity_id : id;
+
email_sub_create_email_id : id;
+
email_sub_create_envelope : envelope option;
+
}
+
+
(** EmailSubmission object for update.
+
Only undoStatus can be updated (to 'canceled').
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7> RFC 8621, Section 7 *)
+
type email_submission_update = patch_object
+
+
(** Server-set info for created email submission.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7.5> RFC 8621, Section 7.5 *)
+
type email_submission_created_info = {
+
email_sub_created_id : id;
+
email_sub_created_thread_id : id;
+
email_sub_created_send_at : utc_date;
+
}
+
+
(** Server-set/computed info for updated email submission.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7.5> RFC 8621, Section 7.5 *)
+
type email_submission_updated_info = email_submission (* Contains only changed server-set props *)
+
+
(** FilterCondition for EmailSubmission/query.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7.3> RFC 8621, Section 7.3 *)
+
type email_submission_filter_condition = {
+
filter_identity_ids : id list option;
+
filter_email_ids : id list option;
+
filter_thread_ids : id list option;
+
filter_undo_status : [ `Pending | `Final | `Canceled ] option;
+
filter_before : utc_date option;
+
filter_after : utc_date option;
+
}
+
+
(** EmailSubmission/get: Args type (specialized from ['record get_args]). *)
+
type email_submission_get_args = email_submission get_args
+
+
(** EmailSubmission/get: Response type (specialized from ['record get_response]). *)
+
type email_submission_get_response = email_submission get_response
+
+
(** EmailSubmission/changes: Args type (specialized from [changes_args]). *)
+
type email_submission_changes_args = changes_args
+
+
(** EmailSubmission/changes: Response type (specialized from [changes_response]). *)
+
type email_submission_changes_response = changes_response
+
+
(** EmailSubmission/query: Args type (specialized from [query_args]). *)
+
type email_submission_query_args = query_args
+
+
(** EmailSubmission/query: Response type (specialized from [query_response]). *)
+
type email_submission_query_response = query_response
+
+
(** EmailSubmission/queryChanges: Args type (specialized from [query_changes_args]). *)
+
type email_submission_query_changes_args = query_changes_args
+
+
(** EmailSubmission/queryChanges: Response type (specialized from [query_changes_response]). *)
+
type email_submission_query_changes_response = query_changes_response
+
+
(** EmailSubmission/set: Args type (specialized from [('c, 'u) set_args]).
+
Includes onSuccess arguments.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-7.5> RFC 8621, Section 7.5 *)
+
type email_submission_set_args = {
+
set_account_id : id;
+
set_if_in_state : string option;
+
set_create : email_submission_create id_map option;
+
set_update : email_submission_update id_map option;
+
set_destroy : id list option;
+
set_on_success_destroy_email : id list option;
+
}
+
+
(** EmailSubmission/set: Response type (specialized from [('c, 'u) set_response]). *)
+
type email_submission_set_response = (email_submission_created_info, email_submission_updated_info) set_response
+24
output/jmap_thread.mli
···
+
(** JMAP Thread.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3> RFC 8621, Section 3 *)
+
+
open Jmap_types
+
open Jmap_methods
+
+
(** Thread object.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-3> RFC 8621, Section 3 *)
+
type thread = {
+
thread_id : id; (** immutable, server-set *)
+
email_ids : id list; (** server-set *)
+
}
+
+
(** Thread/get: Args type (specialized from ['record get_args]). *)
+
type thread_get_args = thread get_args
+
+
(** Thread/get: Response type (specialized from ['record get_response]). *)
+
type thread_get_response = thread get_response
+
+
(** Thread/changes: Args type (specialized from [changes_args]). *)
+
type thread_changes_args = changes_args
+
+
(** Thread/changes: Response type (specialized from [changes_response]). *)
+
type thread_changes_response = changes_response
+38
output/jmap_types.mli
···
+
(** Basic JMAP types as defined in RFC 8620. *)
+
+
(** The Id data type.
+
A string of 1 to 255 octets, using URL-safe base64 characters.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-1.2> RFC 8620, Section 1.2 *)
+
type id = string
+
+
(** The Int data type.
+
An integer in the range [-2^53+1, 2^53-1]. Represented as OCaml's standard [int].
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-1.3> RFC 8620, Section 1.3 *)
+
type jint = int
+
+
(** The UnsignedInt data type.
+
An integer in the range [0, 2^53-1]. Represented as OCaml's standard [int].
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-1.3> RFC 8620, Section 1.3 *)
+
type uint = int
+
+
(** The Date data type.
+
A string in RFC 3339 "date-time" format.
+
Represented as a float using Unix time.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-1.4> RFC 8620, Section 1.4 *)
+
type date = float
+
+
(** The UTCDate data type.
+
A string in RFC 3339 "date-time" format, restricted to UTC (Z timezone).
+
Represented as a float using Unix time.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-1.4> RFC 8620, Section 1.4 *)
+
type utc_date = float
+
+
(** Represents a JSON object used as a map String -> V. *)
+
type 'v string_map = (string, 'v) Hashtbl.t
+
+
(** Represents a JSON object used as a map Id -> V. *)
+
type 'v id_map = (id, 'v) Hashtbl.t
+
+
(** Represents a JSON Pointer path with JMAP extensions.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.7> RFC 8620, Section 3.7 *)
+
type json_pointer = string
+93
output/jmap_unix.mli
···
+
(** Unix-specific JMAP client implementation interface.
+
+
This module provides functions to interact with a JMAP server using
+
Unix sockets for network communication.
+
*)
+
+
open Jmap_types
+
open Jmap_wire
+
open Jmap_session
+
open Jmap_push
+
+
(** Represents an active JMAP connection context. Opaque type. *)
+
type context
+
+
(** Represents an active EventSource connection. Opaque type. *)
+
type event_source_connection
+
+
(** Exception raised for JMAP communication errors. *)
+
exception Jmap_error of Jmap_error.problem_details
+
+
(** Connect to a JMAP server and retrieve the session.
+
This handles discovery (if needed) and authentication (mechanism TBD).
+
[host] is the server hostname.
+
[port] is the server port (default 443 if TLS enabled, TBD if not).
+
[credentials] is an opaque type representing authentication credentials.
+
@param ?session_url Optional direct URL to the Session resource.
+
@param ?username Optional username (e.g., email address) for discovery.
+
*)
+
val connect :
+
?session_url:Uri.t ->
+
?username:string ->
+
host:string ->
+
?port:int ->
+
credentials:'a ->
+
(context * session)
+
+
(** Perform a JMAP API request.
+
@param ctx The connection context.
+
@param request The JMAP request object.
+
*)
+
val request : context -> request -> response
+
+
(** Upload binary data.
+
@param ctx The connection context.
+
@param account_id The target account ID.
+
@param content_type The MIME type of the data.
+
@param data_stream A stream providing the binary data chunks.
+
*)
+
val upload :
+
context ->
+
account_id:id ->
+
content_type:string ->
+
data_stream:string Seq.t ->
+
Jmap_binary.upload_response
+
+
(** Download binary data.
+
@param ctx The connection context.
+
@param account_id The account ID.
+
@param blob_id The blob ID to download.
+
@param content_type The desired Content-Type for the download response.
+
@param name The desired filename for the download response.
+
@return A stream producing the binary data chunks.
+
*)
+
val download :
+
context ->
+
account_id:id ->
+
blob_id:id ->
+
content_type:string ->
+
name:string ->
+
string Seq.t
+
+
(** Connect to the EventSource for push notifications.
+
@param ctx The connection context.
+
@param ?types List of types to subscribe to (default "*").
+
@param ?close_after Request server to close after first state event.
+
@param ?ping Request ping interval in seconds (default 0).
+
@return A tuple of the connection handle and a stream of push events.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-7.3> RFC 8620, Section 7.3 *)
+
val connect_event_source :
+
context ->
+
?types:string list ->
+
?close_after:[`State | `No] ->
+
?ping:uint ->
+
(event_source_connection *
+
([`State of state_change | `Ping of event_source_ping_data ] Seq.t))
+
+
(** Close an EventSource connection.
+
@param conn The EventSource connection handle.
+
*)
+
val close_event_source : event_source_connection -> unit
+
+
(** Close the JMAP connection context. *)
+
val close : context -> unit
+49
output/jmap_vacation.mli
···
+
(** JMAP Vacation Response.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-8> RFC 8621, Section 8 *)
+
+
open Jmap_types
+
open Jmap_methods
+
open Jmap_error
+
+
(** VacationResponse object.
+
Note: id is always "singleton".
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-8> RFC 8621, Section 8 *)
+
type vacation_response_obj = {
+
vacation_id : id; (** immutable, server-set, MUST be "singleton" *)
+
is_enabled : bool;
+
from_date : utc_date option;
+
to_date : utc_date option;
+
subject : string option;
+
text_body : string option;
+
html_body : string option;
+
}
+
+
(** VacationResponse object for update.
+
Patch object, specific structure not enforced here.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-8.2> RFC 8621, Section 8.2 *)
+
type vacation_response_update = patch_object
+
+
(** VacationResponse/get: Args type (specialized from ['record get_args]). *)
+
type vacation_response_get_args = vacation_response_obj get_args
+
+
(** VacationResponse/get: Response type (specialized from ['record get_response]). *)
+
type vacation_response_get_response = vacation_response_obj get_response
+
+
(** VacationResponse/set: Args type.
+
Only allows update, id must be "singleton".
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-8.2> RFC 8621, Section 8.2 *)
+
type vacation_response_set_args = {
+
set_account_id : id;
+
set_if_in_state : string option;
+
set_update : vacation_response_update id_map option;
+
}
+
+
(** VacationResponse/set: Response type.
+
@see <https://www.rfc-editor.org/rfc/rfc8621.html#section-8.2> RFC 8621, Section 8.2 *)
+
type vacation_response_set_response = {
+
set_resp_account_id : id;
+
set_resp_old_state : string option;
+
set_resp_new_state : string;
+
set_resp_updated : vacation_response_obj option id_map option;
+
set_resp_not_updated : set_error id_map option;
+
}
+43
output/jmap_wire.mli
···
+
(** JMAP Wire Protocol Structures (Request/Response). *)
+
+
open Jmap_types
+
open Jmap_error
+
+
(** An invocation tuple within a request or response.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.2> RFC 8620, Section 3.2 *)
+
type invocation = {
+
method_name : string;
+
arguments : Yojson.Safe.t;
+
method_call_id : string;
+
}
+
+
(** A reference to a previous method call's result.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.7> RFC 8620, Section 3.7 *)
+
type result_reference = {
+
result_of : string; (** The method call id of the previous call *)
+
name : string; (** The required response name *)
+
path : json_pointer; (** JSON Pointer path *)
+
}
+
+
(** The Request object.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.3> RFC 8620, Section 3.3 *)
+
type request = {
+
using : string list;
+
method_calls : invocation list;
+
created_ids : id id_map option;
+
}
+
+
(** A response invocation part, which can be a standard response or an error.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.4> RFC 8620, Section 3.4
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.6.2> RFC 8620, Section 3.6.2 *)
+
type response_invocation =
+
| Response of invocation
+
| Error of method_error * string (* string is the method_call_id *)
+
+
(** The Response object.
+
@see <https://www.rfc-editor.org/rfc/rfc8620.html#section-3.4> RFC 8620, Section 3.4 *)
+
type response = {
+
method_responses : response_invocation list;
+
created_ids : id id_map option;
+
session_state : string;
+
}
+94
rfc.py
···
+
# /// script
+
# dependencies = [
+
# "google-genai"
+
# ]
+
# ///
+
import os
+
import json
+
from pydantic import BaseModel
+
from google import genai
+
+
class OCamlFile(BaseModel):
+
filename: str
+
contents: str
+
+
# Create output directory if it doesn't exist
+
output_dir = "output"
+
if not os.path.exists(output_dir):
+
os.makedirs(output_dir)
+
+
# Read the RFC text files
+
with open("rfc8620.txt", "r") as f:
+
rfc8620_content = f.read()
+
+
with open("rfc8621.txt", "r") as f:
+
rfc8621_content = f.read()
+
+
# Read API key from file
+
with open("api-key", "r") as f:
+
api_key = f.read().strip()
+
+
client = genai.Client(api_key=api_key)
+
+
prompt = """I wish to generate a set of OCaml module signatures and types (no implementations) that will type check, for an implementation of the JMAP protocol (RFC8620) and the associated email extensions (RFC8621). The code you generate should have ocamldoc that references the relevant sections of the RFC it is implementing, using <https://www.rfc-editor.org/rfc/rfc8620.html#section-1.2> as a template for the hyperlinks (replace the fragment with the appropriate section identifier).
+
+
The architecture of the modules should be one portable set that implement core JMAP (RFC8620) as an OCaml module called `Jmap` (with module aliases to the submodules that implement that). Then generate another set of modules that implement the email-specific extensions (RFC8621) including flag handling for (e.g.) Apple Mail under a module called `Jmap_email`. These should all be portable OCaml type signatures (the mli files), and then generate another module that implements the interface for a Unix implementation that uses the Unix module to perform real connections. You do not need to implement TLS support for this first iteration of the code interfaces.
+
+
You should also generate a module index file called jmap.mli that explains how all the generated modules fit together, along with a sketch of some example OCaml code that uses it to connect to a JMAP server and list recent unread emails from a particular sender.
+
+
When selecting dependencies, ONLY use Yojson, Uri and Unix in your type signatures aside from the OCaml standard library. The standard Hashtbl is fine for any k/v datastructures and do not use Maps or other functor applications for this. DO NOT generate any AST attributes, and do not use any PPX derivers or other syntax extensions. Just generate clean, conventional OCaml type signatures.
+
+
Also generate the dune scaffolding in your output suitable to build the project. This is:
+
1) a dune-project file that contains "(lang dune 3.17)"
+
2) a dune file with:
+
+
(library
+
(name jmap)
+
(public_name jmap)
+
(libraries yojson uri)
+
(modules_without_implementation jmap <the other modules you generated>))
+
+
+
Output each file you generate in JSON format.
+
+
File = {'filename': str, 'contents': str}
+
Return: list[File]
+
"""
+
+
response = client.models.generate_content(
+
model="gemini-2.5-pro-exp-03-25",
+
contents=[rfc8620_content, rfc8621_content, prompt],
+
config={
+
'response_mime_type': 'application/json',
+
'response_schema': list[OCamlFile],
+
},
+
)
+
+
try:
+
# Use the Pydantic-parsed response
+
files: list[OCamlFile] = response.parsed
+
+
# Iterate through each file and write it to the output directory
+
for file_entry in files:
+
filename = file_entry.filename
+
contents = file_entry.contents
+
+
if filename and contents:
+
output_path = os.path.join(output_dir, filename)
+
with open(output_path, 'w') as f:
+
f.write(contents)
+
print(f"Saved {filename} to {output_path}")
+
+
# Also save the full JSON response for reference
+
with open(os.path.join(output_dir, "full_response.json"), 'w') as f:
+
json.dump([{"filename": file.filename, "contents": file.contents} for file in files], f, indent=2)
+
print(f"Saved full response to {os.path.join(output_dir, 'full_response.json')}")
+
+
except json.JSONDecodeError:
+
print("Failed to parse response as JSON. Raw response:")
+
print(response.text)
+
+
# Save the raw response for debugging
+
with open(os.path.join(output_dir, "raw_response.txt"), 'w') as f:
+
f.write(response.text)
+
print(f"Saved raw response to {os.path.join(output_dir, 'raw_response.txt')}")
+5043
rfc8620.txt
···
+
+
+
+
+
+
+
Internet Engineering Task Force (IETF) N. Jenkins
+
Request for Comments: 8620 Fastmail
+
Category: Standards Track C. Newman
+
ISSN: 2070-1721 Oracle
+
July 2019
+
+
+
The JSON Meta Application Protocol (JMAP)
+
+
Abstract
+
+
This document specifies a protocol for clients to efficiently query,
+
fetch, and modify JSON-based data objects, with support for push
+
notification of changes and fast resynchronisation and for out-of-
+
band binary data upload/download.
+
+
Status of This Memo
+
+
This is an Internet Standards Track document.
+
+
This document is a product of the Internet Engineering Task Force
+
(IETF). It represents the consensus of the IETF community. It has
+
received public review and has been approved for publication by the
+
Internet Engineering Steering Group (IESG). Further information on
+
Internet Standards is available in Section 2 of RFC 7841.
+
+
Information about the current status of this document, any errata,
+
and how to provide feedback on it may be obtained at
+
https://www.rfc-editor.org/info/rfc8620.
+
+
Copyright Notice
+
+
Copyright (c) 2019 IETF Trust and the persons identified as the
+
document authors. All rights reserved.
+
+
This document is subject to BCP 78 and the IETF Trust's Legal
+
Provisions Relating to IETF Documents
+
(https://trustee.ietf.org/license-info) in effect on the date of
+
publication of this document. Please review these documents
+
carefully, as they describe your rights and restrictions with respect
+
to this document. Code Components extracted from this document must
+
include Simplified BSD License text as described in Section 4.e of
+
the Trust Legal Provisions and are provided without warranty as
+
described in the Simplified BSD License.
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 1]
+
+
RFC 8620 JMAP July 2019
+
+
+
Table of Contents
+
+
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 4
+
1.1. Notational Conventions . . . . . . . . . . . . . . . . . 4
+
1.2. The Id Data Type . . . . . . . . . . . . . . . . . . . . 6
+
1.3. The Int and UnsignedInt Data Types . . . . . . . . . . . 6
+
1.4. The Date and UTCDate Data Types . . . . . . . . . . . . . 7
+
1.5. JSON as the Data Encoding Format . . . . . . . . . . . . 7
+
1.6. Terminology . . . . . . . . . . . . . . . . . . . . . . . 7
+
1.6.1. User . . . . . . . . . . . . . . . . . . . . . . . . 7
+
1.6.2. Accounts . . . . . . . . . . . . . . . . . . . . . . 7
+
1.6.3. Data Types and Records . . . . . . . . . . . . . . . 8
+
1.7. The JMAP API Model . . . . . . . . . . . . . . . . . . . 8
+
1.8. Vendor-Specific Extensions . . . . . . . . . . . . . . . 9
+
2. The JMAP Session Resource . . . . . . . . . . . . . . . . . . 9
+
2.1. Example . . . . . . . . . . . . . . . . . . . . . . . . . 14
+
2.2. Service Autodiscovery . . . . . . . . . . . . . . . . . . 15
+
3. Structured Data Exchange . . . . . . . . . . . . . . . . . . 16
+
3.1. Making an API Request . . . . . . . . . . . . . . . . . . 16
+
3.2. The Invocation Data Type . . . . . . . . . . . . . . . . 16
+
3.3. The Request Object . . . . . . . . . . . . . . . . . . . 16
+
3.3.1. Example Request . . . . . . . . . . . . . . . . . . . 18
+
3.4. The Response Object . . . . . . . . . . . . . . . . . . . 18
+
3.4.1. Example Response . . . . . . . . . . . . . . . . . . 19
+
3.5. Omitting Arguments . . . . . . . . . . . . . . . . . . . 19
+
3.6. Errors . . . . . . . . . . . . . . . . . . . . . . . . . 19
+
3.6.1. Request-Level Errors . . . . . . . . . . . . . . . . 20
+
3.6.2. Method-Level Errors . . . . . . . . . . . . . . . . . 21
+
3.7. References to Previous Method Results . . . . . . . . . . 22
+
3.8. Localisation of User-Visible Strings . . . . . . . . . . 27
+
3.9. Security . . . . . . . . . . . . . . . . . . . . . . . . 28
+
3.10. Concurrency . . . . . . . . . . . . . . . . . . . . . . . 28
+
4. The Core/echo Method . . . . . . . . . . . . . . . . . . . . 28
+
4.1. Example . . . . . . . . . . . . . . . . . . . . . . . . . 28
+
5. Standard Methods and Naming Convention . . . . . . . . . . . 29
+
5.1. /get . . . . . . . . . . . . . . . . . . . . . . . . . . 29
+
5.2. /changes . . . . . . . . . . . . . . . . . . . . . . . . 30
+
5.3. /set . . . . . . . . . . . . . . . . . . . . . . . . . . 34
+
5.4. /copy . . . . . . . . . . . . . . . . . . . . . . . . . . 40
+
5.5. /query . . . . . . . . . . . . . . . . . . . . . . . . . 42
+
5.6. /queryChanges . . . . . . . . . . . . . . . . . . . . . . 48
+
5.7. Examples . . . . . . . . . . . . . . . . . . . . . . . . 51
+
5.8. Proxy Considerations . . . . . . . . . . . . . . . . . . 58
+
6. Binary Data . . . . . . . . . . . . . . . . . . . . . . . . . 58
+
6.1. Uploading Binary Data . . . . . . . . . . . . . . . . . . 59
+
6.2. Downloading Binary Data . . . . . . . . . . . . . . . . . 60
+
6.3. Blob/copy . . . . . . . . . . . . . . . . . . . . . . . . 61
+
+
+
+
+
Jenkins & Newman Standards Track [Page 2]
+
+
RFC 8620 JMAP July 2019
+
+
+
7. Push . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
+
7.1. The StateChange Object . . . . . . . . . . . . . . . . . 63
+
7.1.1. Example . . . . . . . . . . . . . . . . . . . . . . . 64
+
7.2. PushSubscription . . . . . . . . . . . . . . . . . . . . 64
+
7.2.1. PushSubscription/get . . . . . . . . . . . . . . . . 67
+
7.2.2. PushSubscription/set . . . . . . . . . . . . . . . . 68
+
7.2.3. Example . . . . . . . . . . . . . . . . . . . . . . . 69
+
7.3. Event Source . . . . . . . . . . . . . . . . . . . . . . 71
+
8. Security Considerations . . . . . . . . . . . . . . . . . . . 73
+
8.1. Transport Confidentiality . . . . . . . . . . . . . . . . 73
+
8.2. Authentication Scheme . . . . . . . . . . . . . . . . . . 73
+
8.3. Service Autodiscovery . . . . . . . . . . . . . . . . . . 73
+
8.4. JSON Parsing . . . . . . . . . . . . . . . . . . . . . . 74
+
8.5. Denial of Service . . . . . . . . . . . . . . . . . . . . 74
+
8.6. Connection to Unknown Push Server . . . . . . . . . . . . 74
+
8.7. Push Encryption . . . . . . . . . . . . . . . . . . . . . 75
+
8.8. Traffic Analysis . . . . . . . . . . . . . . . . . . . . 76
+
9. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 76
+
9.1. Assignment of jmap Service Name . . . . . . . . . . . . . 76
+
9.2. Registration of Well-Known URI Suffix for JMAP . . . . . 76
+
9.3. Registration of the jmap URN Sub-namespace . . . . . . . 77
+
9.4. Creation of "JMAP Capabilities" Registry . . . . . . . . 77
+
9.4.1. Preliminary Community Review . . . . . . . . . . . . 77
+
9.4.2. Submit Request to IANA . . . . . . . . . . . . . . . 78
+
9.4.3. Designated Expert Review . . . . . . . . . . . . . . 78
+
9.4.4. Change Procedures . . . . . . . . . . . . . . . . . . 78
+
9.4.5. JMAP Capabilities Registry Template . . . . . . . . . 79
+
9.4.6. Initial Registration for JMAP Core . . . . . . . . . 79
+
9.4.7. Registration for JMAP Error Placeholder in JMAP
+
Capabilities Registry . . . . . . . . . . . . . . . . 80
+
9.5. Creation of "JMAP Error Codes" Registry . . . . . . . . . 80
+
9.5.1. Expert Review . . . . . . . . . . . . . . . . . . . . 80
+
9.5.2. JMAP Error Codes Registry Template . . . . . . . . . 81
+
9.5.3. Initial Contents for the JMAP Error Codes Registry . 81
+
10. References . . . . . . . . . . . . . . . . . . . . . . . . . 86
+
10.1. Normative References . . . . . . . . . . . . . . . . . . 86
+
10.2. Informative References . . . . . . . . . . . . . . . . . 89
+
Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 90
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 3]
+
+
RFC 8620 JMAP July 2019
+
+
+
1. Introduction
+
+
The JSON Meta Application Protocol (JMAP) is used for synchronising
+
data, such as mail, calendars, or contacts, between a client and a
+
server. It is optimised for mobile and web environments and aims to
+
provide a consistent interface to different data types.
+
+
This specification is for the generic mechanism of data
+
synchronisation. Further specifications define the data models for
+
different data types that may be synchronised via JMAP.
+
+
JMAP is designed to make efficient use of limited network resources.
+
Multiple API calls may be batched in a single request to the server,
+
reducing round trips and improving battery life on mobile devices.
+
Push connections remove the need for polling, and an efficient delta
+
update mechanism ensures a minimum amount of data is transferred.
+
+
JMAP is designed to be horizontally scalable to a very large number
+
of users. This is facilitated by separate endpoints for users after
+
login, the separation of binary and structured data, and a data model
+
for sharing that does not allow data dependencies between accounts.
+
+
1.1. Notational Conventions
+
+
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+
"SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
+
"OPTIONAL" in this document are to be interpreted as described in
+
BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all
+
capitals, as shown here.
+
+
The underlying format used for this specification is JSON.
+
Consequently, the terms "object" and "array" as well as the four
+
primitive types (strings, numbers, booleans, and null) are to be
+
interpreted as described in Section 1 of [RFC8259]. Unless otherwise
+
noted, all the property names and values are case sensitive.
+
+
Some examples in this document contain "partial" JSON documents used
+
for illustrative purposes. In these examples, three periods "..."
+
are used to indicate a portion of the document that has been removed
+
for compactness.
+
+
For compatibility with publishing requirements, line breaks have been
+
inserted inside long JSON strings, with the following continuation
+
lines indented. To form the valid JSON example, any line breaks
+
inside a string must be replaced with a space and any other white
+
space after the line break removed.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 4]
+
+
RFC 8620 JMAP July 2019
+
+
+
Unless otherwise specified, examples of API exchanges only show the
+
methodCalls array of the Request object or the methodResponses array
+
of the Response object. For compactness, the rest of the Request/
+
Response object is omitted.
+
+
Type signatures are given for all JSON values in this document. The
+
following conventions are used:
+
+
o "*" - The type is undefined (the value could be any type, although
+
permitted values may be constrained by the context of this value).
+
+
o "String" - The JSON string type.
+
+
o "Number" - The JSON number type.
+
+
o "Boolean" - The JSON boolean type.
+
+
o "A[B]" - A JSON object where the keys are all of type "A", and the
+
values are all of type "B".
+
+
o "A[]" - An array of values of type "A".
+
+
o "A|B" - The value is either of type "A" or of type "B".
+
+
Other types may also be given, with their representation defined
+
elsewhere in this document.
+
+
Object properties may also have a set of attributes defined along
+
with the type signature. These have the following meanings:
+
+
o "server-set" -- Only the server can set the value for this
+
property. The client MUST NOT send this property when creating a
+
new object of this type.
+
+
o "immutable" -- The value MUST NOT change after the object is
+
created.
+
+
o "default" -- (This is followed by a JSON value). The value that
+
will be used for this property if it is omitted in an argument or
+
when creating a new object of this type.
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 5]
+
+
RFC 8620 JMAP July 2019
+
+
+
1.2. The Id Data Type
+
+
All record ids are assigned by the server and are immutable.
+
+
Where "Id" is given as a data type, it means a "String" of at least 1
+
and a maximum of 255 octets in size, and it MUST only contain
+
characters from the "URL and Filename Safe" base64 alphabet, as
+
defined in Section 5 of [RFC4648], excluding the pad character ("=").
+
This means the allowed characters are the ASCII alphanumeric
+
characters ("A-Za-z0-9"), hyphen ("-"), and underscore ("_").
+
+
These characters are safe to use in almost any context (e.g.,
+
filesystems, URIs, and IMAP atoms). For maximum safety, servers
+
SHOULD also follow defensive allocation strategies to avoid creating
+
risks where glob completion or data type detection may be present
+
(e.g., on filesystems or in spreadsheets). In particular, it is wise
+
to avoid:
+
+
o Ids starting with a dash
+
+
o Ids starting with digits
+
+
o Ids that contain only digits
+
+
o Ids that differ only by ASCII case (for example, A vs. a)
+
+
o the specific sequence of three characters "NIL" (because this
+
sequence can be confused with the IMAP protocol expression of the
+
null value)
+
+
A good solution to these issues is to prefix every id with a single
+
alphabetical character.
+
+
1.3. The Int and UnsignedInt Data Types
+
+
Where "Int" is given as a data type, it means an integer in the range
+
-2^53+1 <= value <= 2^53-1, the safe range for integers stored in a
+
floating-point double, represented as a JSON "Number".
+
+
Where "UnsignedInt" is given as a data type, it means an "Int" where
+
the value MUST be in the range 0 <= value <= 2^53-1.
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 6]
+
+
RFC 8620 JMAP July 2019
+
+
+
1.4. The Date and UTCDate Data Types
+
+
Where "Date" is given as a type, it means a string in "date-time"
+
format [RFC3339]. To ensure a normalised form, the "time-secfrac"
+
MUST always be omitted if zero, and any letters in the string (e.g.,
+
"T" and "Z") MUST be uppercase. For example,
+
"2014-10-30T14:12:00+08:00".
+
+
Where "UTCDate" is given as a type, it means a "Date" where the
+
"time-offset" component MUST be "Z" (i.e., it must be in UTC time).
+
For example, "2014-10-30T06:12:00Z".
+
+
1.5. JSON as the Data Encoding Format
+
+
JSON is a text-based data interchange format as specified in
+
[RFC8259]. The Internet JSON (I-JSON) format defined in [RFC7493] is
+
a strict subset of this, adding restrictions to avoid potentially
+
confusing scenarios (for example, it mandates that an object MUST NOT
+
have two members with the same name).
+
+
All data sent from the client to the server or from the server to the
+
client (except binary file upload/download) MUST be valid I-JSON
+
according to the RFC and is therefore case sensitive and encoded in
+
UTF-8 [RFC3629].
+
+
1.6. Terminology
+
+
1.6.1. User
+
+
A user is a person accessing data via JMAP. A user has a set of
+
permissions determining the data that they can see.
+
+
1.6.2. Accounts
+
+
An account is a collection of data. A single account may contain an
+
arbitrary set of data types, for example, a collection of mail,
+
contacts, and calendars. Most JMAP methods take a mandatory
+
"accountId" argument that specifies on which account the operations
+
are to take place.
+
+
An account is not the same as a user, although it is common for a
+
primary account to directly belong to the user. For example, you may
+
have an account that contains data for a group or business, to which
+
multiple users have access.
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 7]
+
+
RFC 8620 JMAP July 2019
+
+
+
A single set of credentials may provide access to multiple accounts,
+
for example, if another user is sharing their work calendar with the
+
authenticated user or if there is a group mailbox for a support-desk
+
inbox.
+
+
In the event of a severe internal error, a server may have to
+
reallocate ids or do something else that violates standard JMAP data
+
constraints for an account. In this situation, the data on the
+
server is no longer compatible with cached data the client may have
+
from before. The server MUST treat this as though the account has
+
been deleted and then recreated with a new account id. Clients will
+
then be forced to throw away any data with the old account id and
+
refetch all data from scratch.
+
+
1.6.3. Data Types and Records
+
+
JMAP provides a uniform interface for creating, retrieving, updating,
+
and deleting various types of objects. A "data type" is a collection
+
of named, typed properties, just like the schema for a database
+
table. Each instance of a data type is called a "record".
+
+
The id of a record is immutable and assigned by the server. The id
+
MUST be unique among all records of the *same type* within the *same
+
account*. Ids may clash across accounts or for two records of
+
different types within the same account.
+
+
1.7. The JMAP API Model
+
+
JMAP uses HTTP [RFC7230] to expose API, push, upload, and download
+
resources. All HTTP requests MUST use the "https://" scheme (HTTP
+
over TLS [RFC2818]). All HTTP requests MUST be authenticated.
+
+
An authenticated client can fetch the user's Session object with
+
details about the data and capabilities the server can provide as
+
shown in Section 2. The client may then exchange data with the
+
server in the following ways:
+
+
1. The client may make an API request to the server to get or set
+
structured data. This request consists of an ordered series of
+
method calls. These are processed by the server, which then
+
returns an ordered series of responses. This is described in
+
Sections 3, 4, and 5.
+
+
2. The client may download or upload binary files from/to the
+
server. This is detailed in Section 6.
+
+
3. The client may connect to a push channel on the server, to be
+
notified when data has changed. This is explained in Section 7.
+
+
+
+
Jenkins & Newman Standards Track [Page 8]
+
+
RFC 8620 JMAP July 2019
+
+
+
1.8. Vendor-Specific Extensions
+
+
Individual services will have custom features they wish to expose
+
over JMAP. This may take the form of extra data types and/or methods
+
not in the spec, extra arguments to JMAP methods, or extra properties
+
on existing data types (which may also appear in arguments to methods
+
that take property names).
+
+
The server can advertise custom extensions it supports by including
+
the identifiers in the capabilities object. Identifiers for vendor
+
extensions MUST be a URL belonging to a domain owned by the vendor,
+
to avoid conflict. The URL SHOULD resolve to documentation for the
+
changes the extension makes.
+
+
The client MUST opt in to use an extension by passing the appropriate
+
capability identifier in the "using" array of the Request object, as
+
described in Section 3.3. The server MUST only follow the
+
specifications that are opted into and behave as though it does not
+
implement anything else when processing a request. This is to ensure
+
compatibility with clients that don't know about a specific custom
+
extension and for compatibility with future versions of JMAP.
+
+
2. The JMAP Session Resource
+
+
You need two things to connect to a JMAP server:
+
+
1. The URL for the JMAP Session resource. This may be requested
+
directly from the user or discovered automatically based on a
+
username domain (see Section 2.2 below).
+
+
2. Credentials to authenticate with. How to obtain credentials is
+
out of scope for this document.
+
+
A successful authenticated GET request to the JMAP Session resource
+
MUST return a JSON-encoded *Session* object, giving details about the
+
data and capabilities the server can provide to the client given
+
those credentials. It has the following properties:
+
+
o capabilities: "String[Object]"
+
+
An object specifying the capabilities of this server. Each key is
+
a URI for a capability supported by the server. The value for
+
each of these keys is an object with further information about the
+
server's capabilities in relation to that capability.
+
+
The client MUST ignore any properties it does not understand.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 9]
+
+
RFC 8620 JMAP July 2019
+
+
+
The capabilities object MUST include a property called
+
"urn:ietf:params:jmap:core". The value of this property is an
+
object that MUST contain the following information on server
+
capabilities (suggested minimum values for limits are supplied
+
that allow clients to make efficient use of the network):
+
+
* maxSizeUpload: "UnsignedInt"
+
+
The maximum file size, in octets, that the server will accept
+
for a single file upload (for any purpose). Suggested minimum:
+
50,000,000.
+
+
* maxConcurrentUpload: "UnsignedInt"
+
+
The maximum number of concurrent requests the server will
+
accept to the upload endpoint. Suggested minimum: 4.
+
+
* maxSizeRequest: "UnsignedInt"
+
+
The maximum size, in octets, that the server will accept for a
+
single request to the API endpoint. Suggested minimum:
+
10,000,000.
+
+
* maxConcurrentRequests: "UnsignedInt"
+
+
The maximum number of concurrent requests the server will
+
accept to the API endpoint. Suggested minimum: 4.
+
+
* maxCallsInRequest: "UnsignedInt"
+
+
The maximum number of method calls the server will accept in a
+
single request to the API endpoint. Suggested minimum: 16.
+
+
* maxObjectsInGet: "UnsignedInt"
+
+
The maximum number of objects that the client may request in a
+
single /get type method call. Suggested minimum: 500.
+
+
* maxObjectsInSet: "UnsignedInt"
+
+
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. Suggested minimum: 500.
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 10]
+
+
RFC 8620 JMAP July 2019
+
+
+
* collationAlgorithms: "String[]"
+
+
A list of identifiers for algorithms registered in the
+
collation registry, as defined in [RFC4790], that the server
+
supports for sorting when querying records.
+
+
Specifications for future capabilities will define their own
+
properties on the capabilities object.
+
+
Servers MAY advertise vendor-specific JMAP extensions, as
+
described in Section 1.8. To avoid conflict, an identifier for a
+
vendor-specific extension MUST be a URL with a domain owned by the
+
vendor. Clients MUST opt in to any capability it wishes to use
+
(see Section 3.3).
+
+
o accounts: "Id[Account]"
+
+
A map of an account id to an Account object for each account (see
+
Section 1.6.2) the user has access to. An *Account* object has
+
the following properties:
+
+
* name: "String"
+
+
A user-friendly string to show when presenting content from
+
this account, e.g., the email address representing the owner of
+
the account.
+
+
* isPersonal: "Boolean"
+
+
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.
+
+
* isReadOnly: "Boolean"
+
+
This is true if the entire account is read-only.
+
+
* accountCapabilities: "String[Object]"
+
+
The set of capability URIs for the methods supported in this
+
account. Each key is a URI for a capability that has methods
+
you can use with this account. The value for each of these
+
keys is an object with further information about the account's
+
permissions and restrictions with respect to this capability,
+
as defined in the capability's specification.
+
+
The client MUST ignore any properties it does not understand.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 11]
+
+
RFC 8620 JMAP July 2019
+
+
+
The server advertises the full list of capabilities it supports
+
in the capabilities object, as defined above. If the
+
capability defines new methods, the server MUST include it in
+
the accountCapabilities object if the user may use those
+
methods with this account. It MUST NOT include it in the
+
accountCapabilities object if the user cannot use those methods
+
with this account.
+
+
For example, you may have access to your own account with mail,
+
calendars, and contacts data and also a shared account that
+
only has contacts data (a business address book, for example).
+
In this case, the accountCapabilities property on the first
+
account would include something like
+
"urn:ietf:params:jmap:mail", "urn:ietf:params:jmap:calendars",
+
and "urn:ietf:params:jmap:contacts", while the second account
+
would just have the last of these.
+
+
Attempts to use the methods defined in a capability with one of
+
the accounts that does not support that capability are rejected
+
with an "accountNotSupportedByMethod" error (see "Method-Level
+
Errors", Section 3.6.2).
+
+
o primaryAccounts: "String[Id]"
+
+
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.
+
+
o username: "String"
+
+
The username associated with the given credentials, or the empty
+
string if none.
+
+
o apiUrl: "String"
+
+
The URL to use for JMAP API requests.
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 12]
+
+
RFC 8620 JMAP July 2019
+
+
+
o downloadUrl: "String"
+
+
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". The use of these
+
variables is described in Section 6.2. Due to potential encoding
+
issues with slashes in content types, it is RECOMMENDED to put the
+
"type" variable in the query section of the URL.
+
+
o uploadUrl: "String"
+
+
The URL endpoint to use when uploading files, in URI Template
+
(level 1) format [RFC6570]. The URL MUST contain a variable
+
called "accountId". The use of this variable is described in
+
Section 6.1.
+
+
o eventSourceUrl: "String"
+
+
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".
+
The use of these variables is described in Section 7.3.
+
+
o state: "String"
+
+
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.
+
+
To ensure future compatibility, other properties MAY be included on
+
the Session object. Clients MUST ignore any properties they are not
+
expecting.
+
+
Implementors must take care to avoid inappropriate caching of the
+
Session object at the HTTP layer. Since the client should only
+
refetch when it detects there is a change (via the sessionState
+
property of an API response), it is RECOMMENDED to disable HTTP
+
caching altogether, for example, by setting "Cache-Control: no-cache,
+
no-store, must-revalidate" on the response.
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 13]
+
+
RFC 8620 JMAP July 2019
+
+
+
2.1. Example
+
+
In the following example Session object, the user has access to their
+
own mail and contacts via JMAP, as well as read-only access to shared
+
mail from another user. The server is advertising a custom
+
"https://example.com/apis/foobar" capability.
+
+
{
+
"capabilities": {
+
"urn:ietf:params:jmap:core": {
+
"maxSizeUpload": 50000000,
+
"maxConcurrentUpload": 8,
+
"maxSizeRequest": 10000000,
+
"maxConcurrentRequest": 8,
+
"maxCallsInRequest": 32,
+
"maxObjectsInGet": 256,
+
"maxObjectsInSet": 128,
+
"collationAlgorithms": [
+
"i;ascii-numeric",
+
"i;ascii-casemap",
+
"i;unicode-casemap"
+
]
+
},
+
"urn:ietf:params:jmap:mail": {}
+
"urn:ietf:params:jmap:contacts": {},
+
"https://example.com/apis/foobar": {
+
"maxFoosFinangled": 42
+
}
+
},
+
"accounts": {
+
"A13824": {
+
"name": "john@example.com",
+
"isPersonal": true,
+
"isReadOnly": false,
+
"accountCapabilities": {
+
"urn:ietf:params:jmap:mail": {
+
"maxMailboxesPerEmail": null,
+
"maxMailboxDepth": 10,
+
...
+
},
+
"urn:ietf:params:jmap:contacts": {
+
...
+
}
+
}
+
},
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 14]
+
+
RFC 8620 JMAP July 2019
+
+
+
"A97813": {
+
"name": "jane@example.com",
+
"isPersonal": false,
+
"isReadOnly": true,
+
"accountCapabilities": {
+
"urn:ietf:params:jmap:mail": {
+
"maxMailboxesPerEmail": 1,
+
"maxMailboxDepth": 10,
+
...
+
}
+
}
+
}
+
},
+
"primaryAccounts": {
+
"urn:ietf:params:jmap:mail": "A13824",
+
"urn:ietf:params:jmap:contacts": "A13824"
+
},
+
"username": "john@example.com",
+
"apiUrl": "https://jmap.example.com/api/",
+
"downloadUrl": "https://jmap.example.com
+
/download/{accountId}/{blobId}/{name}?accept={type}",
+
"uploadUrl": "https://jmap.example.com/upload/{accountId}/",
+
"eventSourceUrl": "https://jmap.example.com
+
/eventsource/?types={types}&closeafter={closeafter}&ping={ping}",
+
"state": "75128aab4b1b"
+
}
+
+
2.2. Service Autodiscovery
+
+
There are two standardised autodiscovery methods in use for Internet
+
protocols:
+
+
o DNS SRV (see [RFC2782], [RFC6186], and [RFC6764])
+
+
o .well-known/servicename (see [RFC8615])
+
+
A JMAP-supporting host for the domain "example.com" SHOULD publish a
+
SRV record "_jmap._tcp.example.com" that gives a hostname and port
+
(usually port "443"). The JMAP Session resource is then
+
"https://${hostname}[:${port}]/.well-known/jmap" (following any
+
redirects).
+
+
If the client has a username in the form of an email address, it MAY
+
use the domain portion of this to attempt autodiscovery of the JMAP
+
server.
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 15]
+
+
RFC 8620 JMAP July 2019
+
+
+
3. Structured Data Exchange
+
+
The client may make an API request to the server to get or set
+
structured data. This request consists of an ordered series of
+
method calls. These are processed by the server, which then returns
+
an ordered series of responses.
+
+
3.1. Making an API Request
+
+
To make an API request, the client makes an authenticated POST
+
request to the API resource, which is defined by the "apiUrl"
+
property in the Session object (see Section 2).
+
+
The request MUST be of type "application/json" and consist of a
+
single JSON-encoded "Request" object, as defined in Section 3.3. If
+
successful, the response MUST also be of type "application/json" and
+
consist of a single "Response" object, as defined in Section 3.4.
+
+
3.2. The Invocation Data Type
+
+
Method calls and responses are represented by the *Invocation* data
+
type. This is a tuple, represented as a JSON array containing three
+
elements:
+
+
1. A "String" *name* of the method to call or of the response.
+
+
2. A "String[*]" object containing named *arguments* for that method
+
or response.
+
+
3. A "String" *method call id*: an arbitrary string from the client
+
to be echoed back with the responses emitted by that method call
+
(a method may return 1 or more responses, as it may make implicit
+
calls to other methods; all responses initiated by this method
+
call get the same method call id in the response).
+
+
3.3. The Request Object
+
+
A *Request* object has the following properties:
+
+
o using: "String[]"
+
+
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.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 16]
+
+
RFC 8620 JMAP July 2019
+
+
+
o methodCalls: "Invocation[]"
+
+
An array of method calls to process on the server. The method
+
calls MUST be processed sequentially, in order.
+
+
o createdIds: "Id[Id]" (optional)
+
+
A map of a (client-specified) creation id to the id the server
+
assigned when a record was successfully created.
+
+
As described later in this specification, some records may have a
+
property that contains the id of another record. To allow more
+
efficient network usage, you can set this property to reference a
+
record created earlier in the same API request. Since the real id
+
is unknown when the request is created, the client can instead
+
specify the creation id it assigned, prefixed with a "#" (see
+
Section 5.3 for more details).
+
+
As the server processes API requests, any time it successfully
+
creates a new record, it adds the creation id to this map (see the
+
"create" argument to /set in Section 5.3), with the server-
+
assigned real id as the value. If it comes across a reference to
+
a creation id in a create/update, it looks it up in the map and
+
replaces the reference with the real id, if found.
+
+
The client can pass an initial value for this map as the
+
"createdIds" property of the Request object. This may be an empty
+
object. If given in the request, the response will also include a
+
createdIds property. This allows proxy servers to easily split a
+
JMAP request into multiple JMAP requests to send to different
+
servers. For example, it could send the first two method calls to
+
server A, then the third to server B, before sending the fourth to
+
server A again. By passing the createdIds of the previous
+
response to the next request, it can ensure all of these still
+
resolve. See Section 5.8 for further discussion of proxy
+
considerations.
+
+
Future specifications MAY add further properties to the Request
+
object to extend the semantics. To ensure forwards compatibility, a
+
server MUST ignore any other properties it does not understand on the
+
JMAP Request object.
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 17]
+
+
RFC 8620 JMAP July 2019
+
+
+
3.3.1. Example Request
+
+
{
+
"using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ],
+
"methodCalls": [
+
[ "method1", {
+
"arg1": "arg1data",
+
"arg2": "arg2data"
+
}, "c1" ],
+
[ "method2", {
+
"arg1": "arg1data"
+
}, "c2" ],
+
[ "method3", {}, "c3" ]
+
]
+
}
+
+
3.4. The Response Object
+
+
A *Response* object has the following properties:
+
+
o methodResponses: "Invocation[]"
+
+
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.
+
+
o createdIds: "Id[Id]" (optional; only returned if given in the
+
request)
+
+
A map of a (client-specified) creation id to the id the server
+
assigned when a record was successfully created. 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.
+
+
o sessionState: "String"
+
+
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.
+
+
Unless otherwise specified, if the method call completed
+
successfully, its response name is the same as the method name in the
+
request.
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 18]
+
+
RFC 8620 JMAP July 2019
+
+
+
3.4.1. Example Response
+
+
{
+
"methodResponses": [
+
[ "method1", {
+
"arg1": 3,
+
"arg2": "foo"
+
}, "c1" ],
+
[ "method2", {
+
"isBlah": true
+
}, "c2" ],
+
[ "anotherResponseFromMethod2", {
+
"data": 10,
+
"yetmoredata": "Hello"
+
}, "c2"],
+
[ "error", {
+
"type":"unknownMethod"
+
}, "c3" ]
+
],
+
"sessionState": "75128aab4b1b"
+
}
+
+
3.5. Omitting Arguments
+
+
An argument to a method may be specified to have a default value. If
+
omitted by the client, the server MUST treat the method call the same
+
as if the default value had been specified. Similarly, the server
+
MAY omit any argument in a response that has the default value.
+
+
Unless otherwise specified in a method description, null is the
+
default value for any argument in a request or response where this is
+
allowed by the type signature. Other arguments may only be omitted
+
if an explicit default value is defined in the method description.
+
+
3.6. Errors
+
+
There are three different levels of granularity at which an error may
+
be returned in JMAP.
+
+
When an API request is made, the request as a whole may be rejected
+
due to rate limiting, malformed JSON, request for an unknown
+
capability, etc. In this case, the entire request is rejected with
+
an appropriate HTTP error response code and an additional JSON body
+
with more detail for the client.
+
+
Provided the request itself is syntactically valid (the JSON is valid
+
and when decoded, it matches the type signature of a Request object),
+
the methods within it are executed sequentially by the server. Each
+
+
+
+
Jenkins & Newman Standards Track [Page 19]
+
+
RFC 8620 JMAP July 2019
+
+
+
method may individually fail, for example, if invalid arguments are
+
given or an unknown method name is called.
+
+
Finally, methods that make changes to the server state often act upon
+
a number of different records within a single call. Each record
+
change may be separately rejected with a SetError, as described in
+
Section 5.3.
+
+
3.6.1. Request-Level Errors
+
+
When an HTTP error response is returned to the client, the server
+
SHOULD return a JSON "problem details" object as the response body,
+
as per [RFC7807].
+
+
The following problem types are defined:
+
+
o "urn:ietf:params:jmap:error:unknownCapability"
+
The client included a capability in the "using" property of the
+
request that the server does not support.
+
+
o "urn:ietf:params:jmap:error:notJSON"
+
The content type of the request was not "application/json" or the
+
request did not parse as I-JSON.
+
+
o "urn:ietf:params:jmap:error:notRequest"
+
The request parsed as JSON but did not match the type signature of
+
the Request object.
+
+
o "urn:ietf:params:jmap:error:limit"
+
The request was not processed as it would have exceeded one of the
+
request limits defined on the capability object, such as
+
maxSizeRequest, maxCallsInRequest, or maxConcurrentRequests. A
+
"limit" property MUST also be present on the "problem details"
+
object, containing the name of the limit being applied.
+
+
3.6.1.1. Example
+
+
{
+
"type": "urn:ietf:params:jmap:error:unknownCapability",
+
"status": 400,
+
"detail": "The Request object used capability
+
'https://example.com/apis/foobar', which is not supported
+
by this server."
+
}
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 20]
+
+
RFC 8620 JMAP July 2019
+
+
+
Another example:
+
+
{
+
"type": "urn:ietf:params:jmap:error:limit",
+
"limit": "maxSizeRequest",
+
"status": 400,
+
"detail": "The request is larger than the server is willing to
+
process."
+
}
+
+
3.6.2. Method-Level Errors
+
+
If a method encounters an error, the appropriate "error" response
+
MUST be inserted at the current point in the "methodResponses" array
+
and, unless otherwise specified, further processing MUST NOT happen
+
within that method call.
+
+
Any further method calls in the request MUST then be processed as
+
normal. Errors at the method level MUST NOT generate an HTTP-level
+
error.
+
+
An "error" response looks like this:
+
+
[ "error", {
+
"type": "unknownMethod"
+
}, "call-id" ]
+
+
The response name is "error", and it MUST have a type property.
+
Other properties may be present with further information; these are
+
detailed in the error type descriptions where appropriate.
+
+
With the exception of when the "serverPartialFail" error is returned,
+
the externally visible state of the server MUST NOT have changed if
+
an error is returned at the method level.
+
+
The following error types are defined, which may be returned for any
+
method call where appropriate:
+
+
"serverUnavailable": Some internal server resource was temporarily
+
unavailable. Attempting the same operation later (perhaps after a
+
backoff with a random factor) may succeed.
+
+
"serverFail": An unexpected or unknown error occurred during the
+
processing of the call. A "description" property should provide more
+
details about the error. The method call made no changes to the
+
server's state. Attempting the same operation again is expected to
+
fail again. Contacting the service administrator is likely necessary
+
to resolve this problem if it is persistent.
+
+
+
+
Jenkins & Newman Standards Track [Page 21]
+
+
RFC 8620 JMAP July 2019
+
+
+
"serverPartialFail": Some, but not all, expected changes described by
+
the method occurred. The client MUST resynchronise impacted data to
+
determine server state. Use of this error is strongly discouraged.
+
+
"unknownMethod": The server does not recognise this method name.
+
+
"invalidArguments": One of the arguments is of the wrong type or is
+
otherwise invalid, or a required argument is missing. A
+
"description" property MAY be present to help debug with an
+
explanation of what the problem was. This is a non-localised string,
+
and it is not intended to be shown directly to end users.
+
+
"invalidResultReference": The method used a result reference for one
+
of its arguments (see Section 3.7), but this failed to resolve.
+
+
"forbidden": The method and arguments are valid, but executing the
+
method would violate an Access Control List (ACL) or other
+
permissions policy.
+
+
"accountNotFound": The accountId does not correspond to a valid
+
account.
+
+
"accountNotSupportedByMethod": The accountId given corresponds to a
+
valid account, but the account does not support this method or data
+
type.
+
+
"accountReadOnly": This method modifies state, but the account is
+
read-only (as returned on the corresponding Account object in the
+
JMAP Session resource).
+
+
Further possible errors for a particular method are specified in the
+
method descriptions.
+
+
Further general errors MAY be defined in future RFCs. Should a
+
client receive an error type it does not understand, it MUST treat it
+
the same as the "serverFail" type.
+
+
3.7. References 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). The value is a ResultReference object as described
+
below. 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"
+
+
+
+
Jenkins & Newman Standards Track [Page 22]
+
+
RFC 8620 JMAP July 2019
+
+
+
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.
+
+
A *ResultReference* object has the following properties:
+
+
o resultOf: "String"
+
+
The method call id (see Section 3.2) of a previous method call in
+
the current request.
+
+
o name: "String"
+
+
The required name of a response to that method call.
+
+
o path: "String"
+
+
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 (see the
+
description below).
+
+
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):
+
+
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. 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). If the result of applying
+
+
+
+
Jenkins & Newman Standards Track [Page 23]
+
+
RFC 8620 JMAP July 2019
+
+
+
the rest of the pointer tokens to a value was itself an array,
+
its items should be included individually in the output rather
+
than including the array itself (i.e., the result is flattened
+
from an array of arrays to a single array).
+
+
As a simple example, suppose we have the following API request
+
"methodCalls":
+
+
[[ "Foo/changes", {
+
"accountId": "A1",
+
"sinceState": "abcdef"
+
}, "t0" ],
+
[ "Foo/get", {
+
"accountId": "A1",
+
"#ids": {
+
"resultOf": "t0",
+
"name": "Foo/changes",
+
"path": "/created"
+
}
+
}, "t1" ]]
+
+
After executing the first method call, the "methodResponses" array
+
is:
+
+
[[ "Foo/changes", {
+
"accountId": "A1",
+
"oldState": "abcdef",
+
"newState": "123456",
+
"hasMoreChanges": false,
+
"created": [ "f1", "f4" ],
+
"updated": [],
+
"destroyed": []
+
}, "t0" ]]
+
+
To execute the "Foo/get" call, we look through the arguments and find
+
there is one with a "#" prefix. To resolve this, we apply the
+
algorithm above:
+
+
1. Find the first response with method call id "t0". The "Foo/
+
changes" response fulfils this criterion.
+
+
2. Check that the response name is the same as in the result
+
reference. It is, so this is fine.
+
+
3. Apply the "path" as a JSON Pointer to the arguments object. This
+
simply selects the "created" property, so the result of
+
evaluating is: [ "f1", "f4" ].
+
+
+
+
+
Jenkins & Newman Standards Track [Page 24]
+
+
RFC 8620 JMAP July 2019
+
+
+
The JMAP server now continues to process the "Foo/get" call as though
+
the arguments were:
+
+
{
+
"accountId": "A1",
+
"ids": [ "f1", "f4" ]
+
}
+
+
Now, a more complicated example using the JMAP Mail data model: fetch
+
the "from"/"date"/"subject" for every Email in the first 10 Threads
+
in the inbox (sorted newest first):
+
+
[[ "Email/query", {
+
"accountId": "A1",
+
"filter": { "inMailbox": "id_of_inbox" },
+
"sort": [{ "property": "receivedAt", "isAscending": false }],
+
"collapseThreads": true,
+
"position": 0,
+
"limit": 10,
+
"calculateTotal": true
+
}, "t0" ],
+
[ "Email/get", {
+
"accountId": "A1",
+
"#ids": {
+
"resultOf": "t0",
+
"name": "Email/query",
+
"path": "/ids"
+
},
+
"properties": [ "threadId" ]
+
}, "t1" ],
+
[ "Thread/get", {
+
"accountId": "A1",
+
"#ids": {
+
"resultOf": "t1",
+
"name": "Email/get",
+
"path": "/list/*/threadId"
+
}
+
}, "t2" ],
+
[ "Email/get", {
+
"accountId": "A1",
+
"#ids": {
+
"resultOf": "t2",
+
"name": "Thread/get",
+
"path": "/list/*/emailIds"
+
},
+
"properties": [ "from", "receivedAt", "subject" ]
+
}, "t3" ]]
+
+
+
+
+
Jenkins & Newman Standards Track [Page 25]
+
+
RFC 8620 JMAP July 2019
+
+
+
After executing the first 3 method calls, the "methodResponses" array
+
might be:
+
+
[[ "Email/query", {
+
"accountId": "A1",
+
"queryState": "abcdefg",
+
"canCalculateChanges": true,
+
"position": 0,
+
"total": 101,
+
"ids": [ "msg1023", "msg223", "msg110", "msg93", "msg91",
+
"msg38", "msg36", "msg33", "msg11", "msg1" ]
+
}, "t0" ],
+
[ "Email/get", {
+
"accountId": "A1",
+
"state": "123456",
+
"list": [{
+
"id": "msg1023",
+
"threadId": "trd194"
+
}, {
+
"id": "msg223",
+
"threadId": "trd114"
+
},
+
...
+
],
+
"notFound": []
+
}, "t1" ],
+
[ "Thread/get", {
+
"accountId": "A1",
+
"state": "123456",
+
"list": [{
+
"id": "trd194",
+
"emailIds": [ "msg1020", "msg1021", "msg1023" ]
+
}, {
+
"id": "trd114",
+
"emailIds": [ "msg201", "msg223" ]
+
},
+
...
+
],
+
"notFound": []
+
}, "t2" ]]
+
+
To execute the final "Email/get" call, we look through the arguments
+
and find there is one with a "#" prefix. To resolve this, we apply
+
the algorithm:
+
+
1. Find the first response with method call id "t2". The "Thread/
+
get" response fulfils this criterion.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 26]
+
+
RFC 8620 JMAP July 2019
+
+
+
2. "Thread/get" is the name specified in the result reference, so
+
this is fine.
+
+
3. Apply the "path" as a JSON Pointer to the arguments object.
+
Token by token:
+
+
1. "list": get the array of thread objects
+
+
2. "*": for each of the items in the array:
+
+
a. "emailIds": get the array of Email ids
+
+
b. Concatenate these into a single array of all the ids in
+
the result.
+
+
The JMAP server now continues to process the "Email/get" call as
+
though the arguments were:
+
+
{
+
"accountId": "A1",
+
"ids": [ "msg1020", "msg1021", "msg1023", "msg201", "msg223", ... ],
+
"properties": [ "from", "receivedAt", "subject" ]
+
}
+
+
The ResultReference performs a similar role to that of the creation
+
id, in that it allows a chained method call to refer to information
+
not available when the request is generated. However, they are
+
different things and not interchangeable; the only commonality is the
+
octothorpe used to indicate them.
+
+
3.8. Localisation of User-Visible Strings
+
+
If returning a custom string to be displayed to the user, for
+
example, an error message, the server SHOULD use information from the
+
Accept-Language header of the request (as defined in Section 5.3.5 of
+
[RFC7231]) to choose the best available localisation. The Content-
+
Language header of the response (see Section 3.1.3.2 of [RFC7231])
+
SHOULD indicate the language being used for user-visible strings.
+
+
For example, suppose a request was made with the following header:
+
+
Accept-Language: fr-CH, fr;q=0.9, de;q=0.8, en;q=0.7, *;q=0.5
+
+
and a method generated an error to display to the user. The server
+
has translations of the error message in English and German. Looking
+
at the Accept-Language header, the user's preferred language is
+
French. Since we don't have a translation for this, we look at the
+
+
+
+
+
Jenkins & Newman Standards Track [Page 27]
+
+
RFC 8620 JMAP July 2019
+
+
+
next most preferred, which is German. We have a German translation,
+
so the server returns this and indicates the language chosen in a
+
Content-Language header like so:
+
+
Content-Language: de
+
+
3.9. Security
+
+
As always, the server must be strict about data received from the
+
client. Arguments need to be checked for validity; a malicious user
+
could attempt to find an exploit through the API. In case of invalid
+
arguments (unknown/insufficient/wrong type for data, etc.), the
+
method MUST return an "invalidArguments" error and terminate.
+
+
3.10. Concurrency
+
+
Method calls within a single request MUST be executed in order.
+
However, method calls from different concurrent API requests may be
+
interleaved. This means that the data on the server may change
+
between two method calls within a single API request.
+
+
4. The Core/echo Method
+
+
The "Core/echo" method returns exactly the same arguments as it is
+
given. It is useful for testing if you have a valid authenticated
+
connection to a JMAP API endpoint.
+
+
4.1. Example
+
+
Request:
+
+
[[ "Core/echo", {
+
"hello": true,
+
"high": 5
+
}, "b3ff" ]]
+
+
Response:
+
+
[[ "Core/echo", {
+
"hello": true,
+
"high": 5
+
}, "b3ff" ]]
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 28]
+
+
RFC 8620 JMAP July 2019
+
+
+
5. Standard Methods and Naming Convention
+
+
JMAP provides a uniform interface for creating, retrieving, updating,
+
and deleting objects of a particular type. For a "Foo" data type,
+
records of that type would be fetched via a "Foo/get" call and
+
modified via a "Foo/set" call. Delta updates may be fetched via a
+
"Foo/changes" call. These methods all follow a standard format as
+
described below.
+
+
Some types may not have all these methods. Specifications defining
+
types MUST specify which methods are available for the type.
+
+
5.1. /get
+
+
Objects of type Foo are fetched via a call to "Foo/get".
+
+
It takes the following arguments:
+
+
o accountId: "Id"
+
+
The id of the account to use.
+
+
o ids: "Id[]|null"
+
+
The ids of the Foo 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.
+
+
o properties: "String[]|null"
+
+
If supplied, only the properties listed in the array are returned
+
for each Foo 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.
+
+
The response has the following arguments:
+
+
o accountId: "Id"
+
+
The id of the account used for the call.
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 29]
+
+
RFC 8620 JMAP July 2019
+
+
+
o state: "String"
+
+
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 Foo 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.
+
+
o list: "Foo[]"
+
+
An array of the Foo 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.
+
+
o notFound: "Id[]"
+
+
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.
+
+
The following additional error may be returned instead of the "Foo/
+
get" response:
+
+
"requestTooLarge": The number of ids requested by the client exceeds
+
the maximum number the server is willing to process in a single
+
method call.
+
+
5.2. /changes
+
+
When the state of the set of Foo records in an account changes on the
+
server (whether due to creation, updates, or deletion), the "state"
+
property of the "Foo/get" response will change. The "Foo/changes"
+
method allows a client to efficiently update the state of its Foo
+
cache to match the new state on the server. It takes the following
+
arguments:
+
+
o accountId: "Id"
+
+
The id of the account to use.
+
+
+
+
Jenkins & Newman Standards Track [Page 30]
+
+
RFC 8620 JMAP July 2019
+
+
+
o sinceState: "String"
+
+
The current state of the client. This is the string that was
+
returned as the "state" argument in the "Foo/get" response. The
+
server will return the changes that have occurred since this
+
state.
+
+
o maxChanges: "UnsignedInt|null"
+
+
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.
+
+
The response has the following arguments:
+
+
o accountId: "Id"
+
+
The id of the account used for the call.
+
+
o oldState: "String"
+
+
This is the "sinceState" argument echoed back; it's the state from
+
which the server is returning changes.
+
+
o newState: "String"
+
+
This is the state the client will be in after applying the set of
+
changes to the old state.
+
+
o hasMoreChanges: "Boolean"
+
+
If true, the client may call "Foo/changes" again with the
+
"newState" returned to get further updates. If false, "newState"
+
is the current server state.
+
+
o created: "Id[]"
+
+
An array of ids for records that have been created since the old
+
state.
+
+
o updated: "Id[]"
+
+
An array of ids for records that have been updated since the old
+
state.
+
+
+
+
Jenkins & Newman Standards Track [Page 31]
+
+
RFC 8620 JMAP July 2019
+
+
+
o destroyed: "Id[]"
+
+
An array of ids for records that have been destroyed since the old
+
state.
+
+
If a record has been created AND updated since the old state, the
+
server SHOULD just return the id in the "created" list but MAY return
+
it in the "updated" list as well.
+
+
If a record has been updated AND destroyed since the old state, the
+
server SHOULD just return the id in the "destroyed" list but MAY
+
return it in the "updated" list as well.
+
+
If a record has been created AND destroyed since the old state, the
+
server SHOULD remove the id from the response entirely. However, it
+
MAY include it in just the "destroyed" list or in both the
+
"destroyed" and "created" lists.
+
+
If a "maxChanges" is supplied, or set automatically by the server,
+
the server MUST ensure the number of ids returned across "created",
+
"updated", and "destroyed" does not exceed this limit. If there are
+
more changes than this between the client's state and the current
+
server state, the server SHOULD generate an update to take the client
+
to an intermediate state, from which the client can continue to call
+
"Foo/changes" until it is fully up to date. If it is unable to
+
calculate an intermediate state, it MUST return a
+
"cannotCalculateChanges" error response instead.
+
+
When generating intermediate states, the server may choose how to
+
divide up the changes. For many types, it will provide a better user
+
experience to return the more recent changes first, as this is more
+
likely to be what the user is most interested in. The client can
+
then continue to page in the older changes while the user is viewing
+
the newer data. For example, suppose a server went through the
+
following states:
+
+
A -> B -> C -> D -> E
+
+
And a client asks for changes from state "B". The server might first
+
get the ids of records created, updated, or destroyed between states
+
D and E, returning them with:
+
+
state: "B-D-E"
+
hasMoreChanges: true
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 32]
+
+
RFC 8620 JMAP July 2019
+
+
+
The client will then ask for the change from state "B-D-E", and the
+
server can return the changes between states C and D, returning:
+
+
state: "B-C-E"
+
hasMoreChanges: true
+
+
Finally, the client will request the changes from "B-C-E", and the
+
server can return the changes between states B and C, returning:
+
+
state: "E"
+
hasMoreChanges: false
+
+
Should the state on the server be modified in the middle of all this
+
(to "F"), the server still does the same, but now when the update to
+
state "E" is returned, it would indicate that it still has more
+
changes for the client to fetch.
+
+
Where multiple changes to a record are split across different
+
intermediate states, the server MUST NOT return a record as created
+
after a response that deems it as updated or destroyed, and it MUST
+
NOT return a record as destroyed before a response that deems it as
+
created or updated. The server may have to coalesce multiple changes
+
to a record to satisfy this requirement.
+
+
The following additional errors may be returned instead of the "Foo/
+
changes" response:
+
+
"cannotCalculateChanges": The server cannot calculate the changes
+
from the state string given by the client. Usually, this is due to
+
the client's state being too old or the server being unable to
+
produce an update to an intermediate state when there are too many
+
updates. The client MUST invalidate its Foo cache.
+
+
Maintaining state to allow calculation of "Foo/changes" can be
+
expensive for the server, but always returning
+
"cannotCalculateChanges" severely increases network traffic and
+
resource usage for the client. To allow efficient sync, servers
+
SHOULD be able to calculate changes from any state string that was
+
given to a client within the last 30 days (but of course may support
+
calculating updates from states older than this).
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 33]
+
+
RFC 8620 JMAP July 2019
+
+
+
5.3. /set
+
+
Modifying the state of Foo objects on the server is done via the
+
"Foo/set" method. This encompasses creating, updating, and
+
destroying Foo records. This allows the server to sort out ordering
+
and dependencies that may exist if doing multiple operations at once
+
(for example, to ensure there is always a minimum number of a certain
+
record type).
+
+
The "Foo/set" method takes the following arguments:
+
+
o accountId: "Id"
+
+
The id of the account to use.
+
+
o ifInState: "String|null"
+
+
This is a state string as returned by the "Foo/get" method
+
(representing the state of all objects of this type in the
+
account). If supplied, the string must match the current state;
+
otherwise, the method will be aborted and a "stateMismatch" error
+
returned. If null, any changes will be applied to the current
+
state.
+
+
o create: "Id[Foo]|null"
+
+
A map of a *creation id* (a temporary id set by the client) to Foo
+
objects, or null if no objects are to be created.
+
+
The Foo object type definition may define default values for
+
properties. Any such property may be omitted by the client.
+
+
The client MUST omit any properties that may only be set by the
+
server (for example, the "id" property on most object types).
+
+
o update: "Id[PatchObject]|null"
+
+
A map of an id to a Patch object to apply to the current Foo
+
object with that id, or null if no objects are to be updated.
+
+
A *PatchObject* is of type "String[*]" and represents an unordered
+
set of patches. The keys are a path in JSON Pointer format
+
[RFC6901], with an implicit leading "/" (i.e., prefix each key
+
with "/" before applying the JSON Pointer evaluation algorithm).
+
+
All paths MUST also conform to the following restrictions; if
+
there is any violation, the update MUST be rejected with an
+
"invalidPatch" error:
+
+
+
+
Jenkins & Newman Standards Track [Page 34]
+
+
RFC 8620 JMAP July 2019
+
+
+
* The pointer MUST NOT reference inside an array (i.e., you MUST
+
NOT insert/delete from an array; the array MUST be replaced in
+
its entirety instead).
+
+
* All parts prior to the last (i.e., the value after the final
+
slash) MUST already exist on the object being patched.
+
+
* There MUST NOT be two patches in the PatchObject where the
+
pointer of one is the prefix of the pointer of the other, e.g.,
+
"alerts/1/offset" and "alerts".
+
+
The value associated with each pointer determines how to apply
+
that patch:
+
+
* If null, set to the default value if specified for this
+
property; otherwise, remove the property from the patched
+
object. If the key is not present in the parent, this a no-op.
+
+
* Anything else: The value to set for this property (this may be
+
a replacement or addition to the object being patched).
+
+
Any server-set properties MAY be included in the patch if their
+
value is identical to the current server value (before applying
+
the patches to the object). Otherwise, the update MUST be
+
rejected with an "invalidProperties" SetError.
+
+
This patch definition is designed such that an entire Foo object
+
is also a valid PatchObject. The client may choose to optimise
+
network usage by just sending the diff or may send the whole
+
object; the server processes it the same either way.
+
+
o destroy: "Id[]|null"
+
+
A list of ids for Foo objects to permanently delete, or null if no
+
objects are to be destroyed.
+
+
Each creation, modification, or destruction of an object is
+
considered an atomic unit. It is permissible for the server to
+
commit changes to some objects but not others; however, it MUST NOT
+
only commit part of an update to a single record (e.g., update a
+
"name" property but not a "count" property, if both are supplied in
+
the update object).
+
+
The final state MUST be valid after the "Foo/set" is finished;
+
however, the server may have to transition through invalid
+
intermediate states (not exposed to the client) while processing the
+
individual create/update/destroy requests. For example, suppose
+
there is a "name" property that must be unique. A single method call
+
+
+
+
Jenkins & Newman Standards Track [Page 35]
+
+
RFC 8620 JMAP July 2019
+
+
+
could rename an object A => B and simultaneously rename another
+
object B => A. If the final state is valid, this is allowed.
+
Otherwise, each creation, modification, or destruction of an object
+
should be processed sequentially and accepted/rejected based on the
+
current server state.
+
+
If a create, update, or destroy is rejected, the appropriate error
+
MUST be added to the notCreated/notUpdated/notDestroyed property of
+
the response, and the server MUST continue to the next create/update/
+
destroy. It does not terminate the method.
+
+
If an id given cannot be found, the update or destroy MUST be
+
rejected with a "notFound" set error.
+
+
The server MAY skip an update (rejecting it with a "willDestroy"
+
SetError) if that object is destroyed in the same /set request.
+
+
Some records may hold references to other records (foreign keys).
+
That reference may be set (via create or update) in the same request
+
as the referenced record is created. To do this, the client refers
+
to the new record using its creation id prefixed with a "#". The
+
order of the method calls in the request by the client MUST be such
+
that the record being referenced is created in the same or an earlier
+
call. Thus, the server never has to look ahead. Instead, while
+
processing a request, the server MUST keep a simple map for the
+
duration of the request of creation id to record id for each newly
+
created record, so it can substitute in the correct value if
+
necessary in later method calls. In the case of records with
+
references to the same type, the server MUST order the creates and
+
updates within a single method call so that creates happen before
+
their creation ids are referenced by another create/update/destroy in
+
the same call.
+
+
Creation ids are not scoped by type but are a single map for all
+
types. A client SHOULD NOT reuse a creation id anywhere in the same
+
API request. If a creation id is reused, the server MUST map the
+
creation id to the most recently created item with that id. To allow
+
easy proxying of API requests, an initial set of creation id to real
+
id values may be passed with a request (see "The Request Object",
+
Section 3.3) and the final state of the map passed out with the
+
response (see "The Response Object", Section 3.4).
+
+
The response has the following arguments:
+
+
o accountId: "Id"
+
+
The id of the account used for the call.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 36]
+
+
RFC 8620 JMAP July 2019
+
+
+
o oldState: "String|null"
+
+
The state string that would have been returned by "Foo/get" before
+
making the requested changes, or null if the server doesn't know
+
what the previous state string was.
+
+
o newState: "String"
+
+
The state string that will now be returned by "Foo/get".
+
+
o created: "Id[Foo]|null"
+
+
A map of the creation id to an object containing any properties of
+
the created Foo 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 Foo objects were successfully created.
+
+
o updated: "Id[Foo|null]|null"
+
+
The keys in this map are the ids of all Foos that were
+
successfully updated.
+
+
The value for each id is a Foo 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 Foo objects were successfully updated.
+
+
o destroyed: "Id[]|null"
+
+
A list of Foo ids for records that were successfully destroyed, or
+
null if none.
+
+
o notCreated: "Id[SetError]|null"
+
+
A map of the creation id to a SetError object for each record that
+
failed to be created, or null if all successful.
+
+
o notUpdated: "Id[SetError]|null"
+
+
A map of the Foo id to a SetError object for each record that
+
failed to be updated, or null if all successful.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 37]
+
+
RFC 8620 JMAP July 2019
+
+
+
o notDestroyed: "Id[SetError]|null"
+
+
A map of the Foo id to a SetError object for each record that
+
failed to be destroyed, or null if all successful.
+
+
A *SetError* object has the following properties:
+
+
o type: "String"
+
+
The type of error.
+
+
o description: "String|null"
+
+
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.
+
+
The following SetError types are defined and may be returned for set
+
operations on any record type where appropriate:
+
+
o "forbidden": (create; update; destroy). The create/update/destroy
+
would violate an ACL or other permissions policy.
+
+
o "overQuota": (create; update). The create would exceed a server-
+
defined limit on the number or total size of objects of this type.
+
+
o "tooLarge": (create; update). 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.
+
+
o "rateLimit": (create). 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.
+
+
o "notFound": (update; destroy). The id given to update/destroy
+
cannot be found.
+
+
o "invalidPatch": (update). The PatchObject given to update the
+
record was not a valid patch (see the patch description).
+
+
o "willDestroy": (update). 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.
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 38]
+
+
RFC 8620 JMAP July 2019
+
+
+
o "invalidProperties": (create; update). 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.
+
+
o "singleton": (create; destroy). This is a singleton type, so you
+
cannot create another one or destroy the existing one.
+
+
Other possible SetError types MAY be given in specific method
+
descriptions. Other properties MAY also be present on the SetError
+
object, as described in the relevant methods.
+
+
The following additional errors may be returned instead of the "Foo/
+
set" response:
+
+
"requestTooLarge": 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.
+
+
"stateMismatch": An "ifInState" argument was supplied, and it does
+
not match the current state.
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 39]
+
+
RFC 8620 JMAP July 2019
+
+
+
5.4. /copy
+
+
The only way to move Foo records *between* two different accounts is
+
to copy them using the "Foo/copy" method; once the copy has
+
succeeded, delete the original. The "onSuccessDestroyOriginal"
+
argument allows you to try to do this in one method call; however,
+
note that the two different actions are not atomic, so it is possible
+
for the copy to succeed but the original not to be destroyed for some
+
reason.
+
+
The copy is conceptually in three phases:
+
+
1. Reading the current values from the "from" account.
+
+
2. Writing the new copies to the other account.
+
+
3. Destroying the originals in the "from" account, if requested.
+
+
Data may change in between phases due to concurrent requests.
+
+
The "Foo/copy" method takes the following arguments:
+
+
o fromAccountId: "Id"
+
+
The id of the account to copy records from.
+
+
o ifFromInState: "String|null"
+
+
This is a state string as returned by the "Foo/get" method. If
+
supplied, the string must match the current state of the account
+
referenced by the fromAccountId when reading the data to be
+
copied; otherwise, the method will be aborted and a
+
"stateMismatch" error returned. If null, the data will be read
+
from the current state.
+
+
o accountId: "Id"
+
+
The id of the account to copy records to. This MUST be different
+
to the "fromAccountId".
+
+
o ifInState: "String|null"
+
+
This is a state string as returned by the "Foo/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.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 40]
+
+
RFC 8620 JMAP July 2019
+
+
+
o create: "Id[Foo]"
+
+
A map of the *creation id* to a Foo object. The Foo object MUST
+
contain an "id" property, which is the id (in the fromAccount) of
+
the record to be copied. When creating the copy, any other
+
properties included are used instead of the current value for that
+
property on the original.
+
+
o onSuccessDestroyOriginal: "Boolean" (default: false)
+
+
If true, an attempt will be made to destroy the original records
+
that were successfully copied: after emitting the "Foo/copy"
+
response, but before processing the next method, the server MUST
+
make a single call to "Foo/set" to destroy the original of each
+
successfully copied record; the output of this is added to the
+
responses as normal, to be returned to the client.
+
+
o destroyFromIfInState: "String|null"
+
+
This argument is passed on as the "ifInState" argument to the
+
implicit "Foo/set" call, if made at the end of this request to
+
destroy the originals that were successfully copied.
+
+
Each record copy is considered an atomic unit that may succeed or
+
fail individually.
+
+
The response has the following arguments:
+
+
o fromAccountId: "Id"
+
+
The id of the account records were copied from.
+
+
o accountId: "Id"
+
+
The id of the account records were copied to.
+
+
o oldState: "String|null"
+
+
The state string that would have been returned by "Foo/get" on the
+
account records that were copied to before making the requested
+
changes, or null if the server doesn't know what the previous
+
state string was.
+
+
o newState: "String"
+
+
The state string that will now be returned by "Foo/get" on the
+
account records were copied to.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 41]
+
+
RFC 8620 JMAP July 2019
+
+
+
o created: "Id[Foo]|null"
+
+
A map of the creation id to an object containing any properties of
+
the copied Foo object that are set by the server (such as the "id"
+
in most object types; note, the id is likely to be different to
+
the id of the object in the account it was copied from).
+
+
This argument is null if no Foo objects were successfully copied.
+
+
o notCreated: "Id[SetError]|null"
+
+
A map of the creation id to a SetError object for each record that
+
failed to be copied, or null if none.
+
+
The SetError may be any of the standard set errors returned for a
+
create or update. In addition, the following SetError is defined:
+
+
"alreadyExists": The server forbids duplicates, and the record
+
already exists in the target account. An "existingId" property of
+
type "Id" MUST be included on the SetError object with the id of the
+
existing record.
+
+
The following additional errors may be returned instead of the "Foo/
+
copy" response:
+
+
"fromAccountNotFound": The "fromAccountId" does not correspond to a
+
valid account.
+
+
"fromAccountNotSupportedByMethod": The "fromAccountId" given
+
corresponds to a valid account, but the account does not support this
+
data type.
+
+
"stateMismatch": An "ifInState" argument was supplied and it does not
+
match the current state, or an "ifFromInState" argument was supplied
+
and it does not match the current state in the from account.
+
+
5.5. /query
+
+
For data sets where the total amount of data is expected to be very
+
small, clients can just fetch the complete set of data and then do
+
any sorting/filtering locally. However, for large data sets (e.g.,
+
multi-gigabyte mailboxes), the client needs to be able to
+
search/sort/window the data type on the server.
+
+
A query on the set of Foos in an account is made by calling "Foo/
+
query". This takes a number of arguments to determine which records
+
to include, how they should be sorted, and which part of the result
+
+
+
+
+
Jenkins & Newman Standards Track [Page 42]
+
+
RFC 8620 JMAP July 2019
+
+
+
should be returned (the full list may be *very* long). The result is
+
returned as a list of Foo ids.
+
+
A call to "Foo/query" takes the following arguments:
+
+
o accountId: "Id"
+
+
The id of the account to use.
+
+
o filter: "FilterOperator|FilterCondition|null"
+
+
Determines the set of Foos returned in the results. If null, all
+
objects in the account of this type are included in the results.
+
A *FilterOperator* object has the following properties:
+
+
* operator: "String"
+
+
This MUST be one of the following strings:
+
+
+ "AND": All of the conditions must match for the filter to
+
match.
+
+
+ "OR": At least one of the conditions must match for the
+
filter to match.
+
+
+ "NOT": None of the conditions must match for the filter to
+
match.
+
+
* conditions: "(FilterOperator|FilterCondition)[]"
+
+
The conditions to evaluate against each record.
+
+
A *FilterCondition* is an "object" whose allowed properties and
+
semantics depend on the data type and is defined in the /query
+
method specification for that type. It MUST NOT have an
+
"operator" property.
+
+
o sort: "Comparator[]|null"
+
+
Lists the names of properties to compare between two Foo records,
+
and how to compare them, to determine which comes first in the
+
sort. If two Foo 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
+
"Foo/query". A *Comparator* has the following properties:
+
+
+
+
+
Jenkins & Newman Standards Track [Page 43]
+
+
RFC 8620 JMAP July 2019
+
+
+
* property: "String"
+
+
The name of the property on the Foo objects to compare.
+
+
* isAscending: "Boolean" (optional; default: true)
+
+
If true, sort in ascending order. If false, reverse the
+
comparator's results to sort in descending order.
+
+
* collation: "String" (optional; default is server-dependent)
+
+
The identifier, as registered in the collation registry defined
+
in [RFC4790], for the algorithm to use when comparing the order
+
of strings. The algorithms the server supports are advertised
+
in the capabilities object returned with the Session object
+
(see Section 2).
+
+
If omitted, the default algorithm is server dependent, but:
+
+
1. It MUST be unicode-aware.
+
+
2. It MAY be selected based on an Accept-Language header in
+
the request (as defined in [RFC7231], Section 5.3.5) or
+
out-of-band information about the user's language/locale.
+
+
3. It SHOULD be case insensitive where such a concept makes
+
sense for a language/locale. Where the user's language is
+
unknown, it is RECOMMENDED to follow the advice in
+
Section 5.2.3 of [RFC8264].
+
+
The "i;unicode-casemap" collation [RFC5051] and the Unicode
+
Collation Algorithm (<http://www.unicode.org/reports/tr10/>)
+
are two examples that fulfil these criterion and provide
+
reasonable behaviour for a large number of languages.
+
+
When the property being compared is not a string, the
+
"collation" property is ignored, and the following comparison
+
rules apply based on the type. In ascending order:
+
+
+ "Boolean": false comes before true.
+
+
+ "Number": A lower number comes before a higher number.
+
+
+ "Date"/"UTCDate": The earlier date comes first.
+
+
The Comparator object may also have additional properties as
+
required for specific sort operations defined in a type's /query
+
method.
+
+
+
+
Jenkins & Newman Standards Track [Page 44]
+
+
RFC 8620 JMAP July 2019
+
+
+
o position: "Int" (default: 0)
+
+
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.
+
+
o anchor: "Id|null"
+
+
A Foo 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 (see below for more details).
+
+
o anchorOffset: "Int" (default: 0)
+
+
The index of the first result to return relative to the index of
+
the anchor, if an anchor is given. This MAY be negative. For
+
example, "-1" means the Foo immediately preceding the anchor is
+
the first result in the list returned (see below for more
+
details).
+
+
o limit: "UnsignedInt|null"
+
+
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.
+
+
o calculateTotal: "Boolean" (default: false)
+
+
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.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 45]
+
+
RFC 8620 JMAP July 2019
+
+
+
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.
+
+
The response has the following arguments:
+
+
o accountId: "Id"
+
+
The id of the account used for the call.
+
+
o queryState: "String"
+
+
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
+
"Foo/queryChanges" to get the difference.
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 46]
+
+
RFC 8620 JMAP July 2019
+
+
+
o canCalculateChanges: "Boolean"
+
+
This is true if the server supports calling "Foo/queryChanges"
+
with these "filter"/"sort" parameters. Note, this does not
+
guarantee that the "Foo/queryChanges" call will succeed, as it may
+
only be possible for a limited time afterwards due to server
+
internal implementation details.
+
+
o position: "UnsignedInt"
+
+
The zero-based index of the first result in the "ids" array within
+
the complete list of query results.
+
+
o ids: "Id[]"
+
+
The list of ids for each Foo 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.
+
+
o total: "UnsignedInt" (only if requested)
+
+
The total number of Foos in the results (given the "filter").
+
This argument MUST be omitted if the "calculateTotal" request
+
argument is not true.
+
+
o limit: "UnsignedInt" (if set by the server)
+
+
The limit enforced by the server on the maximum number of results
+
to return. This is only returned if the server set a limit or
+
used a different limit than that given in the request.
+
+
The following additional errors may be returned instead of the "Foo/
+
query" response:
+
+
"anchorNotFound": An anchor argument was supplied, but it cannot be
+
found in the results of the query.
+
+
"unsupportedSort": The "sort" is syntactically valid, but it includes
+
a property the server does not support sorting on or a collation
+
method it does not recognise.
+
+
"unsupportedFilter": The "filter" is syntactically valid, but the
+
server cannot process it. If the filter was the result of a user's
+
search input, the client SHOULD suggest that the user simplify their
+
search.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 47]
+
+
RFC 8620 JMAP July 2019
+
+
+
5.6. /queryChanges
+
+
The "Foo/queryChanges" method allows a client to efficiently update
+
the state of a cached query to match the new state on the server. It
+
takes the following arguments:
+
+
o accountId: "Id"
+
+
The id of the account to use.
+
+
o filter: "FilterOperator|FilterCondition|null"
+
+
The filter argument that was used with "Foo/query".
+
+
o sort: "Comparator[]|null"
+
+
The sort argument that was used with "Foo/query".
+
+
o sinceQueryState: "String"
+
+
The current state of the query in the client. This is the string
+
that was returned as the "queryState" argument in the "Foo/query"
+
response with the same sort/filter. The server will return the
+
changes made to the query since this state.
+
+
o maxChanges: "UnsignedInt|null"
+
+
The maximum number of changes to return in the response. See
+
error descriptions below for more details.
+
+
o upToId: "Id|null"
+
+
The last (highest-index) id the client currently has cached from
+
the query results. When there are a large number of results, in a
+
common case, the client may have only downloaded and cached a
+
small subset from the beginning of the results. If the sort and
+
filter are both only on immutable properties, this allows the
+
server to omit changes after this point in the results, which can
+
significantly increase efficiency. If they are not immutable,
+
this argument is ignored.
+
+
o calculateTotal: "Boolean" (default: false)
+
+
Does the client wish to know the total number of results now 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.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 48]
+
+
RFC 8620 JMAP July 2019
+
+
+
The response has the following arguments:
+
+
o accountId: "Id"
+
+
The id of the account used for the call.
+
+
o oldQueryState: "String"
+
+
This is the "sinceQueryState" argument echoed back; that is, the
+
state from which the server is returning changes.
+
+
o newQueryState: "String"
+
+
This is the state the query will be in after applying the set of
+
changes to the old state.
+
+
o total: "UnsignedInt" (only if requested)
+
+
The total number of Foos in the results (given the "filter").
+
This argument MUST be omitted if the "calculateTotal" request
+
argument is not true.
+
+
o removed: "Id[]"
+
+
The "id" for every Foo that was in the query results in the old
+
state and that is not in the results in the new state.
+
+
If the server cannot calculate this exactly, the server MAY return
+
the ids of extra Foos in addition that may have been in the old
+
results but are not in the new results.
+
+
If the sort and filter are both only on immutable properties and
+
an "upToId" is supplied and exists in the results, any ids that
+
were removed but have a higher index than "upToId" SHOULD be
+
omitted.
+
+
If the "filter" or "sort" includes a mutable property, the server
+
MUST include all Foos in the current results for which this
+
property may have changed. The position of these may have moved
+
in the results, so they must be reinserted by the client to ensure
+
its query cache is correct.
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 49]
+
+
RFC 8620 JMAP July 2019
+
+
+
o added: "AddedItem[]"
+
+
The id and index in the query results (in the new state) for every
+
Foo that has been added to the results since the old state AND
+
every Foo in the current results that was included in the
+
"removed" array (due to a filter or sort based upon a mutable
+
property).
+
+
If the sort and filter are both only on immutable properties and
+
an "upToId" is supplied and exists in the results, any ids that
+
were added but have a higher index than "upToId" SHOULD be
+
omitted.
+
+
The array MUST be sorted in order of index, with the lowest index
+
first.
+
+
An *AddedItem* object has the following properties:
+
+
* id: "Id"
+
+
* index: "UnsignedInt"
+
+
The result of this is that if the client has a cached sparse array of
+
Foo ids corresponding to the results in the old state, then:
+
+
fooIds = [ "id1", "id2", null, null, "id3", "id4", null, null, null ]
+
+
If it *splices out* all ids in the removed array that it has in its
+
cached results, then:
+
+
removed = [ "id2", "id31", ... ];
+
fooIds => [ "id1", null, null, "id3", "id4", null, null, null ]
+
+
and *splices in* (one by one in order, starting with the lowest
+
index) all of the ids in the added array:
+
+
added = [{ id: "id5", index: 0, ... }];
+
fooIds => [ "id5", "id1", null, null, "id3", "id4", null, null, null ]
+
+
and *truncates* or *extends* to the new total length, then the
+
results will now be in the new state.
+
+
Note: splicing in adds the item at the given index, incrementing the
+
index of all items previously at that or a higher index. Splicing
+
out is the inverse, removing the item and decrementing the index of
+
every item after it in the array.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 50]
+
+
RFC 8620 JMAP July 2019
+
+
+
The following additional errors may be returned instead of the "Foo/
+
queryChanges" response:
+
+
"tooManyChanges": There are more changes than the client's
+
"maxChanges" argument. Each item in the removed or added array is
+
considered to be one change. The client may retry with higher max
+
changes or invalidate its cache of the query results.
+
+
"cannotCalculateChanges": The server cannot calculate the changes
+
from the queryState string given by the client, usually due to the
+
client's state being too old. The client MUST invalidate its cache
+
of the query results.
+
+
5.7. Examples
+
+
Suppose we have a type *Todo* with the following properties:
+
+
o id: "Id" (immutable; server-set)
+
+
The id of the object.
+
+
o title: "String"
+
+
A brief summary of what is to be done.
+
+
o keywords: "String[Boolean]" (default: {})
+
+
A set of keywords that apply to the Todo. The set is represented
+
as an object, with the keys being the "keywords". The value for
+
each key in the object MUST be true. (This format allows you to
+
update an individual key using patch syntax rather than having to
+
update the whole set of keywords as one, which a "String[]"
+
representation would require.)
+
+
o neuralNetworkTimeEstimation: "Number" (server-set)
+
+
The title and keywords are fed into the server's state-of-the-art
+
neural network to get an estimation of how long this Todo will
+
take, in seconds.
+
+
o subTodoIds: "Id[]|null"
+
+
The ids of a list of other Todos to complete as part of this Todo.
+
+
Suppose also that all the standard methods are defined for this type
+
and the FilterCondition object supports a "hasKeyword" property to
+
match Todos with the given keyword.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 51]
+
+
RFC 8620 JMAP July 2019
+
+
+
A client might want to display the list of Todos with either a
+
"music" keyword or a "video" keyword, so it makes the following
+
method call:
+
+
[[ "Todo/query", {
+
"accountId": "x",
+
"filter": {
+
"operator": "OR",
+
"conditions": [
+
{ "hasKeyword": "music" },
+
{ "hasKeyword": "video" }
+
]
+
},
+
"sort": [{ "property": "title" }],
+
"position": 0,
+
"limit": 10
+
}, "0" ],
+
[ "Todo/get", {
+
"accountId": "x",
+
"#ids": {
+
"resultOf": "0",
+
"name": "Todo/query",
+
"path": "/ids"
+
}
+
}, "1" ]]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 52]
+
+
RFC 8620 JMAP July 2019
+
+
+
This would query the server for the set of Todos with a keyword of
+
either "music" or "video", sorted by title, and limited to the first
+
10 results. It fetches the full object for each of these Todos using
+
back-references to reference the result of the query. The response
+
might look something like:
+
+
[[ "Todo/query", {
+
"accountId": "x",
+
"queryState": "y13213",
+
"canCalculateChanges": true,
+
"position": 0,
+
"ids": [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" ]
+
}, "0" ],
+
[ "Todo/get", {
+
"accountId": "x",
+
"state": "10324",
+
"list": [{
+
"id": "a",
+
"title": "Practise Piano",
+
"keywords": {
+
"music": true,
+
"beethoven": true,
+
"mozart": true,
+
"liszt": true,
+
"rachmaninov": true
+
},
+
"neuralNetworkTimeEstimation": 3600
+
}, {
+
"id": "b",
+
"title": "Watch Daft Punk music video",
+
"keywords": {
+
"music": true,
+
"video": true,
+
"trance": true
+
},
+
"neuralNetworkTimeEstimation": 18000
+
},
+
...
+
]
+
}, "1" ]]
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 53]
+
+
RFC 8620 JMAP July 2019
+
+
+
Now, suppose the user adds a keyword "chopin" and removes the keyword
+
"mozart" from the "Practise Piano" task. The client may send the
+
whole object to the server, as this is a valid PatchObject:
+
+
[[ "Todo/set", {
+
"accountId": "x",
+
"ifInState": "10324",
+
"update": {
+
"a": {
+
"id": "a",
+
"title": "Practise Piano",
+
"keywords": {
+
"music": true,
+
"beethoven": true,
+
"chopin": true,
+
"liszt": true,
+
"rachmaninov": true
+
},
+
"neuralNetworkTimeEstimation": 360
+
}
+
}
+
}, "0" ]]
+
+
or it may send a minimal patch:
+
+
[[ "Todo/set", {
+
"accountId": "x",
+
"ifInState": "10324",
+
"update": {
+
"a": {
+
"keywords/chopin": true,
+
"keywords/mozart": null
+
}
+
}
+
}, "0" ]]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 54]
+
+
RFC 8620 JMAP July 2019
+
+
+
The effect is exactly the same on the server in either case, and
+
presuming the server is still in state "10324", it will probably
+
return success:
+
+
[[ "Todo/set", {
+
"accountId": "x",
+
"oldState": "10324",
+
"newState": "10329",
+
"updated": {
+
"a": {
+
"neuralNetworkTimeEstimation": 5400
+
}
+
}
+
}, "0" ]]
+
+
The server changed the "neuralNetworkTimeEstimation" property on the
+
object as part of this change; as this changed in a way *not*
+
explicitly requested by the PatchObject sent to the server, it is
+
returned with the "updated" confirmation.
+
+
Let us now add a sub-Todo to our new "Practise Piano" Todo. In this
+
example, we can see the use of a reference to a creation id to allow
+
us to set a foreign key reference to a record created in the same
+
request:
+
+
[[ "Todo/set", {
+
"accountId": "x",
+
"create": {
+
"k15": {
+
"title": "Warm up with scales"
+
}
+
},
+
"update": {
+
"a": {
+
"subTodoIds": [ "#k15" ]
+
}
+
}
+
}, "0" ]]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 55]
+
+
RFC 8620 JMAP July 2019
+
+
+
Now, suppose another user deleted the "Listen to Daft Punk" Todo.
+
The first user will receive a push notification (see Section 7) with
+
the changed state string for the "Todo" type. Since the new string
+
does not match its current state, it knows it needs to check for
+
updates. It may make a request like:
+
+
[[ "Todo/changes", {
+
"accountId": "x",
+
"sinceState": "10324",
+
"maxChanges": 50
+
}, "0" ],
+
[ "Todo/queryChanges", {
+
"accountId": "x",
+
"filter": {
+
"operator": "OR",
+
"conditions": [
+
{ "hasKeyword": "music" },
+
{ "hasKeyword": "video" }
+
]
+
},
+
"sort": [{ "property": "title" }],
+
"sinceQueryState": "y13213",
+
"maxChanges": 50
+
}, "1" ]]
+
+
and receive in response:
+
+
[[ "Todo/changes", {
+
"accountId": "x",
+
"oldState": "10324",
+
"newState": "871903",
+
"hasMoreChanges": false,
+
"created": [],
+
"updated": [],
+
"destroyed": ["b"]
+
}, "0" ],
+
[ "Todo/queryChanges", {
+
"accountId": "x",
+
"oldQueryState": "y13213",
+
"newQueryState": "y13218",
+
"removed": ["b"],
+
"added": null
+
}, "1" ]]
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 56]
+
+
RFC 8620 JMAP July 2019
+
+
+
Suppose the user has access to another account "y", for example, a
+
team account shared between multiple users. To move an existing Todo
+
from account "x", the client would call:
+
+
[[ "Todo/copy", {
+
"fromAccountId": "x",
+
"accountId": "y",
+
"create": {
+
"k5122": {
+
"id": "a"
+
}
+
},
+
"onSuccessDestroyOriginal": true
+
}, "0" ]]
+
+
The server successfully copies the Todo to a new account (where it
+
receives a new id) and deletes the original. Due to the implicit
+
call to "Todo/set", there are two responses to the single method
+
call, both with the same method call id:
+
+
[[ "Todo/copy", {
+
"fromAccountId": "x",
+
"accountId": "y",
+
"created": {
+
"k5122": {
+
"id": "DAf97"
+
}
+
},
+
"oldState": "c1d64ecb038c",
+
"newState": "33844835152b"
+
}, "0" ],
+
[ "Todo/set", {
+
"accountId": "x",
+
"oldState": "871903",
+
"newState": "871909",
+
"destroyed": [ "a" ],
+
...
+
}, "0" ]]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 57]
+
+
RFC 8620 JMAP July 2019
+
+
+
5.8. Proxy Considerations
+
+
JMAP has been designed to allow an API endpoint to easily proxy
+
through to one or more JMAP servers. This may be useful for load
+
balancing, augmenting capabilities, or presenting a single endpoint
+
to accounts hosted on different JMAP servers (splitting the request
+
based on each method's "accountId" argument). The proxy need only
+
understand the general structure of a JMAP Request object; it does
+
not need to know anything specifically about the methods and
+
arguments it will pass through to other servers.
+
+
If splitting up the methods in a request to call them on different
+
backend servers, the proxy must do two things to ensure back-
+
references and creation-id references resolve the same as if the
+
entire request were processed on a single server:
+
+
1. It must pass a "createdIds" property with each subrequest. If
+
this is not given by the client, an empty object should be used
+
for the first subrequest. The "createdIds" property of each
+
subresponse should be passed on in the next subrequest.
+
+
2. It must resolve back-references to previous method results that
+
were processed on a different server. This is a relatively
+
simple syntactic substitution, described in Section 3.7.
+
+
When splitting a request based on accountId, proxy implementors do
+
need to be aware of "/copy" methods that copy between accounts. If
+
the accounts are on different servers, the proxy will have to
+
implement this functionality directly.
+
+
6. Binary Data
+
+
Binary data is referenced by a *blobId* in JMAP and uploaded/
+
downloaded separately to the core API. The blobId solely represents
+
the raw bytes of data, not any associated metadata such as a file
+
name or content type. Such metadata is stored alongside the blobId
+
in the object referencing it. The data represented by a blobId is
+
immutable.
+
+
Any blobId that exists within an account may be used when creating/
+
updating another object in that account. For example, an Email type
+
may have a blobId that represents the object in Internet Message
+
Format [RFC5322]. A client could create a new Email object with an
+
attachment and use this blobId, in effect attaching the old message
+
to the new one. Similarly, it could attach any existing attachment
+
of an old message without having to download and upload it again.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 58]
+
+
RFC 8620 JMAP July 2019
+
+
+
When the client uses a blobId in a create/update, the server MAY
+
assign a new blobId to refer to the same binary data within the new/
+
updated object. If it does so, it MUST return any properties that
+
contain a changed blobId in the created/updated response, so the
+
client gets the new ids.
+
+
A blob that is not referenced by a JMAP object (e.g., as a message
+
attachment) MAY be deleted by the server to free up resources.
+
Uploads (see below) are initially unreferenced blobs. To ensure
+
interoperability:
+
+
o The server SHOULD use a separate quota for unreferenced blobs to
+
the account's usual quota. In the case of shared accounts, this
+
quota SHOULD be separate per user.
+
+
o This quota SHOULD be at least the maximum total size that a single
+
object can reference on this server. For example, if supporting
+
JMAP Mail, this should be at least the maximum total attachments
+
size for a message.
+
+
o When an upload would take the user over quota, the server MUST
+
delete unreferenced blobs in date order, oldest first, until there
+
is room for the new blob.
+
+
o Except where quota restrictions force early deletion, an
+
unreferenced blob MUST NOT be deleted for at least 1 hour from the
+
time of upload; if reuploaded, the same blobId MAY be returned,
+
but this SHOULD reset the expiry time.
+
+
o A blob MUST NOT be deleted during the method call that removed the
+
last reference, so that a client can issue a create and a destroy
+
that both reference the blob within the same method call.
+
+
6.1. Uploading Binary Data
+
+
There is a single endpoint that handles all file uploads for an
+
account, regardless of what they are to be used for. The Session
+
object (see Section 2) has an "uploadUrl" property in URI Template
+
(level 1) format [RFC6570], which MUST contain a variable called
+
"accountId". The client may use this template in combination with an
+
"accountId" to get the URL of the file upload resource.
+
+
To upload a file, the client submits an authenticated POST request to
+
the file upload resource.
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 59]
+
+
RFC 8620 JMAP July 2019
+
+
+
A successful request MUST return a single JSON object with the
+
following properties as the response:
+
+
o accountId: "Id"
+
+
The id of the account used for the call.
+
+
o blobId: "Id"
+
+
The id representing the binary data uploaded. The data for this
+
id is immutable. The id *only* refers to the binary data, not any
+
metadata.
+
+
o type: "String"
+
+
The media type of the file (as specified in [RFC6838],
+
Section 4.2) as set in the Content-Type header of the upload HTTP
+
request.
+
+
o size: "UnsignedInt"
+
+
The size of the file in octets.
+
+
If identical binary content to an existing blob in the account is
+
uploaded, the existing blobId MAY be returned.
+
+
Clients should use the blobId returned in a timely manner. Under
+
rare circumstances, the server may have deleted the blob before the
+
client uses it; the client should keep a reference to the local file
+
so it can upload it again in such a situation.
+
+
When an HTTP error response is returned to the client, the server
+
SHOULD return a JSON "problem details" object as the response body,
+
as per [RFC7807].
+
+
As access controls are often determined by the object holding the
+
reference to a blob, unreferenced blobs MUST only be accessible to
+
the uploader, even in shared accounts.
+
+
6.2. Downloading Binary Data
+
+
The Session object (see Section 2) has a "downloadUrl" property,
+
which is in URI Template (level 1) format [RFC6570]. The URL MUST
+
contain variables called "accountId", "blobId", "type", and "name".
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 60]
+
+
RFC 8620 JMAP July 2019
+
+
+
To download a file, the client makes an authenticated GET request to
+
the download URL with the appropriate variables substituted in:
+
+
o "accountId": The id of the account to which the record with the
+
blobId belongs.
+
+
o "blobId": The blobId representing the data of the file to
+
download.
+
+
o "type": The type for the server to set in the "Content-Type"
+
header of the response; the blobId only represents the binary data
+
and does not have a content-type innately associated with it.
+
+
o "name": The name for the file; the server MUST return this as the
+
filename if it sets a "Content-Disposition" header.
+
+
As the data for a particular blobId is immutable, and thus the
+
response in the generated download URL is too, implementors are
+
recommended to set long cache times and use the "immutable" Cache-
+
Control extension [RFC8246] for successful responses, for example,
+
"Cache-Control: private, immutable, max-age=31536000".
+
+
When an HTTP error response is returned to the client, the server
+
SHOULD return a JSON "problem details" object as the response body,
+
as per [RFC7807].
+
+
6.3. Blob/copy
+
+
Binary data may be copied *between* two different accounts using the
+
"Blob/copy" method rather than having to download and then reupload
+
on the client.
+
+
The "Blob/copy" method takes the following arguments:
+
+
o fromAccountId: "Id"
+
+
The id of the account to copy blobs from.
+
+
o accountId: "Id"
+
+
The id of the account to copy blobs to.
+
+
o blobIds: "Id[]"
+
+
A list of ids of blobs to copy to the other account.
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 61]
+
+
RFC 8620 JMAP July 2019
+
+
+
The response has the following arguments:
+
+
o fromAccountId: "Id"
+
+
The id of the account blobs were copied from.
+
+
o accountId: "Id"
+
+
The id of the account blobs were copied to.
+
+
o copied: "Id[Id]|null"
+
+
A map of the blobId in the fromAccount to the id for the blob in
+
the account it was copied to, or null if none were successfully
+
copied.
+
+
o notCopied: "Id[SetError]|null"
+
+
A map of blobId to a SetError object for each blob that failed to
+
be copied, or null if none.
+
+
The SetError may be any of the standard set errors that may be
+
returned for a create, as defined in Section 5.3. In addition, the
+
"notFound" SetError error may be returned if the blobId to be copied
+
cannot be found.
+
+
The following additional method-level error may be returned instead
+
of the "Blob/copy" response:
+
+
"fromAccountNotFound": The "fromAccountId" included with the request
+
does not correspond to a valid account.
+
+
7. Push
+
+
Push notifications allow clients to efficiently update (almost)
+
instantly to stay in sync with data changes on the server. The
+
general model for push is simple and sends minimal data over the push
+
channel: just enough for the client to know whether it needs to
+
resync. The format allows multiple changes to be coalesced into a
+
single push update and the frequency of pushes to be rate limited by
+
the server. It doesn't matter if some push events are dropped before
+
they reach the client; the next time it gets/sets any records of a
+
changed type, it will discover the data has changed and still sync
+
all changes.
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 62]
+
+
RFC 8620 JMAP July 2019
+
+
+
There are two different mechanisms by which a client can receive push
+
notifications, to allow for the different environments in which a
+
client may exist. An event source resource (see Section 7.3) allows
+
clients that can hold transport connections open to receive push
+
notifications directly from the JMAP server. This is simple and
+
avoids third parties, but it is often not feasible on constrained
+
platforms such as mobile devices. Alternatively, clients can make
+
use of any push service supported by their environment. A URL for
+
the push service is registered with the JMAP server (see
+
Section 7.2); the server then POSTs each notification to that URL.
+
The push service is then responsible for routing these to the client.
+
+
7.1. The StateChange Object
+
+
When something changes on the server, the server pushes a StateChange
+
object to the client. A *StateChange* object has the following
+
properties:
+
+
o @type: "String"
+
+
This MUST be the string "StateChange".
+
+
o changed: "Id[TypeState]"
+
+
A map of an "account id" to an object encoding the state of data
+
types that have changed for that account since the last
+
StateChange object was pushed, for each of the accounts to which
+
the user has access and for which something has changed.
+
+
A *TypeState* object is a map. The keys are the type name "Foo"
+
(e.g., "Mailbox" or "Email"), and the value is the "state"
+
property that would currently be returned by a call to "Foo/get".
+
+
The client can compare the new state strings with its current
+
values to see whether it has the current data for these types. If
+
not, the changes can then be efficiently fetched in a single
+
standard API request (using the /changes type methods).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 63]
+
+
RFC 8620 JMAP July 2019
+
+
+
7.1.1. Example
+
+
In this example, the server has amalgamated a few changes together
+
across two different accounts the user has access to, before pushing
+
the following StateChange object to the client:
+
+
{
+
"@type": "StateChange",
+
"changed": {
+
"a3123": {
+
"Email": "d35ecb040aab",
+
"EmailDelivery": "428d565f2440",
+
"CalendarEvent": "87accfac587a"
+
},
+
"a43461d": {
+
"Mailbox": "0af7a512ce70",
+
"CalendarEvent": "7a4297cecd76"
+
}
+
}
+
}
+
+
The client can compare the state strings with its current state for
+
the Email, CalendarEvent, etc., object types in the appropriate
+
accounts to see if it needs to fetch changes.
+
+
If the client is itself making changes, it may receive a StateChange
+
object while the /set API call is in flight. It can wait until the
+
call completes and then compare if the new state string after the
+
/set is the same as was pushed in the StateChange object; if so, and
+
the old state of the /set response matches the client's previous
+
state, it does not need to waste a request asking for changes it
+
already knows.
+
+
7.2. PushSubscription
+
+
Clients may create a PushSubscription to register a URL with the JMAP
+
server. The JMAP server will then make an HTTP POST request to this
+
URL for each push notification it wishes to send to the client.
+
+
As a push subscription causes the JMAP server to make a number of
+
requests to a previously unknown endpoint, it can be used as a vector
+
for launching a denial-of-service attack. To prevent this, when a
+
subscription is created, the JMAP server immediately sends a
+
PushVerification object to that URL (see Section 7.2.2). The JMAP
+
server MUST NOT make any further requests to the URL until the client
+
receives the push and updates the subscription with the correct
+
verification code.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 64]
+
+
RFC 8620 JMAP July 2019
+
+
+
A *PushSubscription* object has the following properties:
+
+
o id: "Id" (immutable; server-set)
+
+
The id of the push subscription.
+
+
o deviceClientId: "String" (immutable)
+
+
An id that uniquely identifies the client + device it is running
+
on. The purpose of this is to allow clients to identify which
+
PushSubscription objects they created even if they lose their
+
local state, so they can revoke or update them. This string MUST
+
be different on different devices and be different from apps from
+
other vendors. It SHOULD be easy to regenerate and not depend on
+
persisted state. It is RECOMMENDED to use a secure hash of a
+
string that contains:
+
+
1. A unique identifier associated with the device where the JMAP
+
client is running, normally supplied by the device's operating
+
system.
+
+
2. A custom vendor/app id, including a domain controlled by the
+
vendor of the JMAP client.
+
+
To protect the privacy of the user, the deviceClientId id MUST NOT
+
contain an unobfuscated device id.
+
+
o url: "String" (immutable)
+
+
An absolute URL where the JMAP server will POST the data for the
+
push message. This MUST begin with "https://".
+
+
o keys: "Object|null" (immutable)
+
+
Client-generated encryption keys. If supplied, the server MUST
+
use them as specified in [RFC8291] to encrypt all data sent to the
+
push subscription. The object MUST have the following properties:
+
+
* p256dh: "String"
+
+
The P-256 Elliptic Curve Diffie-Hellman (ECDH) public key as
+
described in [RFC8291], encoded in URL-safe base64
+
representation as defined in [RFC4648].
+
+
* auth: "String"
+
+
The authentication secret as described in [RFC8291], encoded in
+
URL-safe base64 representation as defined in [RFC4648].
+
+
+
+
Jenkins & Newman Standards Track [Page 65]
+
+
RFC 8620 JMAP July 2019
+
+
+
o verificationCode: "String|null"
+
+
This MUST be null (or omitted) when the subscription is created.
+
The JMAP server then generates a verification code and sends it in
+
a push message, and the client updates the PushSubscription object
+
with the code; see Section 7.2.2 for details.
+
+
o expires: "UTCDate|null"
+
+
The time this push subscription expires. If specified, the JMAP
+
server MUST NOT make further requests to this resource after this
+
time. It MAY automatically destroy the push subscription at or
+
after this time.
+
+
The server MAY choose to set an expiry if none is given by the
+
client or modify the expiry time given by the client to a shorter
+
duration.
+
+
o types: "String[]|null"
+
+
A list of types the client is interested in (using the same names
+
as the keys in the TypeState object defined in the previous
+
section). A StateChange notification will only be sent if the
+
data for one of these types changes. Other types are omitted from
+
the TypeState object. If null, changes will be pushed for all
+
types.
+
+
The POST request MUST have a content type of "application/json" and
+
contain the UTF-8 JSON-encoded object as the body. The request MUST
+
have a "TTL" header and MAY have "Urgency" and/or "Topic" headers, as
+
specified in Section 5 of [RFC8030]. The JMAP server is expected to
+
understand and handle HTTP status responses in a reasonable manner.
+
A "429" (Too Many Requests) response MUST cause the JMAP server to
+
reduce the frequency of pushes; the JMAP push structure allows
+
multiple changes to be coalesced into a single minimal StateChange
+
object. See the security considerations in Section 8.6 for a
+
discussion of the risks in connecting to unknown servers.
+
+
The JMAP server acts as an application server as defined in
+
[RFC8030]. A client MAY use the rest of [RFC8030] in combination
+
with its own push service to form a complete end-to-end solution, or
+
it MAY rely on alternative mechanisms to ensure the delivery of the
+
pushed data after it leaves the JMAP server.
+
+
The push subscription is tied to the credentials used to authenticate
+
the API request that created it. Should these credentials expire or
+
be revoked, the push subscription MUST be destroyed by the JMAP
+
+
+
+
+
Jenkins & Newman Standards Track [Page 66]
+
+
RFC 8620 JMAP July 2019
+
+
+
server. Only subscriptions created by these credentials are returned
+
when the client fetches existing subscriptions.
+
+
When these credentials have their own expiry (i.e., it is a session
+
with a timeout), the server SHOULD NOT set or bound the expiry time
+
for the push subscription given by the client but MUST expire it when
+
the session expires.
+
+
When these credentials are not time bounded (e.g., Basic
+
authentication [RFC7617]), the server SHOULD set an expiry time for
+
the push subscription if none is given and limit the expiry time if
+
set too far in the future. This maximum expiry time MUST be at least
+
48 hours in the future and SHOULD be at least 7 days in the future.
+
An app running on a mobile device may only be able to refresh the
+
push subscription lifetime when it is in the foreground, so this
+
gives a reasonable time frame to allow this to happen.
+
+
In the case of separate access and refresh credentials, as in Oauth
+
2.0 [RFC6749], the server SHOULD tie the push subscription to the
+
validity of the refresh token rather than the access token and behave
+
according to whether this is time-limited or not.
+
+
When a push subscription is destroyed, the server MUST securely erase
+
the URL and encryption keys from memory and storage as soon as
+
possible.
+
+
7.2.1. PushSubscription/get
+
+
Standard /get method as described in Section 5.1, except it does
+
*not* take or return an "accountId" argument, as push subscriptions
+
are not tied to specific accounts. It also does *not* return a
+
"state" argument. The "ids" argument may be null to fetch all at
+
once.
+
+
The server MUST only return push subscriptions that were created
+
using the same authentication credentials as for this
+
"PushSubscription/get" request.
+
+
As the "url" and "keys" properties may contain data that is private
+
to a particular device, the values for these properties MUST NOT be
+
returned. If the "properties" argument is null or omitted, the
+
server MUST default to all properties excluding these two. If one of
+
them is explicitly requested, the method call MUST be rejected with a
+
"forbidden" error.
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 67]
+
+
RFC 8620 JMAP July 2019
+
+
+
7.2.2. PushSubscription/set
+
+
Standard /set method as described in Section 5.3, except it does
+
*not* take or return an "accountId" argument, as push subscriptions
+
are not tied to specific accounts. It also does *not* take an
+
"ifInState" argument or return "oldState" or "newState" arguments.
+
+
The "url" and "keys" properties are immutable; if the client wishes
+
to change these, it must destroy the current push subscription and
+
create a new one.
+
+
When a PushSubscription is created, the server MUST immediately push
+
a *PushVerification* object to the URL. It has the following
+
properties:
+
+
o @type: "String"
+
+
This MUST be the string "PushVerification".
+
+
o pushSubscriptionId: "String"
+
+
The id of the push subscription that was created.
+
+
o verificationCode: "String"
+
+
The verification code to add to the push subscription. This MUST
+
contain sufficient entropy to avoid the client being able to guess
+
the code via brute force.
+
+
The client MUST update the push subscription with the correct
+
verification code before the server makes any further requests to the
+
subscription's URL. Attempts to update the subscription with an
+
invalid verification code MUST be rejected by the server with an
+
"invalidProperties" SetError.
+
+
The client may update the "expires" property to extend (or, less
+
commonly, shorten) the lifetime of a push subscription. The server
+
MAY modify the proposed new expiry time to enforce server-defined
+
limits. Extending the lifetime does not require the subscription to
+
be verified again.
+
+
Clients SHOULD NOT update or destroy a push subscription that they
+
did not create (i.e., has a "deviceClientId" that they do not
+
recognise).
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 68]
+
+
RFC 8620 JMAP July 2019
+
+
+
7.2.3. Example
+
+
At "2018-07-06T02:14:29Z", a client with deviceClientId "a889-ffea-
+
910" fetches the set of push subscriptions currently on the server,
+
making an API request with:
+
+
[[ "PushSubscription/get", {
+
"ids": null
+
}, "0" ]]
+
+
Which returns:
+
+
[[ "PushSubscription/get", {
+
"list": [{
+
"id": "e50b2c1d-9553-41a3-b0a7-a7d26b599ee1",
+
"deviceClientId": "b37ff8001ca0",
+
"verificationCode": "b210ef734fe5f439c1ca386421359f7b",
+
"expires": "2018-07-31T00:13:21Z",
+
"types": [ "Todo" ]
+
}, {
+
"id": "f2d0aab5-e976-4e8b-ad4b-b380a5b987e4",
+
"deviceClientId": "X8980fc",
+
"verificationCode": "f3d4618a9ae15c8b7f5582533786d531",
+
"expires": "2018-07-12T05:55:00Z",
+
"types": [ "Mailbox", "Email", "EmailDelivery" ]
+
}],
+
"notFound": []
+
}, "0" ]]
+
+
Since neither of the returned push subscription objects have the
+
client's deviceClientId, it knows it does not have a current push
+
subscription active on the server. So it creates one, sending this
+
request:
+
+
[[ "PushSubscription/set", {
+
"create": {
+
"4f29": {
+
"deviceClientId": "a889-ffea-910",
+
"url": "https://example.com/push/?device=X8980fc&client=12c6d086",
+
"types": null
+
}
+
}
+
}, "0" ]]
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 69]
+
+
RFC 8620 JMAP July 2019
+
+
+
The server creates the push subscription but limits the expiry time
+
to 7 days in the future, returning this response:
+
+
[[ "PushSubscription/set", {
+
"created": {
+
"4f29": {
+
"id": "P43dcfa4-1dd4-41ef-9156-2c89b3b19c60",
+
"keys": null,
+
"expires": "2018-07-13T02:14:29Z"
+
}
+
}
+
}, "0" ]]
+
+
The server also immediately makes a POST request to
+
"https://example.com/push/?device=X8980fc&client=12c6d086" with the
+
data:
+
+
{
+
"@type": "PushVerification",
+
"pushSubscriptionId": "P43dcfa4-1dd4-41ef-9156-2c89b3b19c60",
+
"verificationCode": "da1f097b11ca17f06424e30bf02bfa67"
+
}
+
+
The client receives this and updates the subscription with the
+
verification code (note there is a potential race condition here; the
+
client MUST be able to handle receiving the push while the request
+
creating the subscription is still in progress):
+
+
[[ "PushSubscription/set", {
+
"update": {
+
"P43dcfa4-1dd4-41ef-9156-2c89b3b19c60": {
+
"verificationCode": "da1f097b11ca17f06424e30bf02bfa67"
+
}
+
}
+
}, "0" ]]
+
+
The server confirms the update was successful and will now make
+
requests to the registered URL when the state changes.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 70]
+
+
RFC 8620 JMAP July 2019
+
+
+
Two days later, the client updates the subscription to extend its
+
lifetime, sending this request:
+
+
[[ "PushSubscription/set", {
+
"update": {
+
"P43dcfa4-1dd4-41ef-9156-2c89b3b19c60": {
+
"expires": "2018-08-13T00:00:00Z"
+
}
+
}
+
}, "0" ]]
+
+
The server extends the expiry time, but only again to its maximum
+
limit of 7 days in the future, returning this response:
+
+
[[ "PushSubscription/set", {
+
"updated": {
+
"P43dcfa4-1dd4-41ef-9156-2c89b3b19c60": {
+
"expires": "2018-07-15T02:22:50Z"
+
}
+
}
+
}, "0" ]]
+
+
7.3. Event Source
+
+
Clients that can hold transport connections open can connect directly
+
to the JMAP server to receive push notifications via a "text/event-
+
stream" resource, as described in [EventSource]. This is a long
+
running HTTP request, where the server can push data to the client by
+
appending data without ending the response.
+
+
When a change occurs in the data on the server, it pushes an event
+
called "state" to any connected clients, with the StateChange object
+
as the data.
+
+
The server SHOULD also send a new event id that encodes the entire
+
server state visible to the user immediately after sending a "state"
+
event. When a new connection is made to the event-source endpoint, a
+
client following the server-sent events specification will send a
+
Last-Event-ID HTTP header field with the last id it saw, which the
+
server can use to work out whether the client has missed some
+
changes. If so, it SHOULD send these changes immediately on
+
connection.
+
+
The Session object (see Section 2) has an "eventSourceUrl" property,
+
which is in URI Template (level 1) format [RFC6570]. The URL MUST
+
contain variables called "types", "closeafter", and "ping".
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 71]
+
+
RFC 8620 JMAP July 2019
+
+
+
To connect to the resource, the client makes an authenticated GET
+
request to the event-source URL with the appropriate variables
+
substituted in:
+
+
o "types": This MUST be either:
+
+
* A comma-separated list of type names, e.g.,
+
"Email,CalendarEvent". The server MUST only push changes for
+
the types in this list.
+
+
* The single character: "*". Changes to all types are pushed.
+
+
o "closeafter": This MUST be one of the following values:
+
+
* "state": The server MUST end the HTTP response after pushing a
+
state event. This can be used by clients in environments where
+
buffering proxies prevent the pushed data from arriving
+
immediately, or indeed at all, when operating in the usual
+
mode.
+
+
* "no": The connection is persisted by the server as a standard
+
event-source resource.
+
+
o "ping": A positive integer value representing a length of time in
+
seconds, e.g., "300". If non-zero, the server MUST send an event
+
called "ping" whenever this time elapses since the previous event
+
was sent. This MUST NOT set a new event id. If the value is "0",
+
the server MUST NOT send ping events.
+
+
The server MAY modify a requested ping interval to be subject to a
+
minimum and/or maximum value. For interoperability, servers MUST
+
NOT have a minimum allowed value higher than 30 or a maximum
+
allowed value less than 300.
+
+
The data for the ping event MUST be a JSON object containing an
+
"interval" property, the value (type "UnsignedInt") being the
+
interval in seconds the server is using to send pings (this may be
+
different to the requested value if the server clamped it to be
+
within a min/max value).
+
+
Clients can monitor for the ping event to help determine when the
+
closeafter mode may be required.
+
+
A client MAY hold open multiple connections to the event-source
+
resource, although it SHOULD try to use a single connection for
+
efficiency.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 72]
+
+
RFC 8620 JMAP July 2019
+
+
+
8. Security Considerations
+
+
8.1. Transport Confidentiality
+
+
To ensure the confidentiality and integrity of data sent and received
+
via JMAP, all requests MUST use TLS 1.2 [RFC5246] [RFC8446] or later,
+
following the recommendations in [RFC7525]. Servers SHOULD support
+
TLS 1.3 [RFC8446] or later.
+
+
Clients MUST validate TLS certificate chains to protect against
+
man-in-the-middle attacks [RFC5280].
+
+
8.2. Authentication Scheme
+
+
A number of HTTP authentication schemes have been standardised (see
+
<https://www.iana.org/assignments/http-authschemes/>). Servers
+
should take care to assess the security characteristics of different
+
schemes in relation to their needs when deciding what to implement.
+
+
Use of the Basic authentication scheme is NOT RECOMMENDED. Services
+
that choose to use it are strongly recommended to require generation
+
of a unique "app password" via some external mechanism for each
+
client they wish to connect. This allows connections from different
+
devices to be differentiated by the server and access to be
+
individually revoked.
+
+
8.3. Service Autodiscovery
+
+
Unless secured by something like DNSSEC, autodiscovery of server
+
details using SRV DNS records is vulnerable to a DNS poisoning
+
attack, which can lead to the client talking to an attacker's server
+
instead of the real JMAP server. The attacker may then intercept
+
requests to execute man-in-the-middle attacks and, depending on the
+
authentication scheme, steal credentials to generate its own
+
requests.
+
+
Clients that do not support SRV lookups are likely to try just using
+
the "/.well-known/jmap" path directly against the domain of the
+
username over HTTPS. Servers SHOULD ensure this path resolves or
+
redirects to the correct JMAP Session resource to allow this to work.
+
If this is not feasible, servers MUST ensure this path cannot be
+
controlled by an attacker, as again it may be used to steal
+
credentials.
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 73]
+
+
RFC 8620 JMAP July 2019
+
+
+
8.4. JSON Parsing
+
+
The Security Considerations of [RFC8259] apply to the use of JSON as
+
the data interchange format.
+
+
As for any serialization format, parsers need to thoroughly check the
+
syntax of the supplied data. JSON uses opening and closing tags for
+
several types and structures, and it is possible that the end of the
+
supplied data will be reached when scanning for a matching closing
+
tag; this is an error condition, and implementations need to stop
+
scanning at the end of the supplied data.
+
+
JSON also uses a string encoding with some escape sequences to encode
+
special characters within a string. Care is needed when processing
+
these escape sequences to ensure that they are fully formed before
+
the special processing is triggered, with special care taken when the
+
escape sequences appear adjacent to other (non-escaped) special
+
characters or adjacent to the end of data (as in the previous
+
paragraph).
+
+
If parsing JSON into a non-textual structured data format,
+
implementations may need to allocate storage to hold JSON string
+
elements. Since JSON does not use explicit string lengths, the risk
+
of denial of service due to resource exhaustion is small, but
+
implementations may still wish to place limits on the size of
+
allocations they are willing to make in any given context, to avoid
+
untrusted data causing excessive memory allocation.
+
+
8.5. Denial of Service
+
+
A small request may result in a very large response and require
+
considerable work on the server if resource limits are not enforced.
+
JMAP provides mechanisms for advertising and enforcing a wide variety
+
of limits for mitigating this threat, including limits on the number
+
of objects fetched in a single method call, number of methods in a
+
single request, number of concurrent requests, etc.
+
+
JMAP servers MUST implement sensible limits to mitigate against
+
resource exhaustion attacks.
+
+
8.6. Connection to Unknown Push Server
+
+
When a push subscription is registered, the application server will
+
make POST requests to the given URL. There are a number of security
+
considerations that MUST be considered when implementing this.
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 74]
+
+
RFC 8620 JMAP July 2019
+
+
+
The server MUST ensure the URL is externally resolvable to avoid
+
server-side request forgery, where the server makes a request to a
+
resource on its internal network.
+
+
A malicious client may use the push subscription to attempt to flood
+
a third party server with requests, creating a denial-of-service
+
attack and masking the attacker's true identity. There is no
+
guarantee that the URL given to the JMAP server is actually a valid
+
push server. Upon creation of a push subscription, the JMAP server
+
sends a PushVerification object to the URL and MUST NOT send any
+
further requests until the client verifies it has received the
+
initial push. The verification code MUST contain sufficient entropy
+
to prevent the client from being able to verify the subscription via
+
brute force.
+
+
The verification code does not guarantee the URL is a valid push
+
server, only that the client is able to access the data submitted to
+
it. While the verification step significantly reduces the set of
+
potential targets, there is still a risk that the server is unrelated
+
to the client and being targeted for a denial-of-service attack.
+
+
The server MUST limit the number of push subscriptions any one user
+
may have to ensure the user cannot cause the server to send a large
+
number of push notifications at once, which could again be used as
+
part of a denial-of-service attack. The rate of creation MUST also
+
be limited to minimise the ability to abuse the verification request
+
as an attack vector.
+
+
8.7. Push Encryption
+
+
When data changes, a small object is pushed with the new state
+
strings for the types that have changed. While the data here is
+
minimal, a passive man-in-the-middle attacker may be able to gain
+
useful information. To ensure confidentiality and integrity, if the
+
push is sent via a third party outside of the control of the client
+
and JMAP server, the client MUST specify encryption keys when
+
establishing the PushSubscription and ignore any push notification
+
received that is not encrypted with those keys.
+
+
The privacy and security considerations of [RFC8030] and [RFC8291]
+
also apply to the use of the PushSubscription mechanism.
+
+
As there is no crypto algorithm agility in Web Push Encryption
+
[RFC8291], a new specification will be needed to provide this if new
+
algorithms are required in the future.
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 75]
+
+
RFC 8620 JMAP July 2019
+
+
+
8.8. Traffic Analysis
+
+
While the data is encrypted, a passive observer with the ability to
+
monitor network traffic may be able to glean information from the
+
timing of API requests and push notifications. For example, suppose
+
an email or calendar invitation is sent from User A (hosted on Server
+
X) to User B (hosted on Server Y). If Server X hosts data for many
+
users, a passive observer can see that the two servers connected but
+
does not know who the data was for. However, if a push notification
+
is immediately sent to User B and the attacker can observe this as
+
well, they may reasonably conclude that someone on Server X is
+
connecting to User B.
+
+
9. IANA Considerations
+
+
9.1. Assignment of jmap Service Name
+
+
IANA has assigned the 'jmap' service name in the "Service Name and
+
Transport Protocol Port Number Registry" [RFC6335].
+
+
Service Name: jmap
+
+
Transport Protocol(s): tcp
+
+
Assignee: IESG
+
+
Contact: IETF Chair
+
+
Description: JSON Meta Application Protocol
+
+
Reference: RFC 8620
+
+
Assignment Notes: This service name was previously assigned under the
+
name "JSON Mail Access Protocol". This has been de-assigned and
+
re-assigned with the approval of the previous assignee.
+
+
9.2. Registration of Well-Known URI Suffix for JMAP
+
+
IANA has registered the following suffix in the "Well-Known URIs"
+
registry for JMAP, as described in [RFC8615]:
+
+
URI Suffix: jmap
+
+
Change Controller: IETF
+
+
Specification Document: RFC 8620, Section 2.2.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 76]
+
+
RFC 8620 JMAP July 2019
+
+
+
9.3. Registration of the jmap URN Sub-namespace
+
+
IANA has registered the following URN sub-namespace in the "IETF URN
+
Sub-namespace for Registered Protocol Parameter Identifiers" registry
+
within the "Uniform Resource Name (URN) Namespace for IETF Use"
+
registry as described in [RFC3553].
+
+
Registered Parameter Identifier: jmap
+
+
Reference: RFC 8620, Section 9.4
+
+
IANA Registry Reference: http://www.iana.org/assignments/jmap
+
+
9.4. Creation of "JMAP Capabilities" Registry
+
+
IANA has created the "JMAP Capabilities" registry as described in
+
Section 2. JMAP capabilities are advertised in the "capabilities"
+
property of the JMAP Session resource. They are used to extend the
+
functionality of a JMAP server. A capability is referenced by a URI.
+
The JMAP capability URI can be a URN starting with
+
"urn:ietf:params:jmap:" plus a unique suffix that is the index value
+
in the jmap URN sub-namespace. Registration of a JMAP capability
+
with another form of URI has no impact on the jmap URN sub-namespace.
+
+
This registry follows the expert review process unless the "intended
+
use" field is "common" or "placeholder", in which case registration
+
follows the specification required process.
+
+
A JMAP capability registration can have an intended use of "common",
+
"placeholder", "limited", or "obsolete". IANA will list common-use
+
registrations prominently and separately from those with other
+
intended use values.
+
+
The JMAP capability registration procedure is not a formal standards
+
process but rather an administrative procedure intended to allow
+
community comment and sanity checking without excessive time delay.
+
+
A "placeholder" registration reserves part of the jmap URN namespace
+
for another purpose but is typically not included in the
+
"capabilities" property of the JMAP Session resource.
+
+
9.4.1. Preliminary Community Review
+
+
Notice of a potential JMAP common-use registration SHOULD be sent to
+
the JMAP mailing list <jmap@ietf.org> for review. This mailing list
+
is appropriate to solicit community feedback on a proposed JMAP
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 77]
+
+
RFC 8620 JMAP July 2019
+
+
+
capability. Registrations that are not intended for common use MAY
+
be sent to the list for review as well; doing so is entirely
+
OPTIONAL, but is encouraged.
+
+
The intent of the public posting to this list is to solicit comments
+
and feedback on the choice of the capability name, the unambiguity of
+
the specification document, and a review of any interoperability or
+
security considerations. The submitter may submit a revised
+
registration proposal or abandon the registration completely at any
+
time.
+
+
9.4.2. Submit Request to IANA
+
+
Registration requests can be sent to <iana@iana.org>.
+
+
9.4.3. Designated Expert Review
+
+
For a limited-use registration, the primary concern of the designated
+
expert (DE) is preventing name collisions and encouraging the
+
submitter to document security and privacy considerations; a
+
published specification is not required. For a common-use
+
registration, the DE is expected to confirm that suitable
+
documentation, as described in Section 4.6 of [RFC8126], is
+
available. The DE should also verify that the capability does not
+
conflict with work that is active or already published within the
+
IETF.
+
+
Before a period of 30 days has passed, the DE will either approve or
+
deny the registration request and publish a notice of the decision to
+
the JMAP WG mailing list or its successor, as well as inform IANA. A
+
denial notice must be justified by an explanation, and, in the cases
+
where it is possible, concrete suggestions on how the request can be
+
modified so as to become acceptable should be provided.
+
+
If the DE does not respond within 30 days, the registrant may request
+
the IESG take action to process the request in a timely manner.
+
+
9.4.4. Change Procedures
+
+
Once a JMAP capability has been published by the IANA, the change
+
controller may request a change to its definition. The same
+
procedure that would be appropriate for the original registration
+
request is used to process a change request.
+
+
JMAP capability registrations may not be deleted; capabilities that
+
are no longer believed appropriate for use can be declared obsolete
+
by a change to their "intended use" field; such capabilities will be
+
clearly marked in the lists published by the IANA.
+
+
+
+
Jenkins & Newman Standards Track [Page 78]
+
+
RFC 8620 JMAP July 2019
+
+
+
Significant changes to a capability's definition should be requested
+
only when there are serious omissions or errors in the published
+
specification. When review is required, a change request may be
+
denied if it renders entities that were valid under the previous
+
definition invalid under the new definition.
+
+
The owner of a JMAP capability may pass responsibility to another
+
person or agency by informing the IANA; this can be done without
+
discussion or review.
+
+
The IESG may reassign responsibility for a JMAP capability. The most
+
common case of this will be to enable changes to be made to
+
capabilities where the author of the registration has died, moved out
+
of contact, or is otherwise unable to make changes that are important
+
to the community.
+
+
9.4.5. JMAP Capabilities Registry Template
+
+
Capability name: (see capability property in Section 2)
+
+
Specification document:
+
+
Intended use: (one of common, limited, placeholder, or obsolete)
+
+
Change controller: ("IETF" for Standards Track / BCP RFCs)
+
+
Security and privacy considerations:
+
+
9.4.6. Initial Registration for JMAP Core
+
+
Capability Name: "urn:ietf:params:jmap:core"
+
+
Specification document: RFC 8620, Section 2
+
+
Intended use: common
+
+
Change Controller: IETF
+
+
Security and privacy considerations: RFC 8620, Section 8.
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 79]
+
+
RFC 8620 JMAP July 2019
+
+
+
9.4.7. Registration for JMAP Error Placeholder in JMAP Capabilities
+
Registry
+
+
Capability Name: "urn:ietf:params:jmap:error:"
+
+
Specification document: RFC 8620, Section 9.5
+
+
Intended use: placeholder
+
+
Change Controller: IETF
+
+
Security and privacy considerations: RFC 8620, Section 8.
+
+
9.5. Creation of "JMAP Error Codes" Registry
+
+
IANA has created the "JMAP Error Codes" registry. JMAP error codes
+
appear in the "type" member of a JSON problem details object (as
+
described in Section 3.6.1), the "type" member in a JMAP error object
+
(as described in Section 3.6.2), or the "type" member of a JMAP
+
method-specific error object (such as SetError in Section 5.3). When
+
used in a problem details object, the prefix
+
"urn:ietf:params:jmap:error:" is always included; when used in JMAP
+
objects, the prefix is always omitted.
+
+
This registry follows the expert review process. Preliminary
+
community review for this registry follows the same procedures as the
+
"JMAP Capabilities" registry, but it is optional. The change
+
procedures for this registry are the same as the change procedures
+
for the "JMAP Capabilities" registry.
+
+
9.5.1. Expert Review
+
+
The designated expert should review the following aspects of the
+
registration:
+
+
1. Verify the error code does not conflict with existing names.
+
+
2. Verify the error code follows the syntax limitations (does not
+
require URI encoding).
+
+
3. Encourage the submitter to follow the naming convention of
+
previously registered errors.
+
+
4. Encourage the submitter to describe client behaviours that are
+
recommended in response to the error code. These may distinguish
+
the error code from other error codes.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 80]
+
+
RFC 8620 JMAP July 2019
+
+
+
5. Encourage the submitter to describe when the server should issue
+
the error as opposed to some other error code.
+
+
6. Encourage the submitter to note any security considerations
+
associated with the error, if any (e.g., an error code that might
+
disclose existence of data the authenticated user does not have
+
permission to know about).
+
+
Steps 3-6 are meant to promote a higher-quality registry. However,
+
the expert is encouraged to approve any registration that would not
+
actively harm JMAP interoperability to make this a relatively
+
lightweight process.
+
+
9.5.2. JMAP Error Codes Registry Template
+
+
JMAP Error Code:
+
+
Intended use: (one of "common", "limited", "obsolete")
+
+
Change Controller: ("IETF" for Standards Track / BCP RFCs)
+
+
Reference: (Optional. Only required if defined in an RFC.)
+
+
Description:
+
+
9.5.3. Initial Contents for the JMAP Error Codes Registry
+
+
o JMAP Error Code: accountNotFound
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 3.6.2
+
Description: The accountId does not correspond to a valid account.
+
+
o JMAP Error Code: accountNotSupportedByMethod
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 3.6.2
+
Description: The accountId given corresponds to a valid account,
+
but the account does not support this method or data type.
+
+
o JMAP Error Code: accountReadOnly
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 3.6.2
+
Description: This method modifies state, but the account is read-
+
only (as returned on the corresponding Account object in the JMAP
+
Session resource).
+
+
+
+
+
Jenkins & Newman Standards Track [Page 81]
+
+
RFC 8620 JMAP July 2019
+
+
+
o JMAP Error Code: anchorNotFound
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 5.5
+
Description: An anchor argument was supplied, but it cannot be
+
found in the results of the query.
+
+
o JMAP Error Code: alreadyExists
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 5.4
+
Description: The server forbids duplicates, and the record already
+
exists in the target account. An existingId property of type Id
+
MUST be included on the SetError object with the id of the
+
existing record.
+
+
o JMAP Error Code: cannotCalculateChanges
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Sections 5.2 and 5.6
+
Description: The server cannot calculate the changes from the
+
state string given by the client.
+
+
o JMAP Error Code: forbidden
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Sections 3.6.2, 5.3, and 7.2.1
+
Description: The action would violate an ACL or other permissions
+
policy.
+
+
o JMAP Error Code: fromAccountNotFound
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Sections 5.4 and 6.3
+
Description: The fromAccountId does not correspond to a valid
+
account.
+
+
o JMAP Error Code: fromAccountNotSupportedByMethod
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 5.4
+
Description: The fromAccountId given corresponds to a valid
+
account, but the account does not support this data type.
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 82]
+
+
RFC 8620 JMAP July 2019
+
+
+
o JMAP Error Code: invalidArguments
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 3.6.2
+
Description: One of the arguments is of the wrong type or
+
otherwise invalid, or a required argument is missing.
+
+
o JMAP Error Code: invalidPatch
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 5.3
+
Description: The PatchObject given to update the record was not a
+
valid patch.
+
+
o JMAP Error Code: invalidProperties
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 5.3
+
Description: The record given is invalid.
+
+
o JMAP Error Code: notFound
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 5.3
+
Description: The id given cannot be found.
+
+
o JMAP Error Code: notJSON
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 3.6.1
+
Description: The content type of the request was not application/
+
json, or the request did not parse as I-JSON.
+
+
o JMAP Error Code: notRequest
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 3.6.1
+
Description: The request parsed as JSON but did not match the type
+
signature of the Request object.
+
+
o JMAP Error Code: overQuota
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 5.3
+
Description: The create would exceed a server-defined limit on the
+
number or total size of objects of this type.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 83]
+
+
RFC 8620 JMAP July 2019
+
+
+
o JMAP Error Code: rateLimit
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 5.3
+
Description: 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.
+
+
o JMAP Error Code: requestTooLarge
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Sections 5.1 and 5.3
+
Description: The total number of actions exceeds the maximum
+
number the server is willing to process in a single method call.
+
+
o JMAP Error Code: invalidResultReference
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 3.6.2
+
Description: The method used a result reference for one of its
+
arguments, but this failed to resolve.
+
+
o JMAP Error Code: serverFail
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 3.6.2
+
Description: An unexpected or unknown error occurred during the
+
processing of the call. The method call made no changes to the
+
server's state.
+
+
o JMAP Error Code: serverPartialFail
+
Intended Use: Limited
+
Change Controller: IETF
+
Reference: RFC 8620, Section 3.6.2
+
Description: Some, but not all, expected changes described by the
+
method occurred. The client MUST resynchronise impacted data to
+
determine the server state. Use of this error is strongly
+
discouraged.
+
+
o JMAP Error Code: serverUnavailable
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 3.6.2
+
Description: Some internal server resource was temporarily
+
unavailable. Attempting the same operation later (perhaps after a
+
backoff with a random factor) may succeed.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 84]
+
+
RFC 8620 JMAP July 2019
+
+
+
o JMAP Error Code: singleton
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 5.3
+
Description: This is a singleton type, so you cannot create
+
another one or destroy the existing one.
+
+
o JMAP Error Code: stateMismatch
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 5.3
+
Description: An ifInState argument was supplied, and it does not
+
match the current state.
+
+
o JMAP Error Code: tooLarge
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 5.3
+
Description: The action would result in an object that exceeds a
+
server-defined limit for the maximum size of a single object of
+
this type.
+
+
o JMAP Error Code: tooManyChanges
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 5.6
+
Description: There are more changes than the client's maxChanges
+
argument.
+
+
o JMAP Error Code: unknownCapability
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 3.6.1
+
Description: The client included a capability in the "using"
+
property of the request that the server does not support.
+
+
o JMAP Error Code: unknownMethod
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 3.6.2
+
Description: The server does not recognise this method name.
+
+
o JMAP Error Code: unsupportedFilter
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 5.5
+
Description: The filter is syntactically valid, but the server
+
cannot process it.
+
+
+
+
Jenkins & Newman Standards Track [Page 85]
+
+
RFC 8620 JMAP July 2019
+
+
+
o JMAP Error Code: unsupportedSort
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 5.5
+
Description: The sort is syntactically valid but includes a
+
property the server does not support sorting on or a collation
+
method it does not recognise.
+
+
o JMAP Error Code: willDestroy
+
Intended Use: Common
+
Change Controller: IETF
+
Reference: RFC 8620, Section 5.3
+
Description: The client requested an object be both updated and
+
destroyed in the same /set request, and the server has decided to
+
therefore ignore the update.
+
+
10. References
+
+
10.1. Normative References
+
+
[EventSource]
+
Hickson, I., "Server-Sent Events", World Wide Web
+
Consortium Recommendation REC-eventsource-20150203,
+
February 2015, <https://www.w3.org/TR/eventsource/>.
+
+
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+
Requirement Levels", BCP 14, RFC 2119,
+
DOI 10.17487/RFC2119, March 1997,
+
<https://www.rfc-editor.org/info/rfc2119>.
+
+
[RFC2782] Gulbrandsen, A., Vixie, P., and L. Esibov, "A DNS RR for
+
specifying the location of services (DNS SRV)", RFC 2782,
+
DOI 10.17487/RFC2782, February 2000,
+
<https://www.rfc-editor.org/info/rfc2782>.
+
+
[RFC2818] Rescorla, E., "HTTP Over TLS", RFC 2818,
+
DOI 10.17487/RFC2818, May 2000,
+
<https://www.rfc-editor.org/info/rfc2818>.
+
+
[RFC3339] Klyne, G. and C. Newman, "Date and Time on the Internet:
+
Timestamps", RFC 3339, DOI 10.17487/RFC3339, July 2002,
+
<https://www.rfc-editor.org/info/rfc3339>.
+
+
[RFC3553] Mealling, M., Masinter, L., Hardie, T., and G. Klyne, "An
+
IETF URN Sub-namespace for Registered Protocol
+
Parameters", BCP 73, RFC 3553, DOI 10.17487/RFC3553, June
+
2003, <https://www.rfc-editor.org/info/rfc3553>.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 86]
+
+
RFC 8620 JMAP July 2019
+
+
+
[RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
+
10646", STD 63, RFC 3629, DOI 10.17487/RFC3629, November
+
2003, <https://www.rfc-editor.org/info/rfc3629>.
+
+
[RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data
+
Encodings", RFC 4648, DOI 10.17487/RFC4648, October 2006,
+
<https://www.rfc-editor.org/info/rfc4648>.
+
+
[RFC4790] Newman, C., Duerst, M., and A. Gulbrandsen, "Internet
+
Application Protocol Collation Registry", RFC 4790,
+
DOI 10.17487/RFC4790, March 2007,
+
<https://www.rfc-editor.org/info/rfc4790>.
+
+
[RFC5051] Crispin, M., "i;unicode-casemap - Simple Unicode Collation
+
Algorithm", RFC 5051, DOI 10.17487/RFC5051, October 2007,
+
<https://www.rfc-editor.org/info/rfc5051>.
+
+
[RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security
+
(TLS) Protocol Version 1.2", RFC 5246,
+
DOI 10.17487/RFC5246, August 2008,
+
<https://www.rfc-editor.org/info/rfc5246>.
+
+
[RFC5280] Cooper, D., Santesson, S., Farrell, S., Boeyen, S.,
+
Housley, R., and W. Polk, "Internet X.509 Public Key
+
Infrastructure Certificate and Certificate Revocation List
+
(CRL) Profile", RFC 5280, DOI 10.17487/RFC5280, May 2008,
+
<https://www.rfc-editor.org/info/rfc5280>.
+
+
[RFC5322] Resnick, P., Ed., "Internet Message Format", RFC 5322,
+
DOI 10.17487/RFC5322, October 2008,
+
<https://www.rfc-editor.org/info/rfc5322>.
+
+
[RFC6186] Daboo, C., "Use of SRV Records for Locating Email
+
Submission/Access Services", RFC 6186,
+
DOI 10.17487/RFC6186, March 2011,
+
<https://www.rfc-editor.org/info/rfc6186>.
+
+
[RFC6335] Cotton, M., Eggert, L., Touch, J., Westerlund, M., and S.
+
Cheshire, "Internet Assigned Numbers Authority (IANA)
+
Procedures for the Management of the Service Name and
+
Transport Protocol Port Number Registry", BCP 165,
+
RFC 6335, DOI 10.17487/RFC6335, August 2011,
+
<https://www.rfc-editor.org/info/rfc6335>.
+
+
[RFC6570] Gregorio, J., Fielding, R., Hadley, M., Nottingham, M.,
+
and D. Orchard, "URI Template", RFC 6570,
+
DOI 10.17487/RFC6570, March 2012,
+
<https://www.rfc-editor.org/info/rfc6570>.
+
+
+
+
Jenkins & Newman Standards Track [Page 87]
+
+
RFC 8620 JMAP July 2019
+
+
+
[RFC6749] Hardt, D., Ed., "The OAuth 2.0 Authorization Framework",
+
RFC 6749, DOI 10.17487/RFC6749, October 2012,
+
<https://www.rfc-editor.org/info/rfc6749>.
+
+
[RFC6764] Daboo, C., "Locating Services for Calendaring Extensions
+
to WebDAV (CalDAV) and vCard Extensions to WebDAV
+
(CardDAV)", RFC 6764, DOI 10.17487/RFC6764, February 2013,
+
<https://www.rfc-editor.org/info/rfc6764>.
+
+
[RFC6838] Freed, N., Klensin, J., and T. Hansen, "Media Type
+
Specifications and Registration Procedures", BCP 13,
+
RFC 6838, DOI 10.17487/RFC6838, January 2013,
+
<https://www.rfc-editor.org/info/rfc6838>.
+
+
[RFC6901] Bryan, P., Ed., Zyp, K., and M. Nottingham, Ed.,
+
"JavaScript Object Notation (JSON) Pointer", RFC 6901,
+
DOI 10.17487/RFC6901, April 2013,
+
<https://www.rfc-editor.org/info/rfc6901>.
+
+
[RFC7230] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer
+
Protocol (HTTP/1.1): Message Syntax and Routing",
+
RFC 7230, DOI 10.17487/RFC7230, June 2014,
+
<https://www.rfc-editor.org/info/rfc7230>.
+
+
[RFC7231] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer
+
Protocol (HTTP/1.1): Semantics and Content", RFC 7231,
+
DOI 10.17487/RFC7231, June 2014,
+
<https://www.rfc-editor.org/info/rfc7231>.
+
+
[RFC7493] Bray, T., Ed., "The I-JSON Message Format", RFC 7493,
+
DOI 10.17487/RFC7493, March 2015,
+
<https://www.rfc-editor.org/info/rfc7493>.
+
+
[RFC7525] Sheffer, Y., Holz, R., and P. Saint-Andre,
+
"Recommendations for Secure Use of Transport Layer
+
Security (TLS) and Datagram Transport Layer Security
+
(DTLS)", BCP 195, RFC 7525, DOI 10.17487/RFC7525, May
+
2015, <https://www.rfc-editor.org/info/rfc7525>.
+
+
[RFC7617] Reschke, J., "The 'Basic' HTTP Authentication Scheme",
+
RFC 7617, DOI 10.17487/RFC7617, September 2015,
+
<https://www.rfc-editor.org/info/rfc7617>.
+
+
[RFC7807] Nottingham, M. and E. Wilde, "Problem Details for HTTP
+
APIs", RFC 7807, DOI 10.17487/RFC7807, March 2016,
+
<https://www.rfc-editor.org/info/rfc7807>.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 88]
+
+
RFC 8620 JMAP July 2019
+
+
+
[RFC8030] Thomson, M., Damaggio, E., and B. Raymor, Ed., "Generic
+
Event Delivery Using HTTP Push", RFC 8030,
+
DOI 10.17487/RFC8030, December 2016,
+
<https://www.rfc-editor.org/info/rfc8030>.
+
+
[RFC8126] Cotton, M., Leiba, B., and T. Narten, "Guidelines for
+
Writing an IANA Considerations Section in RFCs", BCP 26,
+
RFC 8126, DOI 10.17487/RFC8126, June 2017,
+
<https://www.rfc-editor.org/info/rfc8126>.
+
+
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
+
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
+
May 2017, <https://www.rfc-editor.org/info/rfc8174>.
+
+
[RFC8259] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data
+
Interchange Format", STD 90, RFC 8259,
+
DOI 10.17487/RFC8259, December 2017,
+
<https://www.rfc-editor.org/info/rfc8259>.
+
+
[RFC8264] Saint-Andre, P. and M. Blanchet, "PRECIS Framework:
+
Preparation, Enforcement, and Comparison of
+
Internationalized Strings in Application Protocols",
+
RFC 8264, DOI 10.17487/RFC8264, October 2017,
+
<https://www.rfc-editor.org/info/rfc8264>.
+
+
[RFC8291] Thomson, M., "Message Encryption for Web Push", RFC 8291,
+
DOI 10.17487/RFC8291, November 2017,
+
<https://www.rfc-editor.org/info/rfc8291>.
+
+
[RFC8446] Rescorla, E., "The Transport Layer Security (TLS) Protocol
+
Version 1.3", RFC 8446, DOI 10.17487/RFC8446, August 2018,
+
<https://www.rfc-editor.org/info/rfc8446>.
+
+
[RFC8615] Nottingham, M., "Well-Known Uniform Resource Identifiers
+
(URIs)", RFC 8615, DOI 10.17487/RFC8615, May 2019,
+
<https://www.rfc-editor.org/info/rfc8615>.
+
+
10.2. Informative References
+
+
[RFC8246] McManus, P., "HTTP Immutable Responses", RFC 8246,
+
DOI 10.17487/RFC8246, September 2017,
+
<https://www.rfc-editor.org/info/rfc8246>.
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 89]
+
+
RFC 8620 JMAP July 2019
+
+
+
Authors' Addresses
+
+
Neil Jenkins
+
Fastmail
+
PO Box 234, Collins St. West
+
Melbourne, VIC 8007
+
Australia
+
+
Email: neilj@fastmailteam.com
+
URI: https://www.fastmail.com
+
+
+
Chris Newman
+
Oracle
+
440 E. Huntington Dr., Suite 400
+
Arcadia, CA 91006
+
United States of America
+
+
Email: chris.newman@oracle.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 90]
+
+6051
rfc8621.txt
···
+
+
+
+
+
+
+
Internet Engineering Task Force (IETF) N. Jenkins
+
Request for Comments: 8621 Fastmail
+
Updates: 5788 C. Newman
+
Category: Standards Track Oracle
+
ISSN: 2070-1721 August 2019
+
+
+
The JSON Meta Application Protocol (JMAP) for Mail
+
+
Abstract
+
+
This document specifies a data model for synchronising email data
+
with a server using the JSON Meta Application Protocol (JMAP).
+
Clients can use this to efficiently search, access, organise, and
+
send messages, and to get push notifications for fast
+
resynchronisation when new messages are delivered or a change is made
+
in another client.
+
+
Status of This Memo
+
+
This is an Internet Standards Track document.
+
+
This document is a product of the Internet Engineering Task Force
+
(IETF). It represents the consensus of the IETF community. It has
+
received public review and has been approved for publication by the
+
Internet Engineering Steering Group (IESG). Further information on
+
Internet Standards is available in Section 2 of RFC 7841.
+
+
Information about the current status of this document, any errata,
+
and how to provide feedback on it may be obtained at
+
https://www.rfc-editor.org/info/rfc8621.
+
+
Copyright Notice
+
+
Copyright (c) 2019 IETF Trust and the persons identified as the
+
document authors. All rights reserved.
+
+
This document is subject to BCP 78 and the IETF Trust's Legal
+
Provisions Relating to IETF Documents
+
(https://trustee.ietf.org/license-info) in effect on the date of
+
publication of this document. Please review these documents
+
carefully, as they describe your rights and restrictions with respect
+
to this document. Code Components extracted from this document must
+
include Simplified BSD License text as described in Section 4.e of
+
the Trust Legal Provisions and are provided without warranty as
+
described in the Simplified BSD License.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 1]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
Table of Contents
+
+
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 4
+
1.1. Notational Conventions . . . . . . . . . . . . . . . . . 4
+
1.2. Terminology . . . . . . . . . . . . . . . . . . . . . . . 5
+
1.3. Additions to the Capabilities Object . . . . . . . . . . 5
+
1.3.1. urn:ietf:params:jmap:mail . . . . . . . . . . . . . . 5
+
1.3.2. urn:ietf:params:jmap:submission . . . . . . . . . . . 7
+
1.3.3. urn:ietf:params:jmap:vacationresponse . . . . . . . . 8
+
1.4. Data Type Support in Different Accounts . . . . . . . . . 8
+
1.5. Push . . . . . . . . . . . . . . . . . . . . . . . . . . 8
+
1.5.1. Example . . . . . . . . . . . . . . . . . . . . . . . 9
+
1.6. Ids . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
+
2. Mailboxes . . . . . . . . . . . . . . . . . . . . . . . . . . 9
+
2.1. Mailbox/get . . . . . . . . . . . . . . . . . . . . . . . 14
+
2.2. Mailbox/changes . . . . . . . . . . . . . . . . . . . . . 14
+
2.3. Mailbox/query . . . . . . . . . . . . . . . . . . . . . . 14
+
2.4. Mailbox/queryChanges . . . . . . . . . . . . . . . . . . 15
+
2.5. Mailbox/set . . . . . . . . . . . . . . . . . . . . . . . 16
+
2.6. Example . . . . . . . . . . . . . . . . . . . . . . . . . 17
+
3. Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
+
3.1. Thread/get . . . . . . . . . . . . . . . . . . . . . . . 22
+
3.1.1. Example . . . . . . . . . . . . . . . . . . . . . . . 22
+
3.2. Thread/changes . . . . . . . . . . . . . . . . . . . . . 22
+
4. Emails . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
+
4.1. Properties of the Email Object . . . . . . . . . . . . . 23
+
4.1.1. Metadata . . . . . . . . . . . . . . . . . . . . . . 24
+
4.1.2. Header Fields Parsed Forms . . . . . . . . . . . . . 26
+
4.1.3. Header Fields Properties . . . . . . . . . . . . . . 32
+
4.1.4. Body Parts . . . . . . . . . . . . . . . . . . . . . 35
+
4.2. Email/get . . . . . . . . . . . . . . . . . . . . . . . . 42
+
4.2.1. Example . . . . . . . . . . . . . . . . . . . . . . . 44
+
4.3. Email/changes . . . . . . . . . . . . . . . . . . . . . . 45
+
4.4. Email/query . . . . . . . . . . . . . . . . . . . . . . . 45
+
4.4.1. Filtering . . . . . . . . . . . . . . . . . . . . . . 46
+
4.4.2. Sorting . . . . . . . . . . . . . . . . . . . . . . . 49
+
4.4.3. Thread Collapsing . . . . . . . . . . . . . . . . . . 50
+
4.5. Email/queryChanges . . . . . . . . . . . . . . . . . . . 51
+
4.6. Email/set . . . . . . . . . . . . . . . . . . . . . . . . 51
+
4.7. Email/copy . . . . . . . . . . . . . . . . . . . . . . . 53
+
4.8. Email/import . . . . . . . . . . . . . . . . . . . . . . 54
+
4.9. Email/parse . . . . . . . . . . . . . . . . . . . . . . . 56
+
4.10. Examples . . . . . . . . . . . . . . . . . . . . . . . . 58
+
5. Search Snippets . . . . . . . . . . . . . . . . . . . . . . . 68
+
5.1. SearchSnippet/get . . . . . . . . . . . . . . . . . . . . 69
+
5.2. Example . . . . . . . . . . . . . . . . . . . . . . . . . 71
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 2]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
6. Identities . . . . . . . . . . . . . . . . . . . . . . . . . 72
+
6.1. Identity/get . . . . . . . . . . . . . . . . . . . . . . 73
+
6.2. Identity/changes . . . . . . . . . . . . . . . . . . . . 73
+
6.3. Identity/set . . . . . . . . . . . . . . . . . . . . . . 73
+
6.4. Example . . . . . . . . . . . . . . . . . . . . . . . . . 73
+
7. Email Submission . . . . . . . . . . . . . . . . . . . . . . 74
+
7.1. EmailSubmission/get . . . . . . . . . . . . . . . . . . . 80
+
7.2. EmailSubmission/changes . . . . . . . . . . . . . . . . . 80
+
7.3. EmailSubmission/query . . . . . . . . . . . . . . . . . . 80
+
7.4. EmailSubmission/queryChanges . . . . . . . . . . . . . . 81
+
7.5. EmailSubmission/set . . . . . . . . . . . . . . . . . . . 81
+
7.5.1. Example . . . . . . . . . . . . . . . . . . . . . . . 84
+
8. Vacation Response . . . . . . . . . . . . . . . . . . . . . . 86
+
8.1. VacationResponse/get . . . . . . . . . . . . . . . . . . 87
+
8.2. VacationResponse/set . . . . . . . . . . . . . . . . . . 88
+
9. Security Considerations . . . . . . . . . . . . . . . . . . . 88
+
9.1. EmailBodyPart Value . . . . . . . . . . . . . . . . . . . 88
+
9.2. HTML Email Display . . . . . . . . . . . . . . . . . . . 88
+
9.3. Multiple Part Display . . . . . . . . . . . . . . . . . . 91
+
9.4. Email Submission . . . . . . . . . . . . . . . . . . . . 91
+
9.5. Partial Account Access . . . . . . . . . . . . . . . . . 92
+
9.6. Permission to Send from an Address . . . . . . . . . . . 92
+
10. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 93
+
10.1. JMAP Capability Registration for "mail" . . . . . . . . 93
+
10.2. JMAP Capability Registration for "submission" . . . . . 93
+
10.3. JMAP Capability Registration for "vacationresponse" . . 94
+
10.4. IMAP and JMAP Keywords Registry . . . . . . . . . . . . 94
+
10.4.1. Registration of JMAP Keyword "$draft" . . . . . . . 95
+
10.4.2. Registration of JMAP Keyword "$seen" . . . . . . . . 96
+
10.4.3. Registration of JMAP Keyword "$flagged" . . . . . . 97
+
10.4.4. Registration of JMAP Keyword "$answered" . . . . . . 98
+
10.4.5. Registration of "$recent" Keyword . . . . . . . . . 99
+
10.5. IMAP Mailbox Name Attributes Registry . . . . . . . . . 99
+
10.5.1. Registration of "inbox" Role . . . . . . . . . . . . 99
+
10.6. JMAP Error Codes Registry . . . . . . . . . . . . . . . 100
+
10.6.1. mailboxHasChild . . . . . . . . . . . . . . . . . . 100
+
10.6.2. mailboxHasEmail . . . . . . . . . . . . . . . . . . 100
+
10.6.3. blobNotFound . . . . . . . . . . . . . . . . . . . . 100
+
10.6.4. tooManyKeywords . . . . . . . . . . . . . . . . . . 101
+
10.6.5. tooManyMailboxes . . . . . . . . . . . . . . . . . . 101
+
10.6.6. invalidEmail . . . . . . . . . . . . . . . . . . . . 101
+
10.6.7. tooManyRecipients . . . . . . . . . . . . . . . . . 102
+
10.6.8. noRecipients . . . . . . . . . . . . . . . . . . . . 102
+
10.6.9. invalidRecipients . . . . . . . . . . . . . . . . . 102
+
10.6.10. forbiddenMailFrom . . . . . . . . . . . . . . . . . 103
+
10.6.11. forbiddenFrom . . . . . . . . . . . . . . . . . . . 103
+
10.6.12. forbiddenToSend . . . . . . . . . . . . . . . . . . 103
+
+
+
+
+
Jenkins & Newman Standards Track [Page 3]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
11. References . . . . . . . . . . . . . . . . . . . . . . . . . 104
+
11.1. Normative References . . . . . . . . . . . . . . . . . . 104
+
11.2. Informative References . . . . . . . . . . . . . . . . . 107
+
Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 108
+
+
1. Introduction
+
+
The JSON Meta Application Protocol (JMAP) [RFC8620] is a generic
+
protocol for synchronising data, such as mail, calendars, or contacts
+
between a client and a server. It is optimised for mobile and web
+
environments and aims to provide a consistent interface to different
+
data types.
+
+
This specification defines a data model for accessing a mail store
+
over JMAP, allowing you to query, read, organise, and submit mail for
+
sending.
+
+
The data model is designed to allow a server to provide consistent
+
access to the same data via IMAP [RFC3501] as well as JMAP. As in
+
IMAP, a message must belong to a mailbox; however, in JMAP, its id
+
does not change if you move it between mailboxes, and the server may
+
allow it to belong to multiple mailboxes simultaneously (often
+
exposed in a user agent as labels rather than folders).
+
+
As in IMAP, messages may also be assigned zero or more keywords:
+
short arbitrary strings. These are primarily intended to store
+
metadata to inform client display, such as unread status or whether a
+
message has been replied to. An IANA registry allows common
+
semantics to be shared between clients and extended easily in the
+
future.
+
+
A message and its replies are linked on the server by a common Thread
+
id. Clients may fetch the list of messages with a particular Thread
+
id to more easily present a threaded or conversational interface.
+
+
Permissions for message access happen on a per-mailbox basis.
+
Servers may give the user restricted permissions for certain
+
mailboxes, for example, if another user's inbox has been shared as
+
read-only with them.
+
+
1.1. Notational Conventions
+
+
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+
"SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
+
"OPTIONAL" in this document are to be interpreted as described in
+
BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all
+
capitals, as shown here.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 4]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
Type signatures, examples, and property descriptions in this document
+
follow the conventions established in Section 1.1 of [RFC8620]. Data
+
types defined in the core specification are also used in this
+
document.
+
+
Servers MUST support all properties specified for the new data types
+
defined in this document.
+
+
1.2. Terminology
+
+
This document uses the same terminology as in the core JMAP
+
specification.
+
+
The terms Mailbox, Thread, Email, SearchSnippet, EmailSubmission and
+
VacationResponse (with that specific capitalisation) are used to
+
refer to the data types defined in this document and instances of
+
those data types.
+
+
The term message refers to a document in Internet Message Format, as
+
described in [RFC5322]. The Email data type represents messages in
+
the mail store and associated metadata.
+
+
1.3. Additions to the Capabilities Object
+
+
The capabilities object is returned as part of the JMAP Session
+
object; see [RFC8620], Section 2.
+
+
This document defines three additional capability URIs.
+
+
1.3.1. urn:ietf:params:jmap:mail
+
+
This represents support for the Mailbox, Thread, Email, and
+
SearchSnippet data types and associated API methods. The value of
+
this property in the JMAP session "capabilities" property is an empty
+
object.
+
+
The value of this property in an account's "accountCapabilities"
+
property is an object that MUST contain the following information on
+
server capabilities and permissions for that account:
+
+
o maxMailboxesPerEmail: "UnsignedInt|null"
+
+
The maximum number of Mailboxes (see Section 2) that can be can
+
assigned to a single Email object (see Section 4). This MUST be
+
an integer >= 1, or null for no limit (or rather, the limit is
+
always the number of Mailboxes in the account).
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 5]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o maxMailboxDepth: "UnsignedInt|null"
+
+
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.
+
+
o maxSizeMailboxName: "UnsignedInt"
+
+
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.
+
+
o maxSizeAttachmentsPerEmail: "UnsignedInt"
+
+
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.
+
+
o emailQuerySortOptions: "String[]"
+
+
A list of all the values the server supports for the "property"
+
field of the Comparator object in an "Email/query" sort (see
+
Section 4.4.2). 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.
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 6]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o mayCreateTopLevelMailbox: "Boolean"
+
+
If true, the user may create a Mailbox (see Section 2) 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.)
+
+
1.3.2. urn:ietf:params:jmap:submission
+
+
This represents support for the Identity and EmailSubmission data
+
types and associated API methods. The value of this property in the
+
JMAP session "capabilities" property is an empty object.
+
+
The value of this property in an account's "accountCapabilities"
+
property is an object that MUST contain the following information on
+
server capabilities and permissions for that account:
+
+
o maxDelayedSend: "UnsignedInt"
+
+
The number in seconds of the maximum delay the server supports in
+
sending (see the EmailSubmission object description). This is 0
+
if the server does not support delayed send.
+
+
o submissionExtensions: "String[String[]]"
+
+
The set of SMTP submission extensions supported by the server,
+
which the client may use when creating an EmailSubmission object
+
(see Section 7). 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]
+
+
+
+
Jenkins & Newman Standards Track [Page 7]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
* 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
+
<https://www.iana.org/assignments/mail-parameters>.
+
+
1.3.3. urn:ietf:params:jmap:vacationresponse
+
+
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.
+
+
1.4. Data Type Support in Different Accounts
+
+
The server MUST include the appropriate capability strings as keys in
+
the "accountCapabilities" property of any account with which the user
+
may use the data types represented by that URI. Supported data types
+
may differ between accounts the user has access to. For example, in
+
the user's personal account, they may have access to all three sets
+
of data, but in a shared account, they may only have data for
+
"urn:ietf:params:jmap:mail". This means they can access
+
Mailbox/Thread/Email data in the shared account but are not allowed
+
to send as that account (and so do not have access to Identity/
+
EmailSubmission objects) or view/set its VacationResponse.
+
+
1.5. Push
+
+
Servers MUST support the JMAP push mechanisms, as specified in
+
[RFC8620], Section 7, to receive notifications when the state changes
+
for any of the types defined in this specification.
+
+
In addition, servers that implement the "urn:ietf:params:jmap:mail"
+
capability MUST support pushing state changes for a type called
+
"EmailDelivery". There are no methods to act on this type; it only
+
exists as part of the push mechanism. The state string for this MUST
+
change whenever a new Email is added to the store, but it SHOULD NOT
+
change upon any other change to the Email objects, for example, if
+
one is marked as read or deleted.
+
+
Clients in battery-constrained environments may wish to delay
+
fetching changes initiated by the user but fetch new Emails
+
immediately so they can notify the user. To do this, they can
+
register for pushes for the EmailDelivery type rather than the Email
+
type (as defined in Section 4).
+
+
+
+
Jenkins & Newman Standards Track [Page 8]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
1.5.1. Example
+
+
The client has registered for push notifications (see [RFC8620]) just
+
for the EmailDelivery type. The user marks an Email as read on
+
another device, causing the state string for the Email type to
+
change; however, as nothing new was added to the store, the
+
EmailDelivery state does not change and nothing is pushed to the
+
client. A new message arrives in the user's inbox, again causing the
+
Email state to change. This time, the EmailDelivery state also
+
changes, and a StateChange object is pushed to the client with the
+
new state string. The client may then resync to fetch the new Email
+
immediately.
+
+
1.6. Ids
+
+
If a JMAP Mail server also provides an IMAP interface to the data and
+
supports IMAP Extension for Object Identifiers [RFC8474], the ids
+
SHOULD be the same for Mailbox, Thread, and Email objects in JMAP.
+
+
2. Mailboxes
+
+
A Mailbox represents a named set of Email objects. This is the
+
primary mechanism for organising messages within an account. It is
+
analogous to a folder or a label in other systems. A Mailbox may
+
perform a certain role in the system; see below for more details.
+
+
For compatibility with IMAP, an Email MUST belong to one or more
+
Mailboxes. The Email id does not change if the Email changes
+
Mailboxes.
+
+
A *Mailbox* object has the following properties:
+
+
o id: "Id" (immutable; server-set)
+
+
The id of the Mailbox.
+
+
o name: "String"
+
+
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).
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 9]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o parentId: "Id|null" (default: null)
+
+
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.
+
+
o role: "String|null" (default: null)
+
+
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 at
+
<https://www.iana.org/assignments/imap-mailbox-name-attributes/>,
+
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.
+
+
o sortOrder: "UnsignedInt" (default: 0)
+
+
Defines the sort order of Mailboxes when presented in the client's
+
UI, so it is consistent between devices. 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.
+
+
o totalEmails: "UnsignedInt" (server-set)
+
+
The number of Emails in this Mailbox.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 10]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o unreadEmails: "UnsignedInt" (server-set)
+
+
The number of Emails in this Mailbox that have neither the "$seen"
+
keyword nor the "$draft" keyword.
+
+
o totalThreads: "UnsignedInt" (server-set)
+
+
The number of Threads where at least one Email in the Thread is in
+
this Mailbox.
+
+
o unreadThreads: "UnsignedInt" (server-set)
+
+
An indication of the number of "unread" Threads in the Mailbox.
+
+
For compatibility with existing implementations, the way "unread
+
Threads" is determined is not mandated in this document. The
+
simplest solution to implement is simply the number of Threads
+
where at least one Email in the Thread is both in this Mailbox and
+
has neither the "$seen" nor "$draft" keywords.
+
+
However, a quality implementation will return the number of unread
+
items the user would see if they opened that Mailbox. A Thread is
+
shown as unread if it contains any unread Emails that will be
+
displayed when the Thread is opened. Therefore, "unreadThreads"
+
should be the number of Threads where at least one Email in the
+
Thread has neither the "$seen" nor the "$draft" keyword AND at
+
least one Email in the Thread is in this Mailbox. Note that the
+
unread Email does not need to be the one in this Mailbox. In
+
addition, the trash Mailbox (that is, a Mailbox whose "role" is
+
"trash") requires special treatment:
+
+
1. Emails that are *only* in the trash (and no other Mailbox) are
+
ignored when calculating the "unreadThreads" count of other
+
Mailboxes.
+
+
2. Emails that are *not* in the trash are ignored when
+
calculating the "unreadThreads" count for the trash Mailbox.
+
+
The result of this is that Emails in the trash are treated as
+
though they are in a separate Thread for the purposes of unread
+
counts. It is expected that clients will hide Emails in the trash
+
when viewing a Thread in another Mailbox, and vice versa. This
+
allows you to delete a single Email to the trash out of a Thread.
+
+
For example, suppose you have an account where the entire contents
+
is a single Thread with 2 Emails: an unread Email in the trash and
+
a read Email in the inbox. The "unreadThreads" count would be 1
+
for the trash and 0 for the inbox.
+
+
+
+
Jenkins & Newman Standards Track [Page 11]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o myRights: "MailboxRights" (server-set)
+
+
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]. A *MailboxRights* object has
+
the following properties:
+
+
* mayReadItems: "Boolean"
+
+
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).
+
+
* mayAddItems: "Boolean"
+
+
The user may add mail to this Mailbox (by either creating a new
+
Email or moving an existing one). Corresponds to IMAP ACL "i".
+
+
* mayRemoveItems: "Boolean"
+
+
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).
+
+
* maySetSeen: "Boolean"
+
+
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".
+
+
* maySetKeywords: "Boolean"
+
+
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".
+
+
* mayCreateChild: "Boolean"
+
+
The user may create a Mailbox with this Mailbox as its parent.
+
Corresponds to IMAP ACL "k".
+
+
+
+
+
Jenkins & Newman Standards Track [Page 12]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
* mayRename: "Boolean"
+
+
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).
+
+
* mayDelete: "Boolean"
+
+
The user may delete the Mailbox itself. Corresponds to IMAP
+
ACL "x" (although this covers both rename and delete
+
permissions).
+
+
* maySubmit: "Boolean"
+
+
Messages may be submitted directly to this Mailbox.
+
Corresponds to IMAP ACL "p".
+
+
o isSubscribed: "Boolean"
+
+
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.
+
+
For IMAP compatibility, an Email in both the trash and another
+
Mailbox SHOULD be treated by the client as existing in both places
+
(i.e., when emptying the trash, the client should just remove it from
+
the trash Mailbox and leave it in the other Mailbox).
+
+
The following JMAP methods are supported.
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 13]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
2.1. Mailbox/get
+
+
This is a standard "/get" method as described in [RFC8620],
+
Section 5.1. The "ids" argument may be "null" to fetch all at once.
+
+
2.2. Mailbox/changes
+
+
This is a standard "/changes" method as described in [RFC8620],
+
Section 5.2 but with one extra argument to the response:
+
+
o updatedProperties: "String[]|null"
+
+
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.
+
+
Since counts frequently change but other properties are generally
+
only changed rarely, the server can help the client optimise data
+
transfer by keeping track of changes to Email/Thread counts separate
+
from other state changes. The "updatedProperties" array may be used
+
directly via a back-reference in a subsequent "Mailbox/get" call in
+
the same request, so only these properties are returned if nothing
+
else has changed.
+
+
2.3. Mailbox/query
+
+
This is a standard "/query" method as described in [RFC8620],
+
Section 5.5 but with the following additional request argument:
+
+
o sortAsTree: "Boolean" (default: false)
+
+
If true, when sorting the query results and comparing Mailboxes A
+
and B:
+
+
* If A is an ancestor of B, it always comes first regardless of
+
the sort comparators. Similarly, if A is descendant of B, then
+
B always comes first.
+
+
* Otherwise, if A and B do not share a "parentId", find the
+
nearest ancestors of each that do have the same "parentId" and
+
compare the sort properties on those Mailboxes instead.
+
+
The result of this is that the Mailboxes are sorted as a tree
+
according to the parentId properties, with each set of children
+
with a common parent sorted according to the standard sort
+
comparators.
+
+
+
+
Jenkins & Newman Standards Track [Page 14]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o filterAsTree: "Boolean" (default: false)
+
+
If true, a Mailbox is only included in the query if all its
+
ancestors are also included in the query according to the filter.
+
+
A *FilterCondition* object has the following properties, any of which
+
may be omitted:
+
+
o parentId: "Id|null"
+
+
The Mailbox "parentId" property must match the given value
+
exactly.
+
+
o name: "String"
+
+
The Mailbox "name" property contains the given string.
+
+
o role: "String|null"
+
+
The Mailbox "role" property must match the given value exactly.
+
+
o hasAnyRole: "Boolean"
+
+
If true, a Mailbox matches if it has any non-null value for its
+
"role" property.
+
+
o isSubscribed: "Boolean"
+
+
The "isSubscribed" property of the Mailbox must be identical to
+
the value given to match the condition.
+
+
A Mailbox object matches the FilterCondition if and only if all of
+
the given conditions match. If zero properties are specified, it is
+
automatically true for all objects.
+
+
The following Mailbox properties MUST be supported for sorting:
+
+
o "sortOrder"
+
+
o "name"
+
+
2.4. Mailbox/queryChanges
+
+
This is a standard "/queryChanges" method as described in [RFC8620],
+
Section 5.6.
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 15]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
2.5. Mailbox/set
+
+
This is a standard "/set" method as described in [RFC8620],
+
Section 5.3 but with the following additional request argument:
+
+
o onDestroyRemoveEmails: "Boolean" (default: false)
+
+
If false, any attempt to destroy a Mailbox that still has Emails
+
in it will be rejected with a "mailboxHasEmail" SetError. If
+
true, any Emails that were in the Mailbox will be removed from it,
+
and if in no other Mailboxes, they will be destroyed when the
+
Mailbox is destroyed.
+
+
The following extra SetError types are defined:
+
+
For "destroy":
+
+
o "mailboxHasChild": The Mailbox still has at least one child
+
Mailbox. The client MUST remove these before it can delete the
+
parent Mailbox.
+
+
o "mailboxHasEmail": The Mailbox has at least one Email assigned to
+
it, and the "onDestroyRemoveEmails" argument was false.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 16]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
2.6. Example
+
+
Fetching all Mailboxes in an account:
+
+
[[ "Mailbox/get", {
+
"accountId": "u33084183",
+
"ids": null
+
}, "0" ]]
+
+
And the response:
+
+
[[ "Mailbox/get", {
+
"accountId": "u33084183",
+
"state": "78540",
+
"list": [{
+
"id": "MB23cfa8094c0f41e6",
+
"name": "Inbox",
+
"parentId": null,
+
"role": "inbox",
+
"sortOrder": 10,
+
"totalEmails": 16307,
+
"unreadEmails": 13905,
+
"totalThreads": 5833,
+
"unreadThreads": 5128,
+
"myRights": {
+
"mayAddItems": true,
+
"mayRename": false,
+
"maySubmit": true,
+
"mayDelete": false,
+
"maySetKeywords": true,
+
"mayRemoveItems": true,
+
"mayCreateChild": true,
+
"maySetSeen": true,
+
"mayReadItems": true
+
},
+
"isSubscribed": true
+
}, {
+
"id": "MB674cc24095db49ce",
+
"name": "Important mail",
+
...
+
}, ... ],
+
"notFound": []
+
}, "0" ]]
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 17]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
Now suppose an Email is marked read, and we get a push update that
+
the Mailbox state has changed. You might fetch the updates like
+
this:
+
+
[[ "Mailbox/changes", {
+
"accountId": "u33084183",
+
"sinceState": "78540"
+
}, "0" ],
+
[ "Mailbox/get", {
+
"accountId": "u33084183",
+
"#ids": {
+
"resultOf": "0",
+
"name": "Mailbox/changes",
+
"path": "/created"
+
}
+
}, "1" ],
+
[ "Mailbox/get", {
+
"accountId": "u33084183",
+
"#ids": {
+
"resultOf": "0",
+
"name": "Mailbox/changes",
+
"path": "/updated"
+
},
+
"#properties": {
+
"resultOf": "0",
+
"name": "Mailbox/changes",
+
"path": "/updatedProperties"
+
}
+
}, "2" ]]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 18]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
This fetches the list of ids for created/updated/destroyed Mailboxes,
+
then using back-references, it fetches the data for just the created/
+
updated Mailboxes in the same request. The response may look
+
something like this:
+
+
[[ "Mailbox/changes", {
+
"accountId": "u33084183",
+
"oldState": "78541",
+
"newState": "78542",
+
"hasMoreChanges": false,
+
"updatedProperties": [
+
"totalEmails", "unreadEmails",
+
"totalThreads", "unreadThreads"
+
],
+
"created": [],
+
"updated": ["MB23cfa8094c0f41e6"],
+
"destroyed": []
+
}, "0" ],
+
[ "Mailbox/get", {
+
"accountId": "u33084183",
+
"state": "78542",
+
"list": [],
+
"notFound": []
+
}, "1" ],
+
[ "Mailbox/get", {
+
"accountId": "u33084183",
+
"state": "78542",
+
"list": [{
+
"id": "MB23cfa8094c0f41e6",
+
"totalEmails": 16307,
+
"unreadEmails": 13903,
+
"totalThreads": 5833,
+
"unreadThreads": 5127
+
}],
+
"notFound": []
+
}, "2" ]]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 19]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
Here's an example where we try to rename one Mailbox and destroy
+
another:
+
+
[[ "Mailbox/set", {
+
"accountId": "u33084183",
+
"ifInState": "78542",
+
"update": {
+
"MB674cc24095db49ce": {
+
"name": "Maybe important mail"
+
}
+
},
+
"destroy": [ "MB23cfa8094c0f41e6" ]
+
}, "0" ]]
+
+
Suppose the rename succeeds, but we don't have permission to destroy
+
the Mailbox we tried to destroy; we might get back:
+
+
[[ "Mailbox/set", {
+
"accountId": "u33084183",
+
"oldState": "78542",
+
"newState": "78549",
+
"updated": {
+
"MB674cc24095db49ce": null
+
},
+
"notDestroyed": {
+
"MB23cfa8094c0f41e6": {
+
"type": "forbidden"
+
}
+
}
+
}, "0" ]]
+
+
3. Threads
+
+
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.
+
+
The exact algorithm for determining whether two Emails belong to the
+
same Thread is not mandated in this spec to allow for compatibility
+
with different existing systems. For new implementations, it is
+
suggested that two messages belong in the same Thread if both of the
+
following conditions apply:
+
+
1. An identical message id [RFC5322] appears in both messages in any
+
of the Message-Id, In-Reply-To, and References header fields.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 20]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
2. After stripping automatically added prefixes such as "Fwd:",
+
"Re:", "[List-Tag]", etc., and ignoring white space, the subjects
+
are the same. This avoids the situation where a person replies
+
to an old message as a convenient way of finding the right
+
recipient to send to but changes the subject and starts a new
+
conversation.
+
+
If messages are delivered out of order for some reason, a user may
+
have two Emails in the same Thread but without headers that associate
+
them with each other. The arrival of a third Email may provide the
+
missing references to join them all together into a single Thread.
+
Since the "threadId" of an Email is immutable, if the server wishes
+
to merge the Threads, it MUST handle this by deleting and reinserting
+
(with a new Email id) the Emails that change "threadId".
+
+
A *Thread* object has the following properties:
+
+
o id: "Id" (immutable; server-set)
+
+
+
The id of the Thread.
+
+
o emailIds: "Id[]" (server-set)
+
+
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).
+
+
The following JMAP methods are supported.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 21]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
3.1. Thread/get
+
+
This is a standard "/get" method as described in [RFC8620],
+
Section 5.1.
+
+
3.1.1. Example
+
+
Request:
+
+
[[ "Thread/get", {
+
"accountId": "acme",
+
"ids": ["f123u4", "f41u44"]
+
}, "#1" ]]
+
+
with response:
+
+
[[ "Thread/get", {
+
"accountId": "acme",
+
"state": "f6a7e214",
+
"list": [
+
{
+
"id": "f123u4",
+
"emailIds": [ "eaa623", "f782cbb"]
+
},
+
{
+
"id": "f41u44",
+
"emailIds": [ "82cf7bb" ]
+
}
+
],
+
"notFound": []
+
}, "#1" ]]
+
+
3.2. Thread/changes
+
+
This is a standard "/changes" method as described in [RFC8620],
+
Section 5.2.
+
+
4. Emails
+
+
An *Email* object is a representation of a message [RFC5322], which
+
allows clients to avoid the complexities of MIME parsing, transfer
+
encoding, and character encoding.
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 22]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
4.1. Properties of the Email Object
+
+
Broadly, a message consists of two parts: a list of header fields and
+
then a body. The Email data type provides a way to access the full
+
structure or to use simplified properties and avoid some complexity
+
if this is sufficient for the client application.
+
+
While raw headers can be fetched and set, the vast majority of
+
clients should use an appropriate parsed form for each of the header
+
fields it wants to process, as this allows it to avoid the
+
complexities of various encodings that are required in a valid
+
message per RFC 5322.
+
+
The body of a message is normally a MIME-encoded set of documents in
+
a tree structure. This may be arbitrarily nested, but the majority
+
of email clients present a flat model of a message body (normally
+
plaintext or HTML) with a set of attachments. Flattening the MIME
+
structure to form this model can be difficult and causes
+
inconsistency between clients. Therefore, in addition to the
+
"bodyStructure" property, which gives the full tree, the Email object
+
contains 3 alternate properties with flat lists of body parts:
+
+
o "textBody"/"htmlBody": These provide a list of parts that should
+
be rendered sequentially as the "body" of the message. This is a
+
list rather than a single part as messages may have headers and/or
+
footers appended/prepended as separate parts when they are
+
transmitted, and some clients send text and images intended to be
+
displayed inline in the body (or even videos and sound clips) as
+
multiple parts rather than a single HTML part with referenced
+
images.
+
+
Because MIME allows for multiple representations of the same data
+
(using "multipart/alternative"), there is a "textBody" property
+
(which prefers a plaintext representation) and an "htmlBody"
+
property (which prefers an HTML representation) to accommodate the
+
two most common client requirements. The same part may appear in
+
both lists where there is no alternative between the two.
+
+
o "attachments": This provides a list of parts that should be
+
presented as "attachments" to the message. Some images may be
+
solely there for embedding within an HTML body part; clients may
+
wish to not present these as attachments in the user interface if
+
they are displaying the HTML with the embedded images directly.
+
Some parts may also be in htmlBody/textBody; again, clients may
+
wish to not present these as attachments in the user interface if
+
rendered as part of the body.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 23]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
The "bodyValues" property allows for clients to fetch the value of
+
text parts directly without having to do a second request for the
+
blob and to have the server handle decoding the charset into unicode.
+
This data is in a separate property rather than on the EmailBodyPart
+
object to avoid duplication of large amounts of data, as the same
+
part may be included twice if the client fetches more than one of
+
bodyStructure, textBody, and htmlBody.
+
+
In the following subsections, the common notational convention for
+
wildcards has been adopted for content types, so "foo/*" means any
+
content type that starts with "foo/".
+
+
Due to the number of properties involved, the set of Email properties
+
is specified over the following four subsections. This is purely for
+
readability; all properties are top-level peers.
+
+
4.1.1. Metadata
+
+
These properties represent metadata about the message in the mail
+
store and are not derived from parsing the message itself.
+
+
o id: "Id" (immutable; server-set)
+
+
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].
+
+
o blobId: "Id" (immutable; server-set)
+
+
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.
+
+
o threadId: "Id" (immutable; server-set)
+
+
The id of the Thread to which this Email belongs.
+
+
o mailboxIds: "Id[Boolean]"
+
+
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.
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 24]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o keywords: "String[Boolean]" (default: {})
+
+
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 IANA "IMAP and JMAP Keywords" registry at
+
<https://www.iana.org/assignments/imap-jmap-keywords/> 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.
+
+
+
+
Jenkins & Newman Standards Track [Page 25]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
* "$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.
+
+
o size: "UnsignedInt" (immutable; server-set)
+
+
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).
+
+
o receivedAt: "UTCDate" (immutable; default: time of creation on
+
server)
+
+
The date the Email was received by the message store. This is the
+
"internal date" in IMAP [RFC3501].
+
+
4.1.2. Header Fields Parsed Forms
+
+
Header field properties are derived from the message header fields
+
[RFC5322] [RFC6532]. All header fields may be fetched in a raw form.
+
Some header fields may also be fetched in a parsed form. The
+
structured form that may be fetched depends on the header. The forms
+
are defined in the subsections that follow.
+
+
4.1.2.1. Raw
+
+
Type: "String"
+
+
The raw octets of the header field value from the first octet
+
following the header field name terminating colon, up to but
+
excluding the header field terminating CRLF. Any standards-compliant
+
message MUST be either ASCII (RFC 5322) or UTF-8 (RFC 6532); however,
+
other encodings exist in the wild. A server SHOULD replace any octet
+
or octet run with the high bit set that violates UTF-8 syntax with
+
the unicode replacement character (U+FFFD). Any NUL octet MUST be
+
dropped.
+
+
This form will typically have a leading space, as most generated
+
messages insert a space after the colon that terminates the header
+
field name.
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 26]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
4.1.2.2. Text
+
+
Type: "String"
+
+
The header field value with:
+
+
1. White space unfolded (as defined in [RFC5322], Section 2.2.3).
+
+
2. The terminating CRLF at the end of the value removed.
+
+
3. Any SP characters at the beginning of the value removed.
+
+
4. Any syntactically correct encoded sections [RFC2047] with a known
+
character set decoded. Any NUL octets or control characters
+
encoded per [RFC2047] are dropped from the decoded value. Any
+
text that looks like syntax per [RFC2047] but violates placement
+
or white space rules per [RFC2047] MUST NOT be decoded.
+
+
5. The resulting unicode converted to Normalization Form C (NFC)
+
form.
+
+
If any decodings fail, the parser SHOULD insert a unicode replacement
+
character (U+FFFD) and attempt to continue as much as possible.
+
+
To prevent obviously nonsense behaviour, which can lead to
+
interoperability issues, this form may only be fetched or set for the
+
following header fields:
+
+
o Subject
+
+
o Comments
+
+
o Keywords
+
+
o List-Id
+
+
o Any header field not defined in [RFC5322] or [RFC2369]
+
+
4.1.2.3. Addresses
+
+
Type: "EmailAddress[]"
+
+
The header field is parsed as an "address-list" value, as specified
+
in [RFC5322], Section 3.4, into the "EmailAddress[]" type. There is
+
an EmailAddress item for each "mailbox" parsed from the "address-
+
list". Group and comment information is discarded.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 27]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
An *EmailAddress* object has the following properties:
+
+
o name: "String|null"
+
+
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.
+
+
o email: "String"
+
+
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 (see Section 4.1.2.2).
+
+
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).
+
+
For example, the following "address-list" string:
+
+
" James Smythe" <james@example.com>, Friends:
+
jane@example.com, =?UTF-8?Q?John_Sm=C3=AEth?=
+
<john@example.com>;
+
+
would be parsed as:
+
+
[
+
{ "name": "James Smythe", "email": "james@example.com" },
+
{ "name": null, "email": "jane@example.com" },
+
{ "name": "John Smith", "email": "john@example.com" }
+
]
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 28]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
To prevent obviously nonsense behaviour, which can lead to
+
interoperability issues, this form may only be fetched or set for the
+
following header fields:
+
+
o From
+
+
o Sender
+
+
o Reply-To
+
+
o To
+
+
o Cc
+
+
o Bcc
+
+
o Resent-From
+
+
o Resent-Sender
+
+
o Resent-Reply-To
+
+
o Resent-To
+
+
o Resent-Cc
+
+
o Resent-Bcc
+
+
o Any header field not defined in [RFC5322] or [RFC2369]
+
+
4.1.2.4. GroupedAddresses
+
+
Type: "EmailAddressGroup[]"
+
+
This is similar to the Addresses form but preserves group
+
information. The header field is parsed as an "address-list" value,
+
as specified in [RFC5322], Section 3.4, into the "GroupedAddresses[]"
+
type. Consecutive "mailbox" values that are not part of a group are
+
still collected under an EmailAddressGroup object to provide a
+
uniform type.
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 29]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
An *EmailAddressGroup* object has the following properties:
+
+
o name: "String|null"
+
+
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.
+
+
o addresses: "EmailAddress[]"
+
+
The "mailbox" values that belong to this group, represented as
+
EmailAddress objects.
+
+
Any syntactically correct encoded sections [RFC2047] with a known
+
encoding MUST be decoded, following the same rules as for the Text
+
form (see Section 4.1.2.2).
+
+
Parsing SHOULD be best effort in the face of invalid structure to
+
accommodate invalid messages and semi-complete drafts.
+
+
For example, the following "address-list" string:
+
+
" James Smythe" <james@example.com>, Friends:
+
jane@example.com, =?UTF-8?Q?John_Sm=C3=AEth?=
+
<john@example.com>;
+
+
would be parsed as:
+
+
[
+
{ "name": null, "addresses": [
+
{ "name": "James Smythe", "email": "james@example.com" }
+
]},
+
{ "name": "Friends", "addresses": [
+
{ "name": null, "email": "jane@example.com" },
+
{ "name": "John Smith", "email": "john@example.com" }
+
]}
+
]
+
+
To prevent obviously nonsense behaviour, which can lead to
+
interoperability issues, this form may only be fetched or set for the
+
same header fields as the Addresses form (see Section 4.1.2.3).
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 30]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
4.1.2.5. MessageIds
+
+
Type: "String[]|null"
+
+
The header field is parsed as a list of "msg-id" values, as specified
+
in [RFC5322], Section 3.6.4, into the "String[]" type. Comments and/
+
or folding white space (CFWS) and surrounding angle brackets ("<>")
+
are removed. If parsing fails, the value is null.
+
+
To prevent obviously nonsense behaviour, which can lead to
+
interoperability issues, this form may only be fetched or set for the
+
following header fields:
+
+
o Message-ID
+
+
o In-Reply-To
+
+
o References
+
+
o Resent-Message-ID
+
+
o Any header field not defined in [RFC5322] or [RFC2369]
+
+
4.1.2.6. Date
+
+
Type: "Date|null"
+
+
The header field is parsed as a "date-time" value, as specified in
+
[RFC5322], Section 3.3, into the "Date" type. If parsing fails, the
+
value is null.
+
+
To prevent obviously nonsense behaviour, which can lead to
+
interoperability issues, this form may only be fetched or set for the
+
following header fields:
+
+
o Date
+
+
o Resent-Date
+
+
o Any header field not defined in [RFC5322] or [RFC2369]
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 31]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
4.1.2.7. URLs
+
+
Type: "String[]|null"
+
+
The header field is parsed as a list of URLs, as described in
+
[RFC2369], into the "String[]" type. Values do not include the
+
surrounding angle brackets or any comments in the header field with
+
the URLs. If parsing fails, the value is null.
+
+
To prevent obviously nonsense behaviour, which can lead to
+
interoperability issues, this form may only be fetched or set for the
+
following header fields:
+
+
o List-Help
+
+
o List-Unsubscribe
+
+
o List-Subscribe
+
+
o List-Post
+
+
o List-Owner
+
+
o List-Archive
+
+
o Any header field not defined in [RFC5322] or [RFC2369]
+
+
4.1.3. Header Fields Properties
+
+
The following low-level Email property is specified for complete
+
access to the header data of the message:
+
+
o headers: "EmailHeader[]" (immutable)
+
+
This is a list of all header fields [RFC5322], in the same order
+
they appear in the message. An *EmailHeader* object has the
+
following properties:
+
+
* name: "String"
+
+
The header "field name" as defined in [RFC5322], with the same
+
capitalization that it has in the message.
+
+
* value: "String"
+
+
The header "field value" as defined in [RFC5322], in Raw form.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 32]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
In addition, the client may request/send properties representing
+
individual header fields of the form:
+
+
header:{header-field-name}
+
+
Where "{header-field-name}" means any series of one or more printable
+
ASCII characters (i.e., characters that have values between 33 and
+
126, inclusive), except for colon (:). The property may also have
+
the following suffixes:
+
+
o :as{header-form}
+
+
This means the value is in a parsed form, where "{header-form}" is
+
one of the parsed-form names specified above. If not given, the
+
value is in Raw form.
+
+
o :all
+
+
This means the value is an array, with the items corresponding to
+
each instance of the header field, in the order they appear in the
+
message. If this suffix is not used, the result is the value of
+
the *last* instance of the header field (i.e., identical to the
+
last item in the array if :all is used), or null if none.
+
+
If both suffixes are used, they MUST be specified in the order above.
+
Header field names are matched case insensitively. The value is
+
typed according to the requested form or to an array of that type if
+
:all is used. If no header fields exist in the message with the
+
requested name, the value is null if fetching a single instance or an
+
empty array if requesting :all.
+
+
As a simple example, if the client requests a property called
+
"header:subject", this means find the *last* header field in the
+
message named "subject" (matched case insensitively) and return the
+
value in Raw form, or null if no header field of this name is found.
+
+
For a more complex example, consider the client requesting a property
+
called "header:Resent-To:asAddresses:all". This means:
+
+
1. Find *all* header fields named Resent-To (matched case
+
insensitively).
+
+
2. For each instance, parse the header field value in the Addresses
+
form.
+
+
3. The result is of type "EmailAddress[][]" -- each item in the
+
array corresponds to the parsed value (which is itself an array)
+
of the Resent-To header field instance.
+
+
+
+
Jenkins & Newman Standards Track [Page 33]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
The following convenience properties are also specified for the Email
+
object:
+
+
o messageId: "String[]|null" (immutable)
+
+
The value is identical to the value of "header:Message-
+
ID:asMessageIds". For messages conforming to RFC 5322, this will
+
be an array with a single entry.
+
+
o inReplyTo: "String[]|null" (immutable)
+
+
The value is identical to the value of "header:In-Reply-
+
To:asMessageIds".
+
+
o references: "String[]|null" (immutable)
+
+
The value is identical to the value of
+
"header:References:asMessageIds".
+
+
o sender: "EmailAddress[]|null" (immutable)
+
+
The value is identical to the value of
+
"header:Sender:asAddresses".
+
+
o from: "EmailAddress[]|null" (immutable)
+
+
The value is identical to the value of "header:From:asAddresses".
+
+
o to: "EmailAddress[]|null" (immutable)
+
+
The value is identical to the value of "header:To:asAddresses".
+
+
o cc: "EmailAddress[]|null" (immutable)
+
+
The value is identical to the value of "header:Cc:asAddresses".
+
+
o bcc: "EmailAddress[]|null" (immutable)
+
+
The value is identical to the value of "header:Bcc:asAddresses".
+
+
o replyTo: "EmailAddress[]|null" (immutable)
+
+
The value is identical to the value of "header:Reply-
+
To:asAddresses".
+
+
o subject: "String|null" (immutable)
+
+
The value is identical to the value of "header:Subject:asText".
+
+
+
+
Jenkins & Newman Standards Track [Page 34]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o sentAt: "Date|null" (immutable; default on creation: current
+
server time)
+
+
The value is identical to the value of "header:Date:asDate".
+
+
4.1.4. Body Parts
+
+
These properties are derived from the message body [RFC5322] and its
+
MIME entities [RFC2045].
+
+
An *EmailBodyPart* object has the following properties:
+
+
o partId: "String|null"
+
+
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/*".
+
+
o blobId: "Id|null"
+
+
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.
+
+
o size: "UnsignedInt"
+
+
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).
+
+
o headers: "EmailHeader[]"
+
+
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.
+
+
o name: "String|null"
+
+
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].
+
+
+
+
+
Jenkins & Newman Standards Track [Page 35]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o type: "String"
+
+
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.
+
+
o charset: "String|null"
+
+
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".
+
+
o disposition: "String|null"
+
+
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.
+
+
o cid: "String|null"
+
+
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].
+
+
o language: "String[]|null"
+
+
The list of language tags, as defined in [RFC3282], in the
+
Content-Language header field of the part, if present.
+
+
o location: "String|null"
+
+
The URI, as defined in [RFC2557], in the Content-Location header
+
field of the part, if present.
+
+
o subParts: "EmailBodyPart[]|null"
+
+
If the type is "multipart/*", this contains the body parts of each
+
child.
+
+
In addition, the client may request/send EmailBodyPart properties
+
representing individual header fields, following the same syntax and
+
semantics as for the Email object, e.g., "header:Content-Type".
+
+
+
+
+
Jenkins & Newman Standards Track [Page 36]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
The following Email properties are specified for access to the body
+
data of the message:
+
+
o bodyStructure: "EmailBodyPart" (immutable)
+
+
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/*".
+
+
o bodyValues: "String[EmailBodyValue]" (immutable)
+
+
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". An *EmailBodyValue* object has the
+
following properties:
+
+
* value: "String"
+
+
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.
+
+
* isEncodingProblem: "Boolean" (default: false)
+
+
This is true if malformed sections were found while decoding
+
the charset, the charset was unknown, or the content-transfer-
+
encoding was unknown.
+
+
* isTruncated: "Boolean" (default: false)
+
+
This is true if the "value" has been truncated.
+
+
See the Security Considerations section for issues related to
+
truncation and heuristic determination of the content-type and
+
charset.
+
+
+
+
Jenkins & Newman Standards Track [Page 37]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o textBody: "EmailBodyPart[]" (immutable)
+
+
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.
+
+
o htmlBody: "EmailBodyPart[]" (immutable)
+
+
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.
+
+
o attachments: "EmailBodyPart[]" (immutable)
+
+
A list, traversing depth-first, of all parts in "bodyStructure"
+
that 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.
+
+
o hasAttachment: "Boolean" (immutable; server-set)
+
+
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.
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 38]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o preview: "String" (immutable; server-set)
+
+
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.
+
+
The exact algorithm for decomposing bodyStructure into textBody,
+
htmlBody, and attachments part lists is not mandated, as this is a
+
quality-of-service implementation issue and likely to require
+
workarounds for malformed content discovered over time. However, the
+
following algorithm (expressed here in JavaScript) is suggested as a
+
starting point, based on real-world experience:
+
+
function isInlineMediaType ( type ) {
+
return type.startsWith( 'image/' ) ||
+
type.startsWith( 'audio/' ) ||
+
type.startsWith( 'video/' );
+
}
+
+
function parseStructure ( parts, multipartType, inAlternative,
+
htmlBody, textBody, attachments ) {
+
+
// For multipartType == alternative
+
let textLength = textBody ? textBody.length : -1;
+
let htmlLength = htmlBody ? htmlBody.length : -1;
+
+
for ( let i = 0; i < parts.length; i += 1 ) {
+
let part = parts[i];
+
let isMultipart = part.type.startsWith( 'multipart/' );
+
// Is this a body part rather than an attachment
+
let isInline = part.disposition != "attachment" &&
+
// Must be one of the allowed body types
+
( part.type == "text/plain" ||
+
part.type == "text/html" ||
+
isInlineMediaType( part.type ) ) &&
+
+
+
+
+
Jenkins & Newman Standards Track [Page 39]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
// If multipart/related, only the first part can be inline
+
// If a text part with a filename, and not the first item
+
// in the multipart, assume it is an attachment
+
( i === 0 ||
+
( multipartType != "related" &&
+
( isInlineMediaType( part.type ) || !part.name ) ) );
+
+
if ( isMultipart ) {
+
let subMultiType = part.type.split( '/' )[1];
+
parseStructure( part.subParts, subMultiType,
+
inAlternative || ( subMultiType == 'alternative' ),
+
htmlBody, textBody, attachments );
+
} else if ( isInline ) {
+
if ( multipartType == 'alternative' ) {
+
switch ( part.type ) {
+
case 'text/plain':
+
textBody.push( part );
+
break;
+
case 'text/html':
+
htmlBody.push( part );
+
break;
+
default:
+
attachments.push( part );
+
break;
+
}
+
continue;
+
} else if ( inAlternative ) {
+
if ( part.type == 'text/plain' ) {
+
htmlBody = null;
+
}
+
if ( part.type == 'text/html' ) {
+
textBody = null;
+
}
+
}
+
if ( textBody ) {
+
textBody.push( part );
+
}
+
if ( htmlBody ) {
+
htmlBody.push( part );
+
}
+
if ( ( !textBody || !htmlBody ) &&
+
isInlineMediaType( part.type ) ) {
+
attachments.push( part );
+
}
+
} else {
+
attachments.push( part );
+
}
+
}
+
+
+
+
Jenkins & Newman Standards Track [Page 40]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
if ( multipartType == 'alternative' && textBody && htmlBody ) {
+
// Found HTML part only
+
if ( textLength == textBody.length &&
+
htmlLength != htmlBody.length ) {
+
for ( let i = htmlLength; i < htmlBody.length; i += 1 ) {
+
textBody.push( htmlBody[i] );
+
}
+
}
+
// Found plaintext part only
+
if ( htmlLength == htmlBody.length &&
+
textLength != textBody.length ) {
+
for ( let i = textLength; i < textBody.length; i += 1 ) {
+
htmlBody.push( textBody[i] );
+
}
+
}
+
}
+
}
+
+
// Usage:
+
let htmlBody = [];
+
let textBody = [];
+
let attachments = [];
+
+
parseStructure( [ bodyStructure ], 'mixed', false,
+
htmlBody, textBody, attachments );
+
+
For instance, consider a message with both text and HTML versions
+
that has gone through a list software manager that attaches a header
+
and footer. It might have a MIME structure something like:
+
+
multipart/mixed
+
text/plain, content-disposition=inline - A
+
multipart/mixed
+
multipart/alternative
+
multipart/mixed
+
text/plain, content-disposition=inline - B
+
image/jpeg, content-disposition=inline - C
+
text/plain, content-disposition=inline - D
+
multipart/related
+
text/html - E
+
image/jpeg - F
+
image/jpeg, content-disposition=attachment - G
+
application/x-excel - H
+
message/rfc822 - J
+
text/plain, content-disposition=inline - K
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 41]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
In this case, the above algorithm would decompose this to:
+
+
textBody => [ A, B, C, D, K ]
+
htmlBody => [ A, E, K ]
+
attachments => [ C, F, G, H, J ]
+
+
4.2. Email/get
+
+
This is a standard "/get" method as described in [RFC8620],
+
Section 5.1 with the following additional request arguments:
+
+
o bodyProperties: "String[]"
+
+
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" ]
+
+
o fetchTextBodyValues: "Boolean" (default: false)
+
+
If true, the "bodyValues" property includes any "text/*" part in
+
the "textBody" property.
+
+
o fetchHTMLBodyValues: "Boolean" (default: false)
+
+
If true, the "bodyValues" property includes any "text/*" part in
+
the "htmlBody" property.
+
+
o fetchAllBodyValues: "Boolean" (default: false)
+
+
If true, the "bodyValues" property includes any "text/*" part in
+
the "bodyStructure" property.
+
+
o maxBodyValueBytes: "UnsignedInt" (default: 0)
+
+
If greater than zero, the "value" property of any EmailBodyValue
+
object returned in "bodyValues" MUST be truncated if necessary so
+
it does not exceed this number of octets in size. If 0 (the
+
default), no truncation occurs.
+
+
The server MUST ensure the truncation results in valid UTF-8 and
+
does not occur mid-codepoint. If the part is of type "text/html",
+
the server SHOULD NOT truncate inside an HTML tag, e.g., in the
+
middle of "<a href="https://example.com">". There is no
+
requirement for the truncated form to be a balanced tree or valid
+
HTML (indeed, the original source may well be neither of these
+
things).
+
+
+
+
Jenkins & Newman Standards Track [Page 42]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
If the standard "properties" argument is omitted or null, the
+
following default MUST be used instead of "all" properties:
+
+
[ "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 following properties are expected to be fast to fetch in a
+
quality implementation:
+
+
o id
+
+
o blobId
+
+
o threadId
+
+
o mailboxIds
+
+
o keywords
+
+
o size
+
+
o receivedAt
+
+
o messageId
+
+
o inReplyTo
+
+
o sender
+
+
o from
+
+
o to
+
+
o cc
+
+
o bcc
+
+
o replyTo
+
+
o subject
+
+
o sentAt
+
+
o hasAttachment
+
+
o preview
+
+
+
+
Jenkins & Newman Standards Track [Page 43]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
Clients SHOULD take care when fetching any other properties, as there
+
may be significantly longer latency in fetching and returning the
+
data.
+
+
As specified above, parsed forms of headers may only be used on
+
appropriate header fields. Attempting to fetch a form that is
+
forbidden (e.g., "header:From:asDate") MUST result in the method call
+
being rejected with an "invalidArguments" error.
+
+
Where a specific header field is requested as a property, the
+
capitalization of the property name in the response MUST be identical
+
to that used in the request.
+
+
4.2.1. Example
+
+
Request:
+
+
[[ "Email/get", {
+
"ids": [ "f123u456", "f123u457" ],
+
"properties": [ "threadId", "mailboxIds", "from", "subject",
+
"receivedAt", "header:List-POST:asURLs",
+
"htmlBody", "bodyValues" ],
+
"bodyProperties": [ "partId", "blobId", "size", "type" ],
+
"fetchHTMLBodyValues": true,
+
"maxBodyValueBytes": 256
+
}, "#1" ]]
+
+
and response:
+
+
[[ "Email/get", {
+
"accountId": "abc",
+
"state": "41234123231",
+
"list": [
+
{
+
"id": "f123u457",
+
"threadId": "ef1314a",
+
"mailboxIds": { "f123": true },
+
"from": [{ "name": "Joe Bloggs", "email": "joe@example.com" }],
+
"subject": "Dinner on Thursday?",
+
"receivedAt": "2013-10-13T14:12:00Z",
+
"header:List-POST:asURLs": [
+
"mailto:partytime@lists.example.com"
+
],
+
"htmlBody": [{
+
"partId": "1",
+
"blobId": "B841623871",
+
"size": 283331,
+
"type": "text/html"
+
+
+
+
Jenkins & Newman Standards Track [Page 44]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
}, {
+
"partId": "2",
+
"blobId": "B319437193",
+
"size": 10343,
+
"type": "text/plain"
+
}],
+
"bodyValues": {
+
"1": {
+
"isEncodingProblem": false,
+
"isTruncated": true,
+
"value": "<html><body><p>Hello ..."
+
},
+
"2": {
+
"isEncodingProblem": false,
+
"isTruncated": false,
+
"value": "-- Sent by your friendly mailing list ..."
+
}
+
}
+
}
+
],
+
"notFound": [ "f123u456" ]
+
}, "#1" ]]
+
+
4.3. Email/changes
+
+
This is a standard "/changes" method as described in [RFC8620],
+
Section 5.2. If generating intermediate states for a large set of
+
changes, it is recommended that newer changes be returned first, as
+
these are generally of more interest to users.
+
+
4.4. Email/query
+
+
This is a standard "/query" method as described in [RFC8620],
+
Section 5.5 but with the following additional request arguments:
+
+
o collapseThreads: "Boolean" (default: false)
+
+
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.
+
+
In quality implementations, the query "total" property is expected to
+
be fast to calculate when the filter consists solely of a single
+
"inMailbox" property, as it is the same as the totalEmails or
+
totalThreads properties (depending on whether collapseThreads is
+
true) of the associated Mailbox object.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 45]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
4.4.1. Filtering
+
+
A *FilterCondition* object has the following properties, any of which
+
may be omitted:
+
+
o inMailbox: "Id"
+
+
A Mailbox id. An Email must be in this Mailbox to match the
+
condition.
+
+
o inMailboxOtherThan: "Id[]"
+
+
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.
+
+
o before: "UTCDate"
+
+
The "receivedAt" date-time of the Email must be before this date-
+
time to match the condition.
+
+
o after: "UTCDate"
+
+
The "receivedAt" date-time of the Email must be the same or after
+
this date-time to match the condition.
+
+
o minSize: "UnsignedInt"
+
+
The "size" property of the Email must be equal to or greater than
+
this number to match the condition.
+
+
o maxSize: "UnsignedInt"
+
+
The "size" property of the Email must be less than this number to
+
match the condition.
+
+
o allInThreadHaveKeyword: "String"
+
+
All Emails (including this one) in the same Thread as this Email
+
must have the given keyword to match the condition.
+
+
o someInThreadHaveKeyword: "String"
+
+
At least one Email (possibly this one) in the same Thread as this
+
Email must have the given keyword to match the condition.
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 46]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o noneInThreadHaveKeyword: "String"
+
+
All Emails (including this one) in the same Thread as this Email
+
must *not* have the given keyword to match the condition.
+
+
o hasKeyword: "String"
+
+
This Email must have the given keyword to match the condition.
+
+
o notKeyword: "String"
+
+
This Email must not have the given keyword to match the condition.
+
+
o hasAttachment: "Boolean"
+
+
The "hasAttachment" property of the Email must be identical to the
+
value given to match the condition.
+
+
o text: "String"
+
+
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.
+
+
o from: "String"
+
+
Looks for the text in the From header field of the message.
+
+
o to: "String"
+
+
Looks for the text in the To header field of the message.
+
+
o cc: "String"
+
+
Looks for the text in the Cc header field of the message.
+
+
o bcc: "String"
+
+
Looks for the text in the Bcc header field of the message.
+
+
o subject: "String"
+
+
Looks for the text in the Subject header field of the message.
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 47]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o body: "String"
+
+
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.
+
+
o header: "String[]"
+
+
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.
+
+
If zero properties are specified on the FilterCondition, the
+
condition MUST always evaluate to true. If multiple properties are
+
specified, ALL must apply for the condition to be true (it is
+
equivalent to splitting the object into one-property conditions and
+
making them all the child of an AND filter operator).
+
+
The exact semantics for matching "String" fields is *deliberately not
+
defined* to allow for flexibility in indexing implementation, subject
+
to the following:
+
+
o Any syntactically correct encoded sections [RFC2047] of header
+
fields with a known encoding SHOULD be decoded before attempting
+
to match text.
+
+
o When searching inside a "text/html" body part, any text considered
+
markup rather than content SHOULD be ignored, including HTML tags
+
and most attributes, anything inside the "<head>" tag, Cascading
+
Style Sheets (CSS), and JavaScript. Attribute content intended
+
for presentation to the user such as "alt" and "title" SHOULD be
+
considered in the search.
+
+
o Text SHOULD be matched in a case-insensitive manner.
+
+
o Text contained in either (but matched) single (') or double (")
+
quotes SHOULD be treated as a *phrase search*; that is, a match is
+
required for that exact word or sequence of words, excluding the
+
surrounding quotation marks.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 48]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
Within a phrase, to match one of the following characters you MUST
+
escape it by prefixing it with a backslash (\):
+
+
' " \
+
+
o Outside of a phrase, white space SHOULD be treated as dividing
+
separate tokens that may be searched for separately but MUST all
+
be present for the Email to match the filter.
+
+
o Tokens (not part of a phrase) MAY be matched on a whole-word basis
+
using stemming (for example, a text search for "bus" would match
+
"buses" but not "business").
+
+
4.4.2. Sorting
+
+
The following value for the "property" field on the Comparator object
+
MUST be supported for sorting:
+
+
o "receivedAt" - The "receivedAt" date as returned in the Email
+
object.
+
+
The following values for the "property" field on the Comparator
+
object SHOULD be supported for sorting. When specifying a
+
"hasKeyword", "allInThreadHaveKeyword", or "someInThreadHaveKeyword"
+
sort, the Comparator object MUST also have a "keyword" property.
+
+
o "size" - The "size" as returned in the Email object.
+
+
o "from" - This is taken to be either the "name" property or if
+
null/empty, the "email" property of the *first* EmailAddress
+
object in the Email's "from" property. If still none, consider
+
the value to be the empty string.
+
+
o "to" - This is taken to be either the "name" property or if null/
+
empty, the "email" property of the *first* EmailAddress object in
+
the Email's "to" property. If still none, consider the value to
+
be the empty string.
+
+
o "subject" - This is taken to be the base subject of the message,
+
as defined in Section 2.1 of [RFC5256].
+
+
o "sentAt" - The "sentAt" property on the Email object.
+
+
o "hasKeyword" - This value MUST be considered true if the Email has
+
the keyword given as an additional "keyword" property on the
+
Comparator object, or false otherwise.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 49]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o "allInThreadHaveKeyword" - This value MUST be considered true for
+
the Email if *all* of the Emails in the same Thread have the
+
keyword given as an additional "keyword" property on the
+
Comparator object.
+
+
o "someInThreadHaveKeyword" - This value MUST be considered true for
+
the Email if *any* of the Emails in the same Thread have the
+
keyword given as an additional "keyword" property on the
+
Comparator object.
+
+
The server MAY support sorting based on other properties as well. A
+
client can discover which properties are supported by inspecting the
+
account's "capabilities" object (see Section 1.3).
+
+
Example sort:
+
+
[{
+
"property": "someInThreadHaveKeyword",
+
"keyword": "$flagged",
+
"isAscending": false
+
}, {
+
"property": "subject",
+
"collation": "i;ascii-casemap"
+
}, {
+
"property": "receivedAt",
+
"isAscending": false
+
}]
+
+
This would sort Emails in flagged Threads first (the Thread is
+
considered flagged if any Email within it is flagged), in subject
+
order second, and then from newest first for messages with the same
+
subject. If two Emails have identical values for all three
+
properties, then the order is server dependent but must be stable.
+
+
4.4.3. Thread Collapsing
+
+
When "collapseThreads" is true, then after filtering and sorting the
+
Email list, the list is further winnowed by removing any Emails for a
+
Thread id that has already been seen (when passing through the list
+
sequentially). A Thread will therefore only appear *once* in the
+
result, at the position of the first Email in the list that belongs
+
to the Thread (given the current sort/filter).
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 50]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
4.5. Email/queryChanges
+
+
This is a standard "/queryChanges" method as described in [RFC8620],
+
Section 5.6 with the following additional request argument:
+
+
o collapseThreads: "Boolean" (default: false)
+
+
The "collapseThreads" argument that was used with "Email/query".
+
+
4.6. Email/set
+
+
This is a standard "/set" method as described in [RFC8620],
+
Section 5.3. The "Email/set" method encompasses:
+
+
o Creating a draft
+
+
o Changing the keywords of an Email (e.g., unread/flagged status)
+
+
o Adding/removing an Email to/from Mailboxes (moving a message)
+
+
o Deleting Emails
+
+
The format of the "keywords"/"mailboxIds" properties means that when
+
updating an Email, you can either replace the entire set of keywords/
+
Mailboxes (by setting the full value of the property) or add/remove
+
individual ones using the JMAP patch syntax (see [RFC8620],
+
Section 5.3 for the specification and Section 5.7 for an example).
+
+
Due to the format of the Email object, when creating an Email, there
+
are a number of ways to specify the same information. To ensure that
+
the message [RFC5322] to create is unambiguous, the following
+
constraints apply to Email objects submitted for creation:
+
+
o The "headers" property MUST NOT be given on either the top-level
+
Email or an EmailBodyPart -- the client must set each header field
+
as an individual property.
+
+
o There MUST NOT be two properties that represent the same header
+
field (e.g., "header:from" and "from") within the Email or
+
particular EmailBodyPart.
+
+
o Header fields MUST NOT be specified in parsed forms that are
+
forbidden for that particular field.
+
+
o Header fields beginning with "Content-" MUST NOT be specified on
+
the Email object, only on EmailBodyPart objects.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 51]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o If a "bodyStructure" property is given, there MUST NOT be
+
"textBody", "htmlBody", or "attachments" properties.
+
+
o If given, the "bodyStructure" EmailBodyPart MUST NOT contain a
+
property representing a header field that is already defined on
+
the top-level Email object.
+
+
o If given, textBody MUST contain exactly one body part and it MUST
+
be of type "text/plain".
+
+
o If given, htmlBody MUST contain exactly one body part and it MUST
+
be of type "text/html".
+
+
o Within an EmailBodyPart:
+
+
* 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.
+
+
o Within an EmailBodyValue object, isEncodingProblem and isTruncated
+
MUST be either false or omitted.
+
+
Creation attempts that violate any of this SHOULD be rejected with an
+
"invalidProperties" error; however, a server MAY choose to modify the
+
Email (e.g., choose between conflicting headers, use a different
+
content-encoding, etc.) to comply with its requirements instead.
+
+
The server MAY also choose to set additional headers. If not
+
included, the server MUST generate and set a Message-ID header field
+
in conformance with [RFC5322], Section 3.6.4 and a Date header field
+
in conformance with Section 3.6.1.
+
+
The final message generated may be invalid per RFC 5322. For
+
example, if it is a half-finished draft, the To header field may have
+
a value that does not conform to the required syntax for this header.
+
The message will be checked for strict conformance when submitted for
+
sending (see the EmailSubmission object description).
+
+
+
+
Jenkins & Newman Standards Track [Page 52]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
Destroying an Email removes it from all Mailboxes to which it
+
belonged. To just delete an Email to trash, simply change the
+
"mailboxIds" property, so it is now in the Mailbox with a "role"
+
property equal to "trash", and remove all other Mailbox ids.
+
+
When emptying the trash, clients SHOULD NOT destroy Emails that are
+
also in a Mailbox other than trash. For those Emails, they SHOULD
+
just remove the trash Mailbox from the Email.
+
+
For successfully created Email objects, the "created" response
+
contains the "id", "blobId", "threadId", and "size" properties of the
+
object.
+
+
The following extra SetError types are defined:
+
+
For "create":
+
+
o "blobNotFound": At least one blob id given for an EmailBodyPart
+
doesn't exist. An extra "notFound" property of type "Id[]" MUST
+
be included in the SetError object containing every "blobId"
+
referenced by an EmailBodyPart that could not be found on the
+
server.
+
+
For "create" and "update":
+
+
o "tooManyKeywords": The change to the Email's keywords would exceed
+
a server-defined maximum.
+
+
o "tooManyMailboxes": The change to the set of Mailboxes that this
+
Email is in would exceed a server-defined maximum.
+
+
4.7. Email/copy
+
+
This is a standard "/copy" method as described in [RFC8620],
+
Section 5.4, except only the "mailboxIds", "keywords", and
+
"receivedAt" properties may be set during the copy. This method
+
cannot modify the message represented by the Email.
+
+
The server MAY forbid two Email objects with identical message
+
content [RFC5322], or even just with the same Message-ID [RFC5322],
+
to coexist within an account; if the target account already has the
+
Email, the copy will be rejected with a standard "alreadyExists"
+
error.
+
+
For successfully copied Email objects, the "created" response
+
contains the "id", "blobId", "threadId", and "size" properties of the
+
new object.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 53]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
4.8. Email/import
+
+
The "Email/import" method adds messages [RFC5322] to the set of
+
Emails in an account. The server MUST support messages with Email
+
Address Internationalization (EAI) headers [RFC6532]. The messages
+
must first be uploaded as blobs using the standard upload mechanism.
+
The method takes the following arguments:
+
+
o accountId: "Id"
+
+
The id of the account to use.
+
+
o ifInState: "String|null"
+
+
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.
+
+
o emails: "Id[EmailImport]"
+
+
A map of creation id (client specified) to EmailImport objects.
+
+
An *EmailImport* object has the following properties:
+
+
o blobId: "Id"
+
+
The id of the blob containing the raw message [RFC5322].
+
+
o mailboxIds: "Id[Boolean]"
+
+
The ids of the Mailboxes to assign this Email to. At least one
+
Mailbox MUST be given.
+
+
o keywords: "String[Boolean]" (default: {})
+
+
The keywords to apply to the Email.
+
+
o receivedAt: "UTCDate" (default: time of most recent Received
+
header, or time of import on server if none)
+
+
The "receivedAt" date to set on the Email.
+
+
Each Email to import is considered an atomic unit that may succeed or
+
fail individually. Importing successfully creates a new Email object
+
from the data referenced by the blobId and applies the given
+
Mailboxes, keywords, and receivedAt date.
+
+
+
+
Jenkins & Newman Standards Track [Page 54]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
The server MAY forbid two Email objects with the same exact content
+
[RFC5322], or even just with the same Message-ID [RFC5322], to
+
coexist within an account. In this case, it MUST reject attempts to
+
import an Email considered to be a duplicate with an "alreadyExists"
+
SetError. An "existingId" property of type "Id" MUST be included on
+
the SetError object with the id of the existing Email. If duplicates
+
are allowed, the newly created Email object MUST have a separate id
+
and independent mutable properties to the existing object.
+
+
If the "blobId", "mailboxIds", or "keywords" properties are invalid
+
(e.g., missing, wrong type, id not found), the server MUST reject the
+
import with an "invalidProperties" SetError.
+
+
If the Email cannot be imported because it would take the account
+
over quota, the import should be rejected with an "overQuota"
+
SetError.
+
+
If the blob referenced is not a valid message [RFC5322], the server
+
MAY modify the message to fix errors (such as removing NUL octets or
+
fixing invalid headers). If it does this, the "blobId" on the
+
response MUST represent the new representation and therefore be
+
different to the "blobId" on the EmailImport object. Alternatively,
+
the server MAY reject the import with an "invalidEmail" SetError.
+
+
The response has the following arguments:
+
+
o accountId: "Id"
+
+
The id of the account used for this call.
+
+
o oldState: "String|null"
+
+
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.
+
+
o newState: "String"
+
+
The state string that will now be returned by "Email/get" on this
+
account.
+
+
o created: "Id[Email]|null"
+
+
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.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 55]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o notCreated: "Id[SetError]|null"
+
+
A map of the creation id to a SetError object for each Email that
+
failed to be created, or null if all successful. The possible
+
errors are defined above.
+
+
The following additional errors may be returned instead of the
+
"Email/import" response:
+
+
"stateMismatch": An "ifInState" argument was supplied, and it does
+
not match the current state.
+
+
4.9. Email/parse
+
+
This method allows you to parse blobs as messages [RFC5322] to get
+
Email objects. The server MUST support messages with EAI headers
+
[RFC6532]. This can be used to parse and display attached messages
+
without having to import them as top-level Email objects in the mail
+
store in their own right.
+
+
The following metadata properties on the Email objects will be null
+
if requested:
+
+
o id
+
+
o mailboxIds
+
+
o keywords
+
+
o receivedAt
+
+
The "threadId" property of the Email MAY be present if the server can
+
calculate which Thread the Email would be assigned to were it to be
+
imported. Otherwise, this too is null if fetched.
+
+
The "Email/parse" method takes the following arguments:
+
+
o accountId: "Id"
+
+
The id of the account to use.
+
+
o blobIds: "Id[]"
+
+
The ids of the blobs to parse.
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 56]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o properties: "String[]"
+
+
If supplied, only the properties listed in the array are returned
+
for each Email object. If omitted, defaults to:
+
+
[ "messageId", "inReplyTo", "references", "sender", "from", "to",
+
"cc", "bcc", "replyTo", "subject", "sentAt", "hasAttachment",
+
"preview", "bodyValues", "textBody", "htmlBody", "attachments" ]
+
+
o bodyProperties: "String[]"
+
+
A list of properties to fetch for each EmailBodyPart returned. If
+
omitted, defaults to the same value as the "Email/get"
+
"bodyProperties" default argument.
+
+
o fetchTextBodyValues: "Boolean" (default: false)
+
+
If true, the "bodyValues" property includes any "text/*" part in
+
the "textBody" property.
+
+
o fetchHTMLBodyValues: "Boolean" (default: false)
+
+
If true, the "bodyValues" property includes any "text/*" part in
+
the "htmlBody" property.
+
+
o fetchAllBodyValues: "Boolean" (default: false)
+
+
If true, the "bodyValues" property includes any "text/*" part in
+
the "bodyStructure" property.
+
+
o maxBodyValueBytes: "UnsignedInt" (default: 0)
+
+
If greater than zero, the "value" property of any EmailBodyValue
+
object returned in "bodyValues" MUST be truncated if necessary so
+
it does not exceed this number of octets in size. If 0 (the
+
default), no truncation occurs.
+
+
The server MUST ensure the truncation results in valid UTF-8 and
+
does not occur mid-codepoint. If the part is of type "text/html",
+
the server SHOULD NOT truncate inside an HTML tag, e.g., in the
+
middle of "<a href="https://example.com">". There is no
+
requirement for the truncated form to be a balanced tree or valid
+
HTML (indeed, the original source may well be neither of these
+
things).
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 57]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
The response has the following arguments:
+
+
o accountId: "Id"
+
+
The id of the account used for the call.
+
+
o parsed: "Id[Email]|null"
+
+
A map of blob id to parsed Email representation for each
+
successfully parsed blob, or null if none.
+
+
o notParsable: "Id[]|null"
+
+
A list of ids given that corresponded to blobs that could not be
+
parsed as Emails, or null if none.
+
+
o notFound: "Id[]|null"
+
+
A list of blob ids given that could not be found, or null if none.
+
+
As specified above, parsed forms of headers may only be used on
+
appropriate header fields. Attempting to fetch a form that is
+
forbidden (e.g., "header:From:asDate") MUST result in the method call
+
being rejected with an "invalidArguments" error.
+
+
Where a specific header field is requested as a property, the
+
capitalization of the property name in the response MUST be identical
+
to that used in the request.
+
+
4.10. Examples
+
+
A client logs in for the first time. It first fetches the set of
+
Mailboxes. Now it will display the inbox to the user, which we will
+
presume has Mailbox id "fb666a55". The inbox may be (very!) large,
+
but the user's screen is only so big, so the client can just load the
+
Threads it needs to fill the screen and then load in more only when
+
the user scrolls. The client sends this request:
+
+
[[ "Email/query",{
+
"accountId": "ue150411c",
+
"filter": {
+
"inMailbox": "fb666a55"
+
},
+
"sort": [{
+
"isAscending": false,
+
"property": "receivedAt"
+
}],
+
"collapseThreads": true,
+
+
+
+
Jenkins & Newman Standards Track [Page 58]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
"position": 0,
+
"limit": 30,
+
"calculateTotal": true
+
}, "0" ],
+
[ "Email/get", {
+
"accountId": "ue150411c",
+
"#ids": {
+
"resultOf": "0",
+
"name": "Email/query",
+
"path": "/ids"
+
},
+
"properties": [
+
"threadId"
+
]
+
}, "1" ],
+
[ "Thread/get", {
+
"accountId": "ue150411c",
+
"#ids": {
+
"resultOf": "1",
+
"name": "Email/get",
+
"path": "/list/*/threadId"
+
}
+
}, "2" ],
+
[ "Email/get", {
+
"accountId": "ue150411c",
+
"#ids": {
+
"resultOf": "2",
+
"name": "Thread/get",
+
"path": "/list/*/emailIds"
+
},
+
"properties": [
+
"threadId",
+
"mailboxIds",
+
"keywords",
+
"hasAttachment",
+
"from",
+
"subject",
+
"receivedAt",
+
"size",
+
"preview"
+
]
+
}, "3" ]]
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 59]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
Let's break down the 4 method calls to see what they're doing:
+
+
"0": This asks the server for the ids of the first 30 Email objects
+
in the inbox, sorted newest first, ignoring Emails from the same
+
Thread as a newer Email in the Mailbox (i.e., it is the first 30
+
unique Threads).
+
+
"1": Now we use a back-reference to fetch the Thread ids for each of
+
these Email ids.
+
+
"2": Another back-reference fetches the Thread object for each of
+
these Thread ids.
+
+
"3": Finally, we fetch the information we need to display the Mailbox
+
listing (but no more!) for every Email in each of these 30 Threads.
+
The client may aggregate this data for display, for example, by
+
showing the Thread as "flagged" if any of the Emails in it has the
+
"$flagged" keyword.
+
+
The response from the server may look something like this:
+
+
[[ "Email/query", {
+
"accountId": "ue150411c",
+
"queryState": "09aa9a075588-780599:0",
+
"canCalculateChanges": true,
+
"position": 0,
+
"total": 115,
+
"ids": [ "Ma783e5cdf5f2deffbc97930a",
+
"M9bd17497e2a99cb345fc1d0a", ... ]
+
}, "0" ],
+
[ "Email/get", {
+
"accountId": "ue150411c",
+
"state": "780599",
+
"list": [{
+
"id": "Ma783e5cdf5f2deffbc97930a",
+
"threadId": "T36703c2cfe9bd5ed"
+
}, {
+
"id": "M9bd17497e2a99cb345fc1d0a",
+
"threadId": "T0a22ad76e9c097a1"
+
}, ... ],
+
"notFound": []
+
}, "1" ],
+
[ "Thread/get", {
+
"accountId": "ue150411c",
+
"state": "22a8728b",
+
"list": [{
+
"id": "T36703c2cfe9bd5ed",
+
"emailIds": [ "Ma783e5cdf5f2deffbc97930a" ]
+
+
+
+
Jenkins & Newman Standards Track [Page 60]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
}, {
+
"id": "T0a22ad76e9c097a1",
+
"emailIds": [ "M3b568670a63e5d100f518fa5",
+
"M9bd17497e2a99cb345fc1d0a" ]
+
}, ... ],
+
"notFound": []
+
}, "2" ],
+
[ "Email/get", {
+
"accountId": "ue150411c",
+
"state": "780599",
+
"list": [{
+
"id": "Ma783e5cdf5f2deffbc97930a",
+
"threadId": "T36703c2cfe9bd5ed",
+
"mailboxIds": {
+
"fb666a55": true
+
},
+
"keywords": {
+
"$seen": true,
+
"$flagged": true
+
},
+
"hasAttachment": true,
+
"from": [{
+
"email": "jdoe@example.com",
+
"name": "Jane Doe"
+
}],
+
"subject": "The Big Reveal",
+
"receivedAt": "2018-06-27T00:20:35Z",
+
"size": 175047,
+
"preview": "As you may be aware, we are required to prepare a
+
presentation where we wow a panel of 5 random members of the
+
public, on or before 30 June each year. We have drafted..."
+
},
+
...
+
],
+
"notFound": []
+
}, "3" ]]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 61]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
Now, on another device, the user marks the first Email as unread,
+
sending this API request:
+
+
[[ "Email/set", {
+
"accountId": "ue150411c",
+
"update": {
+
"Ma783e5cdf5f2deffbc97930a": {
+
"keywords/$seen": null
+
}
+
}
+
}, "0" ]]
+
+
The server applies this and sends the success response:
+
+
[[ "Email/set", {
+
"accountId": "ue150411c",
+
"oldState": "780605",
+
"newState": "780606",
+
"updated": {
+
"Ma783e5cdf5f2deffbc97930a": null
+
},
+
...
+
}, "0" ]]
+
+
The user also deletes a few Emails, and then a new message arrives.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 62]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
Back on our original machine, we receive a push update that the state
+
string for Email is now "780800". As this does not match the
+
client's current state, it issues a request for the changes:
+
+
[[ "Email/changes", {
+
"accountId": "ue150411c",
+
"sinceState": "780605",
+
"maxChanges": 50
+
}, "3" ],
+
[ "Email/queryChanges", {
+
"accountId": "ue150411c",
+
"filter": {
+
"inMailbox": "fb666a55"
+
},
+
"sort": [{
+
"property": "receivedAt",
+
"isAscending": false
+
}],
+
"collapseThreads": true,
+
"sinceQueryState": "09aa9a075588-780599:0",
+
"upToId": "Mc2781d5e856a908d8a35a564",
+
"maxChanges": 25,
+
"calculateTotal": true
+
}, "11" ]]
+
+
The response:
+
+
[[ "Email/changes", {
+
"accountId": "ue150411c",
+
"oldState": "780605",
+
"newState": "780800",
+
"hasMoreChanges": false,
+
"created": [ "Me8de6c9f6de198239b982ea2" ],
+
"updated": [ "Ma783e5cdf5f2deffbc97930a" ],
+
"destroyed": [ "M9bd17497e2a99cb345fc1d0a", ... ]
+
}, "3" ],
+
[ "Email/queryChanges", {
+
"accountId": "ue150411c",
+
"oldQueryState": "09aa9a075588-780599:0",
+
"newQueryState": "e35e9facf117-780615:0",
+
"added": [{
+
"id": "Me8de6c9f6de198239b982ea2",
+
"index": 0
+
}],
+
"removed": [ "M9bd17497e2a99cb345fc1d0a" ],
+
"total": 115
+
}, "11" ]]
+
+
+
+
+
Jenkins & Newman Standards Track [Page 63]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
The client can update its local cache of the query results by
+
removing "M9bd17497e2a99cb345fc1d0a" and then splicing in
+
"Me8de6c9f6de198239b982ea2" at position 0. As it does not have the
+
data for this new Email, it will then fetch it (it also could have
+
done this in the same request using back-references).
+
+
It knows something has changed about "Ma783e5cdf5f2deffbc97930a", so
+
it will refetch the Mailbox ids and keywords (the only mutable
+
properties) for this Email too.
+
+
The user starts composing a new Email. The email is plaintext and
+
the client knows the email in English so adds this metadata to the
+
body part. The user saves a draft while the composition is still in
+
progress. The client sends:
+
+
[[ "Email/set", {
+
"accountId": "ue150411c",
+
"create": {
+
"k192": {
+
"mailboxIds": {
+
"2ea1ca41b38e": true
+
},
+
"keywords": {
+
"$seen": true,
+
"$draft": true
+
},
+
"from": [{
+
"name": "Joe Bloggs",
+
"email": "joe@example.com"
+
}],
+
"subject": "World domination",
+
"receivedAt": "2018-07-10T01:03:11Z",
+
"sentAt": "2018-07-10T11:03:11+10:00",
+
"bodyStructure": {
+
"type": "text/plain",
+
"partId": "bd48",
+
"header:Content-Language": "en"
+
},
+
"bodyValues": {
+
"bd48": {
+
"value": "I have the most brilliant plan. Let me tell
+
you all about it. What we do is, we",
+
"isTruncated": false
+
}
+
}
+
}
+
}
+
}, "0" ]]
+
+
+
+
Jenkins & Newman Standards Track [Page 64]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
The server creates the message and sends the success response:
+
+
[[ "Email/set", {
+
"accountId": "ue150411c",
+
"oldState": "780823",
+
"newState": "780839",
+
"created": {
+
"k192": {
+
"id": "Mf40b5f831efa7233b9eb1c7f",
+
"blobId": "Gf40b5f831efa7233b9eb1c7f8f97d84eeeee64f7",
+
"threadId": "Td957e72e89f516dc",
+
"size": 359
+
}
+
},
+
...
+
}, "0" ]]
+
+
The message created on the server looks something like this:
+
+
Message-Id: <bbce0ae9-58be-4b24-ac82-deb840d58016@sloti7d1t02>
+
User-Agent: Cyrus-JMAP/3.1.6-736-gdfb8e44
+
Mime-Version: 1.0
+
Date: Tue, 10 Jul 2018 11:03:11 +1000
+
From: "Joe Bloggs" <joe@example.com>
+
Subject: World domination
+
Content-Language: en
+
Content-Type: text/plain
+
+
I have the most brilliant plan. Let me tell you all about it. What we
+
do is, we
+
+
The user adds a recipient and converts the message to HTML so they
+
can add formatting, then saves an updated draft:
+
+
[[ "Email/set", {
+
"accountId": "ue150411c",
+
"create": {
+
"k1546": {
+
"mailboxIds": {
+
"2ea1ca41b38e": true
+
},
+
"keywords": {
+
"$seen": true,
+
"$draft": true
+
},
+
"from": [{
+
"name": "Joe Bloggs",
+
"email": "joe@example.com"
+
+
+
+
Jenkins & Newman Standards Track [Page 65]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
}],
+
"to": [{
+
"name": "John",
+
"email": "john@example.com"
+
}],
+
"subject": "World domination",
+
"receivedAt": "2018-07-10T01:05:08Z",
+
"sentAt": "2018-07-10T11:05:08+10:00",
+
"bodyStructure": {
+
"type": "multipart/alternative",
+
"subParts": [{
+
"partId": "a49d",
+
"type": "text/html",
+
"header:Content-Language": "en"
+
}, {
+
"partId": "bd48",
+
"type": "text/plain",
+
"header:Content-Language": "en"
+
}]
+
},
+
"bodyValues": {
+
"bd48": {
+
"value": "I have the most brilliant plan. Let me tell
+
you all about it. What we do is, we",
+
"isTruncated": false
+
},
+
"a49d": {
+
"value": "<!DOCTYPE html><html><head><title></title>
+
<style type=\"text/css\">div{font-size:16px}</style></head>
+
<body><div>I have the most <b>brilliant</b> plan. Let me
+
tell you all about it. What we do is, we</div></body>
+
</html>",
+
"isTruncated": false
+
}
+
}
+
}
+
},
+
"destroy": [ "Mf40b5f831efa7233b9eb1c7f" ]
+
}, "0" ]]
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 66]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
The server creates the new draft, deletes the old one, and sends the
+
success response:
+
+
[[ "Email/set", {
+
"accountId": "ue150411c",
+
"oldState": "780839",
+
"newState": "780842",
+
"created": {
+
"k1546": {
+
"id": "Md45b47b4877521042cec0938",
+
"blobId": "Ge8de6c9f6de198239b982ea214e0f3a704e4af74",
+
"threadId": "Td957e72e89f516dc",
+
"size": 11721
+
}
+
},
+
"destroyed": [ "Mf40b5f831efa7233b9eb1c7f" ],
+
...
+
}, "0" ]]
+
+
The client moves this draft to a different account. The only way to
+
do this is via the "Email/copy" method. It MUST set a new
+
"mailboxIds" property, since the current value will not be valid
+
Mailbox ids in the destination account:
+
+
[[ "Email/copy", {
+
"fromAccountId": "ue150411c",
+
"accountId": "u6c6c41ac",
+
"create": {
+
"k45": {
+
"id": "Md45b47b4877521042cec0938",
+
"mailboxIds": {
+
"75a4c956": true
+
}
+
}
+
},
+
"onSuccessDestroyOriginal": true
+
}, "0" ]]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 67]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
The server successfully copies the Email and deletes the original.
+
Due to the implicit call to "Email/set", there are two responses to
+
the single method call, both with the same method call id:
+
+
[[ "Email/copy", {
+
"fromAccountId": "ue150411c",
+
"accountId": "u6c6c41ac",
+
"oldState": "7ee7e9263a6d",
+
"newState": "5a0d2447ed26",
+
"created": {
+
"k45": {
+
"id": "M138f9954a5cd2423daeafa55",
+
"blobId": "G6b9fb047cba722c48c611e79233d057c6b0b74e8",
+
"threadId": "T2f242ea424a4079a",
+
"size": 11721
+
}
+
},
+
"notCreated": null
+
}, "0" ],
+
[ "Email/set", {
+
"accountId": "ue150411c",
+
"oldState": "780842",
+
"newState": "780871",
+
"destroyed": [ "Md45b47b4877521042cec0938" ],
+
...
+
}, "0" ]]
+
+
5. Search Snippets
+
+
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.
+
+
A *SearchSnippet* object has the following properties:
+
+
o emailId: "Id"
+
+
The Email id the snippet applies to.
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 68]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o subject: "String|null"
+
+
If text from the filter matches the subject, this is the subject
+
of the Email with the following transformations:
+
+
1. Any instance of the following three characters MUST be
+
replaced by an appropriate HTML entity: & (ampersand), <
+
(less-than sign), and > (greater-than sign) [HTML]. Other
+
characters MAY also be replaced with an HTML entity form.
+
+
2. The matching words/phrases from the filter are wrapped in HTML
+
"<mark></mark>" tags.
+
+
If the subject does not match text from the filter, this property
+
is null.
+
+
o preview: "String|null"
+
+
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.
+
+
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".
+
+
The following JMAP method is supported.
+
+
5.1. SearchSnippet/get
+
+
To fetch search snippets, make a call to "SearchSnippet/get". It
+
takes the following arguments:
+
+
o accountId: "Id"
+
+
The id of the account to use.
+
+
o filter: "FilterOperator|FilterCondition|null"
+
+
The same filter as passed to "Email/query"; see the description of
+
this method in Section 4.4 for details.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 69]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o emailIds: "Id[]"
+
+
The ids of the Emails to fetch snippets for.
+
+
The response has the following arguments:
+
+
o accountId: "Id"
+
+
The id of the account used for the call.
+
+
o list: "SearchSnippet[]"
+
+
An array of SearchSnippet objects for the requested Email ids.
+
This may not be in the same order as the ids that were in the
+
request.
+
+
o notFound: "Id[]|null"
+
+
An array of Email ids requested that could not be found, or null
+
if all ids were found.
+
+
As the search snippets are derived from the message content and the
+
algorithm for doing so could change over time, fetching the same
+
snippets a second time MAY return a different result. However, the
+
previous value is not considered incorrect, so there is no state
+
string or update mechanism needed.
+
+
The following additional errors may be returned instead of the
+
"SearchSnippet/get" response:
+
+
"requestTooLarge": The number of "emailIds" requested by the client
+
exceeds the maximum number the server is willing to process in a
+
single method call.
+
+
"unsupportedFilter": The server is unable to process the given
+
"filter" for any reason.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 70]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
5.2. Example
+
+
Here, we did an "Email/query" to search for any Email in the account
+
containing the word "foo"; now, we are fetching the search snippets
+
for some of the ids that were returned in the results:
+
+
[[ "SearchSnippet/get", {
+
"accountId": "ue150411c",
+
"filter": {
+
"text": "foo"
+
},
+
"emailIds": [
+
"M44200ec123de277c0c1ce69c",
+
"M7bcbcb0b58d7729686e83d99",
+
"M28d12783a0969584b6deaac0",
+
...
+
]
+
}, "0" ]]
+
+
Example response:
+
+
[[ "SearchSnippet/get", {
+
"accountId": "ue150411c",
+
"list": [{
+
"emailId": "M44200ec123de277c0c1ce69c",
+
"subject": null,
+
"preview": null
+
}, {
+
"emailId": "M7bcbcb0b58d7729686e83d99",
+
"subject": "The <mark>Foo</mark>sball competition",
+
"preview": "...year the <mark>foo</mark>sball competition will
+
be held in the Stadium de ..."
+
}, {
+
"emailId": "M28d12783a0969584b6deaac0",
+
"subject": null,
+
"preview": "...the <mark>Foo</mark>/bar method results often
+
returns &lt;1 widget rather than the complete..."
+
},
+
...
+
],
+
"notFound": null
+
}, "0" ]]
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 71]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
6. Identities
+
+
An *Identity* object stores information about an email address or
+
domain the user may send from. It has the following properties:
+
+
o id: "Id" (immutable; server-set)
+
+
The id of the Identity.
+
+
o name: "String" (default: "")
+
+
The "From" name the client SHOULD use when creating a new Email
+
from this Identity.
+
+
o email: "String" (immutable)
+
+
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"), the client may use any valid address ending in
+
that domain (e.g., "foo@example.com").
+
+
o replyTo: "EmailAddress[]|null" (default: null)
+
+
The Reply-To value the client SHOULD set when creating a new Email
+
from this Identity.
+
+
o bcc: "EmailAddress[]|null" (default: null)
+
+
The Bcc value the client SHOULD set when creating a new Email from
+
this Identity.
+
+
o textSignature: "String" (default: "")
+
+
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.
+
+
o htmlSignature: "String" (default: "")
+
+
A signature the client SHOULD insert into new HTML messages that
+
will be sent from this Identity. This text MUST be an HTML
+
snippet to be inserted into the "<body></body>" section of the
+
HTML. Clients MAY ignore this and/or combine this with a client-
+
specific signature preference.
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 72]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o mayDelete: "Boolean" (server-set)
+
+
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.
+
+
See the "Addresses" header form description in the Email object
+
(Section 4.1.2.3) for the definition of EmailAddress.
+
+
Multiple identities with the same email address MAY exist, to allow
+
for different settings the user wants to pick between (for example,
+
with different names/signatures).
+
+
The following JMAP methods are supported.
+
+
6.1. Identity/get
+
+
This is a standard "/get" method as described in [RFC8620],
+
Section 5.1. The "ids" argument may be null to fetch all at once.
+
+
6.2. Identity/changes
+
+
This is a standard "/changes" method as described in [RFC8620],
+
Section 5.2.
+
+
6.3. Identity/set
+
+
This is a standard "/set" method as described in [RFC8620],
+
Section 5.3. The following extra SetError types are defined:
+
+
For "create":
+
+
o "forbiddenFrom": The user is not allowed to send from the address
+
given as the "email" property of the Identity.
+
+
6.4. Example
+
+
Request:
+
+
[ "Identity/get", {
+
"accountId": "acme"
+
}, "0" ]
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 73]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
with response:
+
+
[ "Identity/get", {
+
"accountId": "acme",
+
"state": "99401312ae-11-333",
+
"list": [
+
{
+
"id": "XD-3301-222-11_22AAz",
+
"name": "Joe Bloggs",
+
"email": "joe@example.com",
+
"replyTo": null,
+
"bcc": [{
+
"name": null,
+
"email": "joe+archive@example.com"
+
}],
+
"textSignature": "-- \nJoe Bloggs\nMaster of Email",
+
"htmlSignature": "<div><b>Joe Bloggs</b></div>
+
<div>Master of Email</div>",
+
"mayDelete": false
+
},
+
{
+
"id": "XD-9911312-11_22AAz",
+
"name": "Joe B",
+
"email": "*@example.com",
+
"replyTo": null,
+
"bcc": null,
+
"textSignature": "",
+
"htmlSignature": "",
+
"mayDelete": true
+
}
+
],
+
"notFound": []
+
}, "0" ]
+
+
7. Email Submission
+
+
An *EmailSubmission* object represents the submission of an Email for
+
delivery to one or more recipients. It has the following properties:
+
+
o id: "Id" (immutable; server-set)
+
+
The id of the EmailSubmission.
+
+
o identityId: "Id" (immutable)
+
+
The id of the Identity to associate with this submission.
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 74]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o emailId: "Id" (immutable)
+
+
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.
+
+
o threadId: "Id" (immutable; server-set)
+
+
The Thread id of the Email to send. This is set by the server to
+
the "threadId" property of the Email referenced by the "emailId".
+
+
o envelope: "Envelope|null" (immutable)
+
+
Information for use when sending via SMTP. An *Envelope* object
+
has the following properties:
+
+
* mailFrom: "Address"
+
+
The email address to use as the return address in the SMTP
+
submission, plus any parameters to pass with the MAIL FROM
+
address. The JMAP server MAY allow the address to be the empty
+
string.
+
+
When a JMAP server performs an SMTP message submission, it MAY
+
use the same id string for the ENVID parameter [RFC3461] and
+
the EmailSubmission object id. Servers that do this MAY
+
replace a client-provided value for ENVID with a server-
+
provided value.
+
+
* rcptTo: "Address[]"
+
+
The email addresses to send the message to, and any RCPT TO
+
parameters to pass with the recipient.
+
+
An *Address* object has the following properties:
+
+
* email: "String"
+
+
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].
+
+
* parameters: "Object|null"
+
+
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
+
+
+
+
Jenkins & Newman Standards Track [Page 75]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
"String") or null if the parameter does not take a value. For
+
both name and value, any xtext or unitext encodings are removed
+
(see [RFC3461] and [RFC6533]) and JSON string encoding is
+
applied.
+
+
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.
+
+
If multiple addresses are present in one of these header
+
fields, or there is more than one Sender/From header field, the
+
server SHOULD reject the EmailSubmission as invalid; otherwise,
+
it MUST take the first address in the last Sender/From header
+
field.
+
+
If the address found from this is not allowed by the Identity
+
associated with this submission, the "email" property from the
+
Identity MUST be used instead.
+
+
* "rcptTo": The deduplicated set of email addresses from the To,
+
Cc, and Bcc header fields, if present, with no parameters for
+
any of them.
+
+
o sendAt: "UTCDate" (immutable; server-set)
+
+
The date the submission was/will be released for delivery. If the
+
client successfully used FUTURERELEASE [RFC4865] with the
+
submission, this MUST be the time when the server will release the
+
message; otherwise, it MUST be the time the EmailSubmission was
+
created.
+
+
o undoStatus: "String"
+
+
This represents whether the submission may be canceled. 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.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 76]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
On systems that do not support unsending, the value of this
+
property will always be "final". On systems that do support
+
canceling submission, it will start as "pending" and MAY
+
transition to "final" when the server knows it definitely cannot
+
recall the message, but it MAY just remain "pending". If in
+
pending state, a client can attempt to cancel the submission by
+
setting this property to "canceled"; if the update succeeds, the
+
submission was successfully canceled, and the message has not been
+
delivered to any of the original recipients.
+
+
o deliveryStatus: "String[DeliveryStatus]|null" (server-set)
+
+
This represents the delivery status for each of the submission's
+
recipients, if known. 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.
+
+
A *DeliveryStatus* object has the following properties:
+
+
* smtpReply: "String"
+
+
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.
+
+
Multi-line SMTP responses should be concatenated to a single
+
string as follows:
+
+
+ The hyphen following the SMTP code on all but the last line
+
is replaced with a space.
+
+
+ Any prefix in common with the first line is stripped from
+
lines after the first.
+
+
+ CRLF is replaced by a space.
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 77]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
For example:
+
+
550-5.7.1 Our system has detected that this message is
+
550 5.7.1 likely spam.
+
+
would become:
+
+
550 5.7.1 Our system has detected that this message is likely spam.
+
+
For messages relayed via an alternative to SMTP, the server MAY
+
generate a synthetic string representing the status instead.
+
If it does this, the string MUST be of the following form:
+
+
+ A 3-digit SMTP reply code, as defined in [RFC5321],
+
Section 4.2.3.
+
+
+ Then a single space character.
+
+
+ Then an SMTP Enhanced Mail System Status Code as defined in
+
[RFC3463], with a registry defined in [RFC5248].
+
+
+ Then a single space character.
+
+
+ Then an implementation-specific information string with a
+
human-readable explanation of the response.
+
+
* delivered: "String"
+
+
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 the
+
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.
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 78]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
Note that successful relaying to an external SMTP server SHOULD
+
NOT be taken as an indication that the message has successfully
+
reached the final mail store. In this case though, the server
+
may receive a DSN response, if requested.
+
+
If a DSN is received for the recipient with Action equal to
+
"delivered", as per [RFC3464], Section 2.3.3, then the
+
"delivered" property SHOULD be set to "yes"; if the Action
+
equals "failed", the property SHOULD be set to "no". Receipt
+
of any other DSN SHOULD NOT affect this property.
+
+
The server MAY also set this property based on other feedback
+
channels.
+
+
* displayed: "String"
+
+
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.
+
+
If a Message Disposition Notification (MDN) is received for
+
this recipient with Disposition-Type (as per [RFC8098],
+
Section 3.2.6.2) equal to "displayed", this property SHOULD be
+
set to "yes".
+
+
The server MAY also set this property based on other feedback
+
channels.
+
+
o dsnBlobIds: "Id[]" (server-set)
+
+
A list of blob ids for DSNs [RFC3464] received for this
+
submission, in order of receipt, oldest first. The blob is the
+
whole MIME message (with a top-level content-type of "multipart/
+
report"), as received.
+
+
o mdnBlobIds: "Id[]" (server-set)
+
+
A list of blob ids for MDNs [RFC8098] received for this
+
submission, in order of receipt, oldest first. The blob is the
+
whole MIME message (with a top-level content-type of "multipart/
+
report"), as received.
+
+
+
+
Jenkins & Newman Standards Track [Page 79]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
JMAP servers MAY choose not to expose DSN and MDN responses as Email
+
objects if they correlate to an EmailSubmission object. It SHOULD
+
only do this if it exposes them in the "dsnBlobIds" and "mdnblobIds"
+
fields instead, and it expects the user to be using clients capable
+
of fetching and displaying delivery status via the EmailSubmission
+
object.
+
+
For efficiency, a server MAY destroy EmailSubmission objects at any
+
time after the message is successfully sent or after it has finished
+
retrying to send the message. For very basic SMTP proxies, this MAY
+
be immediately after creation, as it has no way to assign a real id
+
and return the information again if fetched later.
+
+
The following JMAP methods are supported.
+
+
7.1. EmailSubmission/get
+
+
This is a standard "/get" method as described in [RFC8620],
+
Section 5.1.
+
+
7.2. EmailSubmission/changes
+
+
This is a standard "/changes" method as described in [RFC8620],
+
Section 5.2.
+
+
7.3. EmailSubmission/query
+
+
This is a standard "/query" method as described in [RFC8620],
+
Section 5.5.
+
+
A *FilterCondition* object has the following properties, any of which
+
may be omitted:
+
+
o identityIds: "Id[]"
+
+
The EmailSubmission "identityId" property must be in this list to
+
match the condition.
+
+
o emailIds: "Id[]"
+
+
The EmailSubmission "emailId" property must be in this list to
+
match the condition.
+
+
o threadIds: "Id[]"
+
+
The EmailSubmission "threadId" property must be in this list to
+
match the condition.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 80]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o undoStatus: "String"
+
+
The EmailSubmission "undoStatus" property must be identical to the
+
value given to match the condition.
+
+
o before: "UTCDate"
+
+
The "sendAt" property of the EmailSubmission object must be before
+
this date-time to match the condition.
+
+
o after: "UTCDate"
+
+
The "sendAt" property of the EmailSubmission object must be the
+
same as or after this date-time to match the condition.
+
+
An EmailSubmission object matches the FilterCondition if and only if
+
all of the given conditions match. If zero properties are specified,
+
it is automatically true for all objects.
+
+
The following EmailSubmission properties MUST be supported for
+
sorting:
+
+
o "emailId"
+
+
o "threadId"
+
+
o "sentAt"
+
+
7.4. EmailSubmission/queryChanges
+
+
This is a standard "/queryChanges" method as described in [RFC8620],
+
Section 5.6.
+
+
7.5. EmailSubmission/set
+
+
This is a standard "/set" method as described in [RFC8620],
+
Section 5.3 with the following two additional request arguments:
+
+
o onSuccessUpdateEmail: "Id[PatchObject]|null"
+
+
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 "#".)
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 81]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o onSuccessDestroyEmail: "Id[]|null"
+
+
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 "#".)
+
+
After all create/update/destroy items in the "EmailSubmission/set"
+
invocation have been processed, a single implicit "Email/set" call
+
MUST be made to perform any changes requested in these two arguments.
+
The response to this MUST be returned after the "EmailSubmission/set"
+
response.
+
+
An Email is sent by creating an EmailSubmission object. When
+
processing each create, the server must check that the message is
+
valid, and the user has sufficient authorisation to send it. If the
+
creation succeeds, the message will be sent to the recipients given
+
in the envelope "rcptTo" parameter. The server MUST remove any Bcc
+
header field present on the message during delivery. The server MAY
+
add or remove other header fields from the submitted message or make
+
further alterations in accordance with the server's policy during
+
delivery.
+
+
If the referenced Email is destroyed at any point after the
+
EmailSubmission object is created, this MUST NOT change the behaviour
+
of the submission (i.e., it does not cancel a future send). The
+
"emailId" and "threadId" properties of the EmailSubmission object
+
remain, but trying to fetch them (with a standard "Email/get" call)
+
will return a "notFound" error if the corresponding objects have been
+
destroyed.
+
+
Similarly, destroying an EmailSubmission object MUST NOT affect the
+
deliveries it represents. It purely removes the record of the
+
submission. The server MAY automatically destroy EmailSubmission
+
objects after some time or in response to other triggers, and MAY
+
forbid the client from manually destroying EmailSubmission objects.
+
+
If the message to be sent is larger than the server supports sending,
+
a standard "tooLarge" SetError MUST be returned. A "maxSize"
+
"UnsignedInt" property MUST be present on the SetError specifying the
+
maximum size of a message that may be sent, in octets.
+
+
If the Email or Identity id given cannot be found, the submission
+
creation is rejected with a standard "invalidProperties" SetError.
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 82]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
The following extra SetError types are defined:
+
+
For "create":
+
+
o "invalidEmail" - 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.
+
+
o "tooManyRecipients" - 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.
+
+
o "noRecipients" - The envelope (supplied or generated) does not
+
have any rcptTo email addresses.
+
+
o "invalidRecipients" - 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.
+
+
o "forbiddenMailFrom" - The server does not permit the user to send
+
a message with the envelope From address [RFC5321].
+
+
o "forbiddenFrom" - The server does not permit the user to send a
+
message with the From header field [RFC5322] of the message to be
+
sent.
+
+
o "forbiddenToSend" - 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.
+
+
For "update":
+
+
o "cannotUnsend" - The client attempted to update the "undoStatus"
+
of a valid EmailSubmission object from "pending" to "canceled",
+
but the message cannot be unsent.
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 83]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
7.5.1. Example
+
+
The following example presumes a draft of the Email to be sent has
+
already been saved, and its Email id is "M7f6ed5bcfd7e2604d1753f6c".
+
This call then sends the Email immediately, and if successful,
+
removes the "$draft" flag and 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").
+
+
[[ "EmailSubmission/set", {
+
"accountId": "ue411d190",
+
"create": {
+
"k1490": {
+
"identityId": "I64588216",
+
"emailId": "M7f6ed5bcfd7e2604d1753f6c",
+
"envelope": {
+
"mailFrom": {
+
"email": "john@example.com",
+
"parameters": null
+
},
+
"rcptTo": [{
+
"email": "jane@example.com",
+
"parameters": null
+
},
+
...
+
]
+
}
+
}
+
},
+
"onSuccessUpdateEmail": {
+
"#k1490": {
+
"mailboxIds/7cb4e8ee-df87-4757-b9c4-2ea1ca41b38e": null,
+
"mailboxIds/73dbcb4b-bffc-48bd-8c2a-a2e91ca672f6": true,
+
"keywords/$draft": null
+
}
+
}
+
}, "0" ]]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 84]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
A successful response might look like this. Note that there are two
+
responses due to the implicit "Email/set" call, but both have the
+
same method call id as they are due to the same call in the request:
+
+
[[ "EmailSubmission/set", {
+
"accountId": "ue411d190",
+
"oldState": "012421s6-8nrq-4ps4-n0p4-9330r951ns21",
+
"newState": "355421f6-8aed-4cf4-a0c4-7377e951af36",
+
"created": {
+
"k1490": {
+
"id": "ES-3bab7f9a-623e-4acf-99a5-2e67facb02a0"
+
}
+
}
+
}, "0" ],
+
[ "Email/set", {
+
"accountId": "ue411d190",
+
"oldState": "778193",
+
"newState": "778197",
+
"updated": {
+
"M7f6ed5bcfd7e2604d1753f6c": null
+
}
+
}, "0" ]]
+
+
Suppose instead an admin has removed sending rights for the user, so
+
the submission is rejected with a "forbiddenToSend" error. The
+
description argument of the error is intended for display to the
+
user, so it should be localised appropriately. Let's suppose the
+
request was sent with an Accept-Language header like this:
+
+
Accept-Language: de;q=0.9,en;q=0.8
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 85]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
The server should attempt to choose the best localisation from those
+
it has available based on the Accept-Language header, as described in
+
[RFC8620], Section 3.8. If the server has English, French, and
+
German translations, it would choose German as the preferred language
+
and return a response like this:
+
+
[[ "EmailSubmission/set", {
+
"accountId": "ue411d190",
+
"oldState": "012421s6-8nrq-4ps4-n0p4-9330r951ns21",
+
"newState": "012421s6-8nrq-4ps4-n0p4-9330r951ns21",
+
"notCreated": {
+
"k1490": {
+
"type": "forbiddenToSend",
+
"description": "Verzeihung, wegen verdaechtiger Aktivitaeten Ihres
+
Benutzerkontos haben wir den Versand von Nachrichten gesperrt.
+
Bitte wenden Sie sich fuer Hilfe an unser Support Team."
+
}
+
}
+
}, "0" ]]
+
+
8. 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. It has the following
+
properties:
+
+
o id: "Id" (immutable; server-set)
+
+
The id of the object. There is only ever one VacationResponse
+
object, and its id is "singleton".
+
+
o isEnabled: "Boolean"
+
+
Should a vacation response be sent if a message arrives between
+
the "fromDate" and "toDate"?
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 86]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o fromDate: "UTCDate|null"
+
+
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.
+
+
o toDate: "UTCDate|null"
+
+
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.
+
+
o subject: "String|null"
+
+
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.
+
+
o textBody: "String|null"
+
+
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.
+
+
o htmlBody: "String|null"
+
+
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.
+
+
The following JMAP methods are supported.
+
+
8.1. VacationResponse/get
+
+
This is a standard "/get" method as described in [RFC8620],
+
Section 5.1.
+
+
There MUST only be exactly one VacationResponse object in an account.
+
It MUST have the id "singleton".
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 87]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
8.2. VacationResponse/set
+
+
This is a standard "/set" method as described in [RFC8620],
+
Section 5.3.
+
+
9. Security Considerations
+
+
All security considerations of JMAP [RFC8620] apply to this
+
specification. Additional considerations specific to the data types
+
and functionality introduced by this document are described in the
+
following subsections.
+
+
9.1. EmailBodyPart Value
+
+
Service providers typically perform security filtering on incoming
+
messages, and it's important that the detection of content-type and
+
charset for the security filter aligns with the heuristics performed
+
by JMAP servers. Servers that apply heuristics to determine the
+
content-type or charset for an EmailBodyValue SHOULD document the
+
heuristics and provide a mechanism to turn them off in the event they
+
are misaligned with the security filter used at a particular mail
+
host.
+
+
Automatic conversion of charsets that allow hidden channels for ASCII
+
text, such as UTF-7, have been problematic for security filters in
+
the past, so server implementations can mitigate this risk by having
+
such conversions off-by-default and/or separately configurable.
+
+
To allow the client to restrict the volume of data it can receive in
+
response to a request, a maximum length may be requested for the data
+
returned for a textual body part. However, truncating the data may
+
change the semantic meaning, for example, truncating a URL changes
+
its location. Servers that scan for links to malicious sites should
+
take care to either ensure truncation is not at a semantically
+
significant point or rescan the truncated value for malicious content
+
before returning it.
+
+
9.2. HTML Email Display
+
+
HTML message bodies provide richer formatting for messages but
+
present a number of security challenges, especially when embedded in
+
a webmail context in combination with interface HTML. Clients that
+
render HTML messages should carefully consider the potential risks,
+
including:
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 88]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
o Embedded JavaScript can rewrite the message to change its content
+
on subsequent opening, allowing users to be mislead. In webmail
+
systems, if run in the same origin as the interface, it can access
+
and exfiltrate all private data accessible to the user, including
+
all other messages and potentially contacts, calendar events,
+
settings, and credentials. It can also rewrite the interface to
+
undetectably phish passwords. A compromise is likely to be
+
persistent, not just for the duration of page load, due to
+
exfiltration of session credentials or installation of a service
+
worker that can intercept all subsequent network requests
+
(however, this would only be possible if blob downloads are also
+
available on the same origin, and the service worker script is
+
attached to the message).
+
+
o HTML documents may load content directly from the Internet rather
+
than just referencing attached resources. For example, you may
+
have an "<img>" tag with an external "src" attribute. This may
+
leak to the sender when a message is opened, as well as the IP
+
address of the recipient. Cookies may also be sent and set by the
+
server, allowing tracking between different messages and even
+
website visits and advertising profiles.
+
+
o In webmail systems, CSS can break the layout or create phishing
+
vulnerabilities. For example, the use of "position:fixed" can
+
allow a message to draw content outside of its normal bounds,
+
potentially clickjacking a real interface element.
+
+
o If in a webmail context and not inside a separate frame, any
+
styles defined in CSS rules will apply to interface elements as
+
well if the selector matches, allowing the interface to be
+
modified. Similarly, any interface styles that match elements in
+
the message will alter their appearance, potentially breaking the
+
layout of the message.
+
+
o The link text in HTML has no necessary correlation with the actual
+
target of the link, which can be used to make phishing attacks
+
more convincing.
+
+
o Links opened from a message or embedded external content may leak
+
private info in the Referer header sent by default in most
+
systems.
+
+
o Forms can be used to mimic login boxes, providing a potent
+
phishing vector if allowed to submit directly from the message
+
display.
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 89]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
There are a number of ways clients can mitigate these issues, and a
+
defence-in-depth approach that uses a combination of techniques will
+
provide the strongest security.
+
+
o HTML can be filtered before rendering, stripping potentially
+
malicious content. Sanitising HTML correctly is tricky, and
+
implementors are strongly recommended to use a well-tested library
+
with a carefully vetted whitelist-only approach. New features
+
with unexpected security characteristics may be added to HTML
+
rendering engines in the future; a blacklist approach is likely to
+
result in security issues.
+
+
Subtle differences in parsing of HTML can introduce security
+
flaws: to filter with 100% accuracy, you need to use the same
+
parser that the HTML rendering engine will use.
+
+
o Encapsulating the message in an "<iframe sandbox>", as defined in
+
[HTML], Section 4.7.6, can help mitigate a number of risks. This
+
will:
+
+
* Disable JavaScript.
+
+
* Disable form submission.
+
+
* Prevent drawing outside of its bounds or conflicts between
+
message CSS and interface CSS.
+
+
* Establish a unique anonymous origin, separate to the containing
+
origin.
+
+
o A strong Content Security Policy (see <https://www.w3.org/TR/
+
CSP3/>) can, among other things, block JavaScript and the loading
+
of external content should it manage to evade the filter.
+
+
o The leakage of information in the Referer header can be mitigated
+
with the use of a referrer policy (see <https://www.w3.org/TR/
+
referrer-policy/>).
+
+
o A "crossorigin=anonymous" attribute on tags that load remote
+
content can prevent cookies from being sent.
+
+
o If adding "target=_blank" to open links in new tabs, also add
+
"rel=noopener" to ensure the page that opens cannot change the URL
+
in the original tab to redirect the user to a phishing site.
+
+
As highly complex software components, HTML rendering engines
+
increase the attack surface of a client considerably, especially when
+
being used to process untrusted, potentially malicious content.
+
+
+
+
Jenkins & Newman Standards Track [Page 90]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
Serious bugs have been found in image decoders, JavaScript engines,
+
and HTML parsers in the past, which could lead to full system
+
compromise. Clients using an engine should ensure they get the
+
latest version and continue to incorporate any security patches
+
released by the vendor.
+
+
9.3. Multiple Part Display
+
+
Messages may consist of multiple parts to be displayed sequentially
+
as a body. Clients MUST render each part in isolation and MUST NOT
+
concatenate the raw text values to render. Doing so may change the
+
overall semantics of the message. If the client or server is
+
decrypting a Pretty Good Privacy (PGP) or S/MIME encrypted part,
+
concatenating with other parts may leak the decrypted text to an
+
attacker, as described in [EFAIL].
+
+
9.4. Email Submission
+
+
SMTP submission servers [RFC6409] use a number of mechanisms to
+
mitigate damage caused by compromised user accounts and end-user
+
systems including rate limiting, anti-virus/anti-spam milters (mail
+
filters), and other technologies. The technologies work better when
+
they have more information about the client connection. If JMAP
+
email submission is implemented as a proxy to an SMTP submission
+
server, it is useful to communicate this information from the JMAP
+
proxy to the submission server. The de facto XCLIENT extension to
+
SMTP [XCLIENT] can be used to do this, but use of an authenticated
+
channel is recommended to limit use of that extension to explicitly
+
authorised proxies.
+
+
JMAP servers that proxy to an SMTP submission server SHOULD allow use
+
of the submissions port [RFC8314]. Implementation of a mechanism
+
similar to SMTP XCLIENT is strongly encouraged. While Simple
+
Authentication and Security Layer (SASL) PLAIN over TLS [RFC4616] is
+
presently the mandatory-to-implement mechanism for interoperability
+
with SMTP submission servers [RFC4954], a JMAP submission proxy
+
SHOULD implement and prefer a stronger mechanism for this use case
+
such as TLS client certificate authentication with SASL EXTERNAL
+
([RFC4422], Appendix A) or Salted Challenge Response Authentication
+
Mechanism (SCRAM) [RFC7677].
+
+
In the event the JMAP server directly relays mail to SMTP servers in
+
other administrative domains, implementation of the de facto [milter]
+
protocol is strongly encouraged to integrate with third-party
+
products that address security issues including anti-virus/anti-spam,
+
reputation protection, compliance archiving, and data loss
+
prevention. Proxying to a local SMTP submission server may be a
+
simpler way to provide such security services.
+
+
+
+
Jenkins & Newman Standards Track [Page 91]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
9.5. Partial Account Access
+
+
A user may only have permission to access a subset of the data that
+
exists in an account. To avoid leaking unauthorised information, in
+
such a situation, the server MUST treat any data the user does not
+
have permission to access the same as if it did not exist.
+
+
For example, suppose user A has an account with two Mailboxes, inbox
+
and sent, but only shares the inbox with user B. In this case, when
+
user B fetches Mailboxes for this account, the server MUST behave as
+
though the sent Mailbox did not exist. Similarly, when querying or
+
fetching Email objects, it MUST treat any messages that just belong
+
to the sent Mailbox as though they did not exist. Fetching Thread
+
objects MUST only return ids for Email objects the user has
+
permission to access; if none, the Thread again MUST be treated the
+
same as if it did not exist.
+
+
If the server forbids a single account from having two identical
+
messages, or two messages with the same Message-Id header field, a
+
user with write access can use the error returned by trying to
+
create/import such a message to detect whether it already exists in
+
an inaccessible portion of the account.
+
+
9.6. Permission to Send from an Address
+
+
In recent years, the email ecosystem has moved towards associating
+
trust with the From address in the message [RFC5322], particularly
+
with schemes such as Domain-based Message Authentication, Reporting,
+
and Conformance (DMARC) [RFC7489].
+
+
The set of Identity objects (see Section 6) in an account lets the
+
client know which email addresses the user has permission to send
+
from. Each email submission is associated with an Identity, and
+
servers SHOULD reject submissions where the From header field of the
+
message does not correspond to the associated Identity.
+
+
The server MAY allow an exception to send an exact copy of an
+
existing message received into the mail store to another address
+
(otherwise known as "redirecting" or "bouncing"), although it is
+
RECOMMENDED the server limit this to destinations the user has
+
verified they also control.
+
+
If the user attempts to create a new Identity object, the server MUST
+
reject it with the appropriate error if the user does not have
+
permission to use that email address to send from.
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 92]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
The SMTP MAIL FROM address [RFC5321] is often confused with the From
+
message header field [RFC5322]. The user generally only ever sees
+
the address in the message header field, and this is the primary one
+
to enforce. However, the server MUST also enforce appropriate
+
restrictions on the MAIL FROM address [RFC5321] to stop the user from
+
flooding a third-party address with bounces and non-delivery notices.
+
+
The JMAP submission model provides separate errors for impermissible
+
addresses in either context.
+
+
10. IANA Considerations
+
+
10.1. JMAP Capability Registration for "mail"
+
+
IANA has registered the "mail" JMAP Capability as follows:
+
+
Capability Name: urn:ietf:params:jmap:mail
+
+
Specification document: this document
+
+
Intended use: common
+
+
Change Controller: IETF
+
+
Security and privacy considerations: this document, Section 9
+
+
10.2. JMAP Capability Registration for "submission"
+
+
IANA has registered the "submission" JMAP Capability as follows:
+
+
Capability Name: urn:ietf:params:jmap:submission
+
+
Specification document: this document
+
+
Intended use: common
+
+
Change Controller: IETF
+
+
Security and privacy considerations: this document, Section 9
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 93]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
10.3. JMAP Capability Registration for "vacationresponse"
+
+
IANA has registered the "vacationresponse" JMAP Capability as
+
follows:
+
+
Capability Name: urn:ietf:params:jmap:vacationresponse
+
+
Specification document: this document
+
+
Intended use: common
+
+
Change Controller: IETF
+
+
Security and privacy considerations: this document, Section 9
+
+
10.4. IMAP and JMAP Keywords Registry
+
+
This document makes two changes to the IMAP keywords registry as
+
defined in [RFC5788].
+
+
First, the name of the registry is changed to the "IMAP and JMAP
+
Keywords" registry.
+
+
Second, a scope column is added to the template and registry
+
indicating whether a keyword applies to "IMAP-only", "JMAP-only",
+
"both", or "reserved". All keywords already in the IMAP keyword
+
registry have been marked with a scope of "both". The "reserved"
+
status can be used to prevent future registration of a name that
+
would be confusing if registered. Registration of keywords with
+
scope "reserved" omit most fields in the registration template (see
+
registration of "$recent" below for an example); such registrations
+
are intended to be infrequent.
+
+
IMAP clients MAY silently ignore any keywords marked "JMAP-only" or
+
"reserved" in the event they appear in protocol. JMAP clients MAY
+
silently ignore any keywords marked "IMAP-only" or "reserved" in the
+
event they appear in protocol.
+
+
New "JMAP-only" keywords are registered in the following subsections.
+
These keywords correspond to IMAP system keywords and are thus not
+
appropriate for use in IMAP. These keywords cannot be subsequently
+
registered for use in IMAP except via standards action.
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 94]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
10.4.1. Registration of JMAP Keyword "$draft"
+
+
This registers the "JMAP-only" keyword "$draft" in the "IMAP and JMAP
+
Keywords" registry.
+
+
Keyword name: $draft
+
+
Scope: JMAP-only
+
+
Purpose (description): This is set when the user wants to treat the
+
message as a draft the user is composing. This is the JMAP
+
equivalent of the IMAP \Draft flag.
+
+
Private or Shared on a server: BOTH
+
+
Is it an advisory keyword or may it cause an automatic action:
+
Automatic. If the account has an IMAP mailbox marked with the
+
\Drafts special use attribute [RFC6154], setting this flag MAY cause
+
the message to appear in that mailbox automatically. Certain JMAP
+
computed values such as "unreadEmails" will change as a result of
+
changing this flag. In addition, mail clients will typically present
+
draft messages in a composer window rather than a viewer window.
+
+
When/by whom the keyword is set/cleared: This is typically set by a
+
JMAP client when referring to a draft message. One model for draft
+
Emails would result in clearing this flag in an "EmailSubmission/set"
+
operation with an "onSuccessUpdateEmail" argument. In a mail store
+
shared by JMAP and IMAP, this is also set and cleared as necessary so
+
it matches the IMAP \Draft flag.
+
+
Related keywords: None
+
+
Related IMAP/JMAP Capabilities: SPECIAL-USE [RFC6154]
+
+
Security Considerations: A server implementing this keyword as a
+
shared keyword may disclose that a user considers the message a draft
+
message. This information would be exposed to other users with read
+
permission for the Mailbox keywords.
+
+
Published specification: this document
+
+
Person & email address to contact for further information:
+
JMAP mailing list <jmap@ietf.org>
+
+
Intended usage: COMMON
+
+
Owner/Change controller: IESG
+
+
+
+
+
Jenkins & Newman Standards Track [Page 95]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
10.4.2. Registration of JMAP Keyword "$seen"
+
+
This registers the "JMAP-only" keyword "$seen" in the "IMAP and JMAP
+
Keywords" registry.
+
+
Keyword name: $seen
+
+
Scope: JMAP-only
+
+
Purpose (description): This is set when the user wants to treat the
+
message as read. This is the JMAP equivalent of the IMAP \Seen flag.
+
+
Private or Shared on a server: BOTH
+
+
Is it an advisory keyword or may it cause an automatic action:
+
Advisory. However, certain JMAP computed values such as
+
"unreadEmails" will change as a result of changing this flag.
+
+
When/by whom the keyword is set/cleared: This is set by a JMAP client
+
when it presents the message content to the user; clients often offer
+
an option to clear this flag. In a mail store shared by JMAP and
+
IMAP, this is also set and cleared as necessary so it matches the
+
IMAP \Seen flag.
+
+
Related keywords: None
+
+
Related IMAP/JMAP Capabilities: None
+
+
Security Considerations: A server implementing this keyword as a
+
shared keyword may disclose that a user considers the message to have
+
been read. This information would be exposed to other users with
+
read permission for the Mailbox keywords.
+
+
Published specification: this document
+
+
Person & email address to contact for further information:
+
JMAP mailing list <jmap@ietf.org>
+
+
Intended usage: COMMON
+
+
Owner/Change controller: IESG
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 96]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
10.4.3. Registration of JMAP Keyword "$flagged"
+
+
This registers the "JMAP-only" keyword "$flagged" in the "IMAP and
+
JMAP Keywords" registry.
+
+
Keyword name: $flagged
+
+
Scope: JMAP-only
+
+
Purpose (description): This is set when the user wants to treat the
+
message as flagged for urgent/special attention. This is the JMAP
+
equivalent of the IMAP \Flagged flag.
+
+
Private or Shared on a server: BOTH
+
+
Is it an advisory keyword or may it cause an automatic action:
+
Automatic. If the account has an IMAP mailbox marked with the
+
\Flagged special use attribute [RFC6154], setting this flag MAY cause
+
the message to appear in that mailbox automatically.
+
+
When/by whom the keyword is set/cleared: JMAP clients typically allow
+
a user to set/clear this flag as desired. In a mail store shared by
+
JMAP and IMAP, this is also set and cleared as necessary so it
+
matches the IMAP \Flagged flag.
+
+
Related keywords: None
+
+
Related IMAP/JMAP Capabilities: SPECIAL-USE [RFC6154]
+
+
Security Considerations: A server implementing this keyword as a
+
shared keyword may disclose that a user considers the message as
+
flagged for urgent/special attention. This information would be
+
exposed to other users with read permission for the Mailbox keywords.
+
+
Published specification: this document
+
+
Person & email address to contact for further information:
+
JMAP mailing list <jmap@ietf.org>
+
+
Intended usage: COMMON
+
+
Owner/Change controller: IESG
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 97]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
10.4.4. Registration of JMAP Keyword "$answered"
+
+
This registers the "JMAP-only" keyword "$answered" in the "IMAP and
+
JMAP Keywords" registry.
+
+
Keyword name: $answered
+
+
Scope: JMAP-only
+
+
Purpose (description): This is set when the message has been
+
answered.
+
+
Private or Shared on a server: BOTH
+
+
Is it an advisory keyword or may it cause an automatic action:
+
Advisory.
+
+
When/by whom the keyword is set/cleared: JMAP clients typically set
+
this when submitting a reply or answer to the message. It may be set
+
by the "EmailSubmission/set" operation with an "onSuccessUpdateEmail"
+
argument. In a mail store shared by JMAP and IMAP, this is also set
+
and cleared as necessary so it matches the IMAP \Answered flag.
+
+
Related keywords: None
+
+
Related IMAP/JMAP Capabilities: None
+
+
Security Considerations: A server implementing this keyword as a
+
shared keyword may disclose that a user has replied to a message.
+
This information would be exposed to other users with read permission
+
for the Mailbox keywords.
+
+
Published specification: this document
+
+
Person & email address to contact for further information:
+
JMAP mailing list <jmap@ietf.org>
+
+
Intended usage: COMMON
+
+
Owner/Change controller: IESG
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 98]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
10.4.5. Registration of "$recent" Keyword
+
+
This registers the keyword "$recent" in the "IMAP and JMAP Keywords"
+
registry.
+
+
Keyword name: $recent
+
+
Scope: reserved
+
+
Purpose (description): This keyword is not used to avoid confusion
+
with the IMAP \Recent system flag.
+
+
Published specification: this document
+
+
Person & email address to contact for further information:
+
JMAP mailing list <jmap@ietf.org>
+
+
Owner/Change controller: IESG
+
+
10.5. IMAP Mailbox Name Attributes Registry
+
+
10.5.1. Registration of "inbox" Role
+
+
This registers the "JMAP-only" "inbox" attribute in the "IMAP Mailbox
+
Name Attributes" registry, as established in [RFC8457].
+
+
Attribute Name: Inbox
+
+
Description: New mail is delivered here by default.
+
+
Reference: This document, Section 10.5.1
+
+
Usage Notes: JMAP only
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 99]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
10.6. JMAP Error Codes Registry
+
+
The following subsections register several new error codes in the
+
"JMAP Error Codes" registry, as defined in [RFC8620].
+
+
10.6.1. mailboxHasChild
+
+
JMAP Error Code: mailboxHasChild
+
+
Intended use: common
+
+
Change controller: IETF
+
+
Reference: This document, Section 2.5
+
+
Description: The Mailbox still has at least one child Mailbox. The
+
client MUST remove these before it can delete the parent Mailbox.
+
+
10.6.2. mailboxHasEmail
+
+
JMAP Error Code: mailboxHasEmail
+
+
Intended use: common
+
+
Change controller: IETF
+
+
Reference: This document, Section 2.5
+
+
Description: The Mailbox has at least one message assigned to it, and
+
the onDestroyRemoveEmails argument was false.
+
+
10.6.3. blobNotFound
+
+
JMAP Error Code: blobNotFound
+
+
Intended use: common
+
+
Change controller: IETF
+
+
Reference: This document, Section 4.6
+
+
Description: At least one blob id referenced in the object doesn't
+
exist.
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 100]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
10.6.4. tooManyKeywords
+
+
JMAP Error Code: tooManyKeywords
+
+
Intended use: common
+
+
Change controller: IETF
+
+
Reference: This document, Section 4.6
+
+
Description: The change to the Email's keywords would exceed a
+
server-defined maximum.
+
+
10.6.5. tooManyMailboxes
+
+
JMAP Error Code: tooManyMailboxes
+
+
Intended use: common
+
+
Change controller: IETF
+
+
Reference: This document, Section 4.6
+
+
Description: The change to the set of Mailboxes that this Email is in
+
would exceed a server-defined maximum.
+
+
10.6.6. invalidEmail
+
+
JMAP Error Code: invalidEmail
+
+
Intended use: common
+
+
Change controller: IETF
+
+
Reference: This document, Section 7.5
+
+
Description: The Email to be sent is invalid in some way.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 101]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
10.6.7. tooManyRecipients
+
+
JMAP Error Code: tooManyRecipients
+
+
Intended use: common
+
+
Change controller: IETF
+
+
Reference: This document, Section 7.5
+
+
Description: The envelope [RFC5321] (supplied or generated) has more
+
recipients than the server allows.
+
+
10.6.8. noRecipients
+
+
JMAP Error Code: noRecipients
+
+
Intended use: common
+
+
Change controller: IETF
+
+
Reference: This document, Section 7.5
+
+
Description: The envelope [RFC5321] (supplied or generated) does not
+
have any rcptTo email addresses.
+
+
10.6.9. invalidRecipients
+
+
JMAP Error Code: invalidRecipients
+
+
Intended use: common
+
+
Change controller: IETF
+
+
Reference: This document, Section 7.5
+
+
Description: The rcptTo property of the envelope [RFC5321] (supplied
+
or generated) contains at least one rcptTo value that is not a valid
+
email address for sending to.
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 102]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
10.6.10. forbiddenMailFrom
+
+
JMAP Error Code: forbiddenMailFrom
+
+
Intended use: common
+
+
Change controller: IETF
+
+
Reference: This document, Section 7.5
+
+
Description: The server does not permit the user to send a message
+
with this envelope From address [RFC5321].
+
+
10.6.11. forbiddenFrom
+
+
JMAP Error Code: forbiddenFrom
+
+
Intended use: common
+
+
Change controller: IETF
+
+
Reference: This document, Sections 6.3 and 7.5
+
+
Description: The server does not permit the user to send a message
+
with the From header field [RFC5322] of the message to be sent.
+
+
10.6.12. forbiddenToSend
+
+
JMAP Error Code: forbiddenToSend
+
+
Intended use: common
+
+
Change controller: IETF
+
+
Reference: This document, Section 7.5
+
+
Description: The user does not have permission to send at all right
+
now.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 103]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
11. References
+
+
11.1. Normative References
+
+
[HTML] Faulkner, S., Eicholz, A., Leithead, T., Danilo, A., and
+
S. Moon, "HTML 5.2", World Wide Web Consortium
+
Recommendation REC-html52-20171214, December 2017,
+
<https://www.w3.org/TR/html52/>.
+
+
[RFC1870] Klensin, J., Freed, N., and K. Moore, "SMTP Service
+
Extension for Message Size Declaration", STD 10, RFC 1870,
+
DOI 10.17487/RFC1870, November 1995,
+
<https://www.rfc-editor.org/info/rfc1870>.
+
+
[RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
+
Extensions (MIME) Part One: Format of Internet Message
+
Bodies", RFC 2045, DOI 10.17487/RFC2045, November 1996,
+
<https://www.rfc-editor.org/info/rfc2045>.
+
+
[RFC2047] Moore, K., "MIME (Multipurpose Internet Mail Extensions)
+
Part Three: Message Header Extensions for Non-ASCII Text",
+
RFC 2047, DOI 10.17487/RFC2047, November 1996,
+
<https://www.rfc-editor.org/info/rfc2047>.
+
+
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+
Requirement Levels", BCP 14, RFC 2119,
+
DOI 10.17487/RFC2119, March 1997,
+
<https://www.rfc-editor.org/info/rfc2119>.
+
+
[RFC2231] Freed, N. and K. Moore, "MIME Parameter Value and Encoded
+
Word Extensions: Character Sets, Languages, and
+
Continuations", RFC 2231, DOI 10.17487/RFC2231, November
+
1997, <https://www.rfc-editor.org/info/rfc2231>.
+
+
[RFC2369] Neufeld, G. and J. Baer, "The Use of URLs as Meta-Syntax
+
for Core Mail List Commands and their Transport through
+
Message Header Fields", RFC 2369, DOI 10.17487/RFC2369,
+
July 1998, <https://www.rfc-editor.org/info/rfc2369>.
+
+
[RFC2392] Levinson, E., "Content-ID and Message-ID Uniform Resource
+
Locators", RFC 2392, DOI 10.17487/RFC2392, August 1998,
+
<https://www.rfc-editor.org/info/rfc2392>.
+
+
[RFC2557] Palme, J., Hopmann, A., and N. Shelness, "MIME
+
Encapsulation of Aggregate Documents, such as HTML
+
(MHTML)", RFC 2557, DOI 10.17487/RFC2557, March 1999,
+
<https://www.rfc-editor.org/info/rfc2557>.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 104]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
[RFC2852] Newman, D., "Deliver By SMTP Service Extension", RFC 2852,
+
DOI 10.17487/RFC2852, June 2000,
+
<https://www.rfc-editor.org/info/rfc2852>.
+
+
[RFC3282] Alvestrand, H., "Content Language Headers", RFC 3282,
+
DOI 10.17487/RFC3282, May 2002,
+
<https://www.rfc-editor.org/info/rfc3282>.
+
+
[RFC3461] Moore, K., "Simple Mail Transfer Protocol (SMTP) Service
+
Extension for Delivery Status Notifications (DSNs)",
+
RFC 3461, DOI 10.17487/RFC3461, January 2003,
+
<https://www.rfc-editor.org/info/rfc3461>.
+
+
[RFC3463] Vaudreuil, G., "Enhanced Mail System Status Codes",
+
RFC 3463, DOI 10.17487/RFC3463, January 2003,
+
<https://www.rfc-editor.org/info/rfc3463>.
+
+
[RFC3464] Moore, K. and G. Vaudreuil, "An Extensible Message Format
+
for Delivery Status Notifications", RFC 3464,
+
DOI 10.17487/RFC3464, January 2003,
+
<https://www.rfc-editor.org/info/rfc3464>.
+
+
[RFC3834] Moore, K., "Recommendations for Automatic Responses to
+
Electronic Mail", RFC 3834, DOI 10.17487/RFC3834, August
+
2004, <https://www.rfc-editor.org/info/rfc3834>.
+
+
[RFC4314] Melnikov, A., "IMAP4 Access Control List (ACL) Extension",
+
RFC 4314, DOI 10.17487/RFC4314, December 2005,
+
<https://www.rfc-editor.org/info/rfc4314>.
+
+
[RFC4422] Melnikov, A., Ed. and K. Zeilenga, Ed., "Simple
+
Authentication and Security Layer (SASL)", RFC 4422,
+
DOI 10.17487/RFC4422, June 2006,
+
<https://www.rfc-editor.org/info/rfc4422>.
+
+
[RFC4616] Zeilenga, K., Ed., "The PLAIN Simple Authentication and
+
Security Layer (SASL) Mechanism", RFC 4616,
+
DOI 10.17487/RFC4616, August 2006,
+
<https://www.rfc-editor.org/info/rfc4616>.
+
+
[RFC4865] White, G. and G. Vaudreuil, "SMTP Submission Service
+
Extension for Future Message Release", RFC 4865,
+
DOI 10.17487/RFC4865, May 2007,
+
<https://www.rfc-editor.org/info/rfc4865>.
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 105]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
[RFC4954] Siemborski, R., Ed. and A. Melnikov, Ed., "SMTP Service
+
Extension for Authentication", RFC 4954,
+
DOI 10.17487/RFC4954, July 2007,
+
<https://www.rfc-editor.org/info/rfc4954>.
+
+
[RFC5198] Klensin, J. and M. Padlipsky, "Unicode Format for Network
+
Interchange", RFC 5198, DOI 10.17487/RFC5198, March 2008,
+
<https://www.rfc-editor.org/info/rfc5198>.
+
+
[RFC5248] Hansen, T. and J. Klensin, "A Registry for SMTP Enhanced
+
Mail System Status Codes", BCP 138, RFC 5248,
+
DOI 10.17487/RFC5248, June 2008,
+
<https://www.rfc-editor.org/info/rfc5248>.
+
+
[RFC5256] Crispin, M. and K. Murchison, "Internet Message Access
+
Protocol - SORT and THREAD Extensions", RFC 5256,
+
DOI 10.17487/RFC5256, June 2008,
+
<https://www.rfc-editor.org/info/rfc5256>.
+
+
[RFC5321] Klensin, J., "Simple Mail Transfer Protocol", RFC 5321,
+
DOI 10.17487/RFC5321, October 2008,
+
<https://www.rfc-editor.org/info/rfc5321>.
+
+
[RFC5322] Resnick, P., Ed., "Internet Message Format", RFC 5322,
+
DOI 10.17487/RFC5322, October 2008,
+
<https://www.rfc-editor.org/info/rfc5322>.
+
+
[RFC5788] Melnikov, A. and D. Cridland, "IMAP4 Keyword Registry",
+
RFC 5788, DOI 10.17487/RFC5788, March 2010,
+
<https://www.rfc-editor.org/info/rfc5788>.
+
+
[RFC6154] Leiba, B. and J. Nicolson, "IMAP LIST Extension for
+
Special-Use Mailboxes", RFC 6154, DOI 10.17487/RFC6154,
+
March 2011, <https://www.rfc-editor.org/info/rfc6154>.
+
+
[RFC6409] Gellens, R. and J. Klensin, "Message Submission for Mail",
+
STD 72, RFC 6409, DOI 10.17487/RFC6409, November 2011,
+
<https://www.rfc-editor.org/info/rfc6409>.
+
+
[RFC6532] Yang, A., Steele, S., and N. Freed, "Internationalized
+
Email Headers", RFC 6532, DOI 10.17487/RFC6532, February
+
2012, <https://www.rfc-editor.org/info/rfc6532>.
+
+
[RFC6533] Hansen, T., Ed., Newman, C., and A. Melnikov,
+
"Internationalized Delivery Status and Disposition
+
Notifications", RFC 6533, DOI 10.17487/RFC6533, February
+
2012, <https://www.rfc-editor.org/info/rfc6533>.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 106]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
[RFC6710] Melnikov, A. and K. Carlberg, "Simple Mail Transfer
+
Protocol Extension for Message Transfer Priorities",
+
RFC 6710, DOI 10.17487/RFC6710, August 2012,
+
<https://www.rfc-editor.org/info/rfc6710>.
+
+
[RFC7677] Hansen, T., "SCRAM-SHA-256 and SCRAM-SHA-256-PLUS Simple
+
Authentication and Security Layer (SASL) Mechanisms",
+
RFC 7677, DOI 10.17487/RFC7677, November 2015,
+
<https://www.rfc-editor.org/info/rfc7677>.
+
+
[RFC8098] Hansen, T., Ed. and A. Melnikov, Ed., "Message Disposition
+
Notification", STD 85, RFC 8098, DOI 10.17487/RFC8098,
+
February 2017, <https://www.rfc-editor.org/info/rfc8098>.
+
+
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
+
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
+
May 2017, <https://www.rfc-editor.org/info/rfc8174>.
+
+
[RFC8314] Moore, K. and C. Newman, "Cleartext Considered Obsolete:
+
Use of Transport Layer Security (TLS) for Email Submission
+
and Access", RFC 8314, DOI 10.17487/RFC8314, January 2018,
+
<https://www.rfc-editor.org/info/rfc8314>.
+
+
[RFC8457] Leiba, B., Ed., "IMAP "$Important" Keyword and
+
"\Important" Special-Use Attribute", RFC 8457,
+
DOI 10.17487/RFC8457, September 2018,
+
<https://www.rfc-editor.org/info/rfc8457>.
+
+
[RFC8474] Gondwana, B., Ed., "IMAP Extension for Object
+
Identifiers", RFC 8474, DOI 10.17487/RFC8474, September
+
2018, <https://www.rfc-editor.org/info/rfc8474>.
+
+
[RFC8620] Jenkins, N. and C. Newman, "The JSON Meta Application
+
Protocol", RFC 8620, DOI 10.17487/RFC8620, June 2019,
+
<https://www.rfc-editor.org/info/rfc8620>.
+
+
11.2. Informative References
+
+
[EFAIL] Poddebniak, D., Dresen, C., Mueller, J., Ising, F.,
+
Schinzel, S., Friedberger, S., Somorovsky, J., and J.
+
Schwenk, "Efail: Breaking S/MIME and OpenPGP Email
+
Encryption using Exfiltration Channels", August 2018,
+
<https://www.usenix.org/system/files/conference/
+
usenixsecurity18/sec18-poddebniak.pdf>.
+
+
[milter] Postfix, "Postfix before-queue Milter support", 2019,
+
<http://www.postfix.org/MILTER_README.html>.
+
+
+
+
+
Jenkins & Newman Standards Track [Page 107]
+
+
RFC 8621 JMAP Mail August 2019
+
+
+
[RFC3501] Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - VERSION
+
4rev1", RFC 3501, DOI 10.17487/RFC3501, March 2003,
+
<https://www.rfc-editor.org/info/rfc3501>.
+
+
[RFC7489] Kucherawy, M., Ed. and E. Zwicky, Ed., "Domain-based
+
Message Authentication, Reporting, and Conformance
+
(DMARC)", RFC 7489, DOI 10.17487/RFC7489, March 2015,
+
<https://www.rfc-editor.org/info/rfc7489>.
+
+
[XCLIENT] Postfix, "Postfix XCLIENT Howto", 2019,
+
<http://www.postfix.org/XCLIENT_README.html>.
+
+
Authors' Addresses
+
+
Neil Jenkins
+
Fastmail
+
PO Box 234, Collins St. West
+
Melbourne, VIC 8007
+
Australia
+
+
Email: neilj@fastmailteam.com
+
URI: https://www.fastmail.com
+
+
+
Chris Newman
+
Oracle
+
440 E. Huntington Dr., Suite 400
+
Arcadia, CA 91006
+
United States of America
+
+
Email: chris.newman@oracle.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jenkins & Newman Standards Track [Page 108]
+