├── .travis.yml ├── .gitignore ├── project.clj ├── src └── schema_contrib │ ├── gen.clj │ └── core.clj ├── README.md ├── resources ├── uri.abnf ├── date.abnf └── email.abnf ├── test └── schema_contrib │ └── core_test.clj └── LICENSE /.travis.yml: -------------------------------------------------------------------------------- 1 | language: clojure 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject schema-contrib "0.1.5" 2 | :description "Additional validators for Prismatic's Schema." 3 | :url "https://github.com/sfx/schema-contrib" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[instaparse "1.3.0"] 7 | [org.clojure/clojure "1.6.0"] 8 | [org.clojure/test.check "0.5.7"] 9 | [prismatic/schema "0.2.6"]] 10 | :resource-paths ["resources"]) 11 | -------------------------------------------------------------------------------- /src/schema_contrib/gen.clj: -------------------------------------------------------------------------------- 1 | (ns schema-contrib.gen 2 | (:import (java.util Locale)) 3 | (:require [clojure.string :as string] 4 | [clojure.test.check.generators :as gen])) 5 | 6 | (def country 7 | (gen/elements (Locale/getISOCountries))) 8 | 9 | (def country-keyword 10 | (->> (Locale/getISOCountries) 11 | (map keyword) 12 | gen/elements)) 13 | 14 | (def language 15 | (gen/elements (Locale/getISOLanguages))) 16 | 17 | (def language-keyword 18 | (->> (Locale/getISOLanguages) 19 | (map keyword) 20 | gen/elements)) 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # schema-contrib 2 | 3 | Additional validators for [Prismatic's Schema] 4 | (https://github.com/Prismatic/schema). 5 | 6 | [![Build Status](https://travis-ci.org/sfx/schema-contrib.svg)] 7 | (https://travis-ci.org/sfx/schema-contrib) 8 | 9 | ## Clojars 10 | 11 | ```clojure 12 | [schema-contrib "0.1.3"] 13 | ``` 14 | 15 | ## Usage 16 | 17 | ### Schema 18 | 19 | ```clojure 20 | (ns my-ns 21 | (:require [schema.core :as s] 22 | [schema-contrib.core :as sc])) 23 | 24 | (s/validate sc/Country "us") 25 | 26 | (s/validate sc/Country-Keyword :us) 27 | 28 | (s/validate sc/Date "2014-01-01") 29 | 30 | (s/validate sc/Email "brianb@arc90.com") 31 | 32 | (s/validate sc/Language "en") 33 | 34 | (s/validate sc/Language-Keyword :en) 35 | 36 | (s/validate sc/ISO-Date-Time "2014-04-01T20:17:35+00:00") 37 | 38 | (s/validate sc/Time "05:00") 39 | 40 | (s/validate sc/URI "https://www.eff.org") 41 | 42 | (s/validate sc/URI-Reference "relative/path/to/resource.txt") 43 | ``` 44 | 45 | ### Generators 46 | 47 | If you're generating your schema with [test.check] 48 | (https://github.com/clojure/test.check) and [schema-gen] 49 | (https://github.com/MichaelBlume/schema-gen), we have some generators, and are 50 | working on more. 51 | 52 | ```clojure 53 | (ns my-ns 54 | (:require [clojure.test.check.generators :as gen] 55 | [schema-contrib.gen :as scgen])) 56 | 57 | (gen/sample scgen/country 5) ; => ("BY" "GF" "MA" "CD" "LB") 58 | 59 | (gen/sample scgen/country-keyword 5) ; => (:CI :PT :SZ :VU :BN) 60 | 61 | (gen/sample scgen/language 5) ; => ("vo" "br" "oj" "lu" "ss") 62 | 63 | (gen/sample scgen/language-keyword 5) ; => (:cr :hu :ak :ki :mk) 64 | ``` 65 | 66 | ## TODO 67 | 68 | * More validators 69 | * More Unicode email validation support 70 | * ClojureScript support 71 | 72 | ## License 73 | 74 | Copyright © 2014 SFX Entertainment 75 | 76 | Distributed under the Eclipse Public License version 1.0. 77 | 78 | ## Credits 79 | 80 | * [Instaparse](https://github.com/Engelberg/instaparse) 81 | * [Schema](https://github.com/prismatic/schema) 82 | * [test.check](https://github.com/clojure/test.check) 83 | -------------------------------------------------------------------------------- /resources/uri.abnf: -------------------------------------------------------------------------------- 1 | ; http://tools.ietf.org/html/rfc3986#appendix-A 2 | 3 | URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] 4 | 5 | hier-part = "//" authority path-abempty 6 | / path-absolute 7 | / path-rootless 8 | / path-empty 9 | 10 | URI-reference = URI / relative-ref 11 | 12 | absolute-URI = scheme ":" hier-part [ "?" query ] 13 | 14 | relative-ref = relative-part [ "?" query ] [ "#" fragment ] 15 | 16 | relative-part = "//" authority path-abempty 17 | / path-absolute 18 | / path-noscheme 19 | / path-empty 20 | 21 | scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) 22 | 23 | authority = [ userinfo "@" ] host [ ":" port ] 24 | userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) 25 | host = IP-literal / IPv4address / reg-name 26 | port = *DIGIT 27 | 28 | IP-literal = "[" ( IPv6address / IPvFuture ) "]" 29 | 30 | IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) 31 | 32 | IPv6address = 6( h16 ":" ) ls32 33 | / "::" 5( h16 ":" ) ls32 34 | / [ h16 ] "::" 4( h16 ":" ) ls32 35 | / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 36 | / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 37 | / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 38 | / [ *4( h16 ":" ) h16 ] "::" ls32 39 | / [ *5( h16 ":" ) h16 ] "::" h16 40 | / [ *6( h16 ":" ) h16 ] "::" 41 | 42 | h16 = 1*4HEXDIG 43 | ls32 = ( h16 ":" h16 ) / IPv4address 44 | IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet 45 | 46 | dec-octet = DIGIT ; 0-9 47 | / %x31-39 DIGIT ; 10-99 48 | / "1" 2DIGIT ; 100-199 49 | / "2" %x30-34 DIGIT ; 200-249 50 | / "25" %x30-35 ; 250-255 51 | 52 | reg-name = *( unreserved / pct-encoded / sub-delims ) 53 | 54 | path = path-abempty ; begins with "/" or is empty 55 | / path-absolute ; begins with "/" but not "//" 56 | / path-noscheme ; begins with a non-colon segment 57 | / path-rootless ; begins with a segment 58 | / path-empty ; zero characters 59 | 60 | path-abempty = *( "/" segment ) 61 | path-absolute = "/" [ segment-nz *( "/" segment ) ] 62 | path-noscheme = segment-nz-nc *( "/" segment ) 63 | path-rootless = segment-nz *( "/" segment ) 64 | path-empty = 0 65 | 66 | segment = *pchar 67 | segment-nz = 1*pchar 68 | segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) 69 | ; non-zero-length segment without any colon ":" 70 | 71 | pchar = unreserved / pct-encoded / sub-delims / ":" / "@" 72 | 73 | query = *( pchar / "/" / "?" ) 74 | 75 | fragment = *( pchar / "/" / "?" ) 76 | 77 | pct-encoded = "%" HEXDIG HEXDIG 78 | 79 | unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 80 | reserved = gen-delims / sub-delims 81 | gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" 82 | sub-delims = "!" / "$" / "&" / "'" / "(" / ")" 83 | / "*" / "+" / "," / ";" / "=" 84 | -------------------------------------------------------------------------------- /src/schema_contrib/core.clj: -------------------------------------------------------------------------------- 1 | (ns schema-contrib.core 2 | (:import [java.util Locale]) 3 | (:require [clojure.java.io :as io] 4 | [clojure.string :as string] 5 | [instaparse.core :as instaparse] 6 | [schema.core :as schema])) 7 | 8 | ;; Country (ISO 3166-1 alpha-2 country codes) 9 | 10 | (def countries 11 | (apply hash-set (Locale/getISOCountries))) 12 | 13 | (defn countries-contains? 14 | [c] 15 | (contains? countries (string/upper-case c))) 16 | 17 | (def Country 18 | (schema/pred countries-contains? 'Country)) 19 | 20 | (def countries-keywords 21 | (->> (Locale/getISOCountries) 22 | (map keyword) 23 | (apply hash-set))) 24 | 25 | (defn countries-keywords-contains? 26 | [ck] 27 | (contains? 28 | countries-keywords 29 | (-> ck 30 | name 31 | string/upper-case 32 | keyword))) 33 | 34 | (def Country-Keyword 35 | (schema/pred countries-keywords-contains? 'Country-Keyword)) 36 | 37 | ;; Date (ISO 8601) 38 | 39 | (def date-parser 40 | (instaparse/parser 41 | (io/resource "date.abnf") 42 | :input-format :abnf)) 43 | 44 | (defn date? 45 | [d] 46 | (-> d 47 | (date-parser :start :date) 48 | instaparse/failure? 49 | not)) 50 | 51 | (def Date 52 | (schema/pred date? 'Date)) 53 | 54 | (defn iso-date-time? 55 | [i] 56 | (-> i 57 | (date-parser :start :iso-date-time) 58 | instaparse/failure? 59 | not)) 60 | 61 | (def ISO-Date-Time 62 | (schema/pred iso-date-time? 'ISO-Date-Time)) 63 | 64 | (defn time? 65 | [t] 66 | (-> t 67 | (date-parser :start :time) 68 | instaparse/failure? 69 | not)) 70 | 71 | (def Time 72 | (schema/pred time? 'Time)) 73 | 74 | ;; Email 75 | 76 | (def email-parser 77 | (instaparse/parser 78 | (io/resource "email.abnf") 79 | :input-format :abnf)) 80 | 81 | (defn email? 82 | [e] 83 | (-> e 84 | email-parser 85 | instaparse/failure? 86 | not)) 87 | 88 | (def Email 89 | (schema/pred email? 'Email)) 90 | 91 | ;; Language 92 | 93 | (def languages 94 | (apply hash-set (Locale/getISOLanguages))) 95 | 96 | (defn languages-contains? 97 | [l] 98 | (contains? languages (string/lower-case l))) 99 | 100 | (def Language 101 | (schema/pred languages-contains? 'Language)) 102 | 103 | (def languages-keywords 104 | (->> (Locale/getISOLanguages) 105 | (map keyword) 106 | (apply hash-set))) 107 | 108 | (defn languages-keywords-contains? 109 | [lk] 110 | (if (keyword? lk) 111 | (contains? 112 | languages-keywords 113 | (-> lk 114 | name 115 | string/lower-case 116 | keyword)) 117 | false)) 118 | 119 | (def Language-Keyword 120 | (schema/pred languages-keywords-contains? 'Language-Keyword)) 121 | 122 | ;; URI 123 | 124 | (def uri-parser 125 | (instaparse/parser 126 | (io/resource "uri.abnf") 127 | :input-format :abnf)) 128 | 129 | (defn uri? 130 | [u] 131 | (-> u 132 | uri-parser 133 | instaparse/failure? 134 | not)) 135 | 136 | (def URI 137 | (schema/pred uri? 'URI)) 138 | 139 | (defn uri-reference? 140 | [u] 141 | (-> u 142 | (uri-parser :start :URI-reference) 143 | instaparse/failure? 144 | not)) 145 | 146 | (def URI-Reference 147 | (schema/pred uri-reference? 'URI-Reference)) 148 | -------------------------------------------------------------------------------- /resources/date.abnf: -------------------------------------------------------------------------------- 1 | ; http://www.ietf.org/rfc/rfc3339.txt 2 | ; and http://www.odata.org/documentation/odata-version-3-0/abnf/ 3 | 4 | oneToNine = "01" / "02" / "03" / "04" / "05" / "06" / "07" / "08" / "09" 5 | zeroToNine = "00" / oneToNine 6 | 7 | oneToTwelve = oneToNine / "1" ( "0" / "1" / "2" ) 8 | 9 | oneToThirteen = oneToTwelve / "13" 10 | 11 | zeroToFiftyNine = zeroToNine / ( "1" / "2" / "3" / "4" / "5" ) DIGIT 12 | 13 | zeroToSixty = zeroToNine / ( "1" / "2" / "3" / "4" / "5" ) DIGIT / "60" 14 | 15 | oneToThirtyOne = oneToNine / ( "1" / "2" ) DIGIT / "30" / "31" 16 | 17 | zeroToTwentyFour = zeroToNine / "1" DIGIT / "2" ( "0" / "1" / "2" / "3" / "4" ) 18 | 19 | date-century = 2DIGIT ; 00-99 20 | date-decade = DIGIT ; 0-9 21 | date-subdecade = DIGIT ; 0-9 22 | date-year = date-decade date-subdecade 23 | date-fullyear = date-century date-year 24 | date-month = oneToTwelve ; 01-12 25 | date-wday = DIGIT ; 1-7 ; 1 is Monday, 7 is Sunday 26 | date-mday = oneToThirtyOne ; 01-28, 01-29, 01-30, 01-31 based on 27 | ; month/year 28 | date-yday = 3DIGIT ; 001-365, 001-366 based on year 29 | date-week = 2DIGIT ; 01-52, 01-53 based on year 30 | 31 | datepart-fullyear = [date-century] date-year ["-"] 32 | datepart-ptyear = "-" [date-subdecade ["-"]] 33 | datepart-wkyear = datepart-ptyear / datepart-fullyear 34 | 35 | dateopt-century = "-" / date-century 36 | dateopt-fullyear = "-" / datepart-fullyear 37 | dateopt-year = "-" / (date-year ["-"]) 38 | dateopt-month = "-" / (date-month ["-"]) 39 | dateopt-week = "-" / (date-week ["-"]) 40 | 41 | datespec-full = datepart-fullyear date-month ["-"] date-mday 42 | datespec-year = date-century / dateopt-century date-year 43 | datespec-month = "-" dateopt-year date-month [["-"] date-mday] 44 | datespec-mday = "--" dateopt-month date-mday 45 | datespec-week = datepart-wkyear "W" 46 | (date-week / dateopt-week date-wday) 47 | datespec-wday = "---" date-wday 48 | datespec-yday = dateopt-fullyear date-yday 49 | 50 | date = datespec-full / datespec-year 51 | / datespec-month / 52 | datespec-mday / datespec-week / datespec-wday / datespec-yday 53 | 54 | time-hour = zeroToTwentyFour ; 00-24 55 | time-minute = zeroToFiftyNine ; 00-59 56 | time-second = zeroToSixty ; 00-58, 00-59, 00-60 based on 57 | ; leap-second rules 58 | time-fraction = ("," / ".") 1*DIGIT 59 | time-numoffset = 0*1SP ("+" / "-") time-hour [[":"] time-minute] 60 | time-zone = "Z" / time-numoffset 61 | 62 | timeopt-hour = "-" / (time-hour [":"]) 63 | timeopt-minute = "-" / (time-minute [":"]) 64 | 65 | timespec-hour = time-hour [[":"] time-minute [[":"] time-second]] 66 | timespec-minute = timeopt-hour time-minute [[":"] time-second] 67 | timespec-second = "-" timeopt-minute time-second 68 | timespec-base = timespec-hour / timespec-minute / timespec-second 69 | 70 | time = timespec-base [time-fraction] [time-zone] 71 | 72 | date-time-sep = "T" / SP 73 | 74 | iso-date-time = date date-time-sep time 75 | 76 | dur-second = 1*DIGIT "S" 77 | dur-minute = 1*DIGIT "M" [dur-second] 78 | dur-hour = 1*DIGIT "H" [dur-minute] 79 | dur-time = "T" (dur-hour / dur-minute / dur-second) 80 | dur-day = 1*DIGIT "D" 81 | dur-week = 1*DIGIT "W" 82 | dur-month = 1*DIGIT "M" [dur-day] 83 | dur-year = 1*DIGIT "Y" [dur-month] 84 | dur-date = (dur-day / dur-month / dur-year) [dur-time] 85 | 86 | duration = "P" (dur-date / dur-time / dur-week) 87 | -------------------------------------------------------------------------------- /test/schema_contrib/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns schema-contrib.core-test 2 | (:import (clojure.lang ExceptionInfo)) 3 | (:require [clojure.string :as string] 4 | [clojure.test :refer :all] 5 | [clojure.test.check.generators :as gen] 6 | [schema.core :as schema] 7 | [schema-contrib.core :refer :all] 8 | [schema-contrib.gen :as scgen])) 9 | 10 | (defn valid 11 | [s v] 12 | (try 13 | (= (schema/validate s v) v) 14 | (catch ExceptionInfo e false))) 15 | 16 | (defn invalid 17 | [s v] 18 | (not (valid s v))) 19 | 20 | (deftest country-test 21 | (testing "Generated countries pass validation." 22 | (->> (gen/sample scgen/country 100) 23 | (map #(is (valid Country %))) 24 | (doall))) 25 | (is (invalid Country "asdf")) 26 | (is (invalid Country "")) 27 | (is (invalid Country :en))) 28 | 29 | (deftest country-keyword-test 30 | (testing "Generated country keywords pass validation." 31 | (->> (gen/sample scgen/country-keyword 100) 32 | (map #(is (valid Country-Keyword %))) 33 | (doall))) 34 | (is (invalid Country-Keyword :asdf)) 35 | (is (invalid Country-Keyword "en"))) 36 | 37 | (deftest date-test 38 | (is (valid Date "2014-04-01")) 39 | (is (valid Date "2014-W14")) 40 | (is (valid Date "2014-W14-2")) 41 | (is (valid Date "2014-091")) 42 | (is (invalid Date "2014-04-01T20:17:35+00:00")) 43 | (is (invalid Date "2014-04-01T20:17:35Z"))) 44 | 45 | (deftest email-test 46 | (testing "http://en.wikipedia.org/wiki/Email_address" 47 | (is (valid Email "niceandsimple@example.com")) 48 | (is (valid Email "very.common@example.com")) 49 | (is (valid Email "a.little.lengthy.but.fine@dept.example.com")) 50 | (is (valid Email "disposable.style.email.with+symbol@example.com")) 51 | (is (valid Email "other.email-with-dash@example.com")) 52 | (is (valid Email "user@[IPv6:2001:db8:1ff::a0b:dbd0]")) 53 | (is (valid Email "\"much.more unusual\"@example.com")) 54 | (is (valid Email "\"very.unusual.@.unusual.com\"@example.com")) 55 | (is (valid Email (str "\"very.(),:;<>[]\\\".VERY.\\\"very@\\\\ \\\"very\\\\" 56 | ".unusual\"@strange.example.com"))) 57 | (is (valid Email "postbox@com")) 58 | (is (valid Email "admin@mailserver1")) 59 | (is (valid Email "!#$%&'*+-/=?^_`{}|~@example.org")) 60 | (is (valid Email "\"()<>[]:,;@\\\"!#$%&'*+-/=?^_`{}| ~.a\"@example.org")) 61 | (is (valid Email "\" \"@example.org")) 62 | (is (valid Email "üñîçøðé@example.com")) 63 | (is (valid Email "üñîçøðé@üñîçøðé.com")) 64 | (is (valid Email "Pelé@example.com")) 65 | (is (valid Email "δοκιμή@παράδειγμα.δοκιμή")) 66 | (is (valid Email "甲斐@黒川.日本")) 67 | (is (valid Email "чебурашка@ящик-с-апельсинами.рф")) 68 | (is (invalid Email "Abc.example.com")) 69 | (is (invalid Email "A@b@c@example.com")) 70 | (is (invalid Email "a\"b(c)d,e:f;gi[j\\k]l@example.com")) 71 | (is (invalid Email "just\"not\"right@example.com")) 72 | (is (invalid Email "this is\"not\\allowed@example.com")) 73 | (is (invalid Email "this\\ still\\\"not\\\\allowed@example.com")))) 74 | 75 | (deftest language-test 76 | (testing "Generated languages pass validation." 77 | (->> (gen/sample scgen/language 100) 78 | (map #(is (valid Language %))) 79 | (doall))) 80 | (is (invalid Language "asdf")) 81 | (is (invalid Language "")) 82 | (is (invalid Language :en))) 83 | 84 | (deftest language-keyword-test 85 | (testing "Generated languages pass validation." 86 | (->> (gen/sample scgen/language-keyword 100) 87 | (map #(is (valid Language-Keyword %))) 88 | (doall))) 89 | (is (invalid Language-Keyword :asdf)) 90 | (is (invalid Language-Keyword "en"))) 91 | 92 | (deftest iso-date-time-test 93 | (is (valid ISO-Date-Time "2014-04-01T22:17:35+02:00")) 94 | (is (valid ISO-Date-Time "2014-04-01T20:17:35Z")) 95 | (is (valid ISO-Date-Time "2007-04-05T14:59")) 96 | (is (valid ISO-Date-Time "2007-04-05T14:30Z")) 97 | (is (valid ISO-Date-Time "2007-04-05T12:30-02:00")) 98 | 99 | (is (valid ISO-Date-Time "2014-04-01 20:17:35+00:00")) 100 | (is (valid ISO-Date-Time "2014-04-01 21:17:35Z")) 101 | (is (valid ISO-Date-Time "2007-04-05 14:30")) 102 | (is (valid ISO-Date-Time "2007-04-05 14:30Z")) 103 | (is (valid ISO-Date-Time "2007-04-05 12:30-02:00")) 104 | 105 | (is (not (valid ISO-Date-Time "2007-13-05T12:30-02:00"))) 106 | (is (not (valid ISO-Date-Time "2007-12-45T12:30-02:00"))) 107 | (is (not (valid ISO-Date-Time "2007-01-05T25:30-02:00"))) 108 | (is (not (valid ISO-Date-Time "2007-01-05T12:61-02:00"))) 109 | (is (not (valid ISO-Date-Time "2007-01-05T12:30-25:00"))) 110 | (is (not (valid ISO-Date-Time "2007-01-05T12:30-02:61"))) 111 | 112 | (is (not (valid ISO-Date-Time "2007-13-05 12:30-02:00"))) 113 | (is (not (valid ISO-Date-Time "2007-12-45 12:30-02:00"))) 114 | (is (not (valid ISO-Date-Time "2007-01-05 25:30-02:00"))) 115 | (is (not (valid ISO-Date-Time "2007-01-05 12:61-02:00"))) 116 | (is (not (valid ISO-Date-Time "2007-01-05 12:30-25:00"))) 117 | (is (not (valid ISO-Date-Time "2007-01-05 12:30-02:61"))) 118 | 119 | 120 | (is (invalid ISO-Date-Time "2014-04-01")) 121 | (is (invalid ISO-Date-Time "2014-W14")) 122 | (is (invalid ISO-Date-Time "2014-W14-2")) 123 | (is (invalid ISO-Date-Time "2014-091"))) 124 | 125 | (deftest time-test 126 | (is (valid Time "134730")) 127 | (is (valid Time "13:47:30")) 128 | (is (valid Time "00:00")) 129 | (is (valid Time "24:00")) 130 | (is (valid Time "14:30,5")) 131 | (is (valid Time "1430,5")) 132 | (is (valid Time "14:30.5")) 133 | (is (valid Time "1430.5"))) 134 | 135 | ;; http://en.wikipedia.org/wiki/URI#Examples_of_URI_references 136 | (def absolute-uris 137 | ["http://example.org/absolute/URI/with/absolute/path/to/resource.txt" 138 | "ftp://example.org/resource.txt" 139 | "urn:issn:1535-3613" 140 | "http://en.wikipedia.org/wiki/URI#Examples_of_URI_references"]) 141 | 142 | (def uri-references 143 | ["//example.org/scheme-relative/URI/with/absolute/path/to/resource.txt" 144 | "/relative/URI/with/absolute/path/to/resource.txt" 145 | "relative/path/to/resource.txt" 146 | "../../../resource.txt" 147 | "./resource.txt#frag01" 148 | "resource.txt" 149 | "#frag01" 150 | ""]) 151 | 152 | (deftest uri-test 153 | (doall (map #(is (valid URI %)) absolute-uris)) 154 | (doall (map #(is (invalid URI %)) uri-references))) 155 | 156 | (deftest uri-reference-test 157 | (doall (map #(is (valid URI-Reference %)) absolute-uris)) 158 | (doall (map #(is (valid URI-Reference %)) uri-references))) 159 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 2 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 3 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 4 | 5 | 1. DEFINITIONS 6 | 7 | "Contribution" means: 8 | 9 | a) in the case of the initial Contributor, the initial code and 10 | documentation distributed under this Agreement, and 11 | 12 | b) in the case of each subsequent Contributor: 13 | 14 | i) changes to the Program, and 15 | 16 | ii) additions to the Program; 17 | 18 | where such changes and/or additions to the Program originate from and are 19 | distributed by that particular Contributor. A Contribution 'originates' from 20 | a Contributor if it was added to the Program by such Contributor itself or 21 | anyone acting on such Contributor's behalf. Contributions do not include 22 | additions to the Program which: (i) are separate modules of software 23 | distributed in conjunction with the Program under their own license 24 | agreement, and (ii) are not derivative works of the Program. 25 | 26 | "Contributor" means any person or entity that distributes the Program. 27 | 28 | "Licensed Patents" mean patent claims licensable by a Contributor which are 29 | necessarily infringed by the use or sale of its Contribution alone or when 30 | combined with the Program. 31 | 32 | "Program" means the Contributions distributed in accordance with this 33 | Agreement. 34 | 35 | "Recipient" means anyone who receives the Program under this Agreement, 36 | including all Contributors. 37 | 38 | 2. GRANT OF RIGHTS 39 | 40 | a) Subject to the terms of this Agreement, each Contributor hereby grants 41 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 42 | reproduce, prepare derivative works of, publicly display, publicly perform, 43 | distribute and sublicense the Contribution of such Contributor, if any, and 44 | such derivative works, in source code and object code form. 45 | 46 | b) Subject to the terms of this Agreement, each Contributor hereby grants 47 | Recipient a non-exclusive, worldwide, royalty-free patent license under 48 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 49 | transfer the Contribution of such Contributor, if any, in source code and 50 | object code form. This patent license shall apply to the combination of the 51 | Contribution and the Program if, at the time the Contribution is added by the 52 | Contributor, such addition of the Contribution causes such combination to be 53 | covered by the Licensed Patents. The patent license shall not apply to any 54 | other combinations which include the Contribution. No hardware per se is 55 | licensed hereunder. 56 | 57 | c) Recipient understands that although each Contributor grants the licenses 58 | to its Contributions set forth herein, no assurances are provided by any 59 | Contributor that the Program does not infringe the patent or other 60 | intellectual property rights of any other entity. Each Contributor disclaims 61 | any liability to Recipient for claims brought by any other entity based on 62 | infringement of intellectual property rights or otherwise. As a condition to 63 | exercising the rights and licenses granted hereunder, each Recipient hereby 64 | assumes sole responsibility to secure any other intellectual property rights 65 | needed, if any. For example, if a third party patent license is required to 66 | allow Recipient to distribute the Program, it is Recipient's responsibility 67 | to acquire that license before distributing the Program. 68 | 69 | d) Each Contributor represents that to its knowledge it has sufficient 70 | copyright rights in its Contribution, if any, to grant the copyright license 71 | set forth in this Agreement. 72 | 73 | 3. REQUIREMENTS 74 | 75 | A Contributor may choose to distribute the Program in object code form under 76 | its own license agreement, provided that: 77 | 78 | a) it complies with the terms and conditions of this Agreement; and 79 | 80 | b) its license agreement: 81 | 82 | i) effectively disclaims on behalf of all Contributors all warranties and 83 | conditions, express and implied, including warranties or conditions of title 84 | and non-infringement, and implied warranties or conditions of merchantability 85 | and fitness for a particular purpose; 86 | 87 | ii) effectively excludes on behalf of all Contributors all liability for 88 | damages, including direct, indirect, special, incidental and consequential 89 | damages, such as lost profits; 90 | 91 | iii) states that any provisions which differ from this Agreement are offered 92 | by that Contributor alone and not by any other party; and 93 | 94 | iv) states that source code for the Program is available from such 95 | Contributor, and informs licensees how to obtain it in a reasonable manner on 96 | or through a medium customarily used for software exchange. 97 | 98 | When the Program is made available in source code form: 99 | 100 | a) it must be made available under this Agreement; and 101 | 102 | b) a copy of this Agreement must be included with each copy of the Program. 103 | 104 | Contributors may not remove or alter any copyright notices contained within 105 | the Program. 106 | 107 | Each Contributor must identify itself as the originator of its Contribution, 108 | if any, in a manner that reasonably allows subsequent Recipients to identify 109 | the originator of the Contribution. 110 | 111 | 4. COMMERCIAL DISTRIBUTION 112 | 113 | Commercial distributors of software may accept certain responsibilities with 114 | respect to end users, business partners and the like. While this license is 115 | intended to facilitate the commercial use of the Program, the Contributor who 116 | includes the Program in a commercial product offering should do so in a 117 | manner which does not create potential liability for other Contributors. 118 | Therefore, if a Contributor includes the Program in a commercial product 119 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend 120 | and indemnify every other Contributor ("Indemnified Contributor") against any 121 | losses, damages and costs (collectively "Losses") arising from claims, 122 | lawsuits and other legal actions brought by a third party against the 123 | Indemnified Contributor to the extent caused by the acts or omissions of such 124 | Commercial Contributor in connection with its distribution of the Program in 125 | a commercial product offering. The obligations in this section do not apply 126 | to any claims or Losses relating to any actual or alleged intellectual 127 | property infringement. In order to qualify, an Indemnified Contributor must: 128 | a) promptly notify the Commercial Contributor in writing of such claim, and 129 | b) allow the Commercial Contributor tocontrol, and cooperate with the 130 | Commercial Contributor in, the defense and any related settlement 131 | negotiations. The Indemnified Contributor may participate in any such claim 132 | at its own expense. 133 | 134 | For example, a Contributor might include the Program in a commercial product 135 | offering, Product X. That Contributor is then a Commercial Contributor. If 136 | that Commercial Contributor then makes performance claims, or offers 137 | warranties related to Product X, those performance claims and warranties are 138 | such Commercial Contributor's responsibility alone. Under this section, the 139 | Commercial Contributor would have to defend claims against the other 140 | Contributors related to those performance claims and warranties, and if a 141 | court requires any other Contributor to pay any damages as a result, the 142 | Commercial Contributor must pay those damages. 143 | 144 | 5. NO WARRANTY 145 | 146 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON 147 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 148 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR 149 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A 150 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the 151 | appropriateness of using and distributing the Program and assumes all risks 152 | associated with its exercise of rights under this Agreement , including but 153 | not limited to the risks and costs of program errors, compliance with 154 | applicable laws, damage to or loss of data, programs or equipment, and 155 | unavailability or interruption of operations. 156 | 157 | 6. DISCLAIMER OF LIABILITY 158 | 159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 160 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 161 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 162 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 163 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 164 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 165 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 166 | OF SUCH DAMAGES. 167 | 168 | 7. GENERAL 169 | 170 | If any provision of this Agreement is invalid or unenforceable under 171 | applicable law, it shall not affect the validity or enforceability of the 172 | remainder of the terms of this Agreement, and without further action by the 173 | parties hereto, such provision shall be reformed to the minimum extent 174 | necessary to make such provision valid and enforceable. 175 | 176 | If Recipient institutes patent litigation against any entity (including a 177 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 178 | (excluding combinations of the Program with other software or hardware) 179 | infringes such Recipient's patent(s), then such Recipient's rights granted 180 | under Section 2(b) shall terminate as of the date such litigation is filed. 181 | 182 | All Recipient's rights under this Agreement shall terminate if it fails to 183 | comply with any of the material terms or conditions of this Agreement and 184 | does not cure such failure in a reasonable period of time after becoming 185 | aware of such noncompliance. If all Recipient's rights under this Agreement 186 | terminate, Recipient agrees to cease use and distribution of the Program as 187 | soon as reasonably practicable. However, Recipient's obligations under this 188 | Agreement and any licenses granted by Recipient relating to the Program shall 189 | continue and survive. 190 | 191 | Everyone is permitted to copy and distribute copies of this Agreement, but in 192 | order to avoid inconsistency the Agreement is copyrighted and may only be 193 | modified in the following manner. The Agreement Steward reserves the right to 194 | publish new versions (including revisions) of this Agreement from time to 195 | time. No one other than the Agreement Steward has the right to modify this 196 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 197 | Eclipse Foundation may assign the responsibility to serve as the Agreement 198 | Steward to a suitable separate entity. Each new version of the Agreement will 199 | be given a distinguishing version number. The Program (including 200 | Contributions) may always be distributed subject to the version of the 201 | Agreement under which it was received. In addition, after a new version of 202 | the Agreement is published, Contributor may elect to distribute the Program 203 | (including its Contributions) under the new version. Except as expressly 204 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 205 | licenses to the intellectual property of any Contributor under this 206 | Agreement, whether expressly, by implication, estoppel or otherwise. All 207 | rights in the Program not expressly granted under this Agreement are 208 | reserved. 209 | 210 | This Agreement is governed by the laws of the State of Washington and the 211 | intellectual property laws of the United States of America. No party to this 212 | Agreement will bring a legal action under this Agreement more than one year 213 | after the cause of action arose. Each party waives its rights to a jury trial 214 | in any resulting litigation. 215 | -------------------------------------------------------------------------------- /resources/email.abnf: -------------------------------------------------------------------------------- 1 | ; http://tools.ietf.org/html/rfc5322#section-3.4.1 2 | 3 | addr-spec = local-part "@" domain 4 | 5 | local-part = dot-atom / quoted-string / obs-local-part 6 | 7 | domain = dot-atom / domain-literal / obs-domain 8 | 9 | domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS] 10 | 11 | dtext = %d33-90 / ; Printable US-ASCII 12 | %d94-126 / ; characters not including 13 | obs-dtext ; "[", "]", or "\" 14 | 15 | ; http://tools.ietf.org/html/rfc5322#section-3.2.3 16 | 17 | atext = ALPHA / DIGIT / ; Printable US-ASCII 18 | "!" / "#" / ; characters not including 19 | "$" / "%" / ; specials. Used for atoms. 20 | "&" / "'" / 21 | "*" / "+" / 22 | "-" / "/" / 23 | "=" / "?" / 24 | "^" / "_" / 25 | "`" / "{" / 26 | "|" / "}" / 27 | "~" 28 | 29 | atom = [CFWS] 1*atext [CFWS] 30 | 31 | dot-atom-text = 1*atext *("." 1*atext) 32 | 33 | dot-atom = [CFWS] dot-atom-text [CFWS] 34 | 35 | specials = "(" / ")" / ; Special characters that do 36 | "<" / ">" / ; not appear in atext 37 | "[" / "]" / 38 | ":" / ";" / 39 | "@" / "\" / 40 | "," / "." / 41 | DQUOTE 42 | 43 | ; http://tools.ietf.org/html/rfc5322#section-3.2.4 44 | 45 | qtext = %d33 / ; Printable US-ASCII 46 | %d35-91 / ; characters not including 47 | %d93-126 / ; "\" or the quote character 48 | obs-qtext 49 | 50 | qcontent = qtext / quoted-pair 51 | 52 | quoted-string = [CFWS] 53 | DQUOTE *([FWS] qcontent) [FWS] DQUOTE 54 | [CFWS] 55 | 56 | ; http://tools.ietf.org/html/rfc5322#section-3.2.2 57 | 58 | FWS = ([*WSP CRLF] 1*WSP) / obs-FWS 59 | ; Folding white space 60 | 61 | ctext = %d33-39 / ; Printable US-ASCII 62 | %d42-91 / ; characters not including 63 | %d93-126 / ; "(", ")", or "\" 64 | obs-ctext 65 | 66 | ccontent = ctext / quoted-pair / comment 67 | 68 | comment = "(" *([FWS] ccontent) [FWS] ")" 69 | 70 | CFWS = (1*([FWS] comment) [FWS]) / FWS 71 | 72 | ; http://tools.ietf.org/html/rfc5322#section-4.4 73 | 74 | obs-angle-addr = [CFWS] "<" obs-route addr-spec ">" [CFWS] 75 | 76 | obs-route = obs-domain-list ":" 77 | 78 | obs-domain-list = *(CFWS / ",") "@" domain 79 | *("," [CFWS] ["@" domain]) 80 | 81 | obs-mbox-list = *([CFWS] ",") mailbox *("," [mailbox / CFWS]) 82 | 83 | obs-addr-list = *([CFWS] ",") address *("," [address / CFWS]) 84 | 85 | obs-group-list = 1*([CFWS] ",") [CFWS] 86 | 87 | obs-local-part = word *("." word) 88 | 89 | obs-domain = atom *("." atom) 90 | 91 | obs-dtext = obs-NO-WS-CTL / quoted-pair 92 | 93 | ; http://tools.ietf.org/html/rfc5322#section-3.4 94 | 95 | address = mailbox / group 96 | 97 | mailbox = name-addr / addr-spec 98 | 99 | name-addr = [display-name] angle-addr 100 | 101 | angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / 102 | obs-angle-addr 103 | 104 | group = display-name ":" [group-list] ";" [CFWS] 105 | 106 | display-name = phrase 107 | 108 | mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list 109 | 110 | address-list = (address *("," address)) / obs-addr-list 111 | 112 | group-list = mailbox-list / CFWS / obs-group-list 113 | 114 | ; http://tools.ietf.org/html/rfc5322#section-4.1 115 | 116 | obs-NO-WS-CTL = %d1-8 / ; US-ASCII control 117 | %d11 / ; characters that do not 118 | %d12 / ; include the carriage 119 | %d14-31 / ; return, line feed, and 120 | %d127 ; white space characters 121 | 122 | obs-ctext = obs-NO-WS-CTL 123 | 124 | obs-qtext = obs-NO-WS-CTL 125 | 126 | obs-utext = %d0 / obs-NO-WS-CTL / VCHAR 127 | 128 | obs-qp = "\" (%d0 / obs-NO-WS-CTL / LF / CR) 129 | 130 | obs-body = *((*LF *CR *((%d0 / text) *LF *CR)) / CRLF) 131 | 132 | obs-unstruct = *((*LF *CR *(obs-utext *LF *CR)) / FWS) 133 | 134 | obs-phrase = word *(word / "." / CFWS) 135 | 136 | obs-phrase-list = [phrase / CFWS] *("," [phrase / CFWS]) 137 | 138 | ; http://tools.ietf.org/html/rfc5322#section-3.2.5 139 | 140 | word = atom / quoted-string 141 | 142 | phrase = 1*word / obs-phrase 143 | 144 | unstructured = (*([FWS] VCHAR) *WSP) / obs-unstruct 145 | 146 | ; http://tools.ietf.org/html/rfc5322#section-3.2.1 147 | 148 | quoted-pair = ("\" (VCHAR / WSP)) / obs-qp 149 | 150 | ; http://tools.ietf.org/html/rfc5322#section-4.2 151 | 152 | obs-FWS = 1*WSP *(CRLF 1*WSP) 153 | 154 | ; http://tools.ietf.org/html/rfc5322#section-3.5 155 | 156 | message = (fields / obs-fields) 157 | [CRLF body] 158 | 159 | body = (*(*998text CRLF) *998text) / obs-body 160 | 161 | text = %d1-9 / ; Characters excluding CR 162 | %d11 / ; and LF 163 | %d12 / 164 | %d14-127 165 | 166 | ; http://tools.ietf.org/html/rfc5322#section-3.6 167 | 168 | fields = *(trace 169 | *optional-field / 170 | *(resent-date / 171 | resent-from / 172 | resent-sender / 173 | resent-to / 174 | resent-cc / 175 | resent-bcc / 176 | resent-msg-id)) 177 | *(orig-date / 178 | from / 179 | sender / 180 | reply-to / 181 | to / 182 | cc / 183 | bcc / 184 | message-id / 185 | in-reply-to / 186 | references / 187 | subject / 188 | comments / 189 | keywords / 190 | optional-field) 191 | 192 | ; http://tools.ietf.org/html/rfc5322#section-4.5 193 | 194 | obs-fields = *(obs-return / 195 | obs-received / 196 | obs-orig-date / 197 | obs-from / 198 | obs-sender / 199 | obs-reply-to / 200 | obs-to / 201 | obs-cc / 202 | obs-bcc / 203 | obs-message-id / 204 | obs-in-reply-to / 205 | obs-references / 206 | obs-subject / 207 | obs-comments / 208 | obs-keywords / 209 | obs-resent-date / 210 | obs-resent-from / 211 | obs-resent-send / 212 | obs-resent-rply / 213 | obs-resent-to / 214 | obs-resent-cc / 215 | obs-resent-bcc / 216 | obs-resent-mid / 217 | obs-optional) 218 | 219 | ; http://tools.ietf.org/html/rfc5322#section-3.6.7 220 | 221 | trace = [return] 222 | 1*received 223 | 224 | return = "Return-Path:" path CRLF 225 | 226 | path = angle-addr / ([CFWS] "<" [CFWS] ">" [CFWS]) 227 | 228 | received = "Received:" *received-token ";" date-time CRLF 229 | 230 | received-token = word / angle-addr / addr-spec / domain 231 | 232 | ; http://tools.ietf.org/html/rfc5322#section-3.3 233 | 234 | date-time = [ day-of-week "," ] date time [CFWS] 235 | 236 | day-of-week = ([FWS] day-name) / obs-day-of-week 237 | 238 | day-name = "Mon" / "Tue" / "Wed" / "Thu" / 239 | "Fri" / "Sat" / "Sun" 240 | 241 | date = day month year 242 | 243 | day = ([FWS] 1*2DIGIT FWS) / obs-day 244 | 245 | month = "Jan" / "Feb" / "Mar" / "Apr" / 246 | "May" / "Jun" / "Jul" / "Aug" / 247 | "Sep" / "Oct" / "Nov" / "Dec" 248 | 249 | year = (FWS 4*DIGIT FWS) / obs-year 250 | 251 | time = time-of-day zone 252 | 253 | time-of-day = hour ":" minute [ ":" second ] 254 | 255 | hour = 2DIGIT / obs-hour 256 | 257 | minute = 2DIGIT / obs-minute 258 | 259 | second = 2DIGIT / obs-second 260 | 261 | zone = (FWS ( "+" / "-" ) 4DIGIT) / obs-zone 262 | 263 | ; http://tools.ietf.org/html/rfc5322#section-4.3 264 | 265 | obs-day-of-week = [CFWS] day-name [CFWS] 266 | 267 | obs-day = [CFWS] 1*2DIGIT [CFWS] 268 | 269 | obs-year = [CFWS] 2*DIGIT [CFWS] 270 | 271 | obs-hour = [CFWS] 2DIGIT [CFWS] 272 | 273 | obs-minute = [CFWS] 2DIGIT [CFWS] 274 | 275 | obs-second = [CFWS] 2DIGIT [CFWS] 276 | 277 | obs-zone = "UT" / "GMT" / ; Universal Time 278 | ; North American UT 279 | ; offsets 280 | "EST" / "EDT" / ; Eastern: - 5/ - 4 281 | "CST" / "CDT" / ; Central: - 6/ - 5 282 | "MST" / "MDT" / ; Mountain: - 7/ - 6 283 | "PST" / "PDT" / ; Pacific: - 8/ - 7 284 | %d65-73 / ; Military zones - "A" 285 | %d75-90 / ; through "I" and "K" 286 | %d97-105 / ; through "Z", both 287 | %d107-122 ; upper and lower case 288 | 289 | ; http://tools.ietf.org/html/rfc5322#section-3.6.8 290 | 291 | optional-field = field-name ":" unstructured CRLF 292 | 293 | field-name = 1*ftext 294 | 295 | ftext = %d33-57 / ; Printable US-ASCII 296 | %d59-126 ; characters not including 297 | ; ":". 298 | 299 | ; http://tools.ietf.org/html/rfc5322#section-3.6.6 300 | ; https://tools.ietf.org/html/rfc6854#section-2.2 301 | 302 | resent-date = "Resent-Date:" date-time CRLF 303 | 304 | resent-from = "Resent-From:" (mailbox-list / address-list) CRLF 305 | 306 | resent-sender = "Resent-Sender:" (mailbox / address) CRLF 307 | 308 | resent-to = "Resent-To:" address-list CRLF 309 | 310 | resent-cc = "Resent-Cc:" address-list CRLF 311 | 312 | resent-bcc = "Resent-Bcc:" [address-list / CFWS] CRLF 313 | 314 | resent-msg-id = "Resent-Message-ID:" msg-id CRLF 315 | 316 | ; http://tools.ietf.org/html/rfc5322#section-3.6.4 317 | 318 | message-id = "Message-ID:" msg-id CRLF 319 | 320 | in-reply-to = "In-Reply-To:" 1*msg-id CRLF 321 | 322 | references = "References:" 1*msg-id CRLF 323 | 324 | msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS] 325 | 326 | id-left = dot-atom-text / obs-id-left 327 | 328 | id-right = dot-atom-text / no-fold-literal / obs-id-right 329 | 330 | no-fold-literal = "[" *dtext "]" 331 | 332 | ; http://tools.ietf.org/html/rfc5322#section-3.6.4 333 | 334 | obs-message-id = "Message-ID" *WSP ":" msg-id CRLF 335 | 336 | obs-in-reply-to = "In-Reply-To" *WSP ":" *(phrase / msg-id) CRLF 337 | 338 | obs-references = "References" *WSP ":" *(phrase / msg-id) CRLF 339 | 340 | obs-id-left = local-part 341 | 342 | obs-id-right = domain 343 | 344 | ; http://tools.ietf.org/html/rfc5322#section-3.6.1 345 | 346 | orig-date = "Date:" date-time CRLF 347 | 348 | ; https://tools.ietf.org/html/rfc6854#section-2.1 349 | 350 | from = "From:" (mailbox-list / address-list) CRLF 351 | 352 | sender = "Sender:" (mailbox / address) CRLF 353 | 354 | reply-to = "Reply-To:" address-list CRLF 355 | 356 | ; http://tools.ietf.org/html/rfc5322#section-3.6.3 357 | 358 | to = "To:" address-list CRLF 359 | 360 | cc = "Cc:" address-list CRLF 361 | 362 | bcc = "Bcc:" [address-list / CFWS] CRLF 363 | 364 | ; http://tools.ietf.org/html/rfc5322#section-3.6.5 365 | 366 | subject = "Subject:" unstructured CRLF 367 | 368 | comments = "Comments:" unstructured CRLF 369 | 370 | keywords = "Keywords:" phrase *("," phrase) CRLF 371 | 372 | ; http://tools.ietf.org/html/rfc5322#section-4.5.7 373 | 374 | obs-return = "Return-Path" *WSP ":" path CRLF 375 | 376 | obs-received = "Received" *WSP ":" *received-token CRLF 377 | 378 | ; http://tools.ietf.org/html/rfc5322#section-4.5.1 379 | 380 | obs-orig-date = "Date" *WSP ":" date-time CRLF 381 | 382 | ; http://tools.ietf.org/html/rfc5322#section-4.5.2 383 | 384 | obs-from = "From" *WSP ":" mailbox-list CRLF 385 | 386 | obs-sender = "Sender" *WSP ":" mailbox CRLF 387 | 388 | obs-reply-to = "Reply-To" *WSP ":" address-list CRLF 389 | 390 | ; http://tools.ietf.org/html/rfc5322#section-4.5.3 391 | 392 | obs-to = "To" *WSP ":" address-list CRLF 393 | 394 | obs-cc = "Cc" *WSP ":" address-list CRLF 395 | 396 | obs-bcc = "Bcc" *WSP ":" 397 | (address-list / (*([CFWS] ",") [CFWS])) CRLF 398 | 399 | ; http://tools.ietf.org/html/rfc5322#section-4.5.5 400 | 401 | obs-subject = "Subject" *WSP ":" unstructured CRLF 402 | 403 | obs-comments = "Comments" *WSP ":" unstructured CRLF 404 | 405 | obs-keywords = "Keywords" *WSP ":" obs-phrase-list CRLF 406 | 407 | ; http://tools.ietf.org/html/rfc5322#section-4.5.6 408 | 409 | obs-resent-from = "Resent-From" *WSP ":" mailbox-list CRLF 410 | 411 | obs-resent-send = "Resent-Sender" *WSP ":" mailbox CRLF 412 | 413 | obs-resent-date = "Resent-Date" *WSP ":" date-time CRLF 414 | 415 | obs-resent-to = "Resent-To" *WSP ":" address-list CRLF 416 | 417 | obs-resent-cc = "Resent-Cc" *WSP ":" address-list CRLF 418 | 419 | obs-resent-bcc = "Resent-Bcc" *WSP ":" 420 | (address-list / (*([CFWS] ",") [CFWS])) CRLF 421 | 422 | obs-resent-mid = "Resent-Message-ID" *WSP ":" msg-id CRLF 423 | 424 | obs-resent-rply = "Resent-Reply-To" *WSP ":" address-list CRLF 425 | 426 | ; http://tools.ietf.org/html/rfc5322#section-4.5.8 427 | 428 | obs-optional = field-name *WSP ":" unstructured CRLF 429 | 430 | ; http://tools.ietf.org/html/rfc6531#section-3.3 431 | ; http://www.utf8-chartable.de/unicode-utf8-table.pl 432 | UTF8-non-ascii = %x80-FF / ; Latin-1 Supplement 433 | %x100-17F / ; Latin Extended-A 434 | %x370-3FF / ; Greek and Coptic 435 | %x400-4FF / ; Cyrillic 436 | %x500-52F / ; Cyrillic Supplement 437 | %x4E00-9FFF ; CJK Unified Ideographs 438 | atext =/ UTF8-non-ascii 439 | --------------------------------------------------------------------------------