rfc9661.original | rfc9661.txt | |||
---|---|---|---|---|
JMAP K. Murchison | Internet Engineering Task Force (IETF) K. Murchison | |||
Internet-Draft Fastmail | Request for Comments: 9661 Fastmail | |||
Intended status: Standards Track 4 April 2024 | Category: Standards Track September 2024 | |||
Expires: 6 October 2024 | ISSN: 2070-1721 | |||
JMAP for Sieve Scripts | The JSON Meta Application Protocol (JMAP) for Sieve Scripts | |||
draft-ietf-jmap-sieve-22 | ||||
Abstract | Abstract | |||
This document specifies a data model for managing Sieve scripts on a | This document specifies a data model for managing Sieve scripts on a | |||
server using the JSON Meta Application Protocol (JMAP). Clients can | server using the JSON Meta Application Protocol (JMAP). Clients can | |||
use this protocol to efficiently search, access, organize, and | use this protocol to efficiently search, access, organize, and | |||
validate Sieve scripts. | validate Sieve scripts. | |||
Status of This Memo | Status of This Memo | |||
This Internet-Draft is submitted in full conformance with the | This is an Internet Standards Track document. | |||
provisions of BCP 78 and BCP 79. | ||||
Internet-Drafts are working documents of the Internet Engineering | ||||
Task Force (IETF). Note that other groups may also distribute | ||||
working documents as Internet-Drafts. The list of current Internet- | ||||
Drafts is at https://datatracker.ietf.org/drafts/current/. | ||||
Internet-Drafts are draft documents valid for a maximum of six months | This document is a product of the Internet Engineering Task Force | |||
and may be updated, replaced, or obsoleted by other documents at any | (IETF). It represents the consensus of the IETF community. It has | |||
time. It is inappropriate to use Internet-Drafts as reference | received public review and has been approved for publication by the | |||
material or to cite them other than as "work in progress." | Internet Engineering Steering Group (IESG). Further information on | |||
Internet Standards is available in Section 2 of RFC 7841. | ||||
This Internet-Draft will expire on 6 October 2024. | 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/rfc9661. | ||||
Copyright Notice | Copyright Notice | |||
Copyright (c) 2024 IETF Trust and the persons identified as the | Copyright (c) 2024 IETF Trust and the persons identified as the | |||
document authors. All rights reserved. | document authors. All rights reserved. | |||
This document is subject to BCP 78 and the IETF Trust's Legal | This document is subject to BCP 78 and the IETF Trust's Legal | |||
Provisions Relating to IETF Documents (https://trustee.ietf.org/ | Provisions Relating to IETF Documents | |||
license-info) in effect on the date of publication of this document. | (https://trustee.ietf.org/license-info) in effect on the date of | |||
Please review these documents carefully, as they describe your rights | publication of this document. Please review these documents | |||
and restrictions with respect to this document. Code Components | carefully, as they describe your rights and restrictions with respect | |||
extracted from this document must include Revised BSD License text as | to this document. Code Components extracted from this document must | |||
described in Section 4.e of the Trust Legal Provisions and are | include Revised BSD License text as described in Section 4.e of the | |||
provided without warranty as described in the Revised BSD License. | Trust Legal Provisions and are provided without warranty as described | |||
in the Revised BSD License. | ||||
Table of Contents | Table of Contents | |||
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 2 | 1. Introduction | |||
1.1. Notational Conventions . . . . . . . . . . . . . . . . . 3 | 1.1. Notational Conventions | |||
1.2. Addition to the Capabilities Object . . . . . . . . . . . 3 | 1.2. Addition to the Capabilities Object | |||
1.2.1. urn:ietf:params:jmap:sieve . . . . . . . . . . . . . 3 | 1.2.1. urn:ietf:params:jmap:sieve | |||
1.2.2. Example . . . . . . . . . . . . . . . . . . . . . . . 4 | 1.2.2. Example | |||
2. Sieve Scripts . . . . . . . . . . . . . . . . . . . . . . . . 6 | 2. Sieve Scripts | |||
2.1. Sieve Script Properties . . . . . . . . . . . . . . . . . 6 | 2.1. Sieve Script Properties | |||
2.2. Sieve Script Content . . . . . . . . . . . . . . . . . . 7 | 2.2. Sieve Script Content | |||
2.3. SieveScript/get . . . . . . . . . . . . . . . . . . . . . 7 | 2.3. SieveScript/get | |||
2.3.1. Examples . . . . . . . . . . . . . . . . . . . . . . 7 | 2.3.1. Examples | |||
2.4. SieveScript/set . . . . . . . . . . . . . . . . . . . . . 10 | 2.4. SieveScript/set | |||
2.4.1. Examples . . . . . . . . . . . . . . . . . . . . . . 11 | 2.4.1. Examples | |||
2.5. SieveScript/query . . . . . . . . . . . . . . . . . . . . 17 | 2.5. SieveScript/query | |||
2.6. SieveScript/validate . . . . . . . . . . . . . . . . . . 18 | 2.6. SieveScript/validate | |||
3. Quotas . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 | 3. Quotas | |||
4. Compatibility with JMAP Vacation Response . . . . . . . . . . 19 | 4. Compatibility with JMAP Vacation Response | |||
5. Security Considerations . . . . . . . . . . . . . . . . . . . 19 | 5. Security Considerations | |||
6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 20 | 6. IANA Considerations | |||
6.1. JMAP Capability Registration for "sieve" . . . . . . . . 20 | 6.1. JMAP Capability Registration for "sieve" | |||
6.2. JMAP Data Type Registration for "SieveScript" . . . . . . 20 | 6.2. JMAP Data Type Registration for "SieveScript" | |||
6.3. JMAP Error Codes Registry . . . . . . . . . . . . . . . . 20 | 6.3. JMAP Error Codes Registry | |||
6.3.1. invalidSieve . . . . . . . . . . . . . . . . . . . . 20 | 6.3.1. invalidSieve | |||
6.3.2. sieveIsActive . . . . . . . . . . . . . . . . . . . . 21 | 6.3.2. sieveIsActive | |||
7. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 21 | 7. References | |||
8. References . . . . . . . . . . . . . . . . . . . . . . . . . 21 | 7.1. Normative References | |||
8.1. Normative References . . . . . . . . . . . . . . . . . . 21 | 7.2. Informative References | |||
8.2. Informative References . . . . . . . . . . . . . . . . . 22 | Acknowledgments | |||
Appendix A. Change History (To be removed by RFC Editor before | Author's Address | |||
publication) . . . . . . . . . . . . . . . . . . . . . . 23 | ||||
Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 26 | ||||
1. Introduction | 1. Introduction | |||
JMAP [RFC8620] (JSON Meta Application Protocol) is a generic protocol | The JSON Meta Application Protocol (JMAP) [RFC8620] is a generic | |||
for synchronizing data, such as mail, calendars or contacts, between | protocol for synchronizing data, such as mail, calendars, or | |||
a client and a server. It is optimized for mobile and web | contacts, between a client and a server. It is optimized for mobile | |||
environments, and aims to provide a consistent interface to different | and web environments, and it aims to provide a consistent interface | |||
data types. | to different data types. | |||
This specification defines a data model for managing Sieve [RFC5228] | This specification defines a data model for managing Sieve scripts | |||
scripts on a server using JMAP. The data model is designed to allow | [RFC5228] on a server using JMAP. The data model is designed to | |||
a server to provide consistent access to the same scripts via | allow a server to provide consistent access to the same scripts via | |||
ManageSieve [RFC5804] as well as JMAP. | ManageSieve [RFC5804] as well as JMAP. | |||
1.1. Notational Conventions | 1.1. Notational Conventions | |||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", | |||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and | "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and | |||
"OPTIONAL" in this document are to be interpreted as described in BCP | "OPTIONAL" in this document are to be interpreted as described in | |||
14 [RFC2119] [RFC8174] when, and only when, they appear in all | BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all | |||
capitals, as shown here. | capitals, as shown here. | |||
Type signatures, examples, and property descriptions in this document | Type signatures, examples, and property descriptions in this document | |||
follow the conventions established in Section 1.1 of [RFC8620]. This | follow the conventions established in Section 1.1 of [RFC8620]. This | |||
document also uses data types and terminology established in Sections | document also uses data types and terminology established in | |||
1.2-1.6 of [RFC8620]. | Sections 1.2 through 1.6 of [RFC8620]. | |||
The term SieveScript (with this specific capitalization) is used to | The term "SieveScript" (with this specific capitalization) is used to | |||
refer to the data type defined in this document and instances of | refer to the data type defined in Section 2 and instances of this | |||
those data types. Servers MUST support all properties specified for | data type used throughout this document. Servers MUST support all | |||
the data type defined in this document. | properties specified for the data type defined in this document. | |||
For brevity, JMAP API (see Section 3 of [RFC8620]) examples only show | For brevity, JMAP API examples (see Section 3 of [RFC8620]) only show | |||
the "methodCalls" property of the Request object, and the | the "methodCalls" property of the "Request" object and the | |||
"methodResponses" property of the Response object. All other | "methodResponses" property of the "Response" object. All other | |||
examples are shown using the HTTP 1.1 [RFC9112] protocol. | examples are shown using the HTTP/1.1 protocol [RFC9112]. | |||
1.2. Addition to the Capabilities Object | 1.2. Addition to the Capabilities Object | |||
The capabilities object is returned as part of the JMAP Session | The "capabilities" object is returned as part of the JMAP Session | |||
object; see [RFC8620], Section 2. This document defines one | object; see [RFC8620], Section 2. This document defines one | |||
additional capability URI. | additional capability URI. | |||
1.2.1. urn:ietf:params:jmap:sieve | 1.2.1. urn:ietf:params:jmap:sieve | |||
The urn:ietf:params:jmap:sieve URI represents support for the | The urn:ietf:params:jmap:sieve URI represents support for the | |||
SieveScript data type and associated API methods. The value of this | SieveScript data type and associated API methods. The value of this | |||
property in the JMAP Session capabilities property is an object that | property in the JMAP Session "capabilities" property is an object | |||
MUST contain the following information on server capabilities: | that MUST contain the following information on server capabilities: | |||
* *implementation*: String | *implementation*: String | |||
The name and version of the Sieve implementation. | The name and version of the Sieve implementation. | |||
The value of this property in an account's accountCapabilities | The value of this property in an account's "accountCapabilities" | |||
property is an object that MUST contain the following information on | property is an object that MUST contain the following information on | |||
per-account server capabilities: | per-account server capabilities: | |||
* *maxSizeScriptName*: UnsignedInt | *maxSizeScriptName*: UnsignedInt | |||
The maximum length, in octets, allowed for the name of a | The maximum length, in octets, allowed for the name of a | |||
SieveScript. For compatibility with ManageSieve, this MUST be at | SieveScript. For compatibility with ManageSieve, this MUST be at | |||
least 512 (up to 128 Unicode characters). | least 512 (up to 128 Unicode characters). | |||
* *maxSizeScript*: UnsignedInt|null | *maxSizeScript*: UnsignedInt|null | |||
The maximum size (in octets) of a Sieve script the server is | The maximum size (in octets) of a Sieve script the server is | |||
willing to store for the user, or null for no limit. | willing to store for the user, or null for no limit. | |||
* *maxNumberScripts*: UnsignedInt|null | *maxNumberScripts*: UnsignedInt|null | |||
The maximum number of Sieve scripts the server is willing to store | The maximum number of Sieve scripts the server is willing to store | |||
for the user, or null for no limit. | for the user, or null for no limit. | |||
* *maxNumberRedirects*: UnsignedInt|null | *maxNumberRedirects*: UnsignedInt|null | |||
The maximum number of Sieve "redirect" actions a script can | The maximum number of Sieve "redirect" actions a script can | |||
perform during a single evaluation or null for no limit. Note | perform during a single evaluation, or null for no limit. Note | |||
that this is different from the total number of "redirect" actions | that this is different from the total number of "redirect" actions | |||
a script can contain. | a script can contain. | |||
* *sieveExtensions*: String[] | *sieveExtensions*: String[] | |||
A list of case-sensitive Sieve capability strings (as listed in | A list of case-sensitive Sieve capability strings (as listed in | |||
Sieve "require" action; see [RFC5228], Section 3.2) indicating the | the Sieve "require" action; see [RFC5228], Section 3.2) indicating | |||
extensions supported by the Sieve engine. | the extensions supported by the Sieve engine. | |||
* *notificationMethods*: String[]|null | *notificationMethods*: String[]|null | |||
A list of URI schema parts [RFC3986] for notification methods | A list of URI scheme parts [RFC3986] for notification methods | |||
supported by the Sieve "enotify" [RFC5435] extension, or null if | supported by the Sieve "enotify" extension [RFC5435], or null if | |||
the extension is not supported by the Sieve engine. | the extension is not supported by the Sieve engine. | |||
* *externalLists*: String[]|null | *externalLists*: String[]|null | |||
A list of URI schema parts [RFC3986] for externally stored list | A list of URI scheme parts [RFC3986] for externally stored list | |||
types supported by the Sieve "extlists" [RFC6134] extension, or | types supported by the Sieve "extlists" extension [RFC6134], or | |||
null if the extension is not supported by the Sieve engine. | null if the extension is not supported by the Sieve engine. | |||
1.2.2. Example | 1.2.2. Example | |||
A JMAP Session object showing a user that has access to their own | This example JMAP Session object shows a user that has access to | |||
Sieve scripts with support for a few Sieve extensions: | their own Sieve scripts with support for a few Sieve extensions: | |||
{ | { | |||
"capabilities": { | "capabilities": { | |||
"urn:ietf:params:jmap:core": { | "urn:ietf:params:jmap:core": { | |||
... | ... | |||
}, | }, | |||
"urn:ietf:params:jmap:mail": {}, | "urn:ietf:params:jmap:mail": {}, | |||
"urn:ietf:params:jmap:quota": {}, | "urn:ietf:params:jmap:quota": {}, | |||
"urn:ietf:params:jmap:blob": {}, | "urn:ietf:params:jmap:blob": {}, | |||
"urn:ietf:params:jmap:sieve": { | "urn:ietf:params:jmap:sieve": { | |||
skipping to change at page 6, line 28 ¶ | skipping to change at line 250 ¶ | |||
"username": "ken@example.com", | "username": "ken@example.com", | |||
"apiUrl": "/jmap/", | "apiUrl": "/jmap/", | |||
"downloadUrl": | "downloadUrl": | |||
"/jmap/download/{accountId}/{blobId}/{name}?accept={type}", | "/jmap/download/{accountId}/{blobId}/{name}?accept={type}", | |||
"uploadUrl": "/jmap/upload/{accountId}/", | "uploadUrl": "/jmap/upload/{accountId}/", | |||
... | ... | |||
} | } | |||
2. Sieve Scripts | 2. Sieve Scripts | |||
A *SieveScript* object represents a single Sieve [RFC5228] script for | A "SieveScript" object represents a single Sieve script [RFC5228] for | |||
filtering email messages at time of final delivery. | filtering email messages at the time of final delivery. | |||
2.1. Sieve Script Properties | 2.1. Sieve Script Properties | |||
A *SieveScript* object has the following properties: | A "SieveScript" object has the following properties: | |||
* *id*: Id (immutable; server-set) | *id*: Id (immutable; server-set) | |||
The id of the script. | The id of the script. | |||
* *name*: String|null (optional; default is server-dependent) | *name*: String|null (optional; default is server dependent) | |||
User-visible name for the SieveScript. If non-null, this MUST be | User-visible name for the SieveScript. If non-null, this MUST be | |||
a Net-Unicode [RFC5198] string of at least 1 character in length, | a Net-Unicode string [RFC5198] of at least 1 character in length, | |||
subject to the maximum size given in the capability object. | subject to the maximum size given in the "capability" object. | |||
For compatibility with ManageSieve, servers MUST reject names that | For compatibility with ManageSieve, servers MUST reject names that | |||
contain any of the following Unicode characters: U+0000 - U+001F, | contain any of the following Unicode characters: U+0000-U+001F, | |||
U+007F - U+009F, U+2028, U+2029. | U+007F-U+009F, U+2028, or U+2029. | |||
Servers MAY reject names that violate server policy (e.g., names | Servers MAY reject names that violate server policy (e.g., names | |||
containing slash (/)). | containing a slash (/)). | |||
The name MUST be unique among all SieveScripts within an account. | The name MUST be unique among all SieveScripts within an account. | |||
* *blobId*: Id | *blobId*: Id | |||
The id of the blob containing the raw octets of the script. | The id of the blob containing the raw octets of the script. | |||
* *isActive*: Boolean (server-set; default: false) | *isActive*: Boolean (server-set; default: false) | |||
Indicator that the SieveScript is actively filtering incoming | Indicator that the SieveScript is actively filtering incoming | |||
messages. | messages. | |||
A user may have at most one active script. The SieveScript/set | A user may have at most one active script. The SieveScript/set | |||
(Section 2.4) method is used for changing the active script or | method (Section 2.4) is used for changing the active script or | |||
disabling Sieve processing. | disabling Sieve processing. | |||
2.2. Sieve Script Content | 2.2. Sieve Script Content | |||
A script MUST be UTF-8 [RFC3629] content of at least 1 character in | A script MUST be UTF-8 content [RFC3629] of at least 1 character in | |||
length, subject to the syntax of Sieve [RFC5228]. A script MUST NOT | length, subject to the syntax of Sieve [RFC5228]. A script MUST NOT | |||
contain any "require" statement(s) mentioning Sieve capability | contain any "require" statement(s) mentioning Sieve capability | |||
strings not present in the capability (Section 1.2.1) object. Note | strings not present in the "capability" object (Section 1.2.1). Note | |||
that if the Sieve "ihave" [RFC5463] capability string is present in | that if the Sieve "ihave" capability string [RFC5463] is present in | |||
the capability object, the script MAY mention unrecognized/ | the "capability" object, the script MAY mention unrecognized/ | |||
unsupported extensions in the "ihave" test. | unsupported extensions in the "ihave" test. | |||
Script content is treated as a binary blob and uploaded/downloaded | Script content is treated as a binary blob and uploaded/downloaded | |||
via the mechanisms in [RFC8620] Sections 6.1/6.2 respectively and/or | via the mechanisms provided in Sections 6.1 and 6.2 of [RFC8620], | |||
via the JMAP Blob management methods in [RFC9404] Sections 4.1/4.2 | respectively, and/or via the JMAP Blob management methods provided in | |||
respectively. | Sections 4.1 and 4.2 of [RFC9404], respectively. | |||
Downloading script content via the JMAP downloadUrl or the Blob/get | Downloading script content via the JMAP downloadUrl or the Blob/get | |||
method provides equivalent functionality to the GETSCRIPT command in | method provides functionality equivalent to that of the GETSCRIPT | |||
[RFC5804]. | command defined in [RFC5804]. | |||
2.3. SieveScript/get | 2.3. SieveScript/get | |||
This is a standard "/get" method as described in [RFC8620], | This is a standard "/get" method as described in [RFC8620], | |||
Section 5.1. The _ids_ argument may be null to fetch all scripts at | Section 5.1. The "ids" argument may be null to fetch all scripts at | |||
once. | once. | |||
This method provides equivalent functionality to the LISTSCRIPTS | This method provides functionality equivalent to that of the | |||
command in [RFC5804]. | LISTSCRIPTS command defined in [RFC5804]. | |||
2.3.1. Examples | 2.3.1. Examples | |||
List all scripts: | List all scripts: | |||
[ | [ | |||
["SieveScript/get", { | ["SieveScript/get", { | |||
"accountId": "ken" | "accountId": "ken" | |||
}, "0"] | }, "0"] | |||
] | ] | |||
skipping to change at page 8, line 32 ¶ | skipping to change at line 348 ¶ | |||
} | } | |||
], | ], | |||
"notFound": [], | "notFound": [], | |||
"accountId": "ken" | "accountId": "ken" | |||
}, | }, | |||
"0" | "0" | |||
] | ] | |||
] | ] | |||
Download the script content via the JMAP downloadUrl as advertised in | Download the script content via the JMAP downloadUrl as advertised in | |||
Section 1.2.2: | the example in Section 1.2.2: | |||
GET /jmap/download/ken/S7/test1.siv?accept=application/sieve HTTP/1.1 | GET /jmap/download/ken/S7/test1.siv?accept=application/sieve HTTP/1.1 | |||
Host: jmap.example.com | Host: jmap.example.com | |||
Authorization: Basic a2VuOnBhc3N3b3Jk | Authorization: Basic a2VuOnBhc3N3b3Jk | |||
HTTP/1.1 200 OK | HTTP/1.1 200 OK | |||
Date: Fri, 22 Oct 2021 15:27:38 GMT | Date: Fri, 22 Oct 2021 15:27:38 GMT | |||
Content-Type: application/sieve; charset=utf-8 | Content-Type: application/sieve; charset=utf-8 | |||
Content-Disposition: attachment; filename="test1.siv" | Content-Disposition: attachment; filename="test1.siv" | |||
Content-Length: 49 | Content-Length: 49 | |||
skipping to change at page 10, line 4 ¶ | skipping to change at line 414 ¶ | |||
"id": "S7", | "id": "S7", | |||
"data:asText": | "data:asText": | |||
"require [\"fileinto\"];\\r\\nfileinto \"INBOX.target\";\\r\\n", | "require [\"fileinto\"];\\r\\nfileinto \"INBOX.target\";\\r\\n", | |||
"size": 49 | "size": 49 | |||
} | } | |||
], | ], | |||
"notFound": [], | "notFound": [], | |||
"accountId": "ken" | "accountId": "ken" | |||
}, | }, | |||
"1" | "1" | |||
] | ] | |||
] | ] | |||
2.4. SieveScript/set | 2.4. SieveScript/set | |||
This is a standard "/set" method as described in [RFC8620], | This is a standard "/set" method as described in [RFC8620], | |||
Section 5.3 but with the following additional optional request | Section 5.3, but with the following additional optional request | |||
arguments: | arguments: | |||
* *onSuccessActivateScript*: Id | *onSuccessActivateScript*: Id | |||
The id of the SieveScript to activate if and only if all of the | The id of the SieveScript to activate if and only if all of the | |||
creations, modifications, and destructions (if any) succeed. (For | creations, modifications, and destructions (if any) succeed. (For | |||
references to SieveScript creations, this is equivalent to a | references to SieveScript creations, this is equivalent to a | |||
creation-reference, so the id will be the creation id prefixed | creation-reference, so the id will be the creation id prefixed | |||
with a "#".) The currently active SieveScript (if any) will be | with a "#".) The currently active SieveScript (if any) will be | |||
deactivated before activating the specified SieveScript. | deactivated before activating the specified SieveScript. | |||
If omitted, or if the id is either invalid or nonexistent, it MUST | If omitted, or if the id is either invalid or nonexistent, it MUST | |||
be ignored and the currently active SieveScript (if any) will | be ignored, and the currently active SieveScript (if any) will | |||
remain as such. | remain as such. | |||
The id of any activated SieveScript MUST be reported in either the | The id of any activated SieveScript MUST be reported in either the | |||
"created" or "updated" argument in the response as appropriate, | "created" or "updated" argument in the response as appropriate, | |||
including a value of "true" for the "isActive" property. The id | including a value of "true" for the "isActive" property. The id | |||
of any deactivated SieveScript MUST be reported in the "updated" | of any deactivated SieveScript MUST be reported in the "updated" | |||
argument in the response, including a value of "false" for the | argument in the response, including a value of "false" for the | |||
"isActive" property. | "isActive" property. | |||
* *onSuccessDeactivateScript*: Boolean | *onSuccessDeactivateScript*: Boolean | |||
If true, the currently active SieveScript (if any) will be | If "true", the currently active SieveScript (if any) will be | |||
deactivated if and only if all of the creations, modifications, | deactivated if and only if all of the creations, modifications, | |||
and destructions (if any) succeed. If false or omitted, the | and destructions (if any) succeed. If "false" or omitted, the | |||
currently active SieveScript (if any) will remain as such. | currently active SieveScript (if any) will remain as such. | |||
The id of any deactivated SieveScript MUST be reported in the | The id of any deactivated SieveScript MUST be reported in the | |||
"updated" argument in the response, including a value of "false" | "updated" argument in the response, including a value of "false" | |||
for the "isActive" property. | for the "isActive" property. | |||
If both the *onSuccessActivateScript* and *onSuccessDeactivateScript* | If both the "onSuccessActivateScript" and "onSuccessDeactivateScript" | |||
arguments are present in the request, then | arguments are present in the request, then | |||
*onSuccessDeactivateScript* MUST be processed first. If neither | "onSuccessDeactivateScript" MUST be processed first. If neither | |||
argument is present in the request, the currently active SieveScript | argument is present in the request, the currently active SieveScript | |||
(if any) will remain as such. | (if any) will remain as such. | |||
This method provides equivalent functionality to the PUTSCRIPT, | This method provides functionality equivalent to that of the | |||
DELETESCRIPT, RENAMESCRIPT, and SETACTIVE commands in [RFC5804]. | PUTSCRIPT, DELETESCRIPT, RENAMESCRIPT, and SETACTIVE commands defined | |||
in [RFC5804]. | ||||
Script content must first be uploaded as per Section 2.2 prior to | Script content must first be uploaded as per Section 2.2 prior to | |||
referencing it in a SieveScript/set call. | referencing it in a SieveScript/set call. | |||
If the SieveScript can not be created or updated because it would | If the SieveScript cannot be created or updated because it would | |||
result in two SieveScripts with the same name, the server MUST reject | result in two SieveScripts with the same name, the server MUST reject | |||
the request with an "alreadyExists" SetError. An "existingId" | the request with an "alreadyExists" SetError. An "existingId" | |||
property of type "Id" MUST be included on the SetError object with | property of type "Id" MUST be included on the SetError object with | |||
the id of the existing SieveScript. | the id of the existing SieveScript. | |||
If the SieveScript can not be created or updated because its size | If the SieveScript cannot be created or updated because its size | |||
exceeds the "maxSizeScript" limit, the server MUST reject the request | exceeds the "maxSizeScript" limit, the server MUST reject the request | |||
with a "tooLarge" SetError. | with a "tooLarge" SetError. | |||
If the SieveScript can not be created because it would exceed the | If the SieveScript cannot be created because it would exceed the | |||
"maxNumberScripts" limit or would exceed a server-imposed storage | "maxNumberScripts" limit or would exceed a server-imposed storage | |||
limit, the server MUST reject the request with an "overQuota" | limit, the server MUST reject the request with an "overQuota" | |||
SetError. | SetError. | |||
The active SieveScript MUST NOT be destroyed unless it is first | The active SieveScript MUST NOT be destroyed unless it is first | |||
deactivated in a separate SieveScript/set method call. | deactivated in a separate SieveScript/set method call. | |||
The following extra SetError types are defined: | The following extra SetError types are defined: | |||
For "create" and "update": | For "create" and "update": | |||
* *invalidSieve*: | *invalidSieve*: The SieveScript content violates the Sieve grammar | |||
[RFC5228], and/or one or more extensions mentioned in the script's | ||||
The SieveScript content violates the Sieve [RFC5228] grammar and/ | "require" statement(s) are not supported by the Sieve interpreter. | |||
or one or more extensions mentioned in the script's "require" | The "description" property on the SetError object SHOULD contain a | |||
statement(s) are not supported by the Sieve interpreter. The | ||||
_description_ property on the SetError object SHOULD contain a | ||||
specific error message giving at least the line number of the | specific error message giving at least the line number of the | |||
first error. | first error. | |||
For "destroy": | For "destroy": | |||
* *sieveIsActive*: | *sieveIsActive*: The SieveScript is active. | |||
The SieveScript is active. | ||||
2.4.1. Examples | 2.4.1. Examples | |||
Upload a script requiring the Imap4Flags [RFC5232] Extension using | Upload a script requiring the Imap4Flags Extension [RFC5232] using | |||
the JMAP uploadUrl as advertised in Section 1.2.2: | the JMAP uploadUrl as advertised in the example in Section 1.2.2: | |||
POST /jmap/upload/ken/ HTTP/1.1 | POST /jmap/upload/ken/ HTTP/1.1 | |||
Host: jmap.example.com | Host: jmap.example.com | |||
Authorization: Basic a2VuOnBhc3N3b3Jk | Authorization: Basic a2VuOnBhc3N3b3Jk | |||
Content-Type: application/sieve | Content-Type: application/sieve | |||
Content-Length: 98 | Content-Length: 98 | |||
require "imapflags"; | require "imapflags"; | |||
if address :is ["To", "Cc"] "jmap@ietf.org" { | if address :is ["To", "Cc"] "jmap@ietf.org" { | |||
skipping to change at page 15, line 17 ¶ | skipping to change at line 639 ¶ | |||
"destroyed": null, | "destroyed": null, | |||
"notCreated": null, | "notCreated": null, | |||
"notUpdated": null, | "notUpdated": null, | |||
"notDestroyed": null, | "notDestroyed": null, | |||
"accountId": "ken" | "accountId": "ken" | |||
}, | }, | |||
"2" | "2" | |||
] | ] | |||
] | ] | |||
Update the script name and deactivate it: | Update the script name, and deactivate it: | |||
[ | [ | |||
["SieveScript/set", { | ["SieveScript/set", { | |||
"accountId": "ken", | "accountId": "ken", | |||
"update": { "dd1b164f-8cdc-448c-9f54": { | "update": { "dd1b164f-8cdc-448c-9f54": { | |||
"name": "myscript" | "name": "myscript" | |||
} | } | |||
}, | }, | |||
"onSuccessDeactivateScript": true | "onSuccessDeactivateScript": true | |||
}, "3"] | }, "3"] | |||
skipping to change at page 17, line 42 ¶ | skipping to change at line 760 ¶ | |||
"notDestroyed": null, | "notDestroyed": null, | |||
"accountId": "ken" | "accountId": "ken" | |||
}, | }, | |||
"6" | "6" | |||
] | ] | |||
] | ] | |||
2.5. SieveScript/query | 2.5. SieveScript/query | |||
This is a standard "/query" method as described in [RFC8620], | This is a standard "/query" method as described in [RFC8620], | |||
Section 5.5. A _FilterCondition_ object has the following | Section 5.5. A "FilterCondition" object has the following | |||
properties, either of which may be omitted: | properties, either of which may be omitted: | |||
* *name*: String | *name*: String | |||
The SieveScript "name" property contains the given string. | The SieveScript "name" property contains the given string. | |||
* *isActive*: Boolean | *isActive*: Boolean | |||
The "isActive" property of the SieveScript must be identical to | The "isActive" property of the SieveScript must be identical to | |||
the value given to match the condition. | the value given to match the condition. | |||
The following SieveScript properties MUST be supported for sorting: | The following SieveScript properties MUST be supported for sorting: | |||
* *name* | * *name* | |||
* *isActive* | * *isActive* | |||
2.6. SieveScript/validate | 2.6. SieveScript/validate | |||
This method is used by the client to verify Sieve script validity | This method is used by the client to verify Sieve script validity | |||
without storing the script on the server. | without storing the script on the server. | |||
The method takes the following arguments: | The method takes the following arguments: | |||
* *accountId*: Id | *accountId*: Id | |||
The id of the account to use. | The id of the account to use. | |||
* *blobId*: Id | *blobId*: Id | |||
The id of the blob containing the raw octets of the script to | The id of the blob containing the raw octets of the script to | |||
validate, subject to the same requirements in Section 2. | validate, subject to the same requirements in Section 2.2. | |||
The response has the following arguments: | The response has the following arguments: | |||
* *accountId*: Id | *accountId*: Id | |||
The id of the account used for this call. | The id of the account used for this call. | |||
* *error*: SetError|null | *error*: SetError|null | |||
An "invalidSieve" SetError object if the script content is invalid | An "invalidSieve" SetError object if the script content is invalid | |||
(see Section 2.4), or null if the script content is valid. | (see Section 2.4), or null if the script content is valid. | |||
This method provides equivalent functionality to the CHECKSCRIPT | This method provides functionality equivalent to that of the | |||
command in [RFC5804]. | CHECKSCRIPT command defined in [RFC5804]. | |||
Script content must first be uploaded as per Section 2.2 prior to | Script content must first be uploaded as per Section 2.2 prior to | |||
referencing it in a SieveScript/validate call. | referencing it in a SieveScript/validate call. | |||
3. Quotas | 3. Quotas | |||
Servers SHOULD impose quotas on Sieve scripts to prevent malicious | Servers SHOULD impose quotas on Sieve scripts to prevent malicious | |||
users from exceeding available storage. Administration of such | users from exceeding available storage. Administration of such | |||
quotas is outside of the scope of this specification, however | quotas is outside of the scope of this specification; however, | |||
[RFC9425] defines a data model for users to obtain quota details over | [RFC9425] defines a data model for users to obtain quota details over | |||
JMAP. | JMAP. | |||
The mechanism for handling SieveScript requests that would place a | The mechanism for handling SieveScript requests that would place a | |||
user over a quota setting is discussed in Section 2.4. | user over a quota setting is discussed in Section 2.4. | |||
4. Compatibility with JMAP Vacation Response | 4. Compatibility with JMAP Vacation Response | |||
Section 8 of [RFC8621] defines a VacationResponse object to represent | Section 8 of [RFC8621] defines a "VacationResponse" object to | |||
an autoresponder to incoming email messages. Servers that implement | represent an autoresponder to incoming email messages. Servers that | |||
the VacationResponse as a Sieve script that resides amongst other | implement the VacationResponse as a Sieve script that resides among | |||
user scripts are subject to the following requirements: | other user scripts are subject to the following requirements: | |||
* MUST allow the VacationResponse Sieve script to be fetched by the | * MUST allow the VacationResponse Sieve script to be fetched by the | |||
SieveScript/get (Section 2.3) method. | SieveScript/get method (Section 2.3). | |||
* MUST allow the VacationResponse Sieve script to be [de]activated | * MUST allow the VacationResponse Sieve script to be activated or | |||
via the "onSuccessActivateScript" argument to the SieveScript/set | deactivated via the "onSuccessActivateScript" argument to the | |||
(Section 2.4) method. | SieveScript/set method (Section 2.4). | |||
* MUST NOT allow the VacationResponse Sieve script to be destroyed | * MUST NOT allow the VacationResponse Sieve script to be destroyed | |||
or have its content updated by the SieveScript/set (Section 2.4) | or have its content updated by the SieveScript/set method | |||
method. Any such request MUST be rejected with a "forbidden" | (Section 2.4). Any such request MUST be rejected with a | |||
SetError. A "description" property MAY be present with an | "forbidden" SetError. A "description" property MAY be present | |||
explanation that the script can only be modified by a | with an explanation that the script can only be modified by a | |||
VacationResponse/set method. | VacationResponse/set method. | |||
5. Security Considerations | 5. Security Considerations | |||
All security considerations of JMAP [RFC8620] and Sieve [RFC5228] | All security considerations discussed in JMAP [RFC8620] and Sieve | |||
apply to this specification. | [RFC5228] apply to this specification. | |||
Additionally, implementations MUST treat Sieve script content as | Additionally, implementations MUST treat Sieve script content as | |||
untrusted data. As such, script parsers MUST fail gracefully in the | untrusted data. As such, script parsers MUST fail gracefully in the | |||
face of syntactically invalid or malicious content and MUST be | face of syntactically invalid or malicious content and MUST be | |||
prepared to deal with resource exhaustion (E.g., allocation of | prepared to deal with resource exhaustion (e.g., allocation of | |||
enormous strings, lists, or command blocks). | enormous strings, lists, or command blocks). | |||
6. IANA Considerations | 6. IANA Considerations | |||
6.1. JMAP Capability Registration for "sieve" | 6.1. JMAP Capability Registration for "sieve" | |||
IANA will register the "sieve" JMAP Capability as follows: | IANA has registered "sieve" in the "JMAP Capabilities" registry as | |||
follows: | ||||
Capability Name: urn:ietf:params:jmap:sieve | Capability Name: urn:ietf:params:jmap:sieve | |||
Specification document: this document | Reference: RFC 9661 | |||
Intended use: common | Intended Use: common | |||
Change Controller: IETF | Change Controller: IETF | |||
Security and privacy considerations: this document, Section 5 | Security and Privacy Considerations: RFC 9661, Section 5 | |||
6.2. JMAP Data Type Registration for "SieveScript" | 6.2. JMAP Data Type Registration for "SieveScript" | |||
IANA will register the "SieveScript" JMAP Data Type as follows: | IANA has registered "SieveScript" in the "JMAP Data Types" registry | |||
as follows: | ||||
Type Name: SieveScript | Type Name: SieveScript | |||
Can Reference Blobs: yes | Can Reference Blobs: Yes | |||
Can Use for State Change: yes | Can Use for State Change: Yes | |||
Capability: urn:ietf:params:jmap:sieve | Capability: urn:ietf:params:jmap:sieve | |||
Specification document: this document | Reference: RFC 9661 | |||
6.3. JMAP Error Codes Registry | 6.3. JMAP Error Codes Registry | |||
The following sub-sections register two new error codes in the JMAP | IANA has registered the following two new error codes in the "JMAP | |||
Error Codes registry, as defined in [RFC8620]. | Error Codes" registry, as defined in [RFC8620]. | |||
6.3.1. invalidSieve | 6.3.1. invalidSieve | |||
JMAP Error Code: invalidSieve | JMAP Error Code: invalidSieve | |||
Intended use: common | Intended Use: common | |||
Change controller: IETF | Change Controller: IETF | |||
Reference: This document, Section 2.4 | Reference: RFC 9661, Section 2.4 | |||
Description: The SieveScript violates the Sieve grammar [RFC5228] | Description: The SieveScript violates the Sieve grammar [RFC5228], | |||
and/or one or more extensions mentioned in the script's "require" | and/or one or more extensions mentioned in the script's "require" | |||
statement(s) are not supported by the Sieve interpreter. | statement(s) are not supported by the Sieve interpreter. | |||
6.3.2. sieveIsActive | 6.3.2. sieveIsActive | |||
JMAP Error Code: sieveIsActive | JMAP Error Code: sieveIsActive | |||
Intended use: common | ||||
Change controller: IETF | ||||
Reference: This document, Section 2.4 | ||||
Description: The client tried to destroy the active SieveScript. | Intended Use: common | |||
7. Acknowledgments | Change Controller: IETF | |||
The concepts in this document are based largely on those in | Reference: RFC 9661, Section 2.4 | |||
[RFC5804]. The author would like to thank the authors of that | ||||
document for providing both inspiration and some borrowed text for | ||||
this document. | ||||
The author would also like to thank the following individuals for | Description: The client tried to destroy the active SieveScript. | |||
contributing their ideas and support for writing this specification: | ||||
Joris Baum, Mauro De Gennaro, Bron Gondwana, Neil Jenkins, Alexey | ||||
Melnikov, and Ricardo Signes. | ||||
8. References | 7. References | |||
8.1. Normative References | 7.1. Normative References | |||
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate | [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate | |||
Requirement Levels", BCP 14, RFC 2119, | Requirement Levels", BCP 14, RFC 2119, | |||
DOI 10.17487/RFC2119, March 1997, | DOI 10.17487/RFC2119, March 1997, | |||
<https://www.rfc-editor.org/info/rfc2119>. | <https://www.rfc-editor.org/info/rfc2119>. | |||
[RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO | [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO | |||
10646", STD 63, RFC 3629, DOI 10.17487/RFC3629, November | 10646", STD 63, RFC 3629, DOI 10.17487/RFC3629, November | |||
2003, <https://www.rfc-editor.org/info/rfc3629>. | 2003, <https://www.rfc-editor.org/info/rfc3629>. | |||
skipping to change at page 22, line 30 ¶ | skipping to change at line 964 ¶ | |||
May 2017, <https://www.rfc-editor.org/info/rfc8174>. | May 2017, <https://www.rfc-editor.org/info/rfc8174>. | |||
[RFC8620] Jenkins, N. and C. Newman, "The JSON Meta Application | [RFC8620] Jenkins, N. and C. Newman, "The JSON Meta Application | |||
Protocol (JMAP)", RFC 8620, DOI 10.17487/RFC8620, July | Protocol (JMAP)", RFC 8620, DOI 10.17487/RFC8620, July | |||
2019, <https://www.rfc-editor.org/info/rfc8620>. | 2019, <https://www.rfc-editor.org/info/rfc8620>. | |||
[RFC8621] Jenkins, N. and C. Newman, "The JSON Meta Application | [RFC8621] Jenkins, N. and C. Newman, "The JSON Meta Application | |||
Protocol (JMAP) for Mail", RFC 8621, DOI 10.17487/RFC8621, | Protocol (JMAP) for Mail", RFC 8621, DOI 10.17487/RFC8621, | |||
August 2019, <https://www.rfc-editor.org/info/rfc8621>. | August 2019, <https://www.rfc-editor.org/info/rfc8621>. | |||
8.2. Informative References | 7.2. Informative References | |||
[RFC5232] Melnikov, A., "Sieve Email Filtering: Imap4flags | [RFC5232] Melnikov, A., "Sieve Email Filtering: Imap4flags | |||
Extension", RFC 5232, DOI 10.17487/RFC5232, January 2008, | Extension", RFC 5232, DOI 10.17487/RFC5232, January 2008, | |||
<https://www.rfc-editor.org/info/rfc5232>. | <https://www.rfc-editor.org/info/rfc5232>. | |||
[RFC5463] Freed, N., "Sieve Email Filtering: Ihave Extension", | [RFC5463] Freed, N., "Sieve Email Filtering: Ihave Extension", | |||
RFC 5463, DOI 10.17487/RFC5463, March 2009, | RFC 5463, DOI 10.17487/RFC5463, March 2009, | |||
<https://www.rfc-editor.org/info/rfc5463>. | <https://www.rfc-editor.org/info/rfc5463>. | |||
[RFC5804] Melnikov, A., Ed. and T. Martin, "A Protocol for Remotely | [RFC5804] Melnikov, A., Ed. and T. Martin, "A Protocol for Remotely | |||
skipping to change at page 23, line 9 ¶ | skipping to change at line 991 ¶ | |||
[RFC9404] Gondwana, B., Ed., "JSON Meta Application Protocol (JMAP) | [RFC9404] Gondwana, B., Ed., "JSON Meta Application Protocol (JMAP) | |||
Blob Management Extension", RFC 9404, | Blob Management Extension", RFC 9404, | |||
DOI 10.17487/RFC9404, August 2023, | DOI 10.17487/RFC9404, August 2023, | |||
<https://www.rfc-editor.org/info/rfc9404>. | <https://www.rfc-editor.org/info/rfc9404>. | |||
[RFC9425] Cordier, R., Ed., "JSON Meta Application Protocol (JMAP) | [RFC9425] Cordier, R., Ed., "JSON Meta Application Protocol (JMAP) | |||
for Quotas", RFC 9425, DOI 10.17487/RFC9425, June 2023, | for Quotas", RFC 9425, DOI 10.17487/RFC9425, June 2023, | |||
<https://www.rfc-editor.org/info/rfc9425>. | <https://www.rfc-editor.org/info/rfc9425>. | |||
Appendix A. Change History (To be removed by RFC Editor before | Acknowledgments | |||
publication) | ||||
Changes since ietf-21: | ||||
* Rearranged and tweaked some /validate text. | ||||
Changes since ietf-20: | ||||
* Listed Unicode characters prohibited in script names. | ||||
* Cleaned up the language of the optional /set arguments. | ||||
* Added security considerations about parsing Sieve script content. | ||||
* Miscellaneous editorial changes. | ||||
Changes since ietf-19: | ||||
* Tweaked the example captions. | ||||
Changes since ietf-18: | ||||
* Edited JMAP API examples for brevity to match other JMAP specs, | ||||
and added explanatory text to 1.1. | ||||
* Updated <xref> elements to to use "section" and "sectionFormat" | ||||
attributes. | ||||
Changes since ietf-17: | ||||
* Several editorial changes resulting from IESG review comments. | ||||
* Added a section discussuing quotas. | ||||
Changes since ietf-16: | ||||
* Renamed the "invalidScript" and "scriptIsActive" SetErrors to | ||||
"invalidSieve" and "sieveIsActive" respectively. | ||||
Changes since ietf-15: | ||||
* Added registration for SieveScript JMAP Data Type. | ||||
* Miscellaneous editorial changes. | ||||
Changes since ietf-14: | ||||
* Updated reference for JMAP Blobs. | ||||
Changes since ietf-13: | ||||
* Added implementation argument to capabilities. | ||||
* Miscellaneous editorial changes. | ||||
Changes since ietf-12: | ||||
* Added onSuccessDeactivateScipt argument to /set method. | ||||
* Clarified that the "isActive" property must be included in the | ||||
created/uodated/destroyed arguments in a response of the active | ||||
script is changed and/or deactivated. | ||||
* Miscellaneous editorial changes. | ||||
Changes since ietf-11: | ||||
* Fixed examples to be proper JSON and JMAP. | ||||
Changes since ietf-10: | ||||
* Fixed SieveScript/set response deactivating script example. | ||||
* Fixed line line nit in Blob/get request. | ||||
* Removed unused references. | ||||
Changes since ietf-09: | ||||
* Fixed Blob/upload request in example. | ||||
Changes since ietf-08: | ||||
* Fixed Blob/upload response in example. | ||||
* Removed SieveScript/test method (to be written as an extension | ||||
document). | ||||
Changes since ietf-07: | ||||
* Updated example to use Blob/upload rather than Blob/set. | ||||
Changes since ietf-06: | ||||
* None (refreshed to avoid expiration). | ||||
Changes since ietf-05: | ||||
* Converted source from xml2rfc v2 to v3. | ||||
* Added examples for SieveScript/get. | ||||
* Miscellaneous editorial changes. | ||||
Changes since ietf-04: | ||||
* SieveScript/test: Switched from using a JSON array for each | ||||
completed action and its args to a JSON object. | ||||
* Switched to referencing draft-ietf-jmap-blob. | ||||
* Miscellaneous editorial changes. | ||||
Changes since ietf-03: | ||||
* SieveScript/test: Moved positional arguments into their own array | ||||
(because the specfications don't use a consistent method for | ||||
defining the action syntax or naming of positional arguments). | ||||
Changes since ietf-02: | ||||
* Removed open issues. | ||||
* Reverted back to using only blob ids for script content. | ||||
* Added "rateLimit" and "requestTooLarge" to the list of possible | ||||
error codes for /set method. | ||||
* Added Compatibility with JMAP Vacation Response section. | ||||
* Added RFC5228 to Security Considerations. | ||||
* Miscellaneous editorial changes. | ||||
Changes since ietf-01: | ||||
* Removed normative references to ManageSieve (RFC 5804). | ||||
* Added the 'maxSizeScriptName' capability. | ||||
* Made the 'name' property in the SieveScript object optional. | ||||
* Added requirements for the 'name' property in the SieveScript | ||||
object. | ||||
* Removed the 'blobId' property from the SieveScript object. | ||||
* Removed the 'replaceOnCreate' argument from the /set method. | ||||
* Removed the 'blobId' argument from the /validate method. | ||||
* Removed the 'scriptBlobId' argument from, and added the | ||||
'scriptContent' argument to, the /test method. | ||||
* Editorial fixes from Neil Jenkins and Ricardo Signes. | ||||
* Other miscellaneous text reorganization and editorial fixes. | ||||
Changes since ietf-00: | ||||
* Specified that changes made by onSuccessActivateScript MUST be | ||||
reported in the /set response as created and/or updated as | ||||
appropriate. | ||||
* Reworked and specified more of the /test response based on | ||||
implementation experience. | ||||
Changes since murchison-01: | ||||
* Explicitly stated that Sieve capability strings are case- | ||||
sensitive. | ||||
* errorDescription is now String|null. | ||||
* Added /query method. | ||||
* Added /test method. | ||||
Changes since murchison-00: | ||||
* Added IANA registration for "scriptIsActive" JMAP error code. | The concepts in this document are based largely on those in | |||
[RFC5804]. The author would like to thank the authors of that | ||||
document for providing both inspiration and some borrowed text for | ||||
this document. | ||||
* Added open issue about /set{create} with an existing script name. | The author would also like to thank the following individuals for | |||
contributing their ideas and support for writing this specification: | ||||
Joris Baum, Mauro De Gennaro, Bron Gondwana, Neil Jenkins, Alexey | ||||
Melnikov, and Ricardo Signes. | ||||
Author's Address | Author's Address | |||
Kenneth Murchison | Kenneth Murchison | |||
Fastmail US LLC | Fastmail US LLC | |||
1429 Walnut Street - Suite 1201 | 1429 Walnut Street, Suite 1201 | |||
Philadelphia, PA 19102 | Philadelphia, PA 19102 | |||
United States of America | United States of America | |||
Email: murch@fastmailteam.com | Email: murch@fastmailteam.com | |||
End of changes. 110 change blocks. | ||||
398 lines changed or deleted | 209 lines changed or added | |||
This html diff was produced by rfcdiff 1.48. |