diff --git a/NOTICE.txt b/NOTICE.txt
index f68ef87246..de1837df99 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -2933,3 +2933,207 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+
+---
+
+## uasurfer
+
+This product contains a modified portion of `uasurfer`, a Golang library for parsing User-Agent strings.
+
+* HOMEPAGE
+ * https://github.com/avct/uasurfer
+
+* LICENSE
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ Copyright 2015 Avocet Systems Ltd.
+ http://avocet.io/opensource
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ Copyright 2015 Avocet Systems Ltd.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/glide.lock b/glide.lock
index 35af912b83..48a202d5ed 100644
--- a/glide.lock
+++ b/glide.lock
@@ -5,7 +5,9 @@ imports:
version: 3fbce08846379ec7f4f6bc7fce6dd01ce28fae4c
repo: https://github.com/mattermost/log4go.git
- name: github.com/armon/go-metrics
- version: 9a4b6e10bed6220a1665955aa2b75afc91eb10b3
+ version: 7aa49fde808223f8dadfdbfd3a20ff6c19e5f9ec
+- name: github.com/avct/uasurfer
+ version: 7b6f7205267b5b81d20da3f09ec3dea3b3d90cf8
- name: github.com/beorn7/perks
version: 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
subpackages:
@@ -19,7 +21,7 @@ imports:
subpackages:
- spew
- name: github.com/dgryski/dgoogauth
- version: 96977cbd42e27be71f9f731db6634123de7e861a
+ version: fd153d5969d30bd701e10f3530228f001213047c
- name: github.com/dimchansky/utfbom
version: 6c6132ff69f0f6c088739067407b5d32c52e1d0f
- name: github.com/disintegration/imaging
@@ -31,7 +33,7 @@ imports:
- name: github.com/fsnotify/fsnotify
version: 629574ca2a5df945712d3079857300b5e4da0236
- name: github.com/go-ini/ini
- version: f280b3ba517bf5fc98922624f21fb0e7a92adaec
+ version: 32e4c1e6bc4e7d0d8451aa6b75200d19e37a536a
- name: github.com/go-ldap/ldap
version: 8168ee085ee43257585e50c6441aadf54ecb2c9f
- name: github.com/go-redis/redis
@@ -50,7 +52,7 @@ imports:
- raster
- truetype
- name: github.com/golang/protobuf
- version: 1643683e1b54a9e88ad26d98f81400c8c9d9f4f9
+ version: 1e59b77b52bf8e4b449a57e6f79f21226d571845
subpackages:
- proto
- name: github.com/gorilla/context
@@ -70,7 +72,7 @@ imports:
subpackages:
- codec
- name: github.com/hashicorp/go-multierror
- version: 83588e72410abfbe4df460eeb6f30841ae47d4c4
+ version: b7773ae218740a7be65057fc60b366a49b538a44
- name: github.com/hashicorp/go-sockaddr
version: 9b4c5fa5b10a683339a270d664474b9f4aee62fc
- name: github.com/hashicorp/golang-lru
@@ -82,6 +84,7 @@ imports:
subpackages:
- hcl/ast
- hcl/parser
+ - hcl/printer
- hcl/scanner
- hcl/strconv
- hcl/token
@@ -89,15 +92,19 @@ imports:
- json/scanner
- json/token
- name: github.com/hashicorp/memberlist
+<<<<<<< HEAD
version: caa5d20d6a642b7543b3745e54031a96008bee57
- name: github.com/icrowley/fake
version: e64cc2cf92049a299f359734c6ea76073f2a8b2c
+=======
+ version: 3d8438da9589e7b608a83ffac1ef8211486bcb7c
+>>>>>>> Updated dependencies and added avct/uasurfer
- name: github.com/inconshreveable/mousetrap
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
- name: github.com/jehiah/go-strftime
version: 834e15c05a45371503440cc195bbd05c9a0968d9
- name: github.com/lib/pq
- version: b609790bd85edf8e9ab7e0f8912750a786177bcf
+ version: 83612a56d3dd153a94a629cd64925371c9adad78
subpackages:
- oid
- name: github.com/magiconair/properties
@@ -117,9 +124,7 @@ imports:
subpackages:
- pbutil
- name: github.com/miekg/dns
- version: 9fc4eb252eedf0ef8adc05169ce35da5e31beaba
- subpackages:
- - internal/socket
+ version: f5ac34d755eb6975958108413c7802a782ca16c8
- name: github.com/minio/go-homedir
version: 4d76aabb80b22bad8695d3904e943f1fb5e6199f
- name: github.com/minio/minio-go
@@ -134,7 +139,7 @@ imports:
- name: github.com/mitchellh/mapstructure
version: 06020f85339e21b2478f756a78e295255ffa4d6a
- name: github.com/mssola/user_agent
- version: a2f39d5a9b15ecc1fa1005b6aae73cd83da240ef
+ version: 5243daae23628aeae9b6268541406bd5e95d5964
- name: github.com/nicksnyder/go-i18n
version: 0dc1626d56435e9d605a29875701721c54bc9bbd
subpackages:
@@ -143,13 +148,13 @@ imports:
- i18n/language
- i18n/translation
- name: github.com/NYTimes/gziphandler
- version: 843330b8a375e6460183b08e030190b1a1eaea13
+ version: 47ca22a0aeea4c9ceddfb935d818d636d934c312
- name: github.com/pborman/uuid
version: e790cca94e6cc75c7064b1332e63811d4aae1a53
- name: github.com/pelletier/go-toml
- version: 4e9e0ee19b60b13eb79915933f44d8ed5f268bdd
+ version: 0131db6d737cfbbfb678f8b7d92e55e27ce46224
- name: github.com/pkg/errors
- version: f15c970de5b76fac0b59abb32d62c17cc7bed265
+ version: e881fd58d78e04cf6d0de1217f8707c8cc2249bc
- name: github.com/pmezard/go-difflib
version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
subpackages:
@@ -159,19 +164,19 @@ imports:
subpackages:
- prometheus
- name: github.com/prometheus/client_model
- version: 6f3806018612930941127f2a7c6c453ba2c527d2
+ version: 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c
subpackages:
- go
- name: github.com/prometheus/common
- version: e3fb1a1acd7605367a2b378bc2e2f893c05174b7
+ version: 89604d197083d4781071d3c65855d24ecfb0a563
subpackages:
- expfmt
- name: github.com/prometheus/procfs
- version: a6e9df898b1336106c743392c48ee0b71f5c4efa
+ version: b15cd069a83443be3154b719d0cc9fe8117f09fb
- name: github.com/rsc/letsencrypt
version: 33926faef6d434b854ea994228f11d0185faa0c1
- name: github.com/rwcarlsen/goexif
- version: 709fab3d192d7c62f86043caff1e7e3fb0f42bd8
+ version: 17202558c8d9c3fd047859f1a5e73fd9ae709187
subpackages:
- exif
- tiff
@@ -182,19 +187,19 @@ imports:
- name: github.com/segmentio/backo-go
version: 204274ad699c0983a70203a566887f17a717fef4
- name: github.com/spf13/afero
- version: 5660eeed305fe5f69c8fc6cf899132a459a97064
+ version: 57afd63c68602b63ed976de00dd066ccb3c319db
subpackages:
- mem
- name: github.com/spf13/cast
version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4
- name: github.com/spf13/cobra
- version: 2da4a54c5ceefcee7ca5dd0eea1e18a3b6366489
+ version: b95ab734e27d33e0d8fbabf71ca990568d4e2020
- name: github.com/spf13/jwalterweatherman
- version: 12bd96e66386c1960ab0f74ced1362f66f552f7b
+ version: 7c0cea34c8ece3fbeb2b27ab9b59511d360fb394
- name: github.com/spf13/pflag
version: e57e3eeb33f795204c1ca35f56c44f83227c6e66
- name: github.com/spf13/viper
- version: 4dddf7c62e16bce5807744018f5b753bfe21bbd2
+ version: aafc9e6bc7b7bb53ddaa75a5ef49a17d6e654be5
- name: github.com/stretchr/objx
version: cbeaeb16a013161a98496fad62933b1d21786672
- name: github.com/stretchr/testify
@@ -207,19 +212,21 @@ imports:
- name: github.com/tylerb/graceful
version: 4654dfbb6ad53cb5e27f37d99b02e16c1872fbbb
- name: github.com/xenolf/lego
- version: aa94fb4696349fd6f35168dabe3ab030ddf3e484
+ version: b929aa5aab5ad2e197bb3d74ef99fac61bfa47bc
subpackages:
- acme
- name: github.com/xtgo/uuid
version: a0b114877d4caeffbd7f87e3757c17fce570fea7
- name: golang.org/x/crypto
- version: 6a293f2d4b14b8e6d3f0539e383f6d0d30fce3fd
+ version: b3c9a1d25cfbbbab0ff4780b71c4f54e6e92a0de
subpackages:
- bcrypt
- blowfish
+ - ed25519
+ - ed25519/internal/edwards25519
- ocsp
- name: golang.org/x/image
- version: f7e31b4ea2e3413ab91b4e7d2dc83e5f8d19a44c
+ version: 12117c17ca67ffa1ce22e9409f3b0b0a93ac08c7
subpackages:
- bmp
- font
@@ -227,17 +234,22 @@ imports:
- tiff
- tiff/lzw
- name: golang.org/x/net
- version: a337091b0525af65de94df2eb7e98bd9962dcbe2
+ version: 434ec0c7fe3742c984919a691b2018a6e9694425
subpackages:
+ - bpf
- context
- html
- html/atom
+ - internal/iana
+ - internal/socket
+ - ipv4
+ - ipv6
- name: golang.org/x/sys
- version: 1e2299c37cc91a509f1b12369872d27be0ce98a6
+ version: 810d7000345868fc619eb81f46307107118f4ae1
subpackages:
- unix
- name: golang.org/x/text
- version: 88f656faf3f37f690df1a32515b479415e1a6769
+ version: e19ae1496984b1c655b8044a65c0300a3c878dd3
subpackages:
- transform
- unicode/norm
@@ -266,5 +278,5 @@ imports:
subpackages:
- store/memstore
- name: gopkg.in/yaml.v2
- version: eb3733d160e74a9c7e442f435eb3bea458e1d19f
+ version: d670f9405373e636a5a2765eea47fac0c9bc91a4
testImports: []
diff --git a/glide.yaml b/glide.yaml
index a574424421..78b26d9040 100644
--- a/glide.yaml
+++ b/glide.yaml
@@ -95,3 +95,4 @@ import:
version: 2.0.0
- package: github.com/mattermost/html2text
- package: github.com/icrowley/fake
+- package: github.com/avct/uasurfer
diff --git a/vendor/github.com/NYTimes/gziphandler/gzip.go b/vendor/github.com/NYTimes/gziphandler/gzip.go
index 13ee34a426..b3cb8315bb 100644
--- a/vendor/github.com/NYTimes/gziphandler/gzip.go
+++ b/vendor/github.com/NYTimes/gziphandler/gzip.go
@@ -82,7 +82,14 @@ type GzipResponseWriter struct {
buf []byte // Holds the first part of the write before reaching the minSize or the end of the write.
contentTypes []string // Only compress if the response is one of these content-types. All are accepted if empty.
- flushed bool // Indicate if the stream was already flushed
+}
+
+type GzipResponseWriterWithCloseNotify struct {
+ *GzipResponseWriter
+}
+
+func (w *GzipResponseWriterWithCloseNotify) CloseNotify() <-chan bool {
+ return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
}
// Write appends data to the gzip writer.
@@ -170,8 +177,7 @@ func (w *GzipResponseWriter) init() {
func (w *GzipResponseWriter) Close() error {
if w.gw == nil {
// Gzip not trigged yet, write out regular response.
- // WriteHeader only if it wasn't already wrote by a Flush
- if !w.flushed && w.code != 0 {
+ if w.code != 0 {
w.ResponseWriter.WriteHeader(w.code)
}
if w.buf != nil {
@@ -194,16 +200,18 @@ func (w *GzipResponseWriter) Close() error {
// http.ResponseWriter if it is an http.Flusher. This makes GzipResponseWriter
// an http.Flusher.
func (w *GzipResponseWriter) Flush() {
- if w.gw != nil {
- w.gw.Flush()
+ if w.gw == nil {
+ // Only flush once startGzip has been called.
+ //
+ // Flush is thus a no-op until the written body
+ // exceeds minSize.
+ return
}
+ w.gw.Flush()
+
if fw, ok := w.ResponseWriter.(http.Flusher); ok {
- if !w.flushed && w.code != 0 {
- w.ResponseWriter.WriteHeader(w.code)
- }
fw.Flush()
- w.flushed = true
}
}
@@ -264,7 +272,6 @@ func GzipHandlerWithOpts(opts ...option) (func(http.Handler) http.Handler, error
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Add(vary, acceptEncoding)
-
if acceptsGzip(r) {
gw := &GzipResponseWriter{
ResponseWriter: w,
@@ -274,7 +281,13 @@ func GzipHandlerWithOpts(opts ...option) (func(http.Handler) http.Handler, error
}
defer gw.Close()
- h.ServeHTTP(gw, r)
+ if _, ok := w.(http.CloseNotifier); ok {
+ gwcn := GzipResponseWriterWithCloseNotify{gw}
+ h.ServeHTTP(gwcn, r)
+ } else {
+ h.ServeHTTP(gw, r)
+ }
+
} else {
h.ServeHTTP(w, r)
}
diff --git a/vendor/github.com/NYTimes/gziphandler/gzip_test.go b/vendor/github.com/NYTimes/gziphandler/gzip_test.go
index 7decfd17be..ec1543372a 100644
--- a/vendor/github.com/NYTimes/gziphandler/gzip_test.go
+++ b/vendor/github.com/NYTimes/gziphandler/gzip_test.go
@@ -306,23 +306,58 @@ func TestStatusCodes(t *testing.T) {
}
}
-func TestStatusCodesFlushed(t *testing.T) {
+func TestFlushBeforeWrite(t *testing.T) {
+ b := []byte(testBody)
handler := GzipHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(http.StatusNotFound)
rw.(http.Flusher).Flush()
- rw.Write([]byte("Not found"))
+ rw.Write(b)
}))
r := httptest.NewRequest(http.MethodGet, "/", nil)
- r.Header.Set(acceptEncoding, "gzip")
+ r.Header.Set("Accept-Encoding", "gzip")
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
- result := w.Result()
- if result.StatusCode != http.StatusNotFound {
- t.Errorf("StatusCode should have been 404 but was %d", result.StatusCode)
- }
+ res := w.Result()
+ assert.Equal(t, http.StatusNotFound, res.StatusCode)
+ assert.Equal(t, "gzip", res.Header.Get("Content-Encoding"))
+ assert.NotEqual(t, b, w.Body.Bytes())
}
+func TestImplementCloseNotifier(t *testing.T) {
+ GzipHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request){
+ _, ok := rw.(http.CloseNotifier)
+ assert.True(t, ok, "response writer must implement http.CloseNotifier")
+ })).ServeHTTP(&mockRWCloseNotify{}, &http.Request{})
+}
+
+func TestNotImplementCloseNotifier(t *testing.T) {
+ GzipHandler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request){
+ _, ok := rw.(http.CloseNotifier)
+ assert.False(t, ok, "response writer must not implement http.CloseNotifier")
+ })).ServeHTTP(httptest.NewRecorder(), &http.Request{})
+}
+
+
+type mockRWCloseNotify struct{}
+
+func (m *mockRWCloseNotify) CloseNotify() <-chan bool {
+ panic("implement me")
+}
+
+func (m *mockRWCloseNotify) Header() http.Header {
+ return http.Header{}
+}
+
+func (m *mockRWCloseNotify) Write([]byte) (int, error) {
+ panic("implement me")
+}
+
+func (m *mockRWCloseNotify) WriteHeader(int) {
+ panic("implement me")
+}
+
+
func TestIgnoreSubsequentWriteHeader(t *testing.T) {
handler := GzipHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(500)
diff --git a/vendor/github.com/armon/go-metrics/prometheus/prometheus.go b/vendor/github.com/armon/go-metrics/prometheus/prometheus.go
index a647e59659..a5b27d6d3e 100644
--- a/vendor/github.com/armon/go-metrics/prometheus/prometheus.go
+++ b/vendor/github.com/armon/go-metrics/prometheus/prometheus.go
@@ -13,19 +13,91 @@ import (
"github.com/prometheus/client_golang/prometheus"
)
-type PrometheusSink struct {
- mu sync.Mutex
- gauges map[string]prometheus.Gauge
- summaries map[string]prometheus.Summary
- counters map[string]prometheus.Counter
+var (
+ // DefaultPrometheusOpts is the default set of options used when creating a
+ // PrometheusSink.
+ DefaultPrometheusOpts = PrometheusOpts{
+ Expiration: 60 * time.Second,
+ }
+)
+
+// PrometheusOpts is used to configure the Prometheus Sink
+type PrometheusOpts struct {
+ // Expiration is the duration a metric is valid for, after which it will be
+ // untracked. If the value is zero, a metric is never expired.
+ Expiration time.Duration
}
+type PrometheusSink struct {
+ mu sync.Mutex
+ gauges map[string]prometheus.Gauge
+ summaries map[string]prometheus.Summary
+ counters map[string]prometheus.Counter
+ updates map[string]time.Time
+ expiration time.Duration
+}
+
+// NewPrometheusSink creates a new PrometheusSink using the default options.
func NewPrometheusSink() (*PrometheusSink, error) {
- return &PrometheusSink{
- gauges: make(map[string]prometheus.Gauge),
- summaries: make(map[string]prometheus.Summary),
- counters: make(map[string]prometheus.Counter),
- }, nil
+ return NewPrometheusSinkFrom(DefaultPrometheusOpts)
+}
+
+// NewPrometheusSinkFrom creates a new PrometheusSink using the passed options.
+func NewPrometheusSinkFrom(opts PrometheusOpts) (*PrometheusSink, error) {
+ sink := &PrometheusSink{
+ gauges: make(map[string]prometheus.Gauge),
+ summaries: make(map[string]prometheus.Summary),
+ counters: make(map[string]prometheus.Counter),
+ updates: make(map[string]time.Time),
+ expiration: opts.Expiration,
+ }
+
+ return sink, prometheus.Register(sink)
+}
+
+// Describe is needed to meet the Collector interface.
+func (p *PrometheusSink) Describe(c chan<- *prometheus.Desc) {
+ // We must emit some description otherwise an error is returned. This
+ // description isn't shown to the user!
+ prometheus.NewGauge(prometheus.GaugeOpts{Name: "Dummy", Help: "Dummy"}).Describe(c)
+}
+
+// Collect meets the collection interface and allows us to enforce our expiration
+// logic to clean up ephemeral metrics if their value haven't been set for a
+// duration exceeding our allowed expiration time.
+func (p *PrometheusSink) Collect(c chan<- prometheus.Metric) {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+
+ expire := p.expiration != 0
+ now := time.Now()
+ for k, v := range p.gauges {
+ last := p.updates[k]
+ if expire && last.Add(p.expiration).Before(now) {
+ delete(p.updates, k)
+ delete(p.gauges, k)
+ } else {
+ v.Collect(c)
+ }
+ }
+ for k, v := range p.summaries {
+ last := p.updates[k]
+ if expire && last.Add(p.expiration).Before(now) {
+ delete(p.updates, k)
+ delete(p.summaries, k)
+ } else {
+ v.Collect(c)
+ }
+ }
+ for k, v := range p.counters {
+ last := p.updates[k]
+ if expire && last.Add(p.expiration).Before(now) {
+ delete(p.updates, k)
+ delete(p.counters, k)
+ } else {
+ v.Collect(c)
+ }
+ }
}
var forbiddenChars = regexp.MustCompile("[ .=\\-]")
@@ -65,10 +137,10 @@ func (p *PrometheusSink) SetGaugeWithLabels(parts []string, val float32, labels
Help: key,
ConstLabels: prometheusLabels(labels),
})
- prometheus.MustRegister(g)
p.gauges[hash] = g
}
g.Set(float64(val))
+ p.updates[hash] = time.Now()
}
func (p *PrometheusSink) AddSample(parts []string, val float32) {
@@ -87,10 +159,10 @@ func (p *PrometheusSink) AddSampleWithLabels(parts []string, val float32, labels
MaxAge: 10 * time.Second,
ConstLabels: prometheusLabels(labels),
})
- prometheus.MustRegister(g)
p.summaries[hash] = g
}
g.Observe(float64(val))
+ p.updates[hash] = time.Now()
}
// EmitKey is not implemented. Prometheus doesn’t offer a type for which an
@@ -114,8 +186,8 @@ func (p *PrometheusSink) IncrCounterWithLabels(parts []string, val float32, labe
Help: key,
ConstLabels: prometheusLabels(labels),
})
- prometheus.MustRegister(g)
p.counters[hash] = g
}
g.Add(float64(val))
+ p.updates[hash] = time.Now()
}
diff --git a/vendor/github.com/armon/go-metrics/start.go b/vendor/github.com/armon/go-metrics/start.go
index 46f0c2eb2d..dd41861c90 100644
--- a/vendor/github.com/armon/go-metrics/start.go
+++ b/vendor/github.com/armon/go-metrics/start.go
@@ -11,7 +11,7 @@ import (
// Config is used to configure metrics settings
type Config struct {
- ServiceName string // Prefixed with keys to seperate services
+ ServiceName string // Prefixed with keys to separate services
HostName string // Hostname to use. If not provided and EnableHostname, it will be os.Hostname
EnableHostname bool // Enable prefixing gauge values with hostname
EnableHostnameLabel bool // Enable adding hostname to labels
diff --git a/vendor/github.com/avct/uasurfer/.gitignore b/vendor/github.com/avct/uasurfer/.gitignore
new file mode 100644
index 0000000000..35ba52a16a
--- /dev/null
+++ b/vendor/github.com/avct/uasurfer/.gitignore
@@ -0,0 +1,56 @@
+# Compiled bin #
+###################
+
+
+# Compiled source #
+###################
+*.dll
+*.exe
+*.o
+*.so
+
+# Packages #
+############
+# it's better to unpack these files and commit the raw source
+# git has its own built in compression methods
+*.7z
+*.dmg
+*.gz
+*.iso
+*.jar
+*.rar
+*.tar
+*.zip
+
+# Configuration Files #
+#######################
+*.cfg
+
+# Logs and databases #
+######################
+*.log
+*.sql
+*.sqlite
+logs
+coverage.html
+coverage.out
+
+# Test Files #
+#######################
+*.test
+
+# OS generated files #
+######################
+.DS_Store
+.DS_Store?
+.Spotlight-V100
+.Trashes
+ehthumbs.db
+Thumbs.db
+
+# go.rice generated files
+*.rice-box.go
+
+# Dev Tools #
+######################
+.vagrant
\ No newline at end of file
diff --git a/vendor/github.com/avct/uasurfer/.travis.yml b/vendor/github.com/avct/uasurfer/.travis.yml
new file mode 100644
index 0000000000..77b64e6f26
--- /dev/null
+++ b/vendor/github.com/avct/uasurfer/.travis.yml
@@ -0,0 +1,11 @@
+sudo: false
+
+language: go
+
+go:
+ - 1.9.x
+ - 1.8.x
+ - 1.7.x
+
+script:
+ - go test
diff --git a/vendor/github.com/avct/uasurfer/LICENSE b/vendor/github.com/avct/uasurfer/LICENSE
new file mode 100644
index 0000000000..a092343b2d
--- /dev/null
+++ b/vendor/github.com/avct/uasurfer/LICENSE
@@ -0,0 +1,192 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ Copyright 2015 Avocet Systems Ltd.
+ http://avocet.io/opensource
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ Copyright 2015 Avocet Systems Ltd.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/vendor/github.com/avct/uasurfer/README.md b/vendor/github.com/avct/uasurfer/README.md
new file mode 100644
index 0000000000..2a4ab608d6
--- /dev/null
+++ b/vendor/github.com/avct/uasurfer/README.md
@@ -0,0 +1,169 @@
+[](https://travis-ci.org/avct/uasurfer) [](https://godoc.org/github.com/avct/uasurfer) [](https://goreportcard.com/report/github.com/avct/uasurfer)
+
+# uasurfer
+
+
+
+**User Agent Surfer** (uasurfer) is a lightweight Golang package that parses and abstracts [HTTP User-Agent strings](https://en.wikipedia.org/wiki/User_agent) with particular attention to device type.
+
+The following information is returned by uasurfer from a raw HTTP User-Agent string:
+
+| Name | Example | Coverage in 192,792 parses |
+|----------------|---------|--------------------------------|
+| Browser name | `chrome` | 99.85% |
+| Browser version | `53` | 99.17% |
+| Platform | `ipad` | 99.97% |
+| OS name | `ios` | 99.96% |
+| OS version | `10` | 98.81% |
+| Device type | `tablet` | 99.98% |
+
+Layout engine, browser language, and other esoteric attributes are not parsed.
+
+Coverage is estimated from a random sample of real UA strings collected across thousands of sources in US and EU mid-2016.
+
+## Usage
+
+### Parse(ua string) Function
+
+The `Parse()` function accepts a user agent `string` and returns UserAgent struct with named constants and integers for versions (minor, major and patch separately), and the full UA string that was parsed (lowercase). A string can be retrieved by adding `.String()` to a variable, such as `uasurfer.BrowserName.String()`.
+
+```
+// Define a user agent string
+myUA := "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36"
+
+// Parse() returns all attributes, including returning the full UA string last
+ua, uaString := uasurfer.Parse(myUA)
+```
+
+where example UserAgent is:
+```
+{
+ Browser {
+ BrowserName: BrowserChrome,
+ Version: {
+ Major: 45,
+ Minor: 0,
+ Patch: 2454,
+ },
+ },
+ OS {
+ Platform: PlatformMac,
+ Name: OSMacOSX,
+ Version: {
+ Major: 10,
+ Minor: 10,
+ Patch: 5,
+ },
+ },
+ DeviceType: DeviceComputer,
+}
+```
+
+**Usage note:** There are some OSes that do not return a version, see docs below. Linux is typically not reported with a specific Linux distro name or version.
+
+#### Browser Name
+* `BrowserChrome` - Google [Chrome](https://en.wikipedia.org/wiki/Google_Chrome), [Chromium](https://en.wikipedia.org/wiki/Chromium_(web_browser))
+* `BrowserSafari` - Apple [Safari](https://en.wikipedia.org/wiki/Safari_(web_browser)), Google Search ([GSA](https://itunes.apple.com/us/app/google/id284815942))
+* `BrowserIE` - Microsoft [Internet Explorer](https://en.wikipedia.org/wiki/Internet_Explorer), [Edge](https://en.wikipedia.org/wiki/Microsoft_Edge)
+* `BrowserFirefox` - Mozilla [Firefox](https://en.wikipedia.org/wiki/Firefox), GNU [IceCat](https://en.wikipedia.org/wiki/GNU_IceCat), [Iceweasel](https://en.wikipedia.org/wiki/Mozilla_Corporation_software_rebranded_by_the_Debian_project#Iceweasel), [Seamonkey](https://en.wikipedia.org/wiki/SeaMonkey)
+* `BrowserAndroid` - Android [WebView](https://developer.chrome.com/multidevice/webview/overview) (Android OS <4.4 only)
+* `BrowserOpera` - [Opera](https://en.wikipedia.org/wiki/Opera_(web_browser))
+* `BrowserUCBrowser` - [UC Browser](https://en.wikipedia.org/wiki/UC_Browser)
+* `BrowserSilk` - Amazon [Silk](https://en.wikipedia.org/wiki/Amazon_Silk)
+* `BrowserSpotify` - [Spotify](https://en.wikipedia.org/wiki/Spotify#Clients) desktop client
+* `BrowserBlackberry` - RIM [BlackBerry](https://en.wikipedia.org/wiki/BlackBerry)
+* `BrowserUnknown` - Unknown
+
+#### Browser Version
+
+Browser version returns an `unint8` of the major version attribute of the User-Agent String. For example Chrome 45.0.23423 would return `45`. The intention is to support math operators with versions, such as "do XYZ for Chrome version >23".
+
+Unknown version is returned as `0`.
+
+#### Platform
+* `PlatformWindows` - Microsoft Windows
+* `PlatformMac` - Apple Macintosh
+* `PlatformLinux` - Linux, including Android and other OSes
+* `PlatformiPad` - Apple iPad
+* `PlatformiPhone` - Apple iPhone
+* `PlatformBlackberry` - RIM Blackberry
+* `PlatformWindowsPhone` Microsoft Windows Phone & Mobile
+* `PlatformKindle` - Amazon Kindle & Kindle Fire
+* `PlatformPlaystation` - Sony Playstation, Vita, PSP
+* `PlatformXbox` - Microsoft Xbox - `PlatformXbox`
+* `PlatformNintendo` - Nintendo DS, Wii, etc.
+* `PlatformUnknown` - Unknown
+
+#### OS Name
+* `OSWindows`
+* `OSMacOSX` - includes "macOS Sierra"
+* `OSiOS`
+* `OSAndroid`
+* `OSChromeOS`
+* `OSWebOS`
+* `OSLinux`
+* `OSPlaystation`
+* `OSXbox`
+* `OSNintendo`
+* `OSUnknown`
+
+#### OS Version
+
+OS X major version is alway 10 with consecutive minor versions indicating release releases (10 - Yosemite, 11 - El Capitain, 12 Sierra, etc). Windows version is NT version. `Version{0, 0, 0}` indicated version is unknown or not evaluated.
+Versions can be compared using `Less` function: `if ver1.Less(ver2) {}`
+
+Here are some examples across the platform, os.name, and os.version:
+
+* For Windows XP (Windows NT 5.1), "`PlatformWindows`" is the platform, "`OSWindows`" is the name, and `{5, 1, 0}` the version.
+* For OS X 10.5.1, "`PlatformMac`" is the platform, "`OSMacOSX`" the name, and `{10, 5, 1}` the version.
+* For Android 5.1, "`PlatformLinux`" is the platform, "`OSAndroid`" is the name, and `{5, 1, 0}` the version.
+* For iOS 5.1, "`PlatformiPhone`" or "`PlatformiPad`" is the platform, "`OSiOS`" is the name, and `{5, 1, 0}` the version.
+
+###### Windows Version Guide
+
+* Windows 10 - `{10, 0, 0}`
+* Windows 8.1 - `{6, 3, 0}`
+* Windows 8 - `{6, 2, 0}`
+* Windows 7 - `{6, 1, 0}`
+* Windows Vista - `{6, 0, 0}`
+* Windows XP - `{5, 1, 0}` or `{5, 2, 0}`
+* Windows 2000 - `{5, 0, 0}`
+
+Windows 95, 98, and ME represent 0.01% of traffic worldwide and are not available through this package at this time.
+
+#### DeviceType
+DeviceType is typically quite accurate, though determining between phones and tablets on Android is not always possible due to how some vendors design their UA strings. A mobile Android device without tablet indicator defaults to being classified as a phone. DeviceTV supports major brands such as Philips, Sharp, Vizio and steaming boxes such as Apple, Google, Roku, Amazon.
+
+* `DeviceComputer`
+* `DevicePhone`
+* `DeviceTablet`
+* `DeviceTV`
+* `DeviceConsole`
+* `DeviceWearable`
+* `DeviceUnknown`
+
+## Example Combinations of Attributes
+* Surface RT -> `OSWindows8`, `DeviceTablet`, OSVersion >= `6`
+* Android Tablet -> `OSAndroid`, `DeviceTablet`
+* Microsoft Edge -> `BrowserIE`, BrowserVersion >= `12.0.0`
+
+## To do
+
+* Remove compiled regexp in favor of string.Contains wherever possible (lowers mem/alloc)
+* Better version support on Firefox derivatives (e.g. SeaMonkey)
+* Potential additional browser support:
+ * "NetFront" (1% share in India)
+ * "QQ Browser" (6.5% share in China)
+ * "Sogou Explorer" (5% share in China)
+ * "Maxthon" (1.5% share in China)
+ * "Nokia"
+* Potential additional OS support:
+ * "Nokia" (5% share in India)
+ * "Series 40" (5.5% share in India)
+ * Windows 2003 Server
+* iOS safari browser identification based on iOS version
+* Add android version to browser identification
+* old Macs
+ * "opera/9.64 (macintosh; ppc mac os x; u; en) presto/2.1.1"
+* old Windows
+ * "mozilla/5.0 (windows nt 4.0; wow64) applewebkit/537.36 (khtml, like gecko) chrome/37.0.2049.0 safari/537.36"
diff --git a/vendor/github.com/avct/uasurfer/browser.go b/vendor/github.com/avct/uasurfer/browser.go
new file mode 100644
index 0000000000..e156818aba
--- /dev/null
+++ b/vendor/github.com/avct/uasurfer/browser.go
@@ -0,0 +1,192 @@
+package uasurfer
+
+import (
+ "strings"
+)
+
+// Browser struct contains the lowercase name of the browser, along
+// with its browser version number. Browser are grouped together without
+// consideration for device. For example, Chrome (Chrome/43.0) and Chrome for iOS
+// (CriOS/43.0) would both return as "chrome" (name) and 43.0 (version). Similarly
+// Internet Explorer 11 and Edge 12 would return as "ie" and "11" or "12", respectively.
+// type Browser struct {
+// Name BrowserName
+// Version struct {
+// Major int
+// Minor int
+// Patch int
+// }
+// }
+
+// Retrieve browser name from UA strings
+func (u *UserAgent) evalBrowserName(ua string) bool {
+ // Blackberry goes first because it reads as MSIE & Safari
+ if strings.Contains(ua, "blackberry") || strings.Contains(ua, "playbook") || strings.Contains(ua, "bb10") || strings.Contains(ua, "rim ") {
+ u.Browser.Name = BrowserBlackberry
+ return u.isBot()
+ }
+
+ if strings.Contains(ua, "applewebkit") {
+ switch {
+ case strings.Contains(ua, "opr/") || strings.Contains(ua, "opios/"):
+ u.Browser.Name = BrowserOpera
+
+ case strings.Contains(ua, "silk/"):
+ u.Browser.Name = BrowserSilk
+
+ case strings.Contains(ua, "edge/") || strings.Contains(ua, "iemobile/") || strings.Contains(ua, "msie "):
+ u.Browser.Name = BrowserIE
+
+ case strings.Contains(ua, "ucbrowser/") || strings.Contains(ua, "ucweb/"):
+ u.Browser.Name = BrowserUCBrowser
+
+ // Edge, Silk and other chrome-identifying browsers must evaluate before chrome, unless we want to add more overhead
+ case strings.Contains(ua, "chrome/") || strings.Contains(ua, "crios/") || strings.Contains(ua, "chromium/") || strings.Contains(ua, "crmo/"):
+ u.Browser.Name = BrowserChrome
+
+ case strings.Contains(ua, "android") && !strings.Contains(ua, "chrome/") && strings.Contains(ua, "version/") && !strings.Contains(ua, "like android"):
+ // Android WebView on Android >= 4.4 is purposefully being identified as Chrome above -- https://developer.chrome.com/multidevice/webview/overview
+ u.Browser.Name = BrowserAndroid
+
+ case strings.Contains(ua, "fxios"):
+ u.Browser.Name = BrowserFirefox
+
+ case strings.Contains(ua, " spotify/"):
+ u.Browser.Name = BrowserSpotify
+
+ // AppleBot uses webkit signature as well
+ case strings.Contains(ua, "applebot"):
+ u.Browser.Name = BrowserAppleBot
+
+ // presume it's safari unless an esoteric browser is being specified (webOSBrowser, SamsungBrowser, etc.)
+ case strings.Contains(ua, "like gecko") && strings.Contains(ua, "mozilla/") && strings.Contains(ua, "safari/") && !strings.Contains(ua, "linux") && !strings.Contains(ua, "android") && !strings.Contains(ua, "browser/") && !strings.Contains(ua, "os/"):
+ u.Browser.Name = BrowserSafari
+
+ // if we got this far and the device is iPhone or iPad, assume safari. Some agents don't actually contain the word "safari"
+ case strings.Contains(ua, "iphone") || strings.Contains(ua, "ipad"):
+ u.Browser.Name = BrowserSafari
+
+ // Google's search app on iPhone, leverages native Safari rather than Chrome
+ case strings.Contains(ua, " gsa/"):
+ u.Browser.Name = BrowserSafari
+
+ default:
+ goto notwebkit
+
+ }
+ return u.isBot()
+ }
+
+notwebkit:
+ switch {
+ case strings.Contains(ua, "msie") || strings.Contains(ua, "trident"):
+ u.Browser.Name = BrowserIE
+
+ case strings.Contains(ua, "gecko") && (strings.Contains(ua, "firefox") || strings.Contains(ua, "iceweasel") || strings.Contains(ua, "seamonkey") || strings.Contains(ua, "icecat")):
+ u.Browser.Name = BrowserFirefox
+
+ case strings.Contains(ua, "presto") || strings.Contains(ua, "opera"):
+ u.Browser.Name = BrowserOpera
+
+ case strings.Contains(ua, "ucbrowser"):
+ u.Browser.Name = BrowserUCBrowser
+
+ case strings.Contains(ua, "applebot"):
+ u.Browser.Name = BrowserAppleBot
+
+ case strings.Contains(ua, "baiduspider"):
+ u.Browser.Name = BrowserBaiduBot
+
+ case strings.Contains(ua, "adidxbot") || strings.Contains(ua, "bingbot") || strings.Contains(ua, "bingpreview"):
+ u.Browser.Name = BrowserBingBot
+
+ case strings.Contains(ua, "duckduckbot"):
+ u.Browser.Name = BrowserDuckDuckGoBot
+
+ case strings.Contains(ua, "facebot") || strings.Contains(ua, "facebookexternalhit"):
+ u.Browser.Name = BrowserFacebookBot
+
+ case strings.Contains(ua, "googlebot"):
+ u.Browser.Name = BrowserGoogleBot
+
+ case strings.Contains(ua, "linkedinbot"):
+ u.Browser.Name = BrowserLinkedInBot
+
+ case strings.Contains(ua, "msnbot"):
+ u.Browser.Name = BrowserMsnBot
+
+ case strings.Contains(ua, "pingdom.com_bot"):
+ u.Browser.Name = BrowserPingdomBot
+
+ case strings.Contains(ua, "twitterbot"):
+ u.Browser.Name = BrowserTwitterBot
+
+ case strings.Contains(ua, "yandex") || strings.Contains(ua, "yadirectfetcher"):
+ u.Browser.Name = BrowserYandexBot
+
+ case strings.Contains(ua, "yahoo"):
+ u.Browser.Name = BrowserYahooBot
+
+ case strings.Contains(ua, "phantomjs"):
+ u.Browser.Name = BrowserBot
+
+ default:
+ u.Browser.Name = BrowserUnknown
+
+ }
+
+ return u.isBot()
+}
+
+// Retrieve browser version
+// Methods used in order:
+// 1st: look for generic version/#
+// 2nd: look for browser-specific instructions (e.g. chrome/34)
+// 3rd: infer from OS (iOS only)
+func (u *UserAgent) evalBrowserVersion(ua string) {
+ // if there is a 'version/#' attribute with numeric version, use it -- except for Chrome since Android vendors sometimes hijack version/#
+ if u.Browser.Name != BrowserChrome && u.Browser.Version.findVersionNumber(ua, "version/") {
+ return
+ }
+
+ switch u.Browser.Name {
+ case BrowserChrome:
+ // match both chrome and crios
+ _ = u.Browser.Version.findVersionNumber(ua, "chrome/") || u.Browser.Version.findVersionNumber(ua, "crios/") || u.Browser.Version.findVersionNumber(ua, "crmo/")
+
+ case BrowserIE:
+ if u.Browser.Version.findVersionNumber(ua, "msie ") || u.Browser.Version.findVersionNumber(ua, "edge/") {
+ return
+ }
+
+ // get MSIE version from trident version https://en.wikipedia.org/wiki/Trident_(layout_engine)
+ if u.Browser.Version.findVersionNumber(ua, "trident/") {
+ // convert trident versions 3-7 to MSIE version
+ if (u.Browser.Version.Major >= 3) && (u.Browser.Version.Major <= 7) {
+ u.Browser.Version.Major += 4
+ }
+ }
+
+ case BrowserFirefox:
+ _ = u.Browser.Version.findVersionNumber(ua, "firefox/") || u.Browser.Version.findVersionNumber(ua, "fxios/")
+
+ case BrowserSafari: // executes typically if we're on iOS and not using a familiar browser
+ u.Browser.Version = u.OS.Version
+ // early Safari used a version number +1 to OS version
+ if (u.Browser.Version.Major <= 3) && (u.Browser.Version.Major >= 1) {
+ u.Browser.Version.Major++
+ }
+
+ case BrowserUCBrowser:
+ _ = u.Browser.Version.findVersionNumber(ua, "ucbrowser/")
+
+ case BrowserOpera:
+ _ = u.Browser.Version.findVersionNumber(ua, "opr/") || u.Browser.Version.findVersionNumber(ua, "opios/") || u.Browser.Version.findVersionNumber(ua, "opera/")
+
+ case BrowserSilk:
+ _ = u.Browser.Version.findVersionNumber(ua, "silk/")
+
+ case BrowserSpotify:
+ _ = u.Browser.Version.findVersionNumber(ua, "spotify/")
+ }
+}
diff --git a/vendor/github.com/avct/uasurfer/const_string.go b/vendor/github.com/avct/uasurfer/const_string.go
new file mode 100644
index 0000000000..2fa21d86d3
--- /dev/null
+++ b/vendor/github.com/avct/uasurfer/const_string.go
@@ -0,0 +1,49 @@
+// Code generated by "stringer -type=DeviceType,BrowserName,OSName,Platform -output=const_string.go"; DO NOT EDIT.
+
+package uasurfer
+
+import "fmt"
+
+const _DeviceType_name = "DeviceUnknownDeviceComputerDeviceTabletDevicePhoneDeviceConsoleDeviceWearableDeviceTV"
+
+var _DeviceType_index = [...]uint8{0, 13, 27, 39, 50, 63, 77, 85}
+
+func (i DeviceType) String() string {
+ if i < 0 || i >= DeviceType(len(_DeviceType_index)-1) {
+ return fmt.Sprintf("DeviceType(%d)", i)
+ }
+ return _DeviceType_name[_DeviceType_index[i]:_DeviceType_index[i+1]]
+}
+
+const _BrowserName_name = "BrowserUnknownBrowserChromeBrowserIEBrowserSafariBrowserFirefoxBrowserAndroidBrowserOperaBrowserBlackberryBrowserUCBrowserBrowserSilkBrowserNokiaBrowserNetFrontBrowserQQBrowserMaxthonBrowserSogouExplorerBrowserSpotifyBrowserBotBrowserAppleBotBrowserBaiduBotBrowserBingBotBrowserDuckDuckGoBotBrowserFacebookBotBrowserGoogleBotBrowserLinkedInBotBrowserMsnBotBrowserPingdomBotBrowserTwitterBotBrowserYandexBotBrowserYahooBot"
+
+var _BrowserName_index = [...]uint16{0, 14, 27, 36, 49, 63, 77, 89, 106, 122, 133, 145, 160, 169, 183, 203, 217, 227, 242, 257, 271, 291, 309, 325, 343, 356, 373, 390, 406, 421}
+
+func (i BrowserName) String() string {
+ if i < 0 || i >= BrowserName(len(_BrowserName_index)-1) {
+ return fmt.Sprintf("BrowserName(%d)", i)
+ }
+ return _BrowserName_name[_BrowserName_index[i]:_BrowserName_index[i+1]]
+}
+
+const _OSName_name = "OSUnknownOSWindowsPhoneOSWindowsOSMacOSXOSiOSOSAndroidOSBlackberryOSChromeOSOSKindleOSWebOSOSLinuxOSPlaystationOSXboxOSNintendoOSBot"
+
+var _OSName_index = [...]uint8{0, 9, 23, 32, 40, 45, 54, 66, 76, 84, 91, 98, 111, 117, 127, 132}
+
+func (i OSName) String() string {
+ if i < 0 || i >= OSName(len(_OSName_index)-1) {
+ return fmt.Sprintf("OSName(%d)", i)
+ }
+ return _OSName_name[_OSName_index[i]:_OSName_index[i+1]]
+}
+
+const _Platform_name = "PlatformUnknownPlatformWindowsPlatformMacPlatformLinuxPlatformiPadPlatformiPhonePlatformiPodPlatformBlackberryPlatformWindowsPhonePlatformPlaystationPlatformXboxPlatformNintendoPlatformBot"
+
+var _Platform_index = [...]uint8{0, 15, 30, 41, 54, 66, 80, 92, 110, 130, 149, 161, 177, 188}
+
+func (i Platform) String() string {
+ if i < 0 || i >= Platform(len(_Platform_index)-1) {
+ return fmt.Sprintf("Platform(%d)", i)
+ }
+ return _Platform_name[_Platform_index[i]:_Platform_index[i+1]]
+}
diff --git a/vendor/github.com/avct/uasurfer/device.go b/vendor/github.com/avct/uasurfer/device.go
new file mode 100644
index 0000000000..70c00b1126
--- /dev/null
+++ b/vendor/github.com/avct/uasurfer/device.go
@@ -0,0 +1,60 @@
+package uasurfer
+
+import (
+ "strings"
+)
+
+func (u *UserAgent) evalDevice(ua string) {
+ switch {
+
+ case u.OS.Platform == PlatformWindows || u.OS.Platform == PlatformMac || u.OS.Name == OSChromeOS:
+ if strings.Contains(ua, "mobile") || strings.Contains(ua, "touch") {
+ u.DeviceType = DeviceTablet // windows rt, linux haxor tablets
+ return
+ }
+ u.DeviceType = DeviceComputer
+
+ case u.OS.Platform == PlatformiPad || u.OS.Platform == PlatformiPod || strings.Contains(ua, "tablet") || strings.Contains(ua, "kindle/") || strings.Contains(ua, "playbook"):
+ u.DeviceType = DeviceTablet
+
+ case u.OS.Platform == PlatformiPhone || u.OS.Platform == PlatformBlackberry || strings.Contains(ua, "phone"):
+ u.DeviceType = DevicePhone
+
+ // long list of smarttv and tv dongle identifiers
+ case strings.Contains(ua, "tv") || strings.Contains(ua, "crkey") || strings.Contains(ua, "googletv") || strings.Contains(ua, "aftb") || strings.Contains(ua, "adt-") || strings.Contains(ua, "roku") || strings.Contains(ua, "viera") || strings.Contains(ua, "aquos") || strings.Contains(ua, "dtv") || strings.Contains(ua, "appletv") || strings.Contains(ua, "smarttv") || strings.Contains(ua, "tuner") || strings.Contains(ua, "smart-tv") || strings.Contains(ua, "hbbtv") || strings.Contains(ua, "netcast") || strings.Contains(ua, "vizio"):
+ u.DeviceType = DeviceTV
+
+ case u.OS.Name == OSAndroid:
+ // android phones report as "mobile", android tablets should not but often do -- http://android-developers.blogspot.com/2010/12/android-browser-user-agent-issues.html
+ if strings.Contains(ua, "mobile") {
+ u.DeviceType = DevicePhone
+ return
+ }
+
+ if strings.Contains(ua, "tablet") || strings.Contains(ua, "nexus 7") || strings.Contains(ua, "nexus 9") || strings.Contains(ua, "nexus 10") || strings.Contains(ua, "xoom") {
+ u.DeviceType = DeviceTablet
+ return
+ }
+
+ u.DeviceType = DevicePhone // default to phone
+
+ case u.OS.Platform == PlatformPlaystation || u.OS.Platform == PlatformXbox || u.OS.Platform == PlatformNintendo:
+ u.DeviceType = DeviceConsole
+
+ case strings.Contains(ua, "glass") || strings.Contains(ua, "watch") || strings.Contains(ua, "sm-v"):
+ u.DeviceType = DeviceWearable
+
+ // specifically above "mobile" string check as Kindle Fire tablets report as "mobile"
+ case u.Browser.Name == BrowserSilk || u.OS.Name == OSKindle && !strings.Contains(ua, "sd4930ur"):
+ u.DeviceType = DeviceTablet
+
+ case strings.Contains(ua, "mobile") || strings.Contains(ua, "touch") || strings.Contains(ua, " mobi") || strings.Contains(ua, "webos"): //anything "mobile"/"touch" that didn't get captured as tablet, console or wearable is presumed a phone
+ u.DeviceType = DevicePhone
+
+ case u.OS.Name == OSLinux: // linux goes last since it's in so many other device types (tvs, wearables, android-based stuff)
+ u.DeviceType = DeviceComputer
+
+ default:
+ u.DeviceType = DeviceUnknown
+ }
+}
diff --git a/vendor/github.com/avct/uasurfer/system.go b/vendor/github.com/avct/uasurfer/system.go
new file mode 100644
index 0000000000..45a80409f7
--- /dev/null
+++ b/vendor/github.com/avct/uasurfer/system.go
@@ -0,0 +1,332 @@
+package uasurfer
+
+import (
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+var (
+ amazonFireFingerprint = regexp.MustCompile("\\s(k[a-z]{3,5}|sd\\d{4}ur)\\s") //tablet or phone
+)
+
+func (u *UserAgent) evalOS(ua string) bool {
+
+ s := strings.IndexRune(ua, '(')
+ e := strings.IndexRune(ua, ')')
+ if s > e {
+ s = 0
+ e = len(ua)
+ }
+ if e == -1 {
+ e = len(ua)
+ }
+
+ agentPlatform := ua[s+1 : e]
+ specsEnd := strings.Index(agentPlatform, ";")
+ var specs string
+ if specsEnd != -1 {
+ specs = agentPlatform[:specsEnd]
+ } else {
+ specs = agentPlatform
+ }
+
+ //strict OS & version identification
+ switch specs {
+ case "android":
+ u.evalLinux(ua, agentPlatform)
+
+ case "bb10", "playbook":
+ u.OS.Platform = PlatformBlackberry
+ u.OS.Name = OSBlackberry
+
+ case "x11", "linux":
+ u.evalLinux(ua, agentPlatform)
+
+ case "ipad", "iphone", "ipod touch", "ipod":
+ u.evaliOS(specs, agentPlatform)
+
+ case "macintosh":
+ u.evalMacintosh(ua)
+
+ default:
+ switch {
+ // Blackberry
+ case strings.Contains(ua, "blackberry") || strings.Contains(ua, "playbook"):
+ u.OS.Platform = PlatformBlackberry
+ u.OS.Name = OSBlackberry
+
+ // Windows Phone
+ case strings.Contains(agentPlatform, "windows phone "):
+ u.evalWindowsPhone(agentPlatform)
+
+ // Windows, Xbox
+ case strings.Contains(ua, "windows "):
+ u.evalWindows(ua)
+
+ // Kindle
+ case strings.Contains(ua, "kindle/") || amazonFireFingerprint.MatchString(agentPlatform):
+ u.OS.Platform = PlatformLinux
+ u.OS.Name = OSKindle
+
+ // Linux (broader attempt)
+ case strings.Contains(ua, "linux"):
+ u.evalLinux(ua, agentPlatform)
+
+ // WebOS (non-linux flagged)
+ case strings.Contains(ua, "webos") || strings.Contains(ua, "hpwos"):
+ u.OS.Platform = PlatformLinux
+ u.OS.Name = OSWebOS
+
+ // Nintendo
+ case strings.Contains(ua, "nintendo"):
+ u.OS.Platform = PlatformNintendo
+ u.OS.Name = OSNintendo
+
+ // Playstation
+ case strings.Contains(ua, "playstation") || strings.Contains(ua, "vita") || strings.Contains(ua, "psp"):
+ u.OS.Platform = PlatformPlaystation
+ u.OS.Name = OSPlaystation
+
+ // Android
+ case strings.Contains(ua, "android"):
+ u.evalLinux(ua, agentPlatform)
+
+ default:
+ u.OS.Platform = PlatformUnknown
+ u.OS.Name = OSUnknown
+ }
+ }
+
+ return u.isBot()
+}
+
+func (u *UserAgent) isBot() bool {
+
+ if u.OS.Platform == PlatformBot || u.OS.Name == OSBot {
+ u.DeviceType = DeviceComputer
+ return true
+ }
+
+ if u.Browser.Name >= BrowserBot && u.Browser.Name <= BrowserYahooBot {
+ u.OS.Platform = PlatformBot
+ u.OS.Name = OSBot
+ u.DeviceType = DeviceComputer
+ return true
+ }
+
+ return false
+}
+
+// evalLinux returns the `Platform`, `OSName` and Version of UAs with
+// 'linux' listed as their platform.
+func (u *UserAgent) evalLinux(ua string, agentPlatform string) {
+
+ switch {
+ // Kindle Fire
+ case strings.Contains(ua, "kindle") || amazonFireFingerprint.MatchString(agentPlatform):
+ // get the version of Android if available, though we don't call this OSAndroid
+ u.OS.Platform = PlatformLinux
+ u.OS.Name = OSKindle
+ u.OS.Version.findVersionNumber(agentPlatform, "android ")
+
+ // Android, Kindle Fire
+ case strings.Contains(ua, "android") || strings.Contains(ua, "googletv"):
+ // Android
+ u.OS.Platform = PlatformLinux
+ u.OS.Name = OSAndroid
+ u.OS.Version.findVersionNumber(agentPlatform, "android ")
+
+ // ChromeOS
+ case strings.Contains(ua, "cros"):
+ u.OS.Platform = PlatformLinux
+ u.OS.Name = OSChromeOS
+
+ // WebOS
+ case strings.Contains(ua, "webos") || strings.Contains(ua, "hpwos"):
+ u.OS.Platform = PlatformLinux
+ u.OS.Name = OSWebOS
+
+ // Linux, "Linux-like"
+ case strings.Contains(ua, "x11") || strings.Contains(ua, "bsd") || strings.Contains(ua, "suse") || strings.Contains(ua, "debian") || strings.Contains(ua, "ubuntu"):
+ u.OS.Platform = PlatformLinux
+ u.OS.Name = OSLinux
+
+ default:
+ u.OS.Platform = PlatformLinux
+ u.OS.Name = OSLinux
+ }
+}
+
+// evaliOS returns the `Platform`, `OSName` and Version of UAs with
+// 'ipad' or 'iphone' listed as their platform.
+func (u *UserAgent) evaliOS(uaPlatform string, agentPlatform string) {
+
+ switch uaPlatform {
+ // iPhone
+ case "iphone":
+ u.OS.Platform = PlatformiPhone
+ u.OS.Name = OSiOS
+ u.OS.getiOSVersion(agentPlatform)
+
+ // iPad
+ case "ipad":
+ u.OS.Platform = PlatformiPad
+ u.OS.Name = OSiOS
+ u.OS.getiOSVersion(agentPlatform)
+
+ // iPod
+ case "ipod touch", "ipod":
+ u.OS.Platform = PlatformiPod
+ u.OS.Name = OSiOS
+ u.OS.getiOSVersion(agentPlatform)
+
+ default:
+ u.OS.Platform = PlatformiPad
+ u.OS.Name = OSUnknown
+ }
+}
+
+func (u *UserAgent) evalWindowsPhone(agentPlatform string) {
+ u.OS.Platform = PlatformWindowsPhone
+
+ if u.OS.Version.findVersionNumber(agentPlatform, "windows phone os ") || u.OS.Version.findVersionNumber(agentPlatform, "windows phone ") {
+ u.OS.Name = OSWindowsPhone
+ } else {
+ u.OS.Name = OSUnknown
+ }
+}
+
+func (u *UserAgent) evalWindows(ua string) {
+
+ switch {
+ //Xbox -- it reads just like Windows
+ case strings.Contains(ua, "xbox"):
+ u.OS.Platform = PlatformXbox
+ u.OS.Name = OSXbox
+ if !u.OS.Version.findVersionNumber(ua, "windows nt ") {
+ u.OS.Version.Major = 6
+ u.OS.Version.Minor = 0
+ u.OS.Version.Patch = 0
+ }
+
+ // No windows version
+ case !strings.Contains(ua, "windows "):
+ u.OS.Platform = PlatformWindows
+ u.OS.Name = OSUnknown
+
+ case strings.Contains(ua, "windows nt ") && u.OS.Version.findVersionNumber(ua, "windows nt "):
+ u.OS.Platform = PlatformWindows
+ u.OS.Name = OSWindows
+
+ case strings.Contains(ua, "windows xp"):
+ u.OS.Platform = PlatformWindows
+ u.OS.Name = OSWindows
+ u.OS.Version.Major = 5
+ u.OS.Version.Minor = 1
+ u.OS.Version.Patch = 0
+
+ default:
+ u.OS.Platform = PlatformWindows
+ u.OS.Name = OSUnknown
+
+ }
+}
+
+func (u *UserAgent) evalMacintosh(uaPlatformGroup string) {
+ u.OS.Platform = PlatformMac
+ if i := strings.Index(uaPlatformGroup, "os x 10"); i != -1 {
+ u.OS.Name = OSMacOSX
+ u.OS.Version.parse(uaPlatformGroup[i+5:])
+
+ return
+ }
+ u.OS.Name = OSUnknown
+}
+
+func (v *Version) findVersionNumber(s string, m string) bool {
+ if ind := strings.Index(s, m); ind != -1 {
+ return v.parse(s[ind+len(m):])
+ }
+ return false
+}
+
+// getiOSVersion accepts the platform portion of a UA string and returns
+// a Version.
+func (o *OS) getiOSVersion(uaPlatformGroup string) {
+ if i := strings.Index(uaPlatformGroup, "cpu iphone os "); i != -1 {
+ o.Version.parse(uaPlatformGroup[i+14:])
+ return
+ }
+
+ if i := strings.Index(uaPlatformGroup, "cpu os "); i != -1 {
+ o.Version.parse(uaPlatformGroup[i+7:])
+ return
+ }
+
+ o.Version.parse(uaPlatformGroup)
+}
+
+// strToInt simply accepts a string and returns a `int`,
+// with '0' being default.
+func strToInt(str string) int {
+ i, _ := strconv.Atoi(str)
+ return i
+}
+
+// strToVer accepts a string and returns a Version,
+// with {0, 0, 0} being default.
+func (v *Version) parse(str string) bool {
+ if len(str) == 0 || str[0] < '0' || str[0] > '9' {
+ return false
+ }
+ for i := 0; i < 3; i++ {
+ empty := true
+ val := 0
+ l := len(str) - 1
+
+ for k, c := range str {
+ if c >= '0' && c <= '9' {
+ if empty {
+ val = int(c) - 48
+ empty = false
+ if k == l {
+ str = str[:0]
+ }
+ continue
+ }
+
+ if val == 0 {
+ if c == '0' {
+ if k == l {
+ str = str[:0]
+ }
+ continue
+ }
+ str = str[k:]
+ break
+ }
+
+ val = 10*val + int(c) - 48
+ if k == l {
+ str = str[:0]
+ }
+ continue
+ }
+ str = str[k+1:]
+ break
+ }
+
+ switch i {
+ case 0:
+ v.Major = val
+
+ case 1:
+ v.Minor = val
+
+ case 2:
+ v.Patch = val
+ }
+ }
+ return true
+}
diff --git a/vendor/github.com/avct/uasurfer/uasurfer.go b/vendor/github.com/avct/uasurfer/uasurfer.go
new file mode 100644
index 0000000000..15aac6d403
--- /dev/null
+++ b/vendor/github.com/avct/uasurfer/uasurfer.go
@@ -0,0 +1,227 @@
+// Package uasurfer provides fast and reliable abstraction
+// of HTTP User-Agent strings. The philosophy is to identify
+// technologies that holds >1% market share, and to avoid
+// expending resources and accuracy on guessing at esoteric UA
+// strings.
+package uasurfer
+
+import "strings"
+
+//go:generate stringer -type=DeviceType,BrowserName,OSName,Platform -output=const_string.go
+
+// DeviceType (int) returns a constant.
+type DeviceType int
+
+// A complete list of supported devices in the
+// form of constants.
+const (
+ DeviceUnknown DeviceType = iota
+ DeviceComputer
+ DeviceTablet
+ DevicePhone
+ DeviceConsole
+ DeviceWearable
+ DeviceTV
+)
+
+// BrowserName (int) returns a constant.
+type BrowserName int
+
+// A complete list of supported web browsers in the
+// form of constants.
+const (
+ BrowserUnknown BrowserName = iota
+ BrowserChrome
+ BrowserIE
+ BrowserSafari
+ BrowserFirefox
+ BrowserAndroid
+ BrowserOpera
+ BrowserBlackberry
+ BrowserUCBrowser
+ BrowserSilk
+ BrowserNokia
+ BrowserNetFront
+ BrowserQQ
+ BrowserMaxthon
+ BrowserSogouExplorer
+ BrowserSpotify
+ BrowserBot // Bot list begins here
+ BrowserAppleBot
+ BrowserBaiduBot
+ BrowserBingBot
+ BrowserDuckDuckGoBot
+ BrowserFacebookBot
+ BrowserGoogleBot
+ BrowserLinkedInBot
+ BrowserMsnBot
+ BrowserPingdomBot
+ BrowserTwitterBot
+ BrowserYandexBot
+ BrowserYahooBot // Bot list ends here
+)
+
+// OSName (int) returns a constant.
+type OSName int
+
+// A complete list of supported OSes in the
+// form of constants. For handling particular versions
+// of operating systems (e.g. Windows 2000), see
+// the README.md file.
+const (
+ OSUnknown OSName = iota
+ OSWindowsPhone
+ OSWindows
+ OSMacOSX
+ OSiOS
+ OSAndroid
+ OSBlackberry
+ OSChromeOS
+ OSKindle
+ OSWebOS
+ OSLinux
+ OSPlaystation
+ OSXbox
+ OSNintendo
+ OSBot
+)
+
+// Platform (int) returns a constant.
+type Platform int
+
+// A complete list of supported platforms in the
+// form of constants. Many OSes report their
+// true platform, such as Android OS being Linux
+// platform.
+const (
+ PlatformUnknown Platform = iota
+ PlatformWindows
+ PlatformMac
+ PlatformLinux
+ PlatformiPad
+ PlatformiPhone
+ PlatformiPod
+ PlatformBlackberry
+ PlatformWindowsPhone
+ PlatformPlaystation
+ PlatformXbox
+ PlatformNintendo
+ PlatformBot
+)
+
+type Version struct {
+ Major int
+ Minor int
+ Patch int
+}
+
+func (v Version) Less(c Version) bool {
+ if v.Major < c.Major {
+ return true
+ }
+
+ if v.Major > c.Major {
+ return false
+ }
+
+ if v.Minor < c.Minor {
+ return true
+ }
+
+ if v.Minor > c.Minor {
+ return false
+ }
+
+ return v.Patch < c.Patch
+}
+
+type UserAgent struct {
+ Browser Browser
+ OS OS
+ DeviceType DeviceType
+}
+
+type Browser struct {
+ Name BrowserName
+ Version Version
+}
+
+type OS struct {
+ Platform Platform
+ Name OSName
+ Version Version
+}
+
+// Reset resets the UserAgent to it's zero value
+func (ua *UserAgent) Reset() {
+ ua.Browser = Browser{}
+ ua.OS = OS{}
+ ua.DeviceType = DeviceUnknown
+}
+
+// Parse accepts a raw user agent (string) and returns the UserAgent.
+func Parse(ua string) *UserAgent {
+ dest := new(UserAgent)
+ parse(ua, dest)
+ return dest
+}
+
+// ParseUserAgent is the same as Parse, but populates the supplied UserAgent.
+// It is the caller's responsibility to call Reset() on the UserAgent before
+// passing it to this function.
+func ParseUserAgent(ua string, dest *UserAgent) {
+ parse(ua, dest)
+}
+
+func parse(ua string, dest *UserAgent) {
+ ua = normalise(ua)
+ switch {
+ case len(ua) == 0:
+ dest.OS.Platform = PlatformUnknown
+ dest.OS.Name = OSUnknown
+ dest.Browser.Name = BrowserUnknown
+ dest.DeviceType = DeviceUnknown
+
+ // stop on on first case returning true
+ case dest.evalOS(ua):
+ case dest.evalBrowserName(ua):
+ default:
+ dest.evalBrowserVersion(ua)
+ dest.evalDevice(ua)
+ }
+}
+
+// normalise normalises the user supplied agent string so that
+// we can more easily parse it.
+func normalise(ua string) string {
+ if len(ua) <= 1024 {
+ var buf [1024]byte
+ ascii := copyLower(buf[:len(ua)], ua)
+ if !ascii {
+ // Fall back for non ascii characters
+ return strings.ToLower(ua)
+ }
+ return string(buf[:len(ua)])
+ }
+ // Fallback for unusually long strings
+ return strings.ToLower(ua)
+}
+
+// copyLower copies a lowercase version of s to b. It assumes s contains only single byte characters
+// and will panic if b is nil or is not long enough to contain all the bytes from s.
+// It returns early with false if any characters were non ascii.
+func copyLower(b []byte, s string) bool {
+ for j := 0; j < len(s); j++ {
+ c := s[j]
+ if c > 127 {
+ return false
+ }
+
+ if 'A' <= c && c <= 'Z' {
+ c += 'a' - 'A'
+ }
+
+ b[j] = c
+ }
+ return true
+}
diff --git a/vendor/github.com/avct/uasurfer/uasurfer_test.go b/vendor/github.com/avct/uasurfer/uasurfer_test.go
new file mode 100644
index 0000000000..8668a159fa
--- /dev/null
+++ b/vendor/github.com/avct/uasurfer/uasurfer_test.go
@@ -0,0 +1,1064 @@
+package uasurfer
+
+import "testing"
+
+var testUAVars = []struct {
+ UA string
+ UserAgent
+}{
+ // Empty
+ {"",
+ UserAgent{}},
+
+ // Single char
+ {"a",
+ UserAgent{}},
+
+ // Some random string
+ {"some random string",
+ UserAgent{}},
+
+ // Potentially malformed ua
+ {")(",
+ UserAgent{}},
+
+ // iPhone
+ {"Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X) AppleWebKit/546.10 (KHTML, like Gecko) Version/6.0 Mobile/7E18WD Safari/8536.25",
+ UserAgent{
+ Browser{BrowserSafari, Version{6, 0, 0}}, OS{PlatformiPhone, OSiOS, Version{7, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (iPhone; CPU iPhone OS 8_0_2 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12A405 Safari/600.1.4",
+ UserAgent{
+ Browser{BrowserSafari, Version{8, 0, 0}}, OS{PlatformiPhone, OSiOS, Version{8, 0, 2}}, DevicePhone}},
+
+ // iPad
+ {"Mozilla/5.0(iPad; U; CPU iPhone OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B314 Safari/531.21.10",
+ UserAgent{
+ Browser{BrowserSafari, Version{4, 0, 4}}, OS{PlatformiPad, OSiOS, Version{3, 2, 0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (iPad; CPU OS 9_0 like Mac OS X) AppleWebKit/601.1.17 (KHTML, like Gecko) Version/8.0 Mobile/13A175 Safari/600.1.4",
+ UserAgent{
+ Browser{BrowserSafari, Version{8, 0, 0}}, OS{PlatformiPad, OSiOS, Version{9, 0, 0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (iPhone; CPU iPhone OS 10_0 like Mac OS X) AppleWebKit/602.1.32 (KHTML, like Gecko) Version/10.0 Mobile/14A5261v Safari/602.1",
+ UserAgent{
+ Browser{BrowserSafari, Version{10, 0, 0}}, OS{PlatformiPhone, OSiOS, Version{10, 0, 0}}, DevicePhone}},
+
+ // Chrome
+ {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36",
+ UserAgent{
+ Browser{BrowserChrome, Version{43, 0, 2357}}, OS{PlatformMac, OSMacOSX, Version{10, 10, 4}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en) AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60 Mobile/9B206 Safari/534.48.3",
+ UserAgent{
+ Browser{BrowserChrome, Version{19, 0, 1084}}, OS{PlatformiPhone, OSiOS, Version{5, 1, 1}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; Android 6.0; Nexus 5X Build/MDB08L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36",
+ UserAgent{
+ Browser{BrowserChrome, Version{46, 0, 2490}}, OS{PlatformLinux, OSAndroid, Version{6, 0, 0}}, DevicePhone}},
+
+ // Chromium (Chrome)
+ {"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.19 (KHTML, like Gecko) Ubuntu/11.10 Chromium/18.0.1025.142 Chrome/18.0.1025.142 Safari/535.19",
+ UserAgent{
+ Browser{BrowserChrome, Version{18, 0, 1025}}, OS{PlatformLinux, OSLinux, Version{0, 0, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36",
+ UserAgent{
+ Browser{BrowserChrome, Version{45, 0, 2454}}, OS{PlatformMac, OSMacOSX, Version{10, 11, 0}}, DeviceComputer}},
+
+ //TODO: refactor "getVersion()" to handle this device/chrome version douchebaggery
+ // {"Mozilla/5.0 (Linux; Android 4.4.2; en-gb; SAMSUNG SM-G800F Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/1.6 Chrome/28.0.1500.94 Mobile Safari/537.36",
+ // UserAgent{
+ // Browser{BrowserChrome, Version{28,0,1500}, OS{PlatformLinux, OSAndroid, Version{4,4,2}}, DevicePhone}},
+
+ // Safari
+ {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/600.7.12 (KHTML, like Gecko) Version/8.0.7 Safari/600.7.12",
+ UserAgent{
+ Browser{BrowserSafari, Version{8, 0, 7}}, OS{PlatformMac, OSMacOSX, Version{10, 10, 4}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_5; en-us) AppleWebKit/525.26.2 (KHTML, like Gecko) Version/3.2 Safari/525.26.12",
+ UserAgent{
+ Browser{BrowserSafari, Version{3, 2, 0}}, OS{PlatformMac, OSMacOSX, Version{10, 5, 5}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12) AppleWebKit/602.1.32 (KHTML, like Gecko) Version/10.0 Safari/602.1.32", // macOS Sierra dev beta
+ UserAgent{
+ Browser{BrowserSafari, Version{10, 0, 0}}, OS{PlatformMac, OSMacOSX, Version{10, 12, 0}}, DeviceComputer}},
+
+ // Firefox
+ {"Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4",
+ UserAgent{
+ Browser{BrowserFirefox, Version{1, 0, 0}}, OS{PlatformiPhone, OSiOS, Version{8, 3, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Android 4.4; Tablet; rv:41.0) Gecko/41.0 Firefox/41.0",
+ UserAgent{
+ Browser{BrowserFirefox, Version{41, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{4, 4, 0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (Android; Mobile; rv:40.0) Gecko/40.0 Firefox/40.0",
+ UserAgent{
+ Browser{BrowserFirefox, Version{40, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{0, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0",
+ UserAgent{
+ Browser{BrowserFirefox, Version{38, 0, 0}}, OS{PlatformLinux, OSLinux, Version{0, 0, 0}}, DeviceComputer}},
+
+ // Silk
+ {"Mozilla/5.0 (Linux; U; Android 4.4.3; de-de; KFTHWI Build/KTU84M) AppleWebKit/537.36 (KHTML, like Gecko) Silk/3.47 like Chrome/37.0.2026.117 Safari/537.36",
+ UserAgent{
+ Browser{BrowserSilk, Version{3, 47, 0}}, OS{PlatformLinux, OSKindle, Version{4, 4, 3}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (Linux; U; en-us; KFJWI Build/IMM76D) AppleWebKit/535.19 (KHTML like Gecko) Silk/2.4 Safari/535.19 Silk-Acceleratedtrue",
+ UserAgent{
+ Browser{BrowserSilk, Version{2, 4, 0}}, OS{PlatformLinux, OSKindle, Version{0, 0, 0}}, DeviceTablet}},
+
+ // Opera
+ {"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36 OPR/18.0.1284.68",
+ UserAgent{
+ Browser{BrowserOpera, Version{18, 0, 1284}}, OS{PlatformWindows, OSWindows, Version{6, 1, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (iPhone; CPU iPhone OS 8_4 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) OPiOS/10.2.0.93022 Mobile/12H143 Safari/9537.53",
+ UserAgent{
+ Browser{BrowserOpera, Version{10, 2, 0}}, OS{PlatformiPhone, OSiOS, Version{8, 4, 0}}, DevicePhone}},
+
+ // Internet Explorer -- https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
+ {"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.123",
+ UserAgent{
+ Browser{BrowserIE, Version{12, 123, 0}}, OS{PlatformWindows, OSWindows, Version{10, 0, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)",
+ UserAgent{
+ Browser{BrowserIE, Version{10, 0, 0}}, OS{PlatformWindows, OSWindows, Version{6, 2, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C; rv:11.0) like Gecko",
+ UserAgent{
+ Browser{BrowserIE, Version{11, 0, 0}}, OS{PlatformWindows, OSWindows, Version{6, 3, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; DEVICE INFO) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Mobile Safari/537.36 Edge/12.123",
+ UserAgent{
+ Browser{BrowserIE, Version{12, 123, 0}}, OS{PlatformWindowsPhone, OSWindowsPhone, Version{10, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 520) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537",
+ UserAgent{
+ Browser{BrowserIE, Version{11, 0, 0}}, OS{PlatformWindowsPhone, OSWindowsPhone, Version{8, 1, 0}}, DevicePhone}},
+
+ {"Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; SV1; .NET CLR 1.1.4322; .NET CLR 1.0.3705; .NET CLR 2.0.50727)",
+ UserAgent{
+ Browser{BrowserIE, Version{5, 0, 1}}, OS{PlatformWindows, OSWindows, Version{5, 0, 0}}, DeviceComputer}},
+
+ {"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/4.0; GTB6.4; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; OfficeLiveConnector.1.3; OfficeLivePatch.0.0; .NET CLR 1.1.4322)",
+ UserAgent{
+ Browser{BrowserIE, Version{7, 0, 0}}, OS{PlatformWindows, OSWindows, Version{6, 1, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ARM; Trident/6.0; Touch)", //Windows Surface RT tablet
+ UserAgent{
+ Browser{BrowserIE, Version{10, 0, 0}}, OS{PlatformWindows, OSWindows, Version{6, 2, 0}}, DeviceTablet}},
+
+ // UC Browser
+ {"Mozilla/5.0 (Linux; U; Android 2.3.4; en-US; MT11i Build/4.0.2.A.0.62) AppleWebKit/534.31 (KHTML, like Gecko) UCBrowser/9.0.1.275 U3/0.8.0 Mobile Safari/534.31",
+ UserAgent{
+ Browser{BrowserUCBrowser, Version{9, 0, 1}}, OS{PlatformLinux, OSAndroid, Version{2, 3, 4}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 4.0.4; en-US; Micromax P255 Build/IMM76D) AppleWebKit/534.31 (KHTML, like Gecko) UCBrowser/9.2.0.308 U3/0.8.0 Mobile Safari/534.31",
+ UserAgent{
+ Browser{BrowserUCBrowser, Version{9, 2, 0}}, OS{PlatformLinux, OSAndroid, Version{4, 0, 4}}, DevicePhone}},
+
+ {"UCWEB/2.0 (Java; U; MIDP-2.0; en-US; MicromaxQ5) U2/1.0.0 UCBrowser/9.4.0.342 U2/1.0.0 Mobile",
+ UserAgent{
+ Browser{BrowserUCBrowser, Version{9, 4, 0}}, OS{PlatformUnknown, OSUnknown, Version{0, 0, 0}}, DevicePhone}},
+
+ // Nokia Browser
+ // {"Mozilla/5.0 (Series40; Nokia501/14.0.4/java_runtime_version=Nokia_Asha_1_2; Profile/MIDP-2.1 Configuration/CLDC-1.1) Gecko/20100401 S40OviBrowser/4.0.0.0.45",
+ // UserAgent{
+ // Browser{BrowserUnknown, Version{4,0,0}}, OS{PlatformUnknown, OSUnknown, Version{0,0,0}}, DevicePhone}},
+
+ // {"Mozilla/5.0 (Symbian/3; Series60/5.3 NokiaN8-00/111.040.1511; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/535.1 (KHTML, like Gecko) NokiaBrowser/8.3.1.4 Mobile Safari/535.1",
+ // UserAgent{
+ // Browser{BrowserUnknown, Version{8,0,0}}, OS{PlatformUnknown, OSUnknown, Version{0,0,0}}, DevicePhone}},
+
+ // {"NokiaN97/21.1.107 (SymbianOS/9.4; Series60/5.0 Mozilla/5.0; Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebkit/525 (KHTML, like Gecko) BrowserNG/7.1.4",
+ // BrowserUnknown, Version{7,0,0}}, OS{PlatformUnknown, OSUnknown, Version{0,0,0}}, DevicePhone}},
+
+ // ChromeOS
+ {"Mozilla/5.0 (X11; U; CrOS i686 9.10.0; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.253.0 Safari/532.5",
+ UserAgent{
+ Browser{BrowserChrome, Version{4, 0, 253}}, OS{PlatformLinux, OSChromeOS, Version{0, 0, 0}}, DeviceComputer}},
+
+ // iPod, iPod Touch
+ {"mozilla/5.0 (ipod touch; cpu iphone os 9_3_3 like mac os x) applewebkit/601.1.46 (khtml, like gecko) version/9.0 mobile/13g34 safari/601.1",
+ UserAgent{
+ Browser{BrowserSafari, Version{9, 0, 0}}, OS{PlatformiPod, OSiOS, Version{9, 3, 3}}, DeviceTablet}},
+
+ {"mozilla/5.0 (ipod; cpu iphone os 6_1_6 like mac os x) applewebkit/536.26 (khtml, like gecko) version/6.0 mobile/10b500 safari/8536.25",
+ UserAgent{
+ Browser{BrowserSafari, Version{6, 0, 0}}, OS{PlatformiPod, OSiOS, Version{6, 1, 6}}, DeviceTablet}},
+
+ // WebOS
+ {"Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.0; U; de-DE) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.70 Safari/534.6 TouchPad/1.0",
+ UserAgent{
+ Browser{BrowserUnknown, Version{0, 0, 0}}, OS{PlatformLinux, OSWebOS, Version{0, 0, 0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (webOS/1.4.1.1; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pre/1.0",
+ UserAgent{
+ Browser{BrowserUnknown, Version{1, 0, 0}}, OS{PlatformLinux, OSWebOS, Version{0, 0, 0}}, DevicePhone}},
+
+ // Android WebView (Android <= 4.3)
+ {"Mozilla/5.0 (Linux; U; Android 2.2; en-us; DROID2 GLOBAL Build/S273) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
+ UserAgent{
+ Browser{BrowserAndroid, Version{4, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{2, 2, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 4.0.3; de-ch; HTC Sensation Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari53/4.30",
+ UserAgent{
+ Browser{BrowserAndroid, Version{4, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{4, 0, 3}}, DevicePhone}},
+
+ // BlackBerry
+ {"Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML, like Gecko) Version/7.2.1.0 Safari/536.2+",
+ UserAgent{
+ Browser{BrowserBlackberry, Version{7, 2, 1}}, OS{PlatformBlackberry, OSBlackberry, Version{0, 0, 0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (BB10; Kbd) AppleWebKit/537.35+ (KHTML, like Gecko) Version/10.2.1.1925 Mobile Safari/537.35+",
+ UserAgent{
+ Browser{BrowserBlackberry, Version{10, 2, 1}}, OS{PlatformBlackberry, OSBlackberry, Version{0, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0) BlackBerry8703e/4.1.0 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/104",
+ UserAgent{
+ Browser{BrowserBlackberry, Version{0, 0, 0}}, OS{PlatformBlackberry, OSBlackberry, Version{0, 0, 0}}, DevicePhone}},
+
+ // Windows Phone
+ {"Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 625; ANZ941)",
+ UserAgent{
+ Browser{BrowserIE, Version{10, 0, 0}}, OS{PlatformWindowsPhone, OSWindowsPhone, Version{8, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; NOKIA; Lumia 900)",
+ UserAgent{
+ Browser{BrowserIE, Version{9, 0, 0}}, OS{PlatformWindowsPhone, OSWindowsPhone, Version{7, 5, 0}}, DevicePhone}},
+
+ // Kindle eReader
+ {"Mozilla/5.0 (Linux; U; en-US) AppleWebKit/528.5+ (KHTML, like Gecko, Safari/528.5+) Version/4.0 Kindle/3.0 (screen 600×800; rotate)",
+ UserAgent{
+ Browser{BrowserUnknown, Version{4, 0, 0}}, OS{PlatformLinux, OSKindle, Version{0, 0, 0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (X11; U; Linux armv7l like Android; en-us) AppleWebKit/531.2+ (KHTML, like Gecko) Version/5.0 Safari/533.2+ Kindle/3.0+",
+ UserAgent{
+ Browser{BrowserUnknown, Version{5, 0, 0}}, OS{PlatformLinux, OSKindle, Version{0, 0, 0}}, DeviceTablet}},
+
+ // Amazon Fire
+ {"Mozilla/5.0 (Linux; U; Android 4.4.3; de-de; KFTHWI Build/KTU84M) AppleWebKit/537.36 (KHTML, like Gecko) Silk/3.67 like Chrome/39.0.2171.93 Safari/537.36",
+ UserAgent{
+ Browser{BrowserSilk, Version{3, 67, 0}}, OS{PlatformLinux, OSKindle, Version{4, 4, 3}}, DeviceTablet}}, // Fire tablet
+
+ {"Mozilla/5.0 (Linux; U; Android 4.2.2; enus; KFTHWI Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Silk/3.22 like Chrome/34.0.1847.137 Mobile Safari/537.36",
+ UserAgent{
+ Browser{BrowserSilk, Version{3, 22, 0}}, OS{PlatformLinux, OSKindle, Version{4, 2, 2}}, DeviceTablet}}, // Fire tablet, but with "Mobile"
+
+ {"Mozilla/5.0 (Linux; Android 4.4.4; SD4930UR Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/34.0.0.0 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/35.0.0.48.273;]",
+ UserAgent{
+ Browser{BrowserChrome, Version{34, 0, 0}}, OS{PlatformLinux, OSKindle, Version{4, 4, 4}}, DevicePhone}}, // Facebook app on Fire Phone
+
+ {"mozilla/5.0 (linux; android 4.4.3; kfthwi build/ktu84m) applewebkit/537.36 (khtml, like gecko) version/4.0 chrome/34.0.0.0 safari/537.36 [pinterest/android]",
+ UserAgent{
+ Browser{BrowserChrome, Version{34, 0, 0}}, OS{PlatformLinux, OSKindle, Version{4, 4, 3}}, DeviceTablet}}, // Fire tablet running pinterest
+
+ // extra logic to identify phone when using silk has not been added
+ // {"Mozilla/5.0 (Linux; Android 4.4.4; SD4930UR Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Silk/3.67 like Chrome/39.0.2171.93 Mobile Safari/537.36",
+ // UserAgent{
+ // Browser{BrowserSilk, Version{3,0,0}}, OS{PlatformLinux, OSKindle, Version{4,0,0}}, DevicePhone}}, // Silk on Fire Phone
+
+ // Nintendo
+ {"Opera/9.30 (Nintendo Wii; U; ; 2047-7; fr)",
+ UserAgent{
+ Browser{BrowserOpera, Version{9, 30, 0}}, OS{PlatformNintendo, OSNintendo, Version{0, 0, 0}}, DeviceConsole}},
+
+ {"Mozilla/5.0 (Nintendo WiiU) AppleWebKit/534.52 (KHTML, like Gecko) NX/2.1.0.8.21 NintendoBrowser/1.0.0.7494.US",
+ UserAgent{
+ Browser{BrowserUnknown, Version{0, 0, 0}}, OS{PlatformNintendo, OSNintendo, Version{0, 0, 0}}, DeviceConsole}},
+
+ // Xbox
+ {"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; Xbox)", //Xbox 360
+ UserAgent{
+ Browser{BrowserIE, Version{9, 0, 0}}, OS{PlatformXbox, OSXbox, Version{6, 1, 0}}, DeviceConsole}},
+
+ // Playstation
+ {"Mozilla/5.0 (PlayStation 4 4.50) AppleWebKit/601.2 (KHTML, like Gecko)",
+ UserAgent{
+ Browser{BrowserUnknown, Version{0, 0, 0}}, OS{PlatformPlaystation, OSPlaystation, Version{0, 0, 0}}, DeviceConsole}},
+
+ {"Mozilla/5.0 (Playstation Vita 1.61) AppleWebKit/531.22.8 (KHTML, like Gecko) Silk/3.2",
+ UserAgent{
+ Browser{BrowserSilk, Version{3, 2, 0}}, OS{PlatformPlaystation, OSPlaystation, Version{0, 0, 0}}, DeviceConsole}},
+
+ // Smart TVs and TV dongles
+ {"Mozilla/5.0 (CrKey armv7l 1.4.15250) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.0 Safari/537.36", // Chromecast
+ UserAgent{
+ Browser{BrowserChrome, Version{31, 0, 1650}}, OS{PlatformUnknown, OSUnknown, Version{0, 0, 0}}, DeviceTV}},
+
+ {"Mozilla/5.0 (Linux; GoogleTV 3.2; VAP430 Build/MASTER) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.77 Safari/534.24", // Google TV
+ UserAgent{
+ Browser{BrowserChrome, Version{11, 0, 696}}, OS{PlatformLinux, OSAndroid, Version{0, 0, 0}}, DeviceTV}},
+
+ {"Mozilla/5.0 (Linux; Android 5.0; ADT-1 Build/LPX13D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.89 Mobile Safari/537.36", // Android TV
+ UserAgent{
+ Browser{BrowserChrome, Version{40, 0, 2214}}, OS{PlatformLinux, OSAndroid, Version{5, 0, 0}}, DeviceTV}},
+
+ {"Mozilla/5.0 (Linux; Android 4.2.2; AFTB Build/JDQ39) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.173 Mobile Safari/537.22", // Amazon Fire
+ UserAgent{
+ Browser{BrowserChrome, Version{25, 0, 1364}}, OS{PlatformLinux, OSAndroid, Version{4, 2, 2}}, DeviceTV}},
+
+ {"Mozilla/5.0 (Unknown; Linux armv7l) AppleWebKit/537.1+ (KHTML, like Gecko) Safari/537.1+ LG Browser/6.00.00(+mouse+3D+SCREEN+TUNER; LGE; GLOBAL-PLAT5; 03.07.01; 0x00000001;); LG NetCast.TV-2013/03.17.01 (LG, GLOBAL-PLAT4, wired)", // LG TV
+ UserAgent{
+ Browser{BrowserUnknown, Version{0, 0, 0}}, OS{PlatformLinux, OSLinux, Version{0, 0, 0}}, DeviceTV}},
+
+ {"Mozilla/5.0 (X11; FreeBSD; U; Viera; de-DE) AppleWebKit/537.11 (KHTML, like Gecko) Viera/3.10.0 Chrome/23.0.1271.97 Safari/537.11", // Panasonic Viera
+ UserAgent{
+ Browser{BrowserChrome, Version{23, 0, 1271}}, OS{PlatformLinux, OSLinux, Version{0, 0, 0}}, DeviceTV}},
+
+ // TODO: not catching "browser/" and reporting as safari -- ua string not being fully checked?
+ // {"Mozilla/5.0 (DTV) AppleWebKit/531.2+ (KHTML, like Gecko) Espial/6.1.5 AQUOSBrowser/2.0 (US01DTV;V;0001;0001)", // Sharp Aquos
+ // BrowserUnknown, Version{0,0,0}}, OS{PlatformUnknown, OSUnknown, Version{0,0,0}}, DeviceTV}},
+
+ {"Roku/DVP-5.2 (025.02E03197A)", // Roku
+ UserAgent{
+ Browser{BrowserUnknown, Version{0, 0, 0}}, OS{PlatformUnknown, OSUnknown, Version{0, 0, 0}}, DeviceTV}},
+
+ {"mozilla/5.0 (smart-tv; linux; tizen 2.3) applewebkit/538.1 (khtml, like gecko) samsungbrowser/1.0 tv safari/538.1", // Samsung SmartTV
+ UserAgent{
+ Browser{BrowserUnknown, Version{0, 0, 0}}, OS{PlatformLinux, OSLinux, Version{0, 0, 0}}, DeviceTV}},
+
+ {"mozilla/5.0 (linux; u) applewebkit/537.36 (khtml, like gecko) version/4.0 mobile safari/537.36 smarttv/6.0 (netcast)",
+ UserAgent{
+ Browser{BrowserUnknown, Version{4, 0, 0}}, OS{PlatformLinux, OSLinux, Version{0, 0, 0}}, DeviceTV}},
+
+ // Google search app (GSA) for iOS -- it's Safari in disguise as of v6
+ {"Mozilla/5.0 (iPad; CPU OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) GSA/6.0.51363 Mobile/12F69 Safari/600.1.4",
+ UserAgent{
+ Browser{BrowserSafari, Version{8, 3, 0}}, OS{PlatformiPad, OSiOS, Version{8, 3, 0}}, DeviceTablet}},
+
+ // Spotify (applicable for advertising applications)
+ {"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Spotify/1.0.9.133 Safari/537.36",
+ UserAgent{
+ Browser{BrowserSpotify, Version{1, 0, 9}}, OS{PlatformWindows, OSWindows, Version{5, 1, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Spotify/1.0.9.133 Safari/537.36",
+ UserAgent{
+ Browser{BrowserSpotify, Version{1, 0, 9}}, OS{PlatformMac, OSMacOSX, Version{10, 10, 2}}, DeviceComputer}},
+
+ // Bots
+ {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5 (Applebot/0.1; +http://www.apple.com/go/applebot)",
+ UserAgent{
+ Browser{BrowserAppleBot, Version{0, 0, 0}}, OS{PlatformBot, OSBot, Version{10, 10, 1}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)",
+ UserAgent{
+ Browser{BrowserBaiduBot, Version{0, 0, 0}}, OS{PlatformBot, OSBot, Version{0, 0, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)",
+ UserAgent{
+ Browser{BrowserBingBot, Version{0, 0, 0}}, OS{PlatformBot, OSBot, Version{0, 0, 0}}, DeviceComputer}},
+
+ {"DuckDuckBot/1.0; (+http://duckduckgo.com/duckduckbot.html)",
+ UserAgent{
+ Browser{BrowserDuckDuckGoBot, Version{0, 0, 0}}, OS{PlatformBot, OSBot, Version{0, 0, 0}}, DeviceComputer}},
+
+ {"facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)",
+ UserAgent{
+ Browser{BrowserFacebookBot, Version{0, 0, 0}}, OS{PlatformBot, OSBot, Version{0, 0, 0}}, DeviceComputer}},
+
+ {"Facebot/1.0",
+ UserAgent{
+ Browser{BrowserFacebookBot, Version{0, 0, 0}}, OS{PlatformBot, OSBot, Version{0, 0, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)",
+ UserAgent{
+ Browser{BrowserGoogleBot, Version{0, 0, 0}}, OS{PlatformBot, OSBot, Version{0, 0, 0}}, DeviceComputer}},
+
+ {"LinkedInBot/1.0 (compatible; Mozilla/5.0; Jakarta Commons-HttpClient/3.1 +http://www.linkedin.com)",
+ UserAgent{
+ Browser{BrowserLinkedInBot, Version{0, 0, 0}}, OS{PlatformBot, OSBot, Version{0, 0, 0}}, DeviceComputer}},
+
+ {"msnbot/2.0b (+http://search.msn.com/msnbot.htm)",
+ UserAgent{
+ Browser{BrowserMsnBot, Version{0, 0, 0}}, OS{PlatformBot, OSBot, Version{0, 0, 0}}, DeviceComputer}},
+
+ {"Pingdom.com_bot_version_1.4_(http://www.pingdom.com/)",
+ UserAgent{
+ Browser{BrowserPingdomBot, Version{0, 0, 0}}, OS{PlatformBot, OSBot, Version{0, 0, 0}}, DeviceComputer}},
+
+ {"Twitterbot/1.0",
+ UserAgent{
+ Browser{BrowserTwitterBot, Version{0, 0, 0}}, OS{PlatformBot, OSBot, Version{0, 0, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)",
+ UserAgent{
+ Browser{BrowserYandexBot, Version{0, 0, 0}}, OS{PlatformBot, OSBot, Version{0, 0, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)",
+ UserAgent{
+ Browser{BrowserYahooBot, Version{0, 0, 0}}, OS{PlatformBot, OSBot, Version{0, 0, 0}}, DeviceComputer}},
+
+ // {"Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)",
+ // BrowserBot, Version{0,0,0}}, OS{PlatformBot, OSBot, Version{6,0,0}}, DeviceComputer}},
+
+ {"mozilla/5.0 (unknown; linux x86_64) applewebkit/538.1 (khtml, like gecko) phantomjs/2.1.1 safari/538.1",
+ UserAgent{
+ Browser{BrowserBot, Version{0, 0, 0}}, OS{PlatformBot, OSBot, Version{0, 0, 0}}, DeviceComputer}},
+
+ // Unknown or partially handled
+ {"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.1b3pre) Gecko/20090223 SeaMonkey/2.0a3", //Seamonkey (~FF)
+ UserAgent{
+ Browser{BrowserFirefox, Version{0, 0, 0}}, OS{PlatformMac, OSMacOSX, Version{10, 4, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en; rv:1.9.0.8pre) Gecko/2009022800 Camino/2.0b3pre", //Camino (~FF)
+ UserAgent{
+ Browser{BrowserUnknown, Version{0, 0, 0}}, OS{PlatformMac, OSMacOSX, Version{10, 5, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Mobile; rv:26.0) Gecko/26.0 Firefox/26.0", //firefox OS
+ UserAgent{
+ Browser{BrowserFirefox, Version{26, 0, 0}}, OS{PlatformUnknown, OSUnknown, Version{0, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.45 Safari/535.19", //chrome for android having requested desktop site
+ UserAgent{
+ Browser{BrowserChrome, Version{18, 0, 1025}}, OS{PlatformLinux, OSLinux, Version{0, 0, 0}}, DeviceComputer}},
+
+ {"Opera/9.80 (S60; SymbOS; Opera Mobi/352; U; de) Presto/2.4.15 Version/10.00",
+ UserAgent{
+ Browser{BrowserOpera, Version{10, 0, 0}}, OS{PlatformUnknown, OSUnknown, Version{0, 0, 0}}, DevicePhone}},
+
+ // BrowserQQ
+ // {"Mozilla/5.0 (Windows NT 6.2; WOW64; Trident/7.0; Touch; .NET4.0E; .NET4.0C; .NET CLR 3.5.30729; .NET CLR 2.0.50727; .NET CLR 3.0.30729; InfoPath.3; Tablet PC 2.0; QQBrowser/7.6.21433.400; rv:11.0) like Gecko",
+ // UserAgent{
+ // Browser{BrowserQQ, Version{7,0,0}}, OS{PlatformWindows, OSWindows, Version{8,0,0}}, DeviceTablet}},
+
+ // {"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.124 Safari/537.36 QQBrowser/9.0.2191.400",
+ // UserAgent{
+ // Browser{BrowserQQ, Version{9,0,0}}, OS{PlatformWindows, OSWindows, Version{7,0,0}}, DeviceComputer}},
+
+ // ANDROID TESTS
+
+ {"Mozilla/5.0 (Linux; U; Android 1.0; en-us; dream) AppleWebKit/525.10+ (KHTML,like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
+ UserAgent{
+ Browser{BrowserAndroid, Version{3, 0, 4}}, OS{PlatformLinux, OSAndroid, Version{1, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 1.0; en-us; generic) AppleWebKit/525.10 (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
+ UserAgent{
+ Browser{BrowserAndroid, Version{3, 0, 4}}, OS{PlatformLinux, OSAndroid, Version{1, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 1.0.3; de-de; A80KSC Build/ECLAIR) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
+ UserAgent{
+ Browser{BrowserAndroid, Version{4, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{1, 0, 3}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 1.5; en-gb; T-Mobile G1 Build/CRC1) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
+ UserAgent{
+ Browser{BrowserAndroid, Version{3, 1, 2}}, OS{PlatformLinux, OSAndroid, Version{1, 5, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 1.5; es-; FBW1_4 Build/MASTER) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
+ UserAgent{
+ Browser{BrowserAndroid, Version{3, 0, 4}}, OS{PlatformLinux, OSAndroid, Version{1, 5, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux U; Android 1.5 en-us hero) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
+ UserAgent{
+ Browser{BrowserAndroid, Version{3, 0, 4}}, OS{PlatformLinux, OSAndroid, Version{1, 5, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 1.5; en-us; Opus One Build/RBE.00.00) AppleWebKit/528.18.1 (KHTML, like Gecko) Version/3.1.1 Mobile Safari/525.20.1",
+ UserAgent{
+ Browser{BrowserAndroid, Version{3, 1, 1}}, OS{PlatformLinux, OSAndroid, Version{1, 5, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 1.6; ar-us; SonyEricssonX10i Build/R2BA026) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
+ UserAgent{
+ Browser{BrowserAndroid, Version{3, 1, 2}}, OS{PlatformLinux, OSAndroid, Version{1, 6, 0}}, DevicePhone}},
+
+ // TODO: support names of Android OS?
+ //{"Mozilla/5.0 (Linux; U; Android Donut; de-de; HTC Tattoo 1.52.161.1 Build/Donut) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
+ // UserAgent{
+ // Browser{BrowserAndroid, Version{3, 1, 2}}, OS{PlatformLinux, OSAndroid, Version{1, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 1.6; en-gb; HTC Tattoo Build/DRC79) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
+ UserAgent{
+ Browser{BrowserAndroid, Version{3, 0, 4}}, OS{PlatformLinux, OSAndroid, Version{1, 6, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 1.6; ja-jp; Docomo HT-03A Build/DRD08) AppleWebKit/525.10 (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
+ UserAgent{
+ Browser{BrowserAndroid, Version{3, 0, 4}}, OS{PlatformLinux, OSAndroid, Version{1, 6, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 2.1; en-us; Nexus One Build/ERD62) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
+ UserAgent{
+ Browser{BrowserAndroid, Version{4, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{2, 1, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 2.1-update1; en-au; HTC_Desire_A8183 V1.16.841.1 Build/ERE27) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17",
+ UserAgent{
+ Browser{BrowserAndroid, Version{4, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{2, 1, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 2.1; en-us; generic) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
+ UserAgent{
+ Browser{BrowserAndroid, Version{3, 0, 4}}, OS{PlatformLinux, OSAndroid, Version{2, 1, 0}}, DevicePhone}},
+
+ // TODO support named versions of Android?
+ {"Mozilla/5.0 (Linux; U; Android Eclair; en-us; sholes) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2",
+ UserAgent{
+ Browser{BrowserAndroid, Version{3, 0, 4}}, OS{PlatformLinux, OSAndroid, Version{0, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 2.2; en-sa; HTC_DesireHD_A9191 Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
+ UserAgent{
+ Browser{BrowserAndroid, Version{4, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{2, 2, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 2.2.1; en-gb; HTC_DesireZ_A7272 Build/FRG83D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
+ UserAgent{
+ Browser{BrowserAndroid, Version{4, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{2, 2, 1}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 2.3.3; en-us; Sensation_4G Build/GRI40) AppleWebKit/533.1 (KHTML, like Gecko) Version/5.0 Safari/533.16",
+ UserAgent{
+ Browser{BrowserAndroid, Version{5, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{2, 3, 3}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 2.3.5; ko-kr; SHW-M250S Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
+ UserAgent{
+ Browser{BrowserAndroid, Version{4, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{2, 3, 5}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 2.3.7; ja-jp; L-02D Build/GWK74) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
+ UserAgent{
+ Browser{BrowserAndroid, Version{4, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{2, 3, 7}}, DevicePhone}},
+
+ // TODO: is tablet, not phone
+ {"Mozilla/5.0 (Linux; U; Android 3.0; xx-xx; Transformer TF101 Build/HRI66) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13",
+ UserAgent{
+ Browser{BrowserAndroid, Version{4, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{3, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13",
+ UserAgent{
+ Browser{BrowserAndroid, Version{4, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{3, 0, 0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (Linux; U; Android 4.0.1; en-us; sdk Build/ICS_MR0) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
+ UserAgent{
+ Browser{BrowserAndroid, Version{4, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{4, 0, 1}}, DevicePhone}},
+
+ // TODO support "android-" version prefix
+ // However, can't find reference to this naming scheme in real-world UA gathering
+ // {"Mozilla/5.0 (Linux; U; Android-4.0.3; en-us; Galaxy Nexus Build/IML74K) AppleWebKit/535.7 (KHTML, like Gecko) CrMo/16.0.912.75 Mobile Safari/535.7",
+ // UserAgent{
+ // Browser{BrowserChrome, Version{16,0,0}}, OS{PlatformLinux, OSAndroid, Version{4,0,0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Nexus S Build/JRO03E) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
+ UserAgent{
+ Browser{BrowserAndroid, Version{4, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{4, 1, 1}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 4.1; en-gb; Build/JRN84D) AppleWebKit/534.30 (KHTML like Gecko) Version/4.0 Mobile Safari/534.30",
+ UserAgent{
+ Browser{BrowserAndroid, Version{4, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{4, 1, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 4.1.1; el-gr; MB525 Build/JRO03H; CyanogenMod-10) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
+ UserAgent{
+ Browser{BrowserAndroid, Version{4, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{4, 1, 1}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 4.1.1; fr-fr; MB525 Build/JRO03H; CyanogenMod-10) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
+ UserAgent{
+ Browser{BrowserAndroid, Version{4, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{4, 1, 1}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; U; Android 4.2; en-us; Nexus 10 Build/JVP15I) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30",
+ UserAgent{
+ Browser{BrowserAndroid, Version{4, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{4, 2, 0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (Linux; U; Android 4.2; ro-ro; LT18i Build/4.1.B.0.431) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
+ UserAgent{
+ Browser{BrowserAndroid, Version{4, 0, 0}}, OS{PlatformLinux, OSAndroid, Version{4, 2, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; Android 4.3; Nexus 7 Build/JWR66D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.111 Safari/537.36",
+ UserAgent{
+ Browser{BrowserChrome, Version{27, 0, 1453}}, OS{PlatformLinux, OSAndroid, Version{4, 3, 0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (Linux; Android 4.4; Nexus 7 Build/KOT24) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.105 Safari/537.36",
+ UserAgent{
+ Browser{BrowserChrome, Version{30, 0, 1599}}, OS{PlatformLinux, OSAndroid, Version{4, 4, 0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (Linux; Android 4.4; Nexus 4 Build/KRT16E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.105 Mobile Safari",
+ UserAgent{
+ Browser{BrowserChrome, Version{30, 0, 1599}}, OS{PlatformLinux, OSAndroid, Version{4, 4, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; Android 6.0.1; SM-G930V Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.98 Mobile Safari/537.36",
+ UserAgent{
+ Browser{BrowserChrome, Version{52, 0, 2743}}, OS{PlatformLinux, OSAndroid, Version{6, 0, 1}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; Android 7.0; Nexus 5X Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.98 Mobile Safari/537.36",
+ UserAgent{
+ Browser{BrowserChrome, Version{52, 0, 2743}}, OS{PlatformLinux, OSAndroid, Version{7, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Linux; Android 7.0; Nexus 6P Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/52.0.2743.98 Mobile Safari/537.36",
+ UserAgent{
+ Browser{BrowserChrome, Version{52, 0, 2743}}, OS{PlatformLinux, OSAndroid, Version{7, 0, 0}}, DevicePhone}},
+
+ // BLACKBERRY TESTS
+
+ {"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0) BlackBerry8703e/4.1.0 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/104",
+ UserAgent{
+ Browser{BrowserBlackberry, Version{0, 0, 0}}, OS{PlatformBlackberry, OSBlackberry, Version{0, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.1.0.4633 Mobile Safari/537.10+",
+ UserAgent{
+ Browser{BrowserBlackberry, Version{10, 1, 0}}, OS{PlatformBlackberry, OSBlackberry, Version{0, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (BB10; Kbd) AppleWebKit/537.35+ (KHTML, like Gecko) Version/10.2.1.1925 Mobile Safari/537.35+",
+ UserAgent{
+ Browser{BrowserBlackberry, Version{10, 2, 1}}, OS{PlatformBlackberry, OSBlackberry, Version{0, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (PlayBook; U; RIM Tablet OS 1.0.0; en-US) AppleWebKit/534.11 (KHTML, like Gecko) Version/7.1.0.7 Safari/534.11",
+ UserAgent{
+ Browser{BrowserBlackberry, Version{7, 1, 0}}, OS{PlatformBlackberry, OSBlackberry, Version{0, 0, 0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML, like Gecko) Version/7.2.1.0 Safari/536.2+",
+ UserAgent{
+ Browser{BrowserBlackberry, Version{7, 2, 1}}, OS{PlatformBlackberry, OSBlackberry, Version{0, 0, 0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (X11; U; CrOS i686 9.10.0; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.253.0 Safari/532.5",
+ UserAgent{
+ Browser{BrowserChrome, Version{4, 0, 253}}, OS{PlatformLinux, OSChromeOS, Version{0, 0, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (X11; CrOS armv7l 5500.100.6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.120 Safari/537.36",
+ UserAgent{
+ Browser{BrowserChrome, Version{34, 0, 1847}}, OS{PlatformLinux, OSChromeOS, Version{0, 0, 0}}, DeviceComputer}},
+
+ // {"Mozilla/5.0 (Mobile; rv:14.0) Gecko/14.0 Firefox/14.0",
+ // UserAgent{
+ // Browser{BrowserFirefox, 14, OSFirefoxOS, 14}, DevicePhone}},
+
+ // {"Mozilla/5.0 (Mobile; rv:17.0) Gecko/17.0 Firefox/17.0",
+ // UserAgent{
+ // Browser{BrowserFirefox, , OSFirefoxOS}, DevicePhone}},
+
+ // {"Mozilla/5.0 (Mobile; rv:18.1) Gecko/18.1 Firefox/18.1",
+ // UserAgent{
+ // Browser{BrowserFirefox, , OSFirefoxOS}, DevicePhone}},
+
+ // {"Mozilla/5.0 (Tablet; rv:18.1) Gecko/18.1 Firefox/18.1",
+ // UserAgent{
+ // Browser{BrowserFirefox, , OSFirefoxOS}, DevicePhone}},
+
+ // {"Mozilla/5.0 (Mobile; LG-D300; rv:18.1) Gecko/18.1 Firefox/18.1",
+ // UserAgent{
+ // Browser{BrowserFirefox, , OSFirefoxOS}, DevicePhone}},
+
+ {"Mozilla/5.0(iPad; U; CPU iPhone OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B314 Safari/531.21.10",
+ UserAgent{
+ Browser{BrowserSafari, Version{4, 0, 4}}, OS{PlatformiPad, OSiOS, Version{3, 2, 0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7",
+ UserAgent{
+ Browser{BrowserSafari, Version{4, 0, 5}}, OS{PlatformiPhone, OSiOS, Version{4, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3",
+ UserAgent{
+ Browser{BrowserSafari, Version{5, 1, 0}}, OS{PlatformiPhone, OSiOS, Version{5, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3",
+ UserAgent{
+ Browser{BrowserSafari, Version{5, 1, 0}}, OS{PlatformiPad, OSiOS, Version{5, 0, 0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25",
+ UserAgent{
+ Browser{BrowserSafari, Version{6, 0, 0}}, OS{PlatformiPad, OSiOS, Version{6, 0, 0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X) AppleWebKit/546.10 (KHTML, like Gecko) Version/6.0 Mobile/7E18WD Safari/8536.25",
+ UserAgent{
+ Browser{BrowserSafari, Version{6, 0, 0}}, OS{PlatformiPhone, OSiOS, Version{7, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (iPad; CPU OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53",
+ UserAgent{
+ Browser{BrowserSafari, Version{7, 0, 0}}, OS{PlatformiPad, OSiOS, Version{7, 0, 0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (iPad; CPU OS 7_0_2 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A501 Safari/9537.53",
+ UserAgent{
+ Browser{BrowserSafari, Version{7, 0, 0}}, OS{PlatformiPad, OSiOS, Version{7, 0, 2}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (iPhone; CPU iPhone OS 10_2_1 like Mac OS X) AppleWebKit/602.4.6 (KHTML, like Gecko) Mobile/14D27 [FBAN/FBIOS;FBAV/86.0.0.48.52;FBBV/53842252;FBDV/iPhone9,1;FBMD/iPhone;FBSN/iOS;FBSV/10.2.1;FBSS/2;FBCR/Verizon;FBID/phone;FBLC/en_US;FBOP/5;FBRV/0]",
+ UserAgent{
+ Browser{BrowserSafari, Version{10, 2, 1}}, OS{PlatformiPhone, OSiOS, Version{10, 2, 1}}, DevicePhone}},
+
+ // TODO handle default browser based on iOS version
+ // {"Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/538.34.9 (KHTML, like Gecko) Mobile/12A4265u",
+ // UserAgent{
+ // Browser{BrowserSafari, Version{8,0,0}}, OS{PlatformiPhone, OSiOS, Version{8,0,0}}, DevicePhone}},
+
+ // TODO extrapolate browser from iOS version
+ // {"Mozilla/5.0 (iPad; CPU OS 8_0 like Mac OS X) AppleWebKit/538.34.9 (KHTML, like Gecko) Mobile/12A4265u",
+ // UserAgent{
+ // Browser{BrowserSafari, Version{8,0,0}}, OS{PlatformiPad, OSiOS, Version{8,0,0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (iPhone; CPU iPhone OS 8_0_2 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12A405 Safari/600.1.4",
+ UserAgent{
+ Browser{BrowserSafari, Version{8, 0, 0}}, OS{PlatformiPhone, OSiOS, Version{8, 0, 2}}, DevicePhone}},
+
+ {"Mozilla/5.0 (X11; U; Linux x86_64; en; rv:1.9.0.14) Gecko/20080528 Ubuntu/9.10 (karmic) Epiphany/2.22 Firefox/3.0",
+ UserAgent{
+ Browser{BrowserFirefox, Version{3, 0, 0}}, OS{PlatformLinux, OSLinux, Version{0, 0, 0}}, DeviceComputer}},
+
+ // Can't parse browser due to limitation of user agent library
+ {"Mozilla/5.0 (X11; U; Linux x86_64; zh-TW; rv:1.9.0.8) Gecko/2009032712 Ubuntu/8.04 (hardy) Firefox/3.0.8 GTB5",
+ UserAgent{
+ Browser{BrowserFirefox, Version{3, 0, 8}}, OS{PlatformLinux, OSLinux, Version{0, 0, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (compatible; Konqueror/3.5; Linux; x86_64) KHTML/3.5.5 (like Gecko) (Debian)",
+ UserAgent{
+ Browser{BrowserUnknown, Version{0, 0, 0}}, OS{PlatformLinux, OSLinux, Version{0, 0, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.1.5) Gecko/20091112 Iceweasel/3.5.5 (like Firefox/3.5.5; Debian-3.5.5-1)",
+ UserAgent{
+ Browser{BrowserFirefox, Version{3, 5, 5}}, OS{PlatformLinux, OSLinux, Version{0, 0, 0}}, DeviceComputer}},
+
+ // TODO consider bot?
+ // {"Miro/2.0.4 (http://www.getmiro.com/; Darwin 10.3.0 i386)",
+ // UserAgent{
+ // Browser{BrowserUnknown, Version{0,0,0}}, OS{PlatformMac, OSMacOSX, Version{3,0,0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.1b3pre) Gecko/20090223 SeaMonkey/2.0a3",
+ UserAgent{
+ Browser{BrowserFirefox, Version{0, 0, 0}}, OS{PlatformMac, OSMacOSX, Version{10, 4, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_5; en-us) AppleWebKit/525.26.2 (KHTML, like Gecko) Version/3.2 Safari/525.26.12",
+ UserAgent{
+ Browser{BrowserSafari, Version{3, 2, 0}}, OS{PlatformMac, OSMacOSX, Version{10, 5, 5}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en; rv:1.9.0.8pre) Gecko/2009022800 Camino/2.0b3pre",
+ UserAgent{
+ Browser{BrowserUnknown, Version{0, 0, 0}}, OS{PlatformMac, OSMacOSX, Version{10, 5, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; en-US) AppleWebKit/533.1 (KHTML, like Gecko) Chrome/5.0.329.0 Safari/533.1",
+ UserAgent{
+ Browser{BrowserChrome, Version{5, 0, 329}}, OS{PlatformMac, OSMacOSX, Version{10, 6, 2}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 (.NET CLR 3.5.30729)",
+ UserAgent{
+ Browser{BrowserFirefox, Version{3, 5, 6}}, OS{PlatformMac, OSMacOSX, Version{10, 6, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/534.52.7 (KHTML, like Gecko) Version/5.1.2 Safari/534.52.7",
+ UserAgent{
+ Browser{BrowserSafari, Version{5, 1, 2}}, OS{PlatformMac, OSMacOSX, Version{10, 7, 2}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:9.0) Gecko/20111222 Thunderbird/9.0.1",
+ UserAgent{
+ Browser{BrowserUnknown, Version{0, 0, 0}}, OS{PlatformMac, OSMacOSX, Version{10, 7, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.75 Safari/535.7",
+ UserAgent{
+ Browser{BrowserChrome, Version{16, 0, 912}}, OS{PlatformMac, OSMacOSX, Version{10, 7, 2}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8) AppleWebKit/535.18.5 (KHTML, like Gecko) Version/5.2 Safari/535.18.5",
+ UserAgent{
+ Browser{BrowserSafari, Version{5, 2, 0}}, OS{PlatformMac, OSMacOSX, Version{10, 8, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_8; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.0 Safari/532.5",
+ UserAgent{
+ Browser{BrowserChrome, Version{4, 0, 249}}, OS{PlatformMac, OSMacOSX, Version{10, 8, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9) AppleWebKit/537.35.1 (KHTML, like Gecko) Version/6.1 Safari/537.35.1",
+ UserAgent{
+ Browser{BrowserSafari, Version{6, 1, 0}}, OS{PlatformMac, OSMacOSX, Version{10, 9, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/538.34.48 (KHTML, like Gecko) Version/8.0 Safari/538.35.8",
+ UserAgent{
+ Browser{BrowserSafari, Version{8, 0, 0}}, OS{PlatformMac, OSMacOSX, Version{10, 10, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/538.32 (KHTML, like Gecko) Version/7.1 Safari/538.4",
+ UserAgent{
+ Browser{BrowserSafari, Version{7, 1, 0}}, OS{PlatformMac, OSMacOSX, Version{10, 10, 0}}, DeviceComputer}},
+
+ {"Opera/9.80 (S60; SymbOS; Opera Mobi/352; U; de) Presto/2.4.15 Version/10.00",
+ UserAgent{
+ Browser{BrowserOpera, Version{10, 0, 0}}, OS{PlatformUnknown, OSUnknown, Version{0, 0, 0}}, DevicePhone}},
+
+ {"Opera/9.80 (S60; SymbOS; Opera Mobi/352; U; de) Presto/2.4.15 Version/10.00",
+ UserAgent{
+ Browser{BrowserOpera, Version{10, 0, 0}}, OS{PlatformUnknown, OSUnknown, Version{0, 0, 0}}, DevicePhone}},
+
+ // TODO: support OneBrowser? https://play.google.com/store/apps/details?id=com.tencent.ibibo.mtt&hl=en_GB
+ // {"OneBrowser/3.1 (NokiaN70-1/5.0638.3.0.1)",
+ // UserAgent{
+ // Browser{BrowserUnknown, Version{0,0,0}}, OS{PlatformUnknown, OSUnknown, Version{0,0,0}}, DevicePhone}},
+
+ // WebOS reports itself as safari :(
+ {"Mozilla/5.0 (webOS/1.0; U; en-US) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/1.0 Safari/525.27.1 Pre/1.0",
+ UserAgent{
+ Browser{BrowserUnknown, Version{1, 0, 0}}, OS{PlatformLinux, OSWebOS, Version{0, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (webOS/1.4.1.1; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pre/1.0",
+ UserAgent{
+ Browser{BrowserUnknown, Version{1, 0, 0}}, OS{PlatformLinux, OSWebOS, Version{0, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.0; U; de-DE) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.70 Safari/534.6 TouchPad/1.0",
+ UserAgent{
+ Browser{BrowserUnknown, Version{0, 0, 0}}, OS{PlatformLinux, OSWebOS, Version{0, 0, 0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.2; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/234.40.1 Safari/534.6 TouchPad/1.0",
+ UserAgent{
+ Browser{BrowserUnknown, Version{0, 0, 0}}, OS{PlatformLinux, OSWebOS, Version{0, 0, 0}}, DeviceTablet}},
+
+ {"Opera/9.30 (Nintendo Wii; U; ; 2047-7; fr)",
+ UserAgent{
+ Browser{BrowserOpera, Version{9, 30, 0}}, OS{PlatformNintendo, OSNintendo, Version{0, 0, 0}}, DeviceConsole}},
+
+ {"Mozilla/5.0 (Nintendo WiiU) AppleWebKit/534.52 (KHTML, like Gecko) NX/2.1.0.8.21 NintendoBrowser/1.0.0.7494.US",
+ UserAgent{
+ Browser{BrowserUnknown, Version{0, 0, 0}}, OS{PlatformNintendo, OSNintendo, Version{0, 0, 0}}, DeviceConsole}},
+
+ {"Mozilla/5.0 (Nintendo WiiU) AppleWebKit/536.28 (KHTML, like Gecko) NX/3.0.3.12.6 NintendoBrowser/2.0.0.9362.US",
+ UserAgent{
+ Browser{BrowserUnknown, Version{0, 0, 0}}, OS{PlatformNintendo, OSNintendo, Version{0, 0, 0}}, DeviceConsole}},
+
+ // TODO fails to get opera first -- but is this a real UA string or an uncommon spoof?
+ // {"Mozilla/4.0 (compatible; MSIE 5.0; Windows 2000) Opera 6.0 [en]",
+ // BrowserIE, Version{5,0,0}}, OS{PlatformWindows, OSWindows, Version{4,0,0}}, DeviceComputer}},
+
+ {"Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0; SV1; .NET CLR 1.1.4322; .NET CLR 1.0.3705; .NET CLR 2.0.50727)",
+ UserAgent{
+ Browser{BrowserIE, Version{5, 0, 1}}, OS{PlatformWindows, OSWindows, Version{5, 0, 0}}, DeviceComputer}},
+
+ {"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/4.0; GTB6.4; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; OfficeLiveConnector.1.3; OfficeLivePatch.0.0; .NET CLR 1.1.4322)",
+ UserAgent{
+ Browser{BrowserIE, Version{7, 0, 0}}, OS{PlatformWindows, OSWindows, Version{6, 1, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Windows; U; Windows NT 6.1; sk; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7",
+ UserAgent{
+ Browser{BrowserFirefox, Version{3, 5, 7}}, OS{PlatformWindows, OSWindows, Version{6, 1, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)",
+ UserAgent{
+ Browser{BrowserIE, Version{10, 0, 0}}, OS{PlatformWindows, OSWindows, Version{6, 2, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) YaBrowser/1.0.1084.5402 Chrome/19.0.1084.5402 Safari/536.5",
+ UserAgent{
+ Browser{BrowserChrome, Version{19, 0, 1084}}, OS{PlatformWindows, OSWindows, Version{6, 2, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.15 (KHTML, like Gecko) Chrome/24.0.1295.0 Safari/537.15",
+ UserAgent{
+ Browser{BrowserChrome, Version{24, 0, 1295}}, OS{PlatformWindows, OSWindows, Version{6, 2, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko",
+ UserAgent{
+ Browser{BrowserIE, Version{11, 0, 0}}, OS{PlatformWindows, OSWindows, Version{6, 3, 0}}, DeviceTablet}},
+
+ {"Mozilla/5.0 (IE 11.0; Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C; rv:11.0) like Gecko",
+ UserAgent{
+ Browser{BrowserIE, Version{11, 0, 0}}, OS{PlatformWindows, OSWindows, Version{6, 3, 0}}, DeviceComputer}},
+
+ // {"Mozilla/4.0 (compatible; MSIE 4.01; Windows 95)",
+ // UserAgent{
+ // Browser{BrowserIE, Version{5,0,0}}, OS{PlatformWindows, OSWindows95, Version{5,0,0}}, DeviceComputer}},
+
+ // {"Mozilla/4.0 (compatible; MSIE 5.0; Windows 95) Opera 6.02 [en]",
+ // UserAgent{
+ // Browser{BrowserIE, Version{5,0,0}}, OS{PlatformWindows, OSWindows95, Version{5,0,0}}, DeviceComputer}},
+
+ // {"Mozilla/4.0 (compatible; MSIE 6.0b; Windows 98; YComp 5.0.0.0)",
+ // UserAgent{
+ // Browser{BrowserIE, Version{6,0,0}}, OS{PlatformWindows, OSWindows98, Version{5,0,0}}, DeviceComputer}},
+
+ // {"Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)",
+ // UserAgent{
+ // Browser{BrowserIE, Version{4,0,0}}, OS{PlatformWindows, OSWindows98, Version{5,0,0}}, DeviceComputer}},
+
+ // {"Mozilla/5.0 (Windows; U; Windows 98; en-US; rv:1.8.1.8pre) Gecko/20071019 Firefox/2.0.0.8 Navigator/9.0.0.1",
+ // UserAgent{
+ // Browser{BrowserFirefox, Version{2,0,0}}, OS{PlatformWindows, OSWindows98, Version{5,0,0}}, DeviceComputer}},
+
+ //Can't parse due to limitation of user agent library
+ // {"Mozilla/5.0 (Windows; U; Windows CE 5.1; rv:1.8.1a3) Gecko/20060610 Minimo/0.016",
+ // UserAgent{
+ // Browser{ BrowserUnknown, Version{0,0,0}}, OS{PlatformWindowsPhone, OSWindowsPhone, Version{0,0,0}}, DevicePhone}},
+
+ // {"Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; 176x220)",
+ // UserAgent{
+ // Browser{BrowserIE, Version{4,0,0}}, OS{PlatformWindowsPhone, OSWindowsPhone, Version{0,0,0}}, DevicePhone}},
+
+ // Can't parse browser due to limitation of user agent library
+ // {"Mozilla/4.0 (compatible; MSIE 5.0; Windows ME) Opera 6.0 [de]",
+ // UserAgent{
+ // Browser{BrowserUnknown, OSWindowsME}, DeviceComputer}},
+
+ {"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 1.1.4322; InfoPath.2; .NET CLR 3.5.21022; .NET CLR 3.5.30729; MS-RTC LM 8; OfficeLiveConnector.1.4; OfficeLivePatch.1.3; .NET CLR 3.0.30729)",
+ UserAgent{
+ Browser{BrowserIE, Version{8, 0, 0}}, OS{PlatformWindows, OSWindows, Version{6, 0, 0}}, DeviceComputer}},
+
+ {"Mozilla/5.0 (Windows; U; Windows NT 5.1; cs; rv:1.9.1.8) Gecko/20100202 Firefox/3.5.8",
+ UserAgent{
+ Browser{BrowserFirefox, Version{3, 5, 8}}, OS{PlatformWindows, OSWindows, Version{5, 1, 0}}, DeviceComputer}},
+
+ {"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; )",
+ UserAgent{
+ Browser{BrowserIE, Version{7, 0, 0}}, OS{PlatformWindows, OSWindows, Version{5, 1, 0}}, DeviceComputer}},
+
+ // Can't parse due to limitation of user agent library
+ {"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Windows Phone 6.5.3.5)",
+ UserAgent{
+ Browser{BrowserIE, Version{6, 0, 0}}, OS{PlatformWindowsPhone, OSWindowsPhone, Version{6, 5, 3}}, DevicePhone}},
+
+ // desktop mode for Windows Phone 7
+ {"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; XBLWP7; ZuneWP7)",
+ UserAgent{
+ Browser{BrowserIE, Version{7, 0, 0}}, OS{PlatformWindows, OSWindows, Version{6, 1, 0}}, DeviceComputer}},
+
+ // mobile mode for Windows Phone 7
+ {"Mozilla/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident/3.1; IEMobile/7.0; HTC; T8788)",
+ UserAgent{
+ Browser{BrowserIE, Version{7, 0, 0}}, OS{PlatformWindowsPhone, OSWindowsPhone, Version{7, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0)",
+ UserAgent{
+ Browser{BrowserIE, Version{9, 0, 0}}, OS{PlatformWindowsPhone, OSWindowsPhone, Version{7, 5, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 920)",
+ UserAgent{
+ Browser{BrowserIE, Version{10, 0, 0}}, OS{PlatformWindowsPhone, OSWindowsPhone, Version{8, 0, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Windows Phone 8.1; ARM; Trident/7.0; Touch IEMobile/11.0; HTC; Windows Phone 8S by HTC) like Gecko",
+ UserAgent{
+ Browser{BrowserIE, Version{11, 0, 0}}, OS{PlatformWindowsPhone, OSWindowsPhone, Version{8, 1, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (Windows Phone 8.1; ARM; Trident/7.0; Touch IEMobile/11.0; NOKIA; 909) like Gecko",
+ UserAgent{
+ Browser{BrowserIE, Version{11, 0, 0}}, OS{PlatformWindowsPhone, OSWindowsPhone, Version{8, 1, 0}}, DevicePhone}},
+
+ {"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; Xbox)",
+ UserAgent{
+ Browser{BrowserIE, Version{9, 0, 0}}, OS{PlatformXbox, OSXbox, Version{6, 1, 0}}, DeviceConsole}},
+}
+
+func TestAgentSurfer(t *testing.T) {
+ for _, determined := range testUAVars {
+ t.Run("", func(t *testing.T) {
+ testFuncs := []func(string) *UserAgent{
+ Parse,
+ func(ua string) *UserAgent {
+ u := new(UserAgent)
+ ParseUserAgent(ua, u)
+ return u
+ },
+ }
+
+ for _, f := range testFuncs {
+ ua := f(determined.UA)
+
+ if ua.Browser.Name != determined.Browser.Name {
+ t.Errorf("browserName: got %v, wanted %v", ua.Browser.Name, determined.Browser.Name)
+ t.Logf("agent: %s", determined.UA)
+ }
+
+ if ua.Browser.Version != determined.Browser.Version {
+ t.Errorf("browser version: got %d, wanted %d", ua.Browser.Version, determined.Browser.Version)
+ t.Logf("agent: %s", determined.UA)
+ }
+
+ if ua.OS.Platform != determined.OS.Platform {
+ t.Errorf("platform: got %v, wanted %v", ua.OS.Platform, determined.OS.Platform)
+ t.Logf("agent: %s", determined.UA)
+ }
+
+ if ua.OS.Name != determined.OS.Name {
+ t.Errorf("os: got %s, wanted %s", ua.OS.Name, determined.OS.Name)
+ t.Logf("agent: %s", determined.UA)
+ }
+
+ if ua.OS.Version != determined.OS.Version {
+ t.Errorf("os version: got %d, wanted %d", ua.OS.Version, determined.OS.Version)
+ t.Logf("agent: %s", determined.UA)
+ }
+
+ if ua.DeviceType != determined.DeviceType {
+ t.Errorf("device type: got %v, wanted %v", ua.DeviceType, determined.DeviceType)
+ t.Logf("agent: %s", determined.UA)
+ }
+ }
+ })
+ }
+}
+
+func BenchmarkAgentSurfer(b *testing.B) {
+ num := len(testUAVars)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ Parse(testUAVars[i%num].UA)
+ }
+}
+
+func BenchmarkAgentSurferReuse(b *testing.B) {
+ dest := new(UserAgent)
+ num := len(testUAVars)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ dest.Reset()
+ ParseUserAgent(testUAVars[i%num].UA, dest)
+ }
+}
+
+func BenchmarkEvalSystem(b *testing.B) {
+ num := len(testUAVars)
+ v := UserAgent{}
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ v.evalOS(testUAVars[i%num].UA)
+ }
+}
+
+func BenchmarkEvalBrowserName(b *testing.B) {
+ num := len(testUAVars)
+ v := UserAgent{}
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ v.evalBrowserName(testUAVars[i%num].UA)
+ }
+}
+
+func BenchmarkEvalBrowserVersion(b *testing.B) {
+ num := len(testUAVars)
+ v := UserAgent{}
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ v.Browser.Name = testUAVars[i%num].Browser.Name
+ v.evalBrowserVersion(testUAVars[i%num].UA)
+ }
+}
+
+func BenchmarkEvalDevice(b *testing.B) {
+ num := len(testUAVars)
+ v := UserAgent{}
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ v.OS.Name = testUAVars[i%num].OS.Name
+ v.OS.Platform = testUAVars[i%num].OS.Platform
+ v.Browser.Name = testUAVars[i%num].Browser.Name
+ v.evalDevice(testUAVars[i%num].UA)
+ }
+}
+
+// Chrome for Mac
+func BenchmarkParseChromeMac(b *testing.B) {
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ Parse("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36")
+ }
+}
+
+// Chrome for Windows
+func BenchmarkParseChromeWin(b *testing.B) {
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ Parse("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.134 Safari/537.36")
+ }
+}
+
+// Chrome for Android
+func BenchmarkParseChromeAndroid(b *testing.B) {
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ Parse("Mozilla/5.0 (Linux; Android 4.4.2; GT-P5210 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Safari/537.36")
+ }
+}
+
+// Safari for Mac
+func BenchmarkParseSafariMac(b *testing.B) {
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ Parse("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/600.7.12 (KHTML, like Gecko) Version/8.0.7 Safari/600.7.12")
+ }
+}
+
+// Safari for iPad
+func BenchmarkParseSafariiPad(b *testing.B) {
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ Parse("Mozilla/5.0 (iPad; CPU OS 8_1_2 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B440 Safari/600.1.4")
+ }
+}
diff --git a/vendor/github.com/dgryski/dgoogauth/googauth_test.go b/vendor/github.com/dgryski/dgoogauth/googauth_test.go
index 031922c477..43f35d2c94 100644
--- a/vendor/github.com/dgryski/dgoogauth/googauth_test.go
+++ b/vendor/github.com/dgryski/dgoogauth/googauth_test.go
@@ -1,7 +1,7 @@
package dgoogauth
import (
- "strconv"
+ "fmt"
"testing"
"time"
)
@@ -199,11 +199,11 @@ func TestAuthenticate(t *testing.T) {
t0 = int64(time.Now().Unix() / 30)
}
c := ComputeCode(otpconf.Secret, t0)
+ code := fmt.Sprintf("%06d", c)
- invalid := c + 1
attempts = []attempt{
- {strconv.Itoa(invalid), false},
- {strconv.Itoa(c), true},
+ {code + "1", false},
+ {code, true},
}
for _, a := range attempts {
diff --git a/vendor/github.com/go-ini/ini/file.go b/vendor/github.com/go-ini/ini/file.go
index 93ac50836c..ce26c3b315 100644
--- a/vendor/github.com/go-ini/ini/file.go
+++ b/vendor/github.com/go-ini/ini/file.go
@@ -345,6 +345,12 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
return nil, err
}
}
+
+ for _, val := range key.nestedValues {
+ if _, err := buf.WriteString(indent + " " + val + LineBreak); err != nil {
+ return nil, err
+ }
+ }
}
if PrettySection {
diff --git a/vendor/github.com/go-ini/ini/ini.go b/vendor/github.com/go-ini/ini/ini.go
index 508d60c19a..535d3588a7 100644
--- a/vendor/github.com/go-ini/ini/ini.go
+++ b/vendor/github.com/go-ini/ini/ini.go
@@ -32,7 +32,7 @@ const (
// Maximum allowed depth when recursively substituing variable names.
_DEPTH_VALUES = 99
- _VERSION = "1.30.3"
+ _VERSION = "1.32.0"
)
// Version returns current package version literal.
@@ -134,8 +134,16 @@ type LoadOptions struct {
AllowBooleanKeys bool
// AllowShadows indicates whether to keep track of keys with same name under same section.
AllowShadows bool
- // UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value"
+ // AllowNestedValues indicates whether to allow AWS-like nested values.
+ // Docs: http://docs.aws.amazon.com/cli/latest/topic/config-vars.html#nested-values
+ AllowNestedValues bool
+ // UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format
+ // when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value"
UnescapeValueDoubleQuotes bool
+ // UnescapeValueCommentSymbols indicates to unescape comment symbols (\# and \;) inside value to regular format
+ // when value is NOT surrounded by any quotes.
+ // Note: UNSTABLE, behavior might change to only unescape inside double quotes but may noy necessary at all.
+ UnescapeValueCommentSymbols bool
// Some INI formats allow group blocks that store a block of raw content that doesn't otherwise
// conform to key/value pairs. Specify the names of those blocks here.
UnparseableSections []string
diff --git a/vendor/github.com/go-ini/ini/ini_test.go b/vendor/github.com/go-ini/ini/ini_test.go
index 7a1efe4ec5..3e6992d3fa 100644
--- a/vendor/github.com/go-ini/ini/ini_test.go
+++ b/vendor/github.com/go-ini/ini/ini_test.go
@@ -268,6 +268,19 @@ create_repo="创建了仓库 %s"`))
})
})
+ Convey("Unescape comment symbols inside value", func() {
+ f, err := ini.LoadSources(ini.LoadOptions{
+ IgnoreInlineComment: true,
+ UnescapeValueCommentSymbols: true,
+ }, []byte(`
+key = test value more text
+`))
+ So(err, ShouldBeNil)
+ So(f, ShouldNotBeNil)
+
+ So(f.Section("").Key("key").String(), ShouldEqual, `test value more text`)
+ })
+
Convey("Allow unparseable sections", func() {
f, err := ini.LoadSources(ini.LoadOptions{
Insensitive: true,
diff --git a/vendor/github.com/go-ini/ini/key.go b/vendor/github.com/go-ini/ini/key.go
index ab566c2c15..7c8566a1b4 100644
--- a/vendor/github.com/go-ini/ini/key.go
+++ b/vendor/github.com/go-ini/ini/key.go
@@ -34,6 +34,8 @@ type Key struct {
isShadow bool
shadows []*Key
+
+ nestedValues []string
}
// newKey simply return a key object with given values.
@@ -66,6 +68,22 @@ func (k *Key) AddShadow(val string) error {
return k.addShadow(val)
}
+func (k *Key) addNestedValue(val string) error {
+ if k.isAutoIncrement || k.isBooleanType {
+ return errors.New("cannot add nested value to auto-increment or boolean key")
+ }
+
+ k.nestedValues = append(k.nestedValues, val)
+ return nil
+}
+
+func (k *Key) AddNestedValue(val string) error {
+ if !k.s.f.options.AllowNestedValues {
+ return errors.New("nested value is not allowed")
+ }
+ return k.addNestedValue(val)
+}
+
// ValueMapper represents a mapping function for values, e.g. os.ExpandEnv
type ValueMapper func(string) string
@@ -92,6 +110,12 @@ func (k *Key) ValueWithShadows() []string {
return vals
}
+// NestedValues returns nested values stored in the key.
+// It is possible returned value is nil if no nested values stored in the key.
+func (k *Key) NestedValues() []string {
+ return k.nestedValues
+}
+
// transformValue takes a raw value and transforms to its final string.
func (k *Key) transformValue(val string) string {
if k.s.f.ValueMapper != nil {
@@ -114,7 +138,7 @@ func (k *Key) transformValue(val string) string {
// Search in the same section.
nk, err := k.s.GetKey(noption)
- if err != nil {
+ if err != nil || k == nk {
// Search again in default section.
nk, _ = k.s.f.Section("").GetKey(noption)
}
diff --git a/vendor/github.com/go-ini/ini/key_test.go b/vendor/github.com/go-ini/ini/key_test.go
index 588efd4291..a13ad95fa4 100644
--- a/vendor/github.com/go-ini/ini/key_test.go
+++ b/vendor/github.com/go-ini/ini/key_test.go
@@ -15,6 +15,7 @@
package ini_test
import (
+ "bytes"
"fmt"
"strings"
"testing"
@@ -478,3 +479,45 @@ func TestKey_SetValue(t *testing.T) {
So(k.Value(), ShouldEqual, "ini.v1")
})
}
+
+func TestKey_NestedValues(t *testing.T) {
+ Convey("Read and write nested values", t, func() {
+ f, err := ini.LoadSources(ini.LoadOptions{
+ AllowNestedValues: true,
+ }, []byte(`
+aws_access_key_id = foo
+aws_secret_access_key = bar
+region = us-west-2
+s3 =
+ max_concurrent_requests=10
+ max_queue_size=1000`))
+ So(err, ShouldBeNil)
+ So(f, ShouldNotBeNil)
+
+ So(f.Section("").Key("s3").NestedValues(), ShouldResemble, []string{"max_concurrent_requests=10", "max_queue_size=1000"})
+
+ var buf bytes.Buffer
+ _, err = f.WriteTo(&buf)
+ So(err, ShouldBeNil)
+ So(buf.String(), ShouldEqual, `aws_access_key_id = foo
+aws_secret_access_key = bar
+region = us-west-2
+s3 =
+ max_concurrent_requests=10
+ max_queue_size=1000
+
+`)
+ })
+}
+
+func TestRecursiveValues(t *testing.T) {
+ Convey("Recursive values should not reflect on same key", t, func() {
+ f, err := ini.Load([]byte(`
+NAME = ini
+[package]
+NAME = %(NAME)s`))
+ So(err, ShouldBeNil)
+ So(f, ShouldNotBeNil)
+ So(f.Section("package").Key("NAME").String(), ShouldEqual, "ini")
+ })
+}
diff --git a/vendor/github.com/go-ini/ini/parser.go b/vendor/github.com/go-ini/ini/parser.go
index f8ac8026a2..db3af8f004 100644
--- a/vendor/github.com/go-ini/ini/parser.go
+++ b/vendor/github.com/go-ini/ini/parser.go
@@ -193,7 +193,9 @@ func hasSurroundedQuote(in string, quote byte) bool {
strings.IndexByte(in[1:], quote) == len(in)-2
}
-func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment, unescapeValueDoubleQuotes bool) (string, error) {
+func (p *parser) readValue(in []byte,
+ ignoreContinuation, ignoreInlineComment, unescapeValueDoubleQuotes, unescapeValueCommentSymbols bool) (string, error) {
+
line := strings.TrimLeftFunc(string(in), unicode.IsSpace)
if len(line) == 0 {
return "", nil
@@ -243,6 +245,13 @@ func (p *parser) readValue(in []byte, ignoreContinuation, ignoreInlineComment, u
if hasSurroundedQuote(line, '\'') ||
hasSurroundedQuote(line, '"') {
line = line[1 : len(line)-1]
+ } else if len(valQuote) == 0 && unescapeValueCommentSymbols {
+ if strings.Contains(line, `\;`) {
+ line = strings.Replace(line, `\;`, ";", -1)
+ }
+ if strings.Contains(line, `\#`) {
+ line = strings.Replace(line, `\#`, "#", -1)
+ }
}
return line, nil
}
@@ -261,6 +270,10 @@ func (f *File) parse(reader io.Reader) (err error) {
}
section, _ := f.NewSection(name)
+ // This "last" is not strictly equivalent to "previous one" if current key is not the first nested key
+ var isLastValueEmpty bool
+ var lastRegularKey *Key
+
var line []byte
var inUnparseableSection bool
for !p.isEOF {
@@ -269,6 +282,14 @@ func (f *File) parse(reader io.Reader) (err error) {
return err
}
+ if f.options.AllowNestedValues &&
+ isLastValueEmpty && len(line) > 0 {
+ if line[0] == ' ' || line[0] == '\t' {
+ lastRegularKey.addNestedValue(string(bytes.TrimSpace(line)))
+ continue
+ }
+ }
+
line = bytes.TrimLeftFunc(line, unicode.IsSpace)
if len(line) == 0 {
continue
@@ -333,7 +354,8 @@ func (f *File) parse(reader io.Reader) (err error) {
kname, err := p.readValue(line,
f.options.IgnoreContinuation,
f.options.IgnoreInlineComment,
- f.options.UnescapeValueDoubleQuotes)
+ f.options.UnescapeValueDoubleQuotes,
+ f.options.UnescapeValueCommentSymbols)
if err != nil {
return err
}
@@ -359,10 +381,12 @@ func (f *File) parse(reader io.Reader) (err error) {
value, err := p.readValue(line[offset:],
f.options.IgnoreContinuation,
f.options.IgnoreInlineComment,
- f.options.UnescapeValueDoubleQuotes)
+ f.options.UnescapeValueDoubleQuotes,
+ f.options.UnescapeValueCommentSymbols)
if err != nil {
return err
}
+ isLastValueEmpty = len(value) == 0
key, err := section.NewKey(kname, value)
if err != nil {
@@ -371,6 +395,7 @@ func (f *File) parse(reader io.Reader) (err error) {
key.isAutoIncrement = isAutoIncr
key.Comment = strings.TrimSpace(p.comment.String())
p.comment.Reset()
+ lastRegularKey = key
}
return nil
}
diff --git a/vendor/github.com/golang/protobuf/jsonpb/jsonpb.go b/vendor/github.com/golang/protobuf/jsonpb/jsonpb.go
index 110ae13842..dfdfc5b300 100644
--- a/vendor/github.com/golang/protobuf/jsonpb/jsonpb.go
+++ b/vendor/github.com/golang/protobuf/jsonpb/jsonpb.go
@@ -193,8 +193,7 @@ func (m *Marshaler) marshalObject(out *errWriter, v proto.Message, indent, typeU
// "Generated output always contains 3, 6, or 9 fractional digits,
// depending on required precision."
s, ns := s.Field(0).Int(), s.Field(1).Int()
- d := time.Duration(s)*time.Second + time.Duration(ns)*time.Nanosecond
- x := fmt.Sprintf("%.9f", d.Seconds())
+ x := fmt.Sprintf("%d.%09d", s, ns)
x = strings.TrimSuffix(x, "000")
x = strings.TrimSuffix(x, "000")
out.write(`"`)
diff --git a/vendor/github.com/golang/protobuf/jsonpb/jsonpb_test.go b/vendor/github.com/golang/protobuf/jsonpb/jsonpb_test.go
index 2428d0566c..4fdbde15a2 100644
--- a/vendor/github.com/golang/protobuf/jsonpb/jsonpb_test.go
+++ b/vendor/github.com/golang/protobuf/jsonpb/jsonpb_test.go
@@ -407,6 +407,7 @@ var marshalingTests = []struct {
{"Any with WKT", marshaler, anyWellKnown, anyWellKnownJSON},
{"Any with WKT and indent", marshalerAllOptions, anyWellKnown, anyWellKnownPrettyJSON},
{"Duration", marshaler, &pb.KnownTypes{Dur: &durpb.Duration{Seconds: 3}}, `{"dur":"3.000s"}`},
+ {"Duration", marshaler, &pb.KnownTypes{Dur: &durpb.Duration{Seconds: 100000000, Nanos: 1}}, `{"dur":"100000000.000000001s"}`},
{"Struct", marshaler, &pb.KnownTypes{St: &stpb.Struct{
Fields: map[string]*stpb.Value{
"one": {Kind: &stpb.Value_StringValue{"loneliest number"}},
diff --git a/vendor/github.com/hashicorp/go-multierror/.travis.yml b/vendor/github.com/hashicorp/go-multierror/.travis.yml
index 4b865d194a..304a835955 100644
--- a/vendor/github.com/hashicorp/go-multierror/.travis.yml
+++ b/vendor/github.com/hashicorp/go-multierror/.travis.yml
@@ -3,7 +3,7 @@ sudo: false
language: go
go:
- - 1.6
+ - 1.x
branches:
only:
diff --git a/vendor/github.com/hashicorp/memberlist/memberlist.go b/vendor/github.com/hashicorp/memberlist/memberlist.go
index bdf333b436..e9084f9fd4 100644
--- a/vendor/github.com/hashicorp/memberlist/memberlist.go
+++ b/vendor/github.com/hashicorp/memberlist/memberlist.go
@@ -639,7 +639,9 @@ func (m *Memberlist) Shutdown() error {
// Shut down the transport first, which should block until it's
// completely torn down. If we kill the memberlist-side handlers
// those I/O handlers might get stuck.
- m.transport.Shutdown()
+ if err := m.transport.Shutdown(); err != nil {
+ m.logger.Printf("[ERR] Failed to shutdown transport: %v", err)
+ }
// Now tear down everything else.
atomic.StoreInt32(&m.shutdown, 1)
diff --git a/vendor/github.com/hashicorp/memberlist/net.go b/vendor/github.com/hashicorp/memberlist/net.go
index 58e1fce208..a4330c4d20 100644
--- a/vendor/github.com/hashicorp/memberlist/net.go
+++ b/vendor/github.com/hashicorp/memberlist/net.go
@@ -1059,7 +1059,7 @@ func (m *Memberlist) readUserMsg(bufConn io.Reader, dec *codec.Decoder) error {
// operations, given the deadline. The bool return parameter is true if we
// we able to round trip a ping to the other node.
func (m *Memberlist) sendPingAndWaitForAck(addr string, ping ping, deadline time.Time) (bool, error) {
- conn, err := m.transport.DialTimeout(addr, m.config.TCPTimeout)
+ conn, err := m.transport.DialTimeout(addr, deadline.Sub(time.Now()))
if err != nil {
// If the node is actually dead we expect this to fail, so we
// shouldn't spam the logs with it. After this point, errors
diff --git a/vendor/github.com/hashicorp/memberlist/state.go b/vendor/github.com/hashicorp/memberlist/state.go
index 29fe5f1cf1..f51692de0a 100644
--- a/vendor/github.com/hashicorp/memberlist/state.go
+++ b/vendor/github.com/hashicorp/memberlist/state.go
@@ -251,10 +251,17 @@ func (m *Memberlist) probeNode(node *nodeState) {
nackCh := make(chan struct{}, m.config.IndirectChecks+1)
m.setProbeChannels(ping.SeqNo, ackCh, nackCh, probeInterval)
+ // Mark the sent time here, which should be after any pre-processing but
+ // before system calls to do the actual send. This probably over-reports
+ // a bit, but it's the best we can do. We had originally put this right
+ // after the I/O, but that would sometimes give negative RTT measurements
+ // which was not desirable.
+ sent := time.Now()
+
// Send a ping to the node. If this node looks like it's suspect or dead,
// also tack on a suspect message so that it has a chance to refute as
// soon as possible.
- deadline := time.Now().Add(probeInterval)
+ deadline := sent.Add(probeInterval)
addr := node.Address()
if node.State == stateAlive {
if err := m.encodeAndSendMsg(addr, pingMsg, &ping); err != nil {
@@ -284,11 +291,6 @@ func (m *Memberlist) probeNode(node *nodeState) {
}
}
- // Mark the sent time here, which should be after any pre-processing and
- // system calls to do the actual send. This probably under-reports a bit,
- // but it's the best we can do.
- sent := time.Now()
-
// Arrange for our self-awareness to get updated. At this point we've
// sent the ping, so any return statement means the probe succeeded
// which will improve our health until we get to the failure scenarios
diff --git a/vendor/github.com/hashicorp/memberlist/transport.go b/vendor/github.com/hashicorp/memberlist/transport.go
index ca0a660836..6ce55ea47f 100644
--- a/vendor/github.com/hashicorp/memberlist/transport.go
+++ b/vendor/github.com/hashicorp/memberlist/transport.go
@@ -17,7 +17,7 @@ type Packet struct {
// Timestamp is the time when the packet was received. This should be
// taken as close as possible to the actual receipt time to help make an
- // accurate RTT measurements during probes.
+ // accurate RTT measurement during probes.
Timestamp time.Time
}
diff --git a/vendor/github.com/lib/pq/.travis.yml b/vendor/github.com/lib/pq/.travis.yml
index 01468f05ce..4e34e8857c 100644
--- a/vendor/github.com/lib/pq/.travis.yml
+++ b/vendor/github.com/lib/pq/.travis.yml
@@ -16,7 +16,7 @@ env:
- PQGOSSLTESTS=1
- PQSSLCERTTEST_PATH=$PWD/certs
- PGHOST=127.0.0.1
- - MEGACHECK_VERSION=2017.1
+ - MEGACHECK_VERSION=2017.2.1
matrix:
- PGVERSION=10
- PGVERSION=9.6
@@ -46,15 +46,13 @@ script:
- >
goimports -d -e $(find -name '*.go') | awk '{ print } END { exit NR == 0 ? 0 : 1 }'
- go vet ./...
- # For compatibility with Go 1.5, launch only if megacheck is present,
- # ignore SA1019 (deprecation warnings) in conn_test.go (we have to use the
- # deprecated driver.Execer and driver.Queryer interfaces) and S1024
- # (time.Until) everywhere.
+ # For compatibility with Go 1.5, launch only if megacheck is present.
- >
- which megacheck > /dev/null
- && megacheck -ignore 'github.com/lib/pq/conn_test.go:SA1019 github.com/lib/pq/*.go:S1024' ./...
+ which megacheck > /dev/null && megacheck -go 1.5 ./...
|| echo 'megacheck is not supported, skipping check'
# For compatibility with Go 1.5, launch only if golint is present.
- - which golint > /dev/null && golint ./... || echo 'golint is not supported, skipping check'
+ - >
+ which golint > /dev/null && golint ./...
+ || echo 'golint is not supported, skipping check'
- PQTEST_BINARY_PARAMETERS=no go test -race -v ./...
- PQTEST_BINARY_PARAMETERS=yes go test -race -v ./...
diff --git a/vendor/github.com/lib/pq/doc.go b/vendor/github.com/lib/pq/doc.go
index b2c3582c84..a1b0297138 100644
--- a/vendor/github.com/lib/pq/doc.go
+++ b/vendor/github.com/lib/pq/doc.go
@@ -239,7 +239,7 @@ for more information). Note that the channel name will be truncated to 63
bytes by the PostgreSQL server.
You can find a complete, working example of Listener usage at
-http://godoc.org/github.com/lib/pq/examples/listen.
+http://godoc.org/github.com/lib/pq/example/listen.
*/
package pq
diff --git a/vendor/github.com/miekg/dns/.codecov.yml b/vendor/github.com/miekg/dns/.codecov.yml
new file mode 100644
index 0000000000..f91e5c1fe5
--- /dev/null
+++ b/vendor/github.com/miekg/dns/.codecov.yml
@@ -0,0 +1,8 @@
+coverage:
+ status:
+ project:
+ default:
+ target: 40%
+ threshold: null
+ patch: false
+ changes: false
diff --git a/vendor/github.com/miekg/dns/Gopkg.lock b/vendor/github.com/miekg/dns/Gopkg.lock
new file mode 100644
index 0000000000..0c73a64444
--- /dev/null
+++ b/vendor/github.com/miekg/dns/Gopkg.lock
@@ -0,0 +1,21 @@
+# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
+
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/crypto"
+ packages = ["ed25519","ed25519/internal/edwards25519"]
+ revision = "b080dc9a8c480b08e698fb1219160d598526310f"
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/net"
+ packages = ["bpf","internal/iana","internal/socket","ipv4","ipv6"]
+ revision = "894f8ed5849b15b810ae41e9590a0d05395bba27"
+
+[solve-meta]
+ analyzer-name = "dep"
+ analyzer-version = 1
+ inputs-digest = "c4abc38abaeeeeb9be92455c9c02cae32841122b8982aaa067ef25bb8e86ff9d"
+ solver-name = "gps-cdcl"
+ solver-version = 1
diff --git a/vendor/github.com/miekg/dns/Gopkg.toml b/vendor/github.com/miekg/dns/Gopkg.toml
new file mode 100644
index 0000000000..2f655b2c7b
--- /dev/null
+++ b/vendor/github.com/miekg/dns/Gopkg.toml
@@ -0,0 +1,26 @@
+
+# Gopkg.toml example
+#
+# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
+# for detailed Gopkg.toml documentation.
+#
+# required = ["github.com/user/thing/cmd/thing"]
+# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
+#
+# [[constraint]]
+# name = "github.com/user/project"
+# version = "1.0.0"
+#
+# [[constraint]]
+# name = "github.com/user/project2"
+# branch = "dev"
+# source = "github.com/myfork/project2"
+#
+# [[override]]
+# name = "github.com/x/y"
+# version = "2.4.0"
+
+
+[[constraint]]
+ branch = "master"
+ name = "golang.org/x/crypto"
diff --git a/vendor/github.com/miekg/dns/Makefile.fuzz b/vendor/github.com/miekg/dns/Makefile.fuzz
new file mode 100644
index 0000000000..dc158c4ace
--- /dev/null
+++ b/vendor/github.com/miekg/dns/Makefile.fuzz
@@ -0,0 +1,33 @@
+# Makefile for fuzzing
+#
+# Use go-fuzz and needs the tools installed.
+# See https://blog.cloudflare.com/dns-parser-meet-go-fuzzer/
+#
+# Installing go-fuzz:
+# $ make -f Makefile.fuzz get
+# Installs:
+# * github.com/dvyukov/go-fuzz/go-fuzz
+# * get github.com/dvyukov/go-fuzz/go-fuzz-build
+
+all: build
+
+.PHONY: build
+build:
+ go-fuzz-build -tags fuzz github.com/miekg/dns
+
+.PHONY: build-newrr
+build-newrr:
+ go-fuzz-build -func FuzzNewRR -tags fuzz github.com/miekg/dns
+
+.PHONY: fuzz
+fuzz:
+ go-fuzz -bin=dns-fuzz.zip -workdir=fuzz
+
+.PHONY: get
+get:
+ go get github.com/dvyukov/go-fuzz/go-fuzz
+ go get github.com/dvyukov/go-fuzz/go-fuzz-build
+
+.PHONY: clean
+clean:
+ rm *-fuzz.zip
diff --git a/vendor/github.com/miekg/dns/Makefile.release b/vendor/github.com/miekg/dns/Makefile.release
new file mode 100644
index 0000000000..8fb748e8aa
--- /dev/null
+++ b/vendor/github.com/miekg/dns/Makefile.release
@@ -0,0 +1,52 @@
+# Makefile for releasing.
+#
+# The release is controlled from version.go. The version found there is
+# used to tag the git repo, we're not building any artifects so there is nothing
+# to upload to github.
+#
+# * Up the version in version.go
+# * Run: make -f Makefile.release release
+# * will *commit* your change with 'Release $VERSION'
+# * push to github
+#
+
+define GO
+//+build ignore
+
+package main
+
+import (
+ "fmt"
+
+ "github.com/miekg/dns"
+)
+
+func main() {
+ fmt.Println(dns.Version.String())
+}
+endef
+
+$(file > version_release.go,$(GO))
+VERSION:=$(shell go run version_release.go)
+TAG="v$(VERSION)"
+
+all:
+ @echo Use the \'release\' target to start a release $(VERSION)
+ rm -f version_release.go
+
+.PHONY: release
+release: commit push
+ @echo Released $(VERSION)
+ rm -f version_release.go
+
+.PHONY: commit
+commit:
+ @echo Committing release $(VERSION)
+ git commit -am"Release $(VERSION)"
+ git tag $(TAG)
+
+.PHONY: push
+push:
+ @echo Pushing release $(VERSION) to master
+ git push --tags
+ git push
diff --git a/vendor/github.com/miekg/dns/README.md b/vendor/github.com/miekg/dns/README.md
index 94b54fe257..e6dbad25d4 100644
--- a/vendor/github.com/miekg/dns/README.md
+++ b/vendor/github.com/miekg/dns/README.md
@@ -74,7 +74,7 @@ Send pull request if you want to be listed here.
* Parsing RRs ~ 100K RR/s, that's 5M records in about 50 seconds;
* Server side programming (mimicking the net/http package);
* Client side programming;
-* DNSSEC: signing, validating and key generation for DSA, RSA and ECDSA;
+* DNSSEC: signing, validating and key generation for DSA, RSA, ECDSA and Ed25519;
* EDNS0, NSID, Cookies;
* AXFR/IXFR;
* TSIG, SIG(0);
@@ -150,10 +150,13 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
* 6975 - Algorithm Understanding in DNSSEC
* 7043 - EUI48/EUI64 records
* 7314 - DNS (EDNS) EXPIRE Option
+* 7477 - CSYNC RR
* 7828 - edns-tcp-keepalive EDNS0 Option
* 7553 - URI record
* 7858 - DNS over TLS: Initiation and Performance Considerations
+* 7871 - EDNS0 Client Subnet
* 7873 - Domain Name System (DNS) Cookies (draft-ietf-dnsop-cookies)
+* 8080 - EdDSA for DNSSEC
## Loosely based upon
diff --git a/vendor/github.com/miekg/dns/client.go b/vendor/github.com/miekg/dns/client.go
index 0914482ab3..856b1698d5 100644
--- a/vendor/github.com/miekg/dns/client.go
+++ b/vendor/github.com/miekg/dns/client.go
@@ -20,7 +20,7 @@ const tcpIdleTimeout time.Duration = 8 * time.Second
type Conn struct {
net.Conn // a net.Conn holding the connection
UDPSize uint16 // minimum receive buffer for UDP messages
- TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be fully qualified
+ TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
rtt time.Duration
t time.Time
tsigRequestMAC string
@@ -39,7 +39,7 @@ type Client struct {
DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds, or net.Dialer.Timeout if expiring earlier - overridden by Timeout when that value is non-zero
ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero
- TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be fully qualified
+ TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass
group singleflight
}
@@ -192,8 +192,10 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro
}
// ReadMsg reads a message from the connection co.
-// If the received message contains a TSIG record the transaction
-// signature is verified.
+// If the received message contains a TSIG record the transaction signature
+// is verified. This method always tries to return the message, however if an
+// error is returned there are no guarantees that the returned message is a
+// valid representation of the packet read.
func (co *Conn) ReadMsg() (*Msg, error) {
p, err := co.ReadMsgHeader(nil)
if err != nil {
@@ -202,13 +204,10 @@ func (co *Conn) ReadMsg() (*Msg, error) {
m := new(Msg)
if err := m.Unpack(p); err != nil {
- // If ErrTruncated was returned, we still want to allow the user to use
+ // If an error was returned, we still want to allow the user to use
// the message, but naively they can just check err if they don't want
- // to use a truncated message
- if err == ErrTruncated {
- return m, err
- }
- return nil, err
+ // to use an erroneous message
+ return m, err
}
if t := m.IsTsig(); t != nil {
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
diff --git a/vendor/github.com/miekg/dns/client_test.go b/vendor/github.com/miekg/dns/client_test.go
index ef977aefc3..cc41983199 100644
--- a/vendor/github.com/miekg/dns/client_test.go
+++ b/vendor/github.com/miekg/dns/client_test.go
@@ -458,12 +458,12 @@ func TestTruncatedMsg(t *testing.T) {
t.Errorf("error should not be ErrTruncated from question cutoff unpack: %v", err)
}
- // Finally, if we only have the header, we should still return an error
+ // Finally, if we only have the header, we don't return an error.
buf1 = buf[:12]
r = new(Msg)
- if err = r.Unpack(buf1); err == nil || err != ErrTruncated {
- t.Errorf("error not ErrTruncated from header-only unpack: %v", err)
+ if err = r.Unpack(buf1); err != nil {
+ t.Errorf("from header-only unpack should not return an error: %v", err)
}
}
@@ -568,11 +568,11 @@ func TestConcurrentExchanges(t *testing.T) {
wg.Add(len(r))
for i := 0; i < len(r); i++ {
go func(i int) {
+ defer wg.Done()
r[i], _, _ = c.Exchange(m.Copy(), addrstr)
if r[i] == nil {
- t.Fatalf("response %d is nil", i)
+ t.Errorf("response %d is nil", i)
}
- wg.Done()
}(i)
}
select {
diff --git a/vendor/github.com/miekg/dns/clientconfig.go b/vendor/github.com/miekg/dns/clientconfig.go
index 6479fef93b..a606ef696e 100644
--- a/vendor/github.com/miekg/dns/clientconfig.go
+++ b/vendor/github.com/miekg/dns/clientconfig.go
@@ -79,8 +79,10 @@ func ClientConfigFromReader(resolvconf io.Reader) (*ClientConfig, error) {
switch {
case len(s) >= 6 && s[:6] == "ndots:":
n, _ := strconv.Atoi(s[6:])
- if n < 1 {
- n = 1
+ if n < 0 {
+ n = 0
+ } else if n > 15 {
+ n = 15
}
c.Ndots = n
case len(s) >= 8 && s[:8] == "timeout:":
diff --git a/vendor/github.com/miekg/dns/clientconfig_test.go b/vendor/github.com/miekg/dns/clientconfig_test.go
index 4c5d1fb600..5c10360d73 100644
--- a/vendor/github.com/miekg/dns/clientconfig_test.go
+++ b/vendor/github.com/miekg/dns/clientconfig_test.go
@@ -40,6 +40,28 @@ func testConfig(t *testing.T, data string) {
func TestNameserver(t *testing.T) { testConfig(t, normal) }
func TestMissingFinalNewLine(t *testing.T) { testConfig(t, missingNewline) }
+func TestNdots(t *testing.T) {
+ ndotsVariants := map[string]int{
+ "options ndots:0": 0,
+ "options ndots:1": 1,
+ "options ndots:15": 15,
+ "options ndots:16": 15,
+ "options ndots:-1": 0,
+ "": 1,
+ }
+
+ for data := range ndotsVariants {
+ cc, err := ClientConfigFromReader(strings.NewReader(data))
+ if err != nil {
+ t.Errorf("error parsing resolv.conf: %v", err)
+ }
+ if cc.Ndots != ndotsVariants[data] {
+ t.Errorf("Ndots not properly parsed: (Expected: %d / Was: %d)", ndotsVariants[data], cc.Ndots)
+ }
+ }
+
+}
+
func TestReadFromFile(t *testing.T) {
tempDir, err := ioutil.TempDir("", "")
if err != nil {
@@ -67,7 +89,7 @@ func TestReadFromFile(t *testing.T) {
}
}
-func TestNameList(t *testing.T) {
+func TestNameListNdots1(t *testing.T) {
cfg := ClientConfig{
Ndots: 1,
}
@@ -91,10 +113,18 @@ func TestNameList(t *testing.T) {
} else if names[1] != "miek.nl.test." {
t.Errorf("NameList didn't return search last: %v", names[1])
}
+}
+func TestNameListNdots2(t *testing.T) {
+ cfg := ClientConfig{
+ Ndots: 2,
+ }
- cfg.Ndots = 2
// Sent domain has less than NDots and search
- names = cfg.NameList("miek.nl")
+ cfg.Search = []string{
+ "test",
+ }
+ names := cfg.NameList("miek.nl")
+
if len(names) != 2 {
t.Errorf("NameList returned != 2 names: %v", names)
} else if names[0] != "miek.nl.test." {
@@ -103,3 +133,21 @@ func TestNameList(t *testing.T) {
t.Errorf("NameList didn't return sent domain last: %v", names[1])
}
}
+
+func TestNameListNdots0(t *testing.T) {
+ cfg := ClientConfig{
+ Ndots: 0,
+ }
+ cfg.Search = []string{
+ "test",
+ }
+ // Sent domain has less than NDots and search
+ names := cfg.NameList("miek")
+ if len(names) != 2 {
+ t.Errorf("NameList returned != 2 names: %v", names)
+ } else if names[0] != "miek." {
+ t.Errorf("NameList didn't return search first: %v", names[0])
+ } else if names[1] != "miek.test." {
+ t.Errorf("NameList didn't return sent domain last: %v", names[1])
+ }
+}
diff --git a/vendor/github.com/miekg/dns/compress_generate.go b/vendor/github.com/miekg/dns/compress_generate.go
index d2e5db26e9..87fb36f68c 100644
--- a/vendor/github.com/miekg/dns/compress_generate.go
+++ b/vendor/github.com/miekg/dns/compress_generate.go
@@ -18,8 +18,7 @@ import (
)
var packageHdr = `
-// *** DO NOT MODIFY ***
-// AUTOGENERATED BY go generate from compress_generate.go
+// Code generated by "go run compress_generate.go"; DO NOT EDIT.
package dns
diff --git a/vendor/github.com/miekg/dns/defaults.go b/vendor/github.com/miekg/dns/defaults.go
index c34890eecc..14e18b0b38 100644
--- a/vendor/github.com/miekg/dns/defaults.go
+++ b/vendor/github.com/miekg/dns/defaults.go
@@ -273,8 +273,11 @@ func (t Type) String() string {
// String returns the string representation for the class c.
func (c Class) String() string {
- if c1, ok := ClassToString[uint16(c)]; ok {
- return c1
+ if s, ok := ClassToString[uint16(c)]; ok {
+ // Only emit mnemonics when they are unambiguous, specically ANY is in both.
+ if _, ok := StringToType[s]; !ok {
+ return s
+ }
}
return "CLASS" + strconv.Itoa(int(c))
}
diff --git a/vendor/github.com/miekg/dns/dns_test.go b/vendor/github.com/miekg/dns/dns_test.go
index 5835c13d56..feface4bf2 100644
--- a/vendor/github.com/miekg/dns/dns_test.go
+++ b/vendor/github.com/miekg/dns/dns_test.go
@@ -1,6 +1,7 @@
package dns
import (
+ "bytes"
"encoding/hex"
"net"
"testing"
@@ -317,8 +318,9 @@ func TestNoRdataPack(t *testing.T) {
func TestNoRdataUnpack(t *testing.T) {
data := make([]byte, 1024)
for typ, fn := range TypeToRR {
- if typ == TypeSOA || typ == TypeTSIG {
+ if typ == TypeSOA || typ == TypeTSIG || typ == TypeTKEY {
// SOA, TSIG will not be seen (like this) in dyn. updates?
+ // TKEY requires length fields to be present for the Key and OtherData fields
continue
}
r := fn()
@@ -405,3 +407,60 @@ func TestMsgPackBuffer(t *testing.T) {
}
}
}
+
+// Make sure we can decode a TKEY packet from the string, modify the RR, and then pack it again.
+func TestTKEY(t *testing.T) {
+ // An example TKEY RR captured. There is no known accepted standard text format for a TKEY
+ // record so we do this from a hex string instead of from a text readable string.
+ tkeyStr := "0737362d6d732d370932322d3332633233332463303439663961662d633065612d313165372d363839362d6463333937396666656666640000f900ff0000000000d2086773732d747369670059fd01f359fe53730003000000b8a181b53081b2a0030a0100a10b06092a864882f712010202a2819d04819a60819706092a864886f71201020202006f8187308184a003020105a10302010fa2783076a003020112a26f046db29b1b1d2625da3b20b49dafef930dd1e9aad335e1c5f45dcd95e0005d67a1100f3e573d70506659dbed064553f1ab890f68f65ae10def0dad5b423b39f240ebe666f2886c5fe03819692d29182bbed87b83e1f9d16b7334ec16a3c4fc5ad4a990088e0be43f0c6957916f5fe60000"
+ tkeyBytes, err := hex.DecodeString(tkeyStr)
+ if err != nil {
+ t.Fatal("unable to decode TKEY string ", err)
+ }
+ // Decode the RR
+ rr, tkeyLen, unPackErr := UnpackRR(tkeyBytes, 0)
+ if unPackErr != nil {
+ t.Fatal("unable to decode TKEY RR", unPackErr)
+ }
+ // Make sure it's a TKEY record
+ if rr.Header().Rrtype != TypeTKEY {
+ t.Fatal("Unable to decode TKEY")
+ }
+ // Make sure we get back the same length
+ if rr.len() != len(tkeyBytes) {
+ t.Fatalf("Lengths don't match %d != %d", rr.len(), len(tkeyBytes))
+ }
+ // make space for it with some fudge room
+ msg := make([]byte, tkeyLen+1000)
+ offset, packErr := PackRR(rr, msg, 0, nil, false)
+ if packErr != nil {
+ t.Fatal("unable to pack TKEY RR", packErr)
+ }
+ if offset != len(tkeyBytes) {
+ t.Fatalf("mismatched TKEY RR size %d != %d", len(tkeyBytes), offset)
+ }
+ if bytes.Compare(tkeyBytes, msg[0:offset]) != 0 {
+ t.Fatal("mismatched TKEY data after rewriting bytes")
+ }
+ t.Logf("got TKEY of: " + rr.String())
+ // Now add some bytes to this and make sure we can encode OtherData properly
+ tkey := rr.(*TKEY)
+ tkey.OtherData = "abcd"
+ tkey.OtherLen = 2
+ offset, packErr = PackRR(tkey, msg, 0, nil, false)
+ if packErr != nil {
+ t.Fatal("unable to pack TKEY RR after modification", packErr)
+ }
+ if offset != (len(tkeyBytes) + 2) {
+ t.Fatalf("mismatched TKEY RR size %d != %d", offset, len(tkeyBytes)+2)
+ }
+ t.Logf("modified to TKEY of: " + rr.String())
+
+ // Make sure we can parse our string output
+ tkey.Hdr.Class = ClassINET // https://github.com/miekg/dns/issues/577
+ newRR, newError := NewRR(tkey.String())
+ if newError != nil {
+ t.Fatalf("unable to parse TKEY string: %s", newError)
+ }
+ t.Log("got reparsed TKEY of newRR: " + newRR.String())
+}
diff --git a/vendor/github.com/miekg/dns/dnssec.go b/vendor/github.com/miekg/dns/dnssec.go
index 3bd55388d8..ac9fdd45ee 100644
--- a/vendor/github.com/miekg/dns/dnssec.go
+++ b/vendor/github.com/miekg/dns/dnssec.go
@@ -19,6 +19,8 @@ import (
"sort"
"strings"
"time"
+
+ "golang.org/x/crypto/ed25519"
)
// DNSSEC encryption algorithm codes.
@@ -38,6 +40,8 @@ const (
ECCGOST
ECDSAP256SHA256
ECDSAP384SHA384
+ ED25519
+ ED448
INDIRECT uint8 = 252
PRIVATEDNS uint8 = 253 // Private (experimental keys)
PRIVATEOID uint8 = 254
@@ -56,6 +60,8 @@ var AlgorithmToString = map[uint8]string{
ECCGOST: "ECC-GOST",
ECDSAP256SHA256: "ECDSAP256SHA256",
ECDSAP384SHA384: "ECDSAP384SHA384",
+ ED25519: "ED25519",
+ ED448: "ED448",
INDIRECT: "INDIRECT",
PRIVATEDNS: "PRIVATEDNS",
PRIVATEOID: "PRIVATEOID",
@@ -73,6 +79,7 @@ var AlgorithmToHash = map[uint8]crypto.Hash{
ECDSAP256SHA256: crypto.SHA256,
ECDSAP384SHA384: crypto.SHA384,
RSASHA512: crypto.SHA512,
+ ED25519: crypto.Hash(0),
}
// DNSSEC hashing algorithm codes.
@@ -301,17 +308,33 @@ func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
return ErrAlg
}
- h := hash.New()
- h.Write(signdata)
- h.Write(wire)
+ switch rr.Algorithm {
+ case ED25519:
+ // ed25519 signs the raw message and performs hashing internally.
+ // All other supported signature schemes operate over the pre-hashed
+ // message, and thus ed25519 must be handled separately here.
+ //
+ // The raw message is passed directly into sign and crypto.Hash(0) is
+ // used to signal to the crypto.Signer that the data has not been hashed.
+ signature, err := sign(k, append(signdata, wire...), crypto.Hash(0), rr.Algorithm)
+ if err != nil {
+ return err
+ }
- signature, err := sign(k, h.Sum(nil), hash, rr.Algorithm)
- if err != nil {
- return err
+ rr.Signature = toBase64(signature)
+ default:
+ h := hash.New()
+ h.Write(signdata)
+ h.Write(wire)
+
+ signature, err := sign(k, h.Sum(nil), hash, rr.Algorithm)
+ if err != nil {
+ return err
+ }
+
+ rr.Signature = toBase64(signature)
}
- rr.Signature = toBase64(signature)
-
return nil
}
@@ -352,6 +375,9 @@ func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte,
// signature = append(signature, intToBytes(r1, 20)...)
// signature = append(signature, intToBytes(s1, 20)...)
// rr.Signature = signature
+
+ case ED25519:
+ return signature, nil
}
return nil, ErrAlg
@@ -456,6 +482,17 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
}
return ErrSig
+ case ED25519:
+ pubkey := k.publicKeyED25519()
+ if pubkey == nil {
+ return ErrKey
+ }
+
+ if ed25519.Verify(pubkey, append(signeddata, wire...), sigbuf) {
+ return nil
+ }
+ return ErrSig
+
default:
return ErrAlg
}
@@ -578,6 +615,17 @@ func (k *DNSKEY) publicKeyDSA() *dsa.PublicKey {
return pubkey
}
+func (k *DNSKEY) publicKeyED25519() ed25519.PublicKey {
+ keybuf, err := fromBase64([]byte(k.PublicKey))
+ if err != nil {
+ return nil
+ }
+ if len(keybuf) != ed25519.PublicKeySize {
+ return nil
+ }
+ return keybuf
+}
+
type wireSlice [][]byte
func (p wireSlice) Len() int { return len(p) }
@@ -615,6 +663,10 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
switch x := r1.(type) {
case *NS:
x.Ns = strings.ToLower(x.Ns)
+ case *MD:
+ x.Md = strings.ToLower(x.Md)
+ case *MF:
+ x.Mf = strings.ToLower(x.Mf)
case *CNAME:
x.Target = strings.ToLower(x.Target)
case *SOA:
@@ -633,6 +685,18 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
x.Email = strings.ToLower(x.Email)
case *MX:
x.Mx = strings.ToLower(x.Mx)
+ case *RP:
+ x.Mbox = strings.ToLower(x.Mbox)
+ x.Txt = strings.ToLower(x.Txt)
+ case *AFSDB:
+ x.Hostname = strings.ToLower(x.Hostname)
+ case *RT:
+ x.Host = strings.ToLower(x.Host)
+ case *SIG:
+ x.SignerName = strings.ToLower(x.SignerName)
+ case *PX:
+ x.Map822 = strings.ToLower(x.Map822)
+ x.Mapx400 = strings.ToLower(x.Mapx400)
case *NAPTR:
x.Replacement = strings.ToLower(x.Replacement)
case *KX:
diff --git a/vendor/github.com/miekg/dns/dnssec_keygen.go b/vendor/github.com/miekg/dns/dnssec_keygen.go
index 5e4b7741a6..33e913ac52 100644
--- a/vendor/github.com/miekg/dns/dnssec_keygen.go
+++ b/vendor/github.com/miekg/dns/dnssec_keygen.go
@@ -8,6 +8,8 @@ import (
"crypto/rand"
"crypto/rsa"
"math/big"
+
+ "golang.org/x/crypto/ed25519"
)
// Generate generates a DNSKEY of the given bit size.
@@ -38,6 +40,10 @@ func (k *DNSKEY) Generate(bits int) (crypto.PrivateKey, error) {
if bits != 384 {
return nil, ErrKeySize
}
+ case ED25519:
+ if bits != 256 {
+ return nil, ErrKeySize
+ }
}
switch k.Algorithm {
@@ -75,6 +81,13 @@ func (k *DNSKEY) Generate(bits int) (crypto.PrivateKey, error) {
}
k.setPublicKeyECDSA(priv.PublicKey.X, priv.PublicKey.Y)
return priv, nil
+ case ED25519:
+ pub, priv, err := ed25519.GenerateKey(rand.Reader)
+ if err != nil {
+ return nil, err
+ }
+ k.setPublicKeyED25519(pub)
+ return priv, nil
default:
return nil, ErrAlg
}
@@ -117,6 +130,15 @@ func (k *DNSKEY) setPublicKeyDSA(_Q, _P, _G, _Y *big.Int) bool {
return true
}
+// Set the public key for Ed25519
+func (k *DNSKEY) setPublicKeyED25519(_K ed25519.PublicKey) bool {
+ if _K == nil {
+ return false
+ }
+ k.PublicKey = toBase64(_K)
+ return true
+}
+
// Set the public key (the values E and N) for RSA
// RFC 3110: Section 2. RSA Public KEY Resource Records
func exponentToBuf(_E int) []byte {
diff --git a/vendor/github.com/miekg/dns/dnssec_keyscan.go b/vendor/github.com/miekg/dns/dnssec_keyscan.go
index 4f8d830b85..e2d9d8f924 100644
--- a/vendor/github.com/miekg/dns/dnssec_keyscan.go
+++ b/vendor/github.com/miekg/dns/dnssec_keyscan.go
@@ -1,6 +1,7 @@
package dns
import (
+ "bytes"
"crypto"
"crypto/dsa"
"crypto/ecdsa"
@@ -9,6 +10,8 @@ import (
"math/big"
"strconv"
"strings"
+
+ "golang.org/x/crypto/ed25519"
)
// NewPrivateKey returns a PrivateKey by parsing the string s.
@@ -86,6 +89,8 @@ func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, er
}
priv.PublicKey = *pub
return priv, nil
+ case ED25519:
+ return readPrivateKeyED25519(m)
default:
return nil, ErrPrivKey
}
@@ -166,13 +171,56 @@ func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) {
return p, nil
}
+func readPrivateKeyED25519(m map[string]string) (ed25519.PrivateKey, error) {
+ var p ed25519.PrivateKey
+ // TODO: validate that the required flags are present
+ for k, v := range m {
+ switch k {
+ case "privatekey":
+ p1, err := fromBase64([]byte(v))
+ if err != nil {
+ return nil, err
+ }
+ if len(p1) != 32 {
+ return nil, ErrPrivKey
+ }
+ // RFC 8080 and Golang's x/crypto/ed25519 differ as to how the
+ // private keys are represented. RFC 8080 specifies that private
+ // keys be stored solely as the seed value (p1 above) while the
+ // ed25519 package represents them as the seed value concatenated
+ // to the public key, which is derived from the seed value.
+ //
+ // ed25519.GenerateKey reads exactly 32 bytes from the passed in
+ // io.Reader and uses them as the seed. It also derives the
+ // public key and produces a compatible private key.
+ _, p, err = ed25519.GenerateKey(bytes.NewReader(p1))
+ if err != nil {
+ return nil, err
+ }
+ case "created", "publish", "activate":
+ /* not used in Go (yet) */
+ }
+ }
+ return p, nil
+}
+
// parseKey reads a private key from r. It returns a map[string]string,
// with the key-value pairs, or an error when the file is not correct.
func parseKey(r io.Reader, file string) (map[string]string, error) {
- s := scanInit(r)
+ s, cancel := scanInit(r)
m := make(map[string]string)
c := make(chan lex)
k := ""
+ defer func() {
+ cancel()
+ // zlexer can send up to two tokens, the next one and possibly 1 remainders.
+ // Do a non-blocking read.
+ _, ok := <-c
+ _, ok = <-c
+ if !ok {
+ // too bad
+ }
+ }()
// Start the lexer
go klexer(s, c)
for l := range c {
diff --git a/vendor/github.com/miekg/dns/dnssec_privkey.go b/vendor/github.com/miekg/dns/dnssec_privkey.go
index 56f3ea934f..46f3215c8f 100644
--- a/vendor/github.com/miekg/dns/dnssec_privkey.go
+++ b/vendor/github.com/miekg/dns/dnssec_privkey.go
@@ -7,6 +7,8 @@ import (
"crypto/rsa"
"math/big"
"strconv"
+
+ "golang.org/x/crypto/ed25519"
)
const format = "Private-key-format: v1.3\n"
@@ -79,6 +81,12 @@ func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string {
"Private_value(x): " + priv + "\n" +
"Public_value(y): " + pub + "\n"
+ case ed25519.PrivateKey:
+ private := toBase64(p[:32])
+ return format +
+ "Algorithm: " + algorithm + "\n" +
+ "PrivateKey: " + private + "\n"
+
default:
return ""
}
diff --git a/vendor/github.com/miekg/dns/dnssec_test.go b/vendor/github.com/miekg/dns/dnssec_test.go
index d5de439baa..7cf7c417af 100644
--- a/vendor/github.com/miekg/dns/dnssec_test.go
+++ b/vendor/github.com/miekg/dns/dnssec_test.go
@@ -8,6 +8,8 @@ import (
"strings"
"testing"
"time"
+
+ "golang.org/x/crypto/ed25519"
)
func getKey() *DNSKEY {
@@ -461,6 +463,55 @@ func TestSignVerifyECDSA2(t *testing.T) {
}
}
+func TestSignVerifyEd25519(t *testing.T) {
+ srv1, err := NewRR("srv.miek.nl. IN SRV 1000 800 0 web1.miek.nl.")
+ if err != nil {
+ t.Fatal(err)
+ }
+ srv := srv1.(*SRV)
+
+ // With this key
+ key := new(DNSKEY)
+ key.Hdr.Rrtype = TypeDNSKEY
+ key.Hdr.Name = "miek.nl."
+ key.Hdr.Class = ClassINET
+ key.Hdr.Ttl = 14400
+ key.Flags = 256
+ key.Protocol = 3
+ key.Algorithm = ED25519
+ privkey, err := key.Generate(256)
+ if err != nil {
+ t.Fatal("failure to generate key")
+ }
+
+ // Fill in the values of the Sig, before signing
+ sig := new(RRSIG)
+ sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
+ sig.TypeCovered = srv.Hdr.Rrtype
+ sig.Labels = uint8(CountLabel(srv.Hdr.Name)) // works for all 3
+ sig.OrigTtl = srv.Hdr.Ttl
+ sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
+ sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
+ sig.KeyTag = key.KeyTag() // Get the keyfrom the Key
+ sig.SignerName = key.Hdr.Name
+ sig.Algorithm = ED25519
+
+ if sig.Sign(privkey.(ed25519.PrivateKey), []RR{srv}) != nil {
+ t.Fatal("failure to sign the record")
+ }
+
+ err = sig.Verify(key, []RR{srv})
+ if err != nil {
+ t.Logf("failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v",
+ key.String(),
+ srv.String(),
+ sig.String(),
+ key.PrivateKeyString(privkey),
+ err,
+ )
+ }
+}
+
// Here the test vectors from the relevant RFCs are checked.
// rfc6605 6.1
func TestRFC6605P256(t *testing.T) {
@@ -590,6 +641,144 @@ PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
}
}
+// rfc8080 6.1
+func TestRFC8080Ed25519Example1(t *testing.T) {
+ exDNSKEY := `example.com. 3600 IN DNSKEY 257 3 15 (
+ l02Woi0iS8Aa25FQkUd9RMzZHJpBoRQwAQEX1SxZJA4= )`
+ exPriv := `Private-key-format: v1.2
+Algorithm: 15 (ED25519)
+PrivateKey: ODIyNjAzODQ2MjgwODAxMjI2NDUxOTAyMDQxNDIyNjI=`
+ rrDNSKEY, err := NewRR(exDNSKEY)
+ if err != nil {
+ t.Fatal(err)
+ }
+ priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ exDS := `example.com. 3600 IN DS 3613 15 2 (
+ 3aa5ab37efce57f737fc1627013fee07bdf241bd10f3b1964ab55c78e79
+ a304b )`
+ rrDS, err := NewRR(exDS)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256)
+ if !reflect.DeepEqual(ourDS, rrDS.(*DS)) {
+ t.Fatalf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS))
+ }
+
+ exMX := `example.com. 3600 IN MX 10 mail.example.com.`
+ exRRSIG := `example.com. 3600 IN RRSIG MX 15 2 3600 (
+ 1440021600 1438207200 3613 example.com. (
+ oL9krJun7xfBOIWcGHi7mag5/hdZrKWw15jPGrHpjQeRAvTdszaPD+QLs3f
+ x8A4M3e23mRZ9VrbpMngwcrqNAg== ) )`
+ rrMX, err := NewRR(exMX)
+ if err != nil {
+ t.Fatal(err)
+ }
+ rrRRSIG, err := NewRR(exRRSIG)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil {
+ t.Errorf("failure to validate the spec RRSIG: %v", err)
+ }
+
+ ourRRSIG := &RRSIG{
+ Hdr: RR_Header{
+ Ttl: rrMX.Header().Ttl,
+ },
+ KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(),
+ SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,
+ Algorithm: rrDNSKEY.(*DNSKEY).Algorithm,
+ }
+ ourRRSIG.Expiration, _ = StringToTime("20150819220000")
+ ourRRSIG.Inception, _ = StringToTime("20150729220000")
+ err = ourRRSIG.Sign(priv.(ed25519.PrivateKey), []RR{rrMX})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil {
+ t.Errorf("failure to validate our RRSIG: %v", err)
+ }
+
+ if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {
+ t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG))
+ }
+}
+
+// rfc8080 6.1
+func TestRFC8080Ed25519Example2(t *testing.T) {
+ exDNSKEY := `example.com. 3600 IN DNSKEY 257 3 15 (
+ zPnZ/QwEe7S8C5SPz2OfS5RR40ATk2/rYnE9xHIEijs= )`
+ exPriv := `Private-key-format: v1.2
+Algorithm: 15 (ED25519)
+PrivateKey: DSSF3o0s0f+ElWzj9E/Osxw8hLpk55chkmx0LYN5WiY=`
+ rrDNSKEY, err := NewRR(exDNSKEY)
+ if err != nil {
+ t.Fatal(err)
+ }
+ priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ exDS := `example.com. 3600 IN DS 35217 15 2 (
+ 401781b934e392de492ec77ae2e15d70f6575a1c0bc59c5275c04ebe80c
+ 6614c )`
+ rrDS, err := NewRR(exDS)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256)
+ if !reflect.DeepEqual(ourDS, rrDS.(*DS)) {
+ t.Fatalf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS))
+ }
+
+ exMX := `example.com. 3600 IN MX 10 mail.example.com.`
+ exRRSIG := `example.com. 3600 IN RRSIG MX 15 2 3600 (
+ 1440021600 1438207200 35217 example.com. (
+ zXQ0bkYgQTEFyfLyi9QoiY6D8ZdYo4wyUhVioYZXFdT410QPRITQSqJSnzQ
+ oSm5poJ7gD7AQR0O7KuI5k2pcBg== ) )`
+ rrMX, err := NewRR(exMX)
+ if err != nil {
+ t.Fatal(err)
+ }
+ rrRRSIG, err := NewRR(exRRSIG)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil {
+ t.Errorf("failure to validate the spec RRSIG: %v", err)
+ }
+
+ ourRRSIG := &RRSIG{
+ Hdr: RR_Header{
+ Ttl: rrMX.Header().Ttl,
+ },
+ KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(),
+ SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,
+ Algorithm: rrDNSKEY.(*DNSKEY).Algorithm,
+ }
+ ourRRSIG.Expiration, _ = StringToTime("20150819220000")
+ ourRRSIG.Inception, _ = StringToTime("20150729220000")
+ err = ourRRSIG.Sign(priv.(ed25519.PrivateKey), []RR{rrMX})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrMX}); err != nil {
+ t.Errorf("failure to validate our RRSIG: %v", err)
+ }
+
+ if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {
+ t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG))
+ }
+}
+
func TestInvalidRRSet(t *testing.T) {
goodRecords := make([]RR, 2)
goodRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
diff --git a/vendor/github.com/miekg/dns/doc.go b/vendor/github.com/miekg/dns/doc.go
index 26e288dfe6..1d8114744f 100644
--- a/vendor/github.com/miekg/dns/doc.go
+++ b/vendor/github.com/miekg/dns/doc.go
@@ -63,7 +63,7 @@ class) is as easy as setting:
c.SingleInflight = true
-More advanced options are availabe using a net.Dialer and the corresponding API.
+More advanced options are available using a net.Dialer and the corresponding API.
For example it is possible to set a timeout, or to specify a source IP address
and port to use for the connection:
diff --git a/vendor/github.com/miekg/dns/edns.go b/vendor/github.com/miekg/dns/edns.go
index a6a36bdf27..6f9d2ea393 100644
--- a/vendor/github.com/miekg/dns/edns.go
+++ b/vendor/github.com/miekg/dns/edns.go
@@ -13,19 +13,18 @@ import (
const (
EDNS0LLQ = 0x1 // long lived queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
EDNS0UL = 0x2 // update lease draft: http://files.dns-sd.org/draft-sekar-dns-ul.txt
- EDNS0NSID = 0x3 // nsid (RFC5001)
+ EDNS0NSID = 0x3 // nsid (See RFC 5001)
EDNS0DAU = 0x5 // DNSSEC Algorithm Understood
EDNS0DHU = 0x6 // DS Hash Understood
EDNS0N3U = 0x7 // NSEC3 Hash Understood
- EDNS0SUBNET = 0x8 // client-subnet (RFC6891)
+ EDNS0SUBNET = 0x8 // client-subnet (See RFC 7871)
EDNS0EXPIRE = 0x9 // EDNS0 expire
EDNS0COOKIE = 0xa // EDNS0 Cookie
- EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (RFC7828)
- EDNS0PADDING = 0xc // EDNS0 padding (RFC7830)
- EDNS0SUBNETDRAFT = 0x50fa // Don't use! Use EDNS0SUBNET
- EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (RFC6891)
- EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (RFC6891)
- _DO = 1 << 15 // dnssec ok
+ EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (See RFC 7828)
+ EDNS0PADDING = 0xc // EDNS0 padding (See RFC 7830)
+ EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (See RFC 6891)
+ EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (See RFC 6891)
+ _DO = 1 << 15 // DNSSEC OK
)
// OPT is the EDNS0 RR appended to messages to convey extra (meta) information.
@@ -58,9 +57,6 @@ func (rr *OPT) String() string {
}
case *EDNS0_SUBNET:
s += "\n; SUBNET: " + o.String()
- if o.(*EDNS0_SUBNET).DraftOption {
- s += " (draft)"
- }
case *EDNS0_COOKIE:
s += "\n; COOKIE: " + o.String()
case *EDNS0_UL:
@@ -188,7 +184,7 @@ func (e *EDNS0_NSID) unpack(b []byte) error { e.Nsid = hex.EncodeToString(b); re
func (e *EDNS0_NSID) String() string { return string(e.Nsid) }
// EDNS0_SUBNET is the subnet option that is used to give the remote nameserver
-// an idea of where the client lives. It can then give back a different
+// an idea of where the client lives. See RFC 7871. It can then give back a different
// answer depending on the location or network topology.
// Basic use pattern for creating an subnet option:
//
@@ -204,26 +200,19 @@ func (e *EDNS0_NSID) String() string { return string(e.Nsid) }
// // e.Address = net.ParseIP("2001:7b8:32a::2") // for IPV6
// o.Option = append(o.Option, e)
//
-// Note: the spec (draft-ietf-dnsop-edns-client-subnet-00) has some insane logic
-// for which netmask applies to the address. This code will parse all the
-// available bits when unpacking (up to optlen). When packing it will apply
-// SourceNetmask. If you need more advanced logic, patches welcome and good luck.
+// This code will parse all the available bits when unpacking (up to optlen).
+// When packing it will apply SourceNetmask. If you need more advanced logic,
+// patches welcome and good luck.
type EDNS0_SUBNET struct {
Code uint16 // Always EDNS0SUBNET
Family uint16 // 1 for IP, 2 for IP6
SourceNetmask uint8
SourceScope uint8
Address net.IP
- DraftOption bool // Set to true if using the old (0x50fa) option code
}
// Option implements the EDNS0 interface.
-func (e *EDNS0_SUBNET) Option() uint16 {
- if e.DraftOption {
- return EDNS0SUBNETDRAFT
- }
- return EDNS0SUBNET
-}
+func (e *EDNS0_SUBNET) Option() uint16 { return EDNS0SUBNET }
func (e *EDNS0_SUBNET) pack() ([]byte, error) {
b := make([]byte, 4)
@@ -431,6 +420,7 @@ func (e *EDNS0_LLQ) String() string {
return s
}
+// EDNS0_DUA implements the EDNS0 "DNSSEC Algorithm Understood" option. See RFC 6975.
type EDNS0_DAU struct {
Code uint16 // Always EDNS0DAU
AlgCode []uint8
@@ -453,6 +443,7 @@ func (e *EDNS0_DAU) String() string {
return s
}
+// EDNS0_DHU implements the EDNS0 "DS Hash Understood" option. See RFC 6975.
type EDNS0_DHU struct {
Code uint16 // Always EDNS0DHU
AlgCode []uint8
@@ -475,6 +466,7 @@ func (e *EDNS0_DHU) String() string {
return s
}
+// EDNS0_N3U implements the EDNS0 "NSEC3 Hash Understood" option. See RFC 6975.
type EDNS0_N3U struct {
Code uint16 // Always EDNS0N3U
AlgCode []uint8
@@ -498,6 +490,7 @@ func (e *EDNS0_N3U) String() string {
return s
}
+// EDNS0_EXPIRE implementes the EDNS0 option as described in RFC 7314.
type EDNS0_EXPIRE struct {
Code uint16 // Always EDNS0EXPIRE
Expire uint32
diff --git a/vendor/github.com/miekg/dns/fuzz.go b/vendor/github.com/miekg/dns/fuzz.go
new file mode 100644
index 0000000000..a8a09184d4
--- /dev/null
+++ b/vendor/github.com/miekg/dns/fuzz.go
@@ -0,0 +1,23 @@
+// +build fuzz
+
+package dns
+
+func Fuzz(data []byte) int {
+ msg := new(Msg)
+
+ if err := msg.Unpack(data); err != nil {
+ return 0
+ }
+ if _, err := msg.Pack(); err != nil {
+ return 0
+ }
+
+ return 1
+}
+
+func FuzzNewRR(data []byte) int {
+ if _, err := NewRR(string(data)); err != nil {
+ return 0
+ }
+ return 1
+}
diff --git a/vendor/github.com/miekg/dns/fuzz_test.go b/vendor/github.com/miekg/dns/fuzz_test.go
deleted file mode 100644
index 255869730e..0000000000
--- a/vendor/github.com/miekg/dns/fuzz_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package dns
-
-import "testing"
-
-func TestFuzzString(t *testing.T) {
- testcases := []string{"", " MINFO ", " RP ", " NSEC 0 0", " \" NSEC 0 0\"", " \" MINFO \"",
- ";a ", ";a����������",
- " NSAP O ", " NSAP N ",
- " TYPE4 TYPE6a789a3bc0045c8a5fb42c7d1bd998f5444 IN 9579b47d46817afbd17273e6",
- " TYPE45 3 3 4147994 TYPE\\(\\)\\)\\(\\)\\(\\(\\)\\(\\)\\)\\)\\(\\)\\(\\)\\(\\(\\R 948\"\")\\(\\)\\)\\)\\(\\ ",
- "$GENERATE 0-3 ${441189,5039418474430,o}",
- "$INCLUDE 00 TYPE00000000000n ",
- "$INCLUDE PE4 TYPE061463623/727071511 \\(\\)\\$GENERATE 6-462/0",
- }
- for i, tc := range testcases {
- rr, err := NewRR(tc)
- if err == nil {
- // rr can be nil because we can (for instance) just parse a comment
- if rr == nil {
- continue
- }
- t.Fatalf("parsed mailformed RR %d: %s", i, rr.String())
- }
- }
-}
diff --git a/vendor/github.com/miekg/dns/idn/code_points.go b/vendor/github.com/miekg/dns/idn/code_points.go
deleted file mode 100644
index 129c3742f5..0000000000
--- a/vendor/github.com/miekg/dns/idn/code_points.go
+++ /dev/null
@@ -1,2346 +0,0 @@
-package idn
-
-const (
- propertyUnknown property = iota // unknown character property
- propertyPVALID // allowed to be used in IDNs
- propertyCONTEXTJ // invisible or problematic characters (join controls)
- propertyCONTEXTO // invisible or problematic characters (others)
- propertyDISALLOWED // should not be included in IDNs
- propertyUNASSIGNED // code points that are not designated in the Unicode Standard
-)
-
-// property stores the property of a code point, as described in RFC 5892,
-// section 1
-type property int
-
-// codePoints list all code points in Unicode Character Database (UCD) Format
-// according to RFC 5892, appendix B.1. Thanks to libidn2 (GNU) -
-// http://www.gnu.org/software/libidn/libidn2/
-var codePoints = []struct {
- start rune
- end rune
- state property
-}{
- {0x0000, 0x002C, propertyDISALLOWED}, // ..COMMA
- {0x002D, 0x0, propertyPVALID}, // HYPHEN-MINUS
- {0x002E, 0x002F, propertyDISALLOWED}, // FULL STOP..SOLIDUS
- {0x0030, 0x0039, propertyPVALID}, // DIGIT ZERO..DIGIT NINE
- {0x003A, 0x0060, propertyDISALLOWED}, // COLON..GRAVE ACCENT
- {0x0041, 0x005A, propertyPVALID}, // LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z
- {0x0061, 0x007A, propertyPVALID}, // LATIN SMALL LETTER A..LATIN SMALL LETTER Z
- {0x007B, 0x00B6, propertyDISALLOWED}, // LEFT CURLY BRACKET..PILCROW SIGN
- {0x00B7, 0x0, propertyCONTEXTO}, // MIDDLE DOT
- {0x00B8, 0x00DE, propertyDISALLOWED}, // CEDILLA..LATIN CAPITAL LETTER THORN
- {0x00DF, 0x00F6, propertyPVALID}, // LATIN SMALL LETTER SHARP S..LATIN SMALL LETT
- {0x00F7, 0x0, propertyDISALLOWED}, // DIVISION SIGN
- {0x00F8, 0x00FF, propertyPVALID}, // LATIN SMALL LETTER O WITH STROKE..LATIN SMAL
- {0x0100, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH MACRON
- {0x0101, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH MACRON
- {0x0102, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH BREVE
- {0x0103, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH BREVE
- {0x0104, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH OGONEK
- {0x0105, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH OGONEK
- {0x0106, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER C WITH ACUTE
- {0x0107, 0x0, propertyPVALID}, // LATIN SMALL LETTER C WITH ACUTE
- {0x0108, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER C WITH CIRCUMFLEX
- {0x0109, 0x0, propertyPVALID}, // LATIN SMALL LETTER C WITH CIRCUMFLEX
- {0x010A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER C WITH DOT ABOVE
- {0x010B, 0x0, propertyPVALID}, // LATIN SMALL LETTER C WITH DOT ABOVE
- {0x010C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER C WITH CARON
- {0x010D, 0x0, propertyPVALID}, // LATIN SMALL LETTER C WITH CARON
- {0x010E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER D WITH CARON
- {0x010F, 0x0, propertyPVALID}, // LATIN SMALL LETTER D WITH CARON
- {0x0110, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER D WITH STROKE
- {0x0111, 0x0, propertyPVALID}, // LATIN SMALL LETTER D WITH STROKE
- {0x0112, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH MACRON
- {0x0113, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH MACRON
- {0x0114, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH BREVE
- {0x0115, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH BREVE
- {0x0116, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH DOT ABOVE
- {0x0117, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH DOT ABOVE
- {0x0118, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH OGONEK
- {0x0119, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH OGONEK
- {0x011A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH CARON
- {0x011B, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH CARON
- {0x011C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH CIRCUMFLEX
- {0x011D, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH CIRCUMFLEX
- {0x011E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH BREVE
- {0x011F, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH BREVE
- {0x0120, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH DOT ABOVE
- {0x0121, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH DOT ABOVE
- {0x0122, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH CEDILLA
- {0x0123, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH CEDILLA
- {0x0124, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER H WITH CIRCUMFLEX
- {0x0125, 0x0, propertyPVALID}, // LATIN SMALL LETTER H WITH CIRCUMFLEX
- {0x0126, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER H WITH STROKE
- {0x0127, 0x0, propertyPVALID}, // LATIN SMALL LETTER H WITH STROKE
- {0x0128, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH TILDE
- {0x0129, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH TILDE
- {0x012A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH MACRON
- {0x012B, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH MACRON
- {0x012C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH BREVE
- {0x012D, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH BREVE
- {0x012E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH OGONEK
- {0x012F, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH OGONEK
- {0x0130, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH DOT ABOVE
- {0x0131, 0x0, propertyPVALID}, // LATIN SMALL LETTER DOTLESS I
- {0x0132, 0x0134, propertyDISALLOWED}, // LATIN CAPITAL LIGATURE IJ..LATIN CAPITAL LET
- {0x0135, 0x0, propertyPVALID}, // LATIN SMALL LETTER J WITH CIRCUMFLEX
- {0x0136, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER K WITH CEDILLA
- {0x0137, 0x0138, propertyPVALID}, // LATIN SMALL LETTER K WITH CEDILLA..LATIN SMA
- {0x0139, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER L WITH ACUTE
- {0x013A, 0x0, propertyPVALID}, // LATIN SMALL LETTER L WITH ACUTE
- {0x013B, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER L WITH CEDILLA
- {0x013C, 0x0, propertyPVALID}, // LATIN SMALL LETTER L WITH CEDILLA
- {0x013D, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER L WITH CARON
- {0x013E, 0x0, propertyPVALID}, // LATIN SMALL LETTER L WITH CARON
- {0x013F, 0x0141, propertyDISALLOWED}, // LATIN CAPITAL LETTER L WITH MIDDLE DOT..LATI
- {0x0142, 0x0, propertyPVALID}, // LATIN SMALL LETTER L WITH STROKE
- {0x0143, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER N WITH ACUTE
- {0x0144, 0x0, propertyPVALID}, // LATIN SMALL LETTER N WITH ACUTE
- {0x0145, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER N WITH CEDILLA
- {0x0146, 0x0, propertyPVALID}, // LATIN SMALL LETTER N WITH CEDILLA
- {0x0147, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER N WITH CARON
- {0x0148, 0x0, propertyPVALID}, // LATIN SMALL LETTER N WITH CARON
- {0x0149, 0x014A, propertyDISALLOWED}, // LATIN SMALL LETTER N PRECEDED BY APOSTROPHE.
- {0x014B, 0x0, propertyPVALID}, // LATIN SMALL LETTER ENG
- {0x014C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH MACRON
- {0x014D, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH MACRON
- {0x014E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH BREVE
- {0x014F, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH BREVE
- {0x0150, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
- {0x0151, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH DOUBLE ACUTE
- {0x0152, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LIGATURE OE
- {0x0153, 0x0, propertyPVALID}, // LATIN SMALL LIGATURE OE
- {0x0154, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER R WITH ACUTE
- {0x0155, 0x0, propertyPVALID}, // LATIN SMALL LETTER R WITH ACUTE
- {0x0156, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER R WITH CEDILLA
- {0x0157, 0x0, propertyPVALID}, // LATIN SMALL LETTER R WITH CEDILLA
- {0x0158, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER R WITH CARON
- {0x0159, 0x0, propertyPVALID}, // LATIN SMALL LETTER R WITH CARON
- {0x015A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER S WITH ACUTE
- {0x015B, 0x0, propertyPVALID}, // LATIN SMALL LETTER S WITH ACUTE
- {0x015C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER S WITH CIRCUMFLEX
- {0x015D, 0x0, propertyPVALID}, // LATIN SMALL LETTER S WITH CIRCUMFLEX
- {0x015E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER S WITH CEDILLA
- {0x015F, 0x0, propertyPVALID}, // LATIN SMALL LETTER S WITH CEDILLA
- {0x0160, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER S WITH CARON
- {0x0161, 0x0, propertyPVALID}, // LATIN SMALL LETTER S WITH CARON
- {0x0162, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER T WITH CEDILLA
- {0x0163, 0x0, propertyPVALID}, // LATIN SMALL LETTER T WITH CEDILLA
- {0x0164, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER T WITH CARON
- {0x0165, 0x0, propertyPVALID}, // LATIN SMALL LETTER T WITH CARON
- {0x0166, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER T WITH STROKE
- {0x0167, 0x0, propertyPVALID}, // LATIN SMALL LETTER T WITH STROKE
- {0x0168, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH TILDE
- {0x0169, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH TILDE
- {0x016A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH MACRON
- {0x016B, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH MACRON
- {0x016C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH BREVE
- {0x016D, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH BREVE
- {0x016E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH RING ABOVE
- {0x016F, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH RING ABOVE
- {0x0170, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
- {0x0171, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH DOUBLE ACUTE
- {0x0172, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH OGONEK
- {0x0173, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH OGONEK
- {0x0174, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER W WITH CIRCUMFLEX
- {0x0175, 0x0, propertyPVALID}, // LATIN SMALL LETTER W WITH CIRCUMFLEX
- {0x0176, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
- {0x0177, 0x0, propertyPVALID}, // LATIN SMALL LETTER Y WITH CIRCUMFLEX
- {0x0178, 0x0179, propertyDISALLOWED}, // LATIN CAPITAL LETTER Y WITH DIAERESIS..LATIN
- {0x017A, 0x0, propertyPVALID}, // LATIN SMALL LETTER Z WITH ACUTE
- {0x017B, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Z WITH DOT ABOVE
- {0x017C, 0x0, propertyPVALID}, // LATIN SMALL LETTER Z WITH DOT ABOVE
- {0x017D, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Z WITH CARON
- {0x017E, 0x0, propertyPVALID}, // LATIN SMALL LETTER Z WITH CARON
- {0x017F, 0x0, propertyDISALLOWED}, // LATIN SMALL LETTER LONG S
- {0x0180, 0x0, propertyPVALID}, // LATIN SMALL LETTER B WITH STROKE
- {0x0181, 0x0182, propertyDISALLOWED}, // LATIN CAPITAL LETTER B WITH HOOK..LATIN CAPI
- {0x0183, 0x0, propertyPVALID}, // LATIN SMALL LETTER B WITH TOPBAR
- {0x0184, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER TONE SIX
- {0x0185, 0x0, propertyPVALID}, // LATIN SMALL LETTER TONE SIX
- {0x0186, 0x0187, propertyDISALLOWED}, // LATIN CAPITAL LETTER OPEN O..LATIN CAPITAL L
- {0x0188, 0x0, propertyPVALID}, // LATIN SMALL LETTER C WITH HOOK
- {0x0189, 0x018B, propertyDISALLOWED}, // LATIN CAPITAL LETTER AFRICAN D..LATIN CAPITA
- {0x018C, 0x018D, propertyPVALID}, // LATIN SMALL LETTER D WITH TOPBAR..LATIN SMAL
- {0x018E, 0x0191, propertyDISALLOWED}, // LATIN CAPITAL LETTER REVERSED E..LATIN CAPIT
- {0x0192, 0x0, propertyPVALID}, // LATIN SMALL LETTER F WITH HOOK
- {0x0193, 0x0194, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH HOOK..LATIN CAPI
- {0x0195, 0x0, propertyPVALID}, // LATIN SMALL LETTER HV
- {0x0196, 0x0198, propertyDISALLOWED}, // LATIN CAPITAL LETTER IOTA..LATIN CAPITAL LET
- {0x0199, 0x019B, propertyPVALID}, // LATIN SMALL LETTER K WITH HOOK..LATIN SMALL
- {0x019C, 0x019D, propertyDISALLOWED}, // LATIN CAPITAL LETTER TURNED M..LATIN CAPITAL
- {0x019E, 0x0, propertyPVALID}, // LATIN SMALL LETTER N WITH LONG RIGHT LEG
- {0x019F, 0x01A0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH MIDDLE TILDE..LA
- {0x01A1, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH HORN
- {0x01A2, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER OI
- {0x01A3, 0x0, propertyPVALID}, // LATIN SMALL LETTER OI
- {0x01A4, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER P WITH HOOK
- {0x01A5, 0x0, propertyPVALID}, // LATIN SMALL LETTER P WITH HOOK
- {0x01A6, 0x01A7, propertyDISALLOWED}, // LATIN LETTER YR..LATIN CAPITAL LETTER TONE T
- {0x01A8, 0x0, propertyPVALID}, // LATIN SMALL LETTER TONE TWO
- {0x01A9, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER ESH
- {0x01AA, 0x01AB, propertyPVALID}, // LATIN LETTER REVERSED ESH LOOP..LATIN SMALL
- {0x01AC, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER T WITH HOOK
- {0x01AD, 0x0, propertyPVALID}, // LATIN SMALL LETTER T WITH HOOK
- {0x01AE, 0x01AF, propertyDISALLOWED}, // LATIN CAPITAL LETTER T WITH RETROFLEX HOOK..
- {0x01B0, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH HORN
- {0x01B1, 0x01B3, propertyDISALLOWED}, // LATIN CAPITAL LETTER UPSILON..LATIN CAPITAL
- {0x01B4, 0x0, propertyPVALID}, // LATIN SMALL LETTER Y WITH HOOK
- {0x01B5, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Z WITH STROKE
- {0x01B6, 0x0, propertyPVALID}, // LATIN SMALL LETTER Z WITH STROKE
- {0x01B7, 0x01B8, propertyDISALLOWED}, // LATIN CAPITAL LETTER EZH..LATIN CAPITAL LETT
- {0x01B9, 0x01BB, propertyPVALID}, // LATIN SMALL LETTER EZH REVERSED..LATIN LETTE
- {0x01BC, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER TONE FIVE
- {0x01BD, 0x01C3, propertyPVALID}, // LATIN SMALL LETTER TONE FIVE..LATIN LETTER R
- {0x01C4, 0x01CD, propertyDISALLOWED}, // LATIN CAPITAL LETTER DZ WITH CARON..LATIN CA
- {0x01CE, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH CARON
- {0x01CF, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH CARON
- {0x01D0, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH CARON
- {0x01D1, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH CARON
- {0x01D2, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH CARON
- {0x01D3, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH CARON
- {0x01D4, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH CARON
- {0x01D5, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH DIAERESIS AND MA
- {0x01D6, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH DIAERESIS AND MACR
- {0x01D7, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH DIAERESIS AND AC
- {0x01D8, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH DIAERESIS AND ACUT
- {0x01D9, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH DIAERESIS AND CA
- {0x01DA, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH DIAERESIS AND CARO
- {0x01DB, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH DIAERESIS AND GR
- {0x01DC, 0x01DD, propertyPVALID}, // LATIN SMALL LETTER U WITH DIAERESIS AND GRAV
- {0x01DE, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH DIAERESIS AND MA
- {0x01DF, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH DIAERESIS AND MACR
- {0x01E0, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH DOT ABOVE AND MA
- {0x01E1, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH DOT ABOVE AND MACR
- {0x01E2, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER AE WITH MACRON
- {0x01E3, 0x0, propertyPVALID}, // LATIN SMALL LETTER AE WITH MACRON
- {0x01E4, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH STROKE
- {0x01E5, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH STROKE
- {0x01E6, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER G WITH CARON
- {0x01E7, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH CARON
- {0x01E8, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER K WITH CARON
- {0x01E9, 0x0, propertyPVALID}, // LATIN SMALL LETTER K WITH CARON
- {0x01EA, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH OGONEK
- {0x01EB, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH OGONEK
- {0x01EC, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH OGONEK AND MACRO
- {0x01ED, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH OGONEK AND MACRON
- {0x01EE, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER EZH WITH CARON
- {0x01EF, 0x01F0, propertyPVALID}, // LATIN SMALL LETTER EZH WITH CARON..LATIN SMA
- {0x01F1, 0x01F4, propertyDISALLOWED}, // LATIN CAPITAL LETTER DZ..LATIN CAPITAL LETTE
- {0x01F5, 0x0, propertyPVALID}, // LATIN SMALL LETTER G WITH ACUTE
- {0x01F6, 0x01F8, propertyDISALLOWED}, // LATIN CAPITAL LETTER HWAIR..LATIN CAPITAL LE
- {0x01F9, 0x0, propertyPVALID}, // LATIN SMALL LETTER N WITH GRAVE
- {0x01FA, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH RING ABOVE AND A
- {0x01FB, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH RING ABOVE AND ACU
- {0x01FC, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER AE WITH ACUTE
- {0x01FD, 0x0, propertyPVALID}, // LATIN SMALL LETTER AE WITH ACUTE
- {0x01FE, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH STROKE AND ACUTE
- {0x01FF, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH STROKE AND ACUTE
- {0x0200, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH DOUBLE GRAVE
- {0x0201, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH DOUBLE GRAVE
- {0x0202, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH INVERTED BREVE
- {0x0203, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH INVERTED BREVE
- {0x0204, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH DOUBLE GRAVE
- {0x0205, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH DOUBLE GRAVE
- {0x0206, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH INVERTED BREVE
- {0x0207, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH INVERTED BREVE
- {0x0208, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH DOUBLE GRAVE
- {0x0209, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH DOUBLE GRAVE
- {0x020A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER I WITH INVERTED BREVE
- {0x020B, 0x0, propertyPVALID}, // LATIN SMALL LETTER I WITH INVERTED BREVE
- {0x020C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH DOUBLE GRAVE
- {0x020D, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH DOUBLE GRAVE
- {0x020E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH INVERTED BREVE
- {0x020F, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH INVERTED BREVE
- {0x0210, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER R WITH DOUBLE GRAVE
- {0x0211, 0x0, propertyPVALID}, // LATIN SMALL LETTER R WITH DOUBLE GRAVE
- {0x0212, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER R WITH INVERTED BREVE
- {0x0213, 0x0, propertyPVALID}, // LATIN SMALL LETTER R WITH INVERTED BREVE
- {0x0214, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH DOUBLE GRAVE
- {0x0215, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH DOUBLE GRAVE
- {0x0216, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER U WITH INVERTED BREVE
- {0x0217, 0x0, propertyPVALID}, // LATIN SMALL LETTER U WITH INVERTED BREVE
- {0x0218, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER S WITH COMMA BELOW
- {0x0219, 0x0, propertyPVALID}, // LATIN SMALL LETTER S WITH COMMA BELOW
- {0x021A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER T WITH COMMA BELOW
- {0x021B, 0x0, propertyPVALID}, // LATIN SMALL LETTER T WITH COMMA BELOW
- {0x021C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER YOGH
- {0x021D, 0x0, propertyPVALID}, // LATIN SMALL LETTER YOGH
- {0x021E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER H WITH CARON
- {0x021F, 0x0, propertyPVALID}, // LATIN SMALL LETTER H WITH CARON
- {0x0220, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER N WITH LONG RIGHT LEG
- {0x0221, 0x0, propertyPVALID}, // LATIN SMALL LETTER D WITH CURL
- {0x0222, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER OU
- {0x0223, 0x0, propertyPVALID}, // LATIN SMALL LETTER OU
- {0x0224, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Z WITH HOOK
- {0x0225, 0x0, propertyPVALID}, // LATIN SMALL LETTER Z WITH HOOK
- {0x0226, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH DOT ABOVE
- {0x0227, 0x0, propertyPVALID}, // LATIN SMALL LETTER A WITH DOT ABOVE
- {0x0228, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER E WITH CEDILLA
- {0x0229, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH CEDILLA
- {0x022A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH DIAERESIS AND MA
- {0x022B, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH DIAERESIS AND MACR
- {0x022C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH TILDE AND MACRON
- {0x022D, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH TILDE AND MACRON
- {0x022E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH DOT ABOVE
- {0x022F, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH DOT ABOVE
- {0x0230, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER O WITH DOT ABOVE AND MA
- {0x0231, 0x0, propertyPVALID}, // LATIN SMALL LETTER O WITH DOT ABOVE AND MACR
- {0x0232, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Y WITH MACRON
- {0x0233, 0x0239, propertyPVALID}, // LATIN SMALL LETTER Y WITH MACRON..LATIN SMAL
- {0x023A, 0x023B, propertyDISALLOWED}, // LATIN CAPITAL LETTER A WITH STROKE..LATIN CA
- {0x023C, 0x0, propertyPVALID}, // LATIN SMALL LETTER C WITH STROKE
- {0x023D, 0x023E, propertyDISALLOWED}, // LATIN CAPITAL LETTER L WITH BAR..LATIN CAPIT
- {0x023F, 0x0240, propertyPVALID}, // LATIN SMALL LETTER S WITH SWASH TAIL..LATIN
- {0x0241, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER GLOTTAL STOP
- {0x0242, 0x0, propertyPVALID}, // LATIN SMALL LETTER GLOTTAL STOP
- {0x0243, 0x0246, propertyDISALLOWED}, // LATIN CAPITAL LETTER B WITH STROKE..LATIN CA
- {0x0247, 0x0, propertyPVALID}, // LATIN SMALL LETTER E WITH STROKE
- {0x0248, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER J WITH STROKE
- {0x0249, 0x0, propertyPVALID}, // LATIN SMALL LETTER J WITH STROKE
- {0x024A, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL
- {0x024B, 0x0, propertyPVALID}, // LATIN SMALL LETTER Q WITH HOOK TAIL
- {0x024C, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER R WITH STROKE
- {0x024D, 0x0, propertyPVALID}, // LATIN SMALL LETTER R WITH STROKE
- {0x024E, 0x0, propertyDISALLOWED}, // LATIN CAPITAL LETTER Y WITH STROKE
- {0x024F, 0x02AF, propertyPVALID}, // LATIN SMALL LETTER Y WITH STROKE..LATIN SMAL
- {0x02B0, 0x02B8, propertyDISALLOWED}, // MODIFIER LETTER SMALL H..MODIFIER LETTER SMA
- {0x02B9, 0x02C1, propertyPVALID}, // MODIFIER LETTER PRIME..MODIFIER LETTER REVER
- {0x02C2, 0x02C5, propertyDISALLOWED}, // MODIFIER LETTER LEFT ARROWHEAD..MODIFIER LET
- {0x02C6, 0x02D1, propertyPVALID}, // MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER
- {0x02D2, 0x02EB, propertyDISALLOWED}, // MODIFIER LETTER CENTRED RIGHT HALF RING..MOD
- {0x02EC, 0x0, propertyPVALID}, // MODIFIER LETTER VOICING
- {0x02ED, 0x0, propertyDISALLOWED}, // MODIFIER LETTER UNASPIRATED
- {0x02EE, 0x0, propertyPVALID}, // MODIFIER LETTER DOUBLE APOSTROPHE
- {0x02EF, 0x02FF, propertyDISALLOWED}, // MODIFIER LETTER LOW DOWN ARROWHEAD..MODIFIER
- {0x0300, 0x033F, propertyPVALID}, // COMBINING GRAVE ACCENT..COMBINING DOUBLE OVE
- {0x0340, 0x0341, propertyDISALLOWED}, // COMBINING GRAVE TONE MARK..COMBINING ACUTE T
- {0x0342, 0x0, propertyPVALID}, // COMBINING GREEK PERISPOMENI
- {0x0343, 0x0345, propertyDISALLOWED}, // COMBINING GREEK KORONIS..COMBINING GREEK YPO
- {0x0346, 0x034E, propertyPVALID}, // COMBINING BRIDGE ABOVE..COMBINING UPWARDS AR
- {0x034F, 0x0, propertyDISALLOWED}, // COMBINING GRAPHEME JOINER
- {0x0350, 0x036F, propertyPVALID}, // COMBINING RIGHT ARROWHEAD ABOVE..COMBINING L
- {0x0370, 0x0, propertyDISALLOWED}, // GREEK CAPITAL LETTER HETA
- {0x0371, 0x0, propertyPVALID}, // GREEK SMALL LETTER HETA
- {0x0372, 0x0, propertyDISALLOWED}, // GREEK CAPITAL LETTER ARCHAIC SAMPI
- {0x0373, 0x0, propertyPVALID}, // GREEK SMALL LETTER ARCHAIC SAMPI
- {0x0374, 0x0, propertyDISALLOWED}, // GREEK NUMERAL SIGN
- {0x0375, 0x0, propertyCONTEXTO}, // GREEK LOWER NUMERAL SIGN
- {0x0376, 0x0, propertyDISALLOWED}, // GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA
- {0x0377, 0x0, propertyPVALID}, // GREEK SMALL LETTER PAMPHYLIAN DIGAMMA
- {0x0378, 0x0379, propertyUNASSIGNED}, // ..
- {0x037A, 0x0, propertyDISALLOWED}, // GREEK YPOGEGRAMMENI
- {0x037B, 0x037D, propertyPVALID}, // GREEK SMALL REVERSED LUNATE SIGMA SYMBOL..GR
- {0x037E, 0x0, propertyDISALLOWED}, // GREEK QUESTION MARK
- {0x037F, 0x0383, propertyUNASSIGNED}, // ..
- {0x0384, 0x038A, propertyDISALLOWED}, // GREEK TONOS..GREEK CAPITAL LETTER IOTA WITH
- {0x038B, 0x0, propertyUNASSIGNED}, //
- {0x038C, 0x0, propertyDISALLOWED}, // GREEK CAPITAL LETTER OMICRON WITH TONOS
- {0x038D, 0x0, propertyUNASSIGNED}, //
- {0x038E, 0x038F, propertyDISALLOWED}, // GREEK CAPITAL LETTER UPSILON WITH TONOS..GRE
- {0x0390, 0x0, propertyPVALID}, // GREEK SMALL LETTER IOTA WITH DIALYTIKA AND T
- {0x0391, 0x03A1, propertyDISALLOWED}, // GREEK CAPITAL LETTER ALPHA..GREEK CAPITAL LE
- {0x03A2, 0x0, propertyUNASSIGNED}, //
- {0x03A3, 0x03AB, propertyDISALLOWED}, // GREEK CAPITAL LETTER SIGMA..GREEK CAPITAL LE
- {0x03AC, 0x03CE, propertyPVALID}, // GREEK SMALL LETTER ALPHA WITH TONOS..GREEK S
- {0x03CF, 0x03D6, propertyDISALLOWED}, // GREEK CAPITAL KAI SYMBOL..GREEK PI SYMBOL
- {0x03D7, 0x0, propertyPVALID}, // GREEK KAI SYMBOL
- {0x03D8, 0x0, propertyDISALLOWED}, // GREEK LETTER ARCHAIC KOPPA
- {0x03D9, 0x0, propertyPVALID}, // GREEK SMALL LETTER ARCHAIC KOPPA
- {0x03DA, 0x0, propertyDISALLOWED}, // GREEK LETTER STIGMA
- {0x03DB, 0x0, propertyPVALID}, // GREEK SMALL LETTER STIGMA
- {0x03DC, 0x0, propertyDISALLOWED}, // GREEK LETTER DIGAMMA
- {0x03DD, 0x0, propertyPVALID}, // GREEK SMALL LETTER DIGAMMA
- {0x03DE, 0x0, propertyDISALLOWED}, // GREEK LETTER KOPPA
- {0x03DF, 0x0, propertyPVALID}, // GREEK SMALL LETTER KOPPA
- {0x03E0, 0x0, propertyDISALLOWED}, // GREEK LETTER SAMPI
- {0x03E1, 0x0, propertyPVALID}, // GREEK SMALL LETTER SAMPI
- {0x03E2, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER SHEI
- {0x03E3, 0x0, propertyPVALID}, // COPTIC SMALL LETTER SHEI
- {0x03E4, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER FEI
- {0x03E5, 0x0, propertyPVALID}, // COPTIC SMALL LETTER FEI
- {0x03E6, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER KHEI
- {0x03E7, 0x0, propertyPVALID}, // COPTIC SMALL LETTER KHEI
- {0x03E8, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER HORI
- {0x03E9, 0x0, propertyPVALID}, // COPTIC SMALL LETTER HORI
- {0x03EA, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER GANGIA
- {0x03EB, 0x0, propertyPVALID}, // COPTIC SMALL LETTER GANGIA
- {0x03EC, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER SHIMA
- {0x03ED, 0x0, propertyPVALID}, // COPTIC SMALL LETTER SHIMA
- {0x03EE, 0x0, propertyDISALLOWED}, // COPTIC CAPITAL LETTER DEI
- {0x03EF, 0x0, propertyPVALID}, // COPTIC SMALL LETTER DEI
- {0x03F0, 0x03F2, propertyDISALLOWED}, // GREEK KAPPA SYMBOL..GREEK LUNATE SIGMA SYMBO
- {0x03F3, 0x0, propertyPVALID}, // GREEK LETTER YOT
- {0x03F4, 0x03F7, propertyDISALLOWED}, // GREEK CAPITAL THETA SYMBOL..GREEK CAPITAL LE
- {0x03F8, 0x0, propertyPVALID}, // GREEK SMALL LETTER SHO
- {0x03F9, 0x03FA, propertyDISALLOWED}, // GREEK CAPITAL LUNATE SIGMA SYMBOL..GREEK CAP
- {0x03FB, 0x03FC, propertyPVALID}, // GREEK SMALL LETTER SAN..GREEK RHO WITH STROK
- {0x03FD, 0x042F, propertyDISALLOWED}, // GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL..
- {0x0430, 0x045F, propertyPVALID}, // CYRILLIC SMALL LETTER A..CYRILLIC SMALL LETT
- {0x0460, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER OMEGA
- {0x0461, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER OMEGA
- {0x0462, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER YAT
- {0x0463, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER YAT
- {0x0464, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER IOTIFIED E
- {0x0465, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER IOTIFIED E
- {0x0466, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER LITTLE YUS
- {0x0467, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER LITTLE YUS
- {0x0468, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS
- {0x0469, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER IOTIFIED LITTLE YUS
- {0x046A, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER BIG YUS
- {0x046B, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER BIG YUS
- {0x046C, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS
- {0x046D, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER IOTIFIED BIG YUS
- {0x046E, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KSI
- {0x046F, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KSI
- {0x0470, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER PSI
- {0x0471, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER PSI
- {0x0472, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER FITA
- {0x0473, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER FITA
- {0x0474, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER IZHITSA
- {0x0475, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER IZHITSA
- {0x0476, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE
- {0x0477, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GR
- {0x0478, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER UK
- {0x0479, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER UK
- {0x047A, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ROUND OMEGA
- {0x047B, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ROUND OMEGA
- {0x047C, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER OMEGA WITH TITLO
- {0x047D, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER OMEGA WITH TITLO
- {0x047E, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER OT
- {0x047F, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER OT
- {0x0480, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOPPA
- {0x0481, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOPPA
- {0x0482, 0x0, propertyDISALLOWED}, // CYRILLIC THOUSANDS SIGN
- {0x0483, 0x0487, propertyPVALID}, // COMBINING CYRILLIC TITLO..COMBINING CYRILLIC
- {0x0488, 0x048A, propertyDISALLOWED}, // COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..C
- {0x048B, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER SHORT I WITH TAIL
- {0x048C, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER SEMISOFT SIGN
- {0x048D, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER SEMISOFT SIGN
- {0x048E, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ER WITH TICK
- {0x048F, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ER WITH TICK
- {0x0490, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER GHE WITH UPTURN
- {0x0491, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER GHE WITH UPTURN
- {0x0492, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER GHE WITH STROKE
- {0x0493, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER GHE WITH STROKE
- {0x0494, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK
- {0x0495, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER GHE WITH MIDDLE HOOK
- {0x0496, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER
- {0x0497, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ZHE WITH DESCENDER
- {0x0498, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ZE WITH DESCENDER
- {0x0499, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ZE WITH DESCENDER
- {0x049A, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KA WITH DESCENDER
- {0x049B, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KA WITH DESCENDER
- {0x049C, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KA WITH VERTICAL STR
- {0x049D, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KA WITH VERTICAL STROK
- {0x049E, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KA WITH STROKE
- {0x049F, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KA WITH STROKE
- {0x04A0, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER BASHKIR KA
- {0x04A1, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER BASHKIR KA
- {0x04A2, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EN WITH DESCENDER
- {0x04A3, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EN WITH DESCENDER
- {0x04A4, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LIGATURE EN GHE
- {0x04A5, 0x0, propertyPVALID}, // CYRILLIC SMALL LIGATURE EN GHE
- {0x04A6, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK
- {0x04A7, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER PE WITH MIDDLE HOOK
- {0x04A8, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ABKHASIAN HA
- {0x04A9, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ABKHASIAN HA
- {0x04AA, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ES WITH DESCENDER
- {0x04AB, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ES WITH DESCENDER
- {0x04AC, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER TE WITH DESCENDER
- {0x04AD, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER TE WITH DESCENDER
- {0x04AE, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER STRAIGHT U
- {0x04AF, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER STRAIGHT U
- {0x04B0, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER STRAIGHT U WITH STRO
- {0x04B1, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE
- {0x04B2, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER HA WITH DESCENDER
- {0x04B3, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER HA WITH DESCENDER
- {0x04B4, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LIGATURE TE TSE
- {0x04B5, 0x0, propertyPVALID}, // CYRILLIC SMALL LIGATURE TE TSE
- {0x04B6, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER CHE WITH DESCENDER
- {0x04B7, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER CHE WITH DESCENDER
- {0x04B8, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER CHE WITH VERTICAL ST
- {0x04B9, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER CHE WITH VERTICAL STRO
- {0x04BA, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER SHHA
- {0x04BB, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER SHHA
- {0x04BC, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ABKHASIAN CHE
- {0x04BD, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ABKHASIAN CHE
- {0x04BE, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH D
- {0x04BF, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ABKHASIAN CHE WITH DES
- {0x04C0, 0x04C1, propertyDISALLOWED}, // CYRILLIC LETTER PALOCHKA..CYRILLIC CAPITAL L
- {0x04C2, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ZHE WITH BREVE
- {0x04C3, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KA WITH HOOK
- {0x04C4, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KA WITH HOOK
- {0x04C5, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EL WITH TAIL
- {0x04C6, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EL WITH TAIL
- {0x04C7, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EN WITH HOOK
- {0x04C8, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EN WITH HOOK
- {0x04C9, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EN WITH TAIL
- {0x04CA, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EN WITH TAIL
- {0x04CB, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KHAKASSIAN CHE
- {0x04CC, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KHAKASSIAN CHE
- {0x04CD, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EM WITH TAIL
- {0x04CE, 0x04CF, propertyPVALID}, // CYRILLIC SMALL LETTER EM WITH TAIL..CYRILLIC
- {0x04D0, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER A WITH BREVE
- {0x04D1, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER A WITH BREVE
- {0x04D2, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER A WITH DIAERESIS
- {0x04D3, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER A WITH DIAERESIS
- {0x04D4, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LIGATURE A IE
- {0x04D5, 0x0, propertyPVALID}, // CYRILLIC SMALL LIGATURE A IE
- {0x04D6, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER IE WITH BREVE
- {0x04D7, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER IE WITH BREVE
- {0x04D8, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER SCHWA
- {0x04D9, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER SCHWA
- {0x04DA, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS
- {0x04DB, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS
- {0x04DC, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS
- {0x04DD, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ZHE WITH DIAERESIS
- {0x04DE, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS
- {0x04DF, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ZE WITH DIAERESIS
- {0x04E0, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ABKHASIAN DZE
- {0x04E1, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ABKHASIAN DZE
- {0x04E2, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER I WITH MACRON
- {0x04E3, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER I WITH MACRON
- {0x04E4, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER I WITH DIAERESIS
- {0x04E5, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER I WITH DIAERESIS
- {0x04E6, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER O WITH DIAERESIS
- {0x04E7, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER O WITH DIAERESIS
- {0x04E8, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER BARRED O
- {0x04E9, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER BARRED O
- {0x04EA, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER BARRED O WITH DIAERE
- {0x04EB, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER BARRED O WITH DIAERESI
- {0x04EC, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER E WITH DIAERESIS
- {0x04ED, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER E WITH DIAERESIS
- {0x04EE, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER U WITH MACRON
- {0x04EF, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER U WITH MACRON
- {0x04F0, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER U WITH DIAERESIS
- {0x04F1, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER U WITH DIAERESIS
- {0x04F2, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE
- {0x04F3, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE
- {0x04F4, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS
- {0x04F5, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER CHE WITH DIAERESIS
- {0x04F6, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER GHE WITH DESCENDER
- {0x04F7, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER GHE WITH DESCENDER
- {0x04F8, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS
- {0x04F9, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER YERU WITH DIAERESIS
- {0x04FA, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER GHE WITH STROKE AND
- {0x04FB, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER GHE WITH STROKE AND HO
- {0x04FC, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER HA WITH HOOK
- {0x04FD, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER HA WITH HOOK
- {0x04FE, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER HA WITH STROKE
- {0x04FF, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER HA WITH STROKE
- {0x0500, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI DE
- {0x0501, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI DE
- {0x0502, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI DJE
- {0x0503, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI DJE
- {0x0504, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI ZJE
- {0x0505, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI ZJE
- {0x0506, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI DZJE
- {0x0507, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI DZJE
- {0x0508, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI LJE
- {0x0509, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI LJE
- {0x050A, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI NJE
- {0x050B, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI NJE
- {0x050C, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI SJE
- {0x050D, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI SJE
- {0x050E, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER KOMI TJE
- {0x050F, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER KOMI TJE
- {0x0510, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER REVERSED ZE
- {0x0511, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER REVERSED ZE
- {0x0512, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EL WITH HOOK
- {0x0513, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EL WITH HOOK
- {0x0514, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER LHA
- {0x0515, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER LHA
- {0x0516, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER RHA
- {0x0517, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER RHA
- {0x0518, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER YAE
- {0x0519, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER YAE
- {0x051A, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER QA
- {0x051B, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER QA
- {0x051C, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER WE
- {0x051D, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER WE
- {0x051E, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER ALEUT KA
- {0x051F, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER ALEUT KA
- {0x0520, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EL WITH MIDDLE HOOK
- {0x0521, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EL WITH MIDDLE HOOK
- {0x0522, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER EN WITH MIDDLE HOOK
- {0x0523, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER EN WITH MIDDLE HOOK
- {0x0524, 0x0, propertyDISALLOWED}, // CYRILLIC CAPITAL LETTER PE WITH DESCENDER
- {0x0525, 0x0, propertyPVALID}, // CYRILLIC SMALL LETTER PE WITH DESCENDER
- {0x0526, 0x0530, propertyUNASSIGNED}, // ..
- {0x0531, 0x0556, propertyDISALLOWED}, // ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITA
- {0x0557, 0x0558, propertyUNASSIGNED}, // ..
- {0x0559, 0x0, propertyPVALID}, // ARMENIAN MODIFIER LETTER LEFT HALF RING
- {0x055A, 0x055F, propertyDISALLOWED}, // ARMENIAN APOSTROPHE..ARMENIAN ABBREVIATION M
- {0x0560, 0x0, propertyUNASSIGNED}, //
- {0x0561, 0x0586, propertyPVALID}, // ARMENIAN SMALL LETTER AYB..ARMENIAN SMALL LE
- {0x0587, 0x0, propertyDISALLOWED}, // ARMENIAN SMALL LIGATURE ECH YIWN
- {0x0588, 0x0, propertyUNASSIGNED}, //
- {0x0589, 0x058A, propertyDISALLOWED}, // ARMENIAN FULL STOP..ARMENIAN HYPHEN
- {0x058B, 0x0590, propertyUNASSIGNED}, // ..
- {0x0591, 0x05BD, propertyPVALID}, // HEBREW ACCENT ETNAHTA..HEBREW POINT METEG
- {0x05BE, 0x0, propertyDISALLOWED}, // HEBREW PUNCTUATION MAQAF
- {0x05BF, 0x0, propertyPVALID}, // HEBREW POINT RAFE
- {0x05C0, 0x0, propertyDISALLOWED}, // HEBREW PUNCTUATION PASEQ
- {0x05C1, 0x05C2, propertyPVALID}, // HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT
- {0x05C3, 0x0, propertyDISALLOWED}, // HEBREW PUNCTUATION SOF PASUQ
- {0x05C4, 0x05C5, propertyPVALID}, // HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT
- {0x05C6, 0x0, propertyDISALLOWED}, // HEBREW PUNCTUATION NUN HAFUKHA
- {0x05C7, 0x0, propertyPVALID}, // HEBREW POINT QAMATS QATAN
- {0x05C8, 0x05CF, propertyUNASSIGNED}, // ..
- {0x05D0, 0x05EA, propertyPVALID}, // HEBREW LETTER ALEF..HEBREW LETTER TAV
- {0x05EB, 0x05EF, propertyUNASSIGNED}, // ..
- {0x05F0, 0x05F2, propertyPVALID}, // HEBREW LIGATURE YIDDISH DOUBLE VAV..HEBREW L
- {0x05F3, 0x05F4, propertyCONTEXTO}, // HEBREW PUNCTUATION GERESH..HEBREW PUNCTUATIO
- {0x05F5, 0x05FF, propertyUNASSIGNED}, // ..
- {0x0600, 0x0603, propertyDISALLOWED}, // ARABIC NUMBER SIGN..ARABIC SIGN SAFHA
- {0x0604, 0x0605, propertyUNASSIGNED}, // ..
- {0x0606, 0x060F, propertyDISALLOWED}, // ARABIC-INDIC CUBE ROOT..ARABIC SIGN MISRA
- {0x0610, 0x061A, propertyPVALID}, // ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..AR
- {0x061B, 0x0, propertyDISALLOWED}, // ARABIC SEMICOLON
- {0x061C, 0x061D, propertyUNASSIGNED}, // ..
- {0x061E, 0x061F, propertyDISALLOWED}, // ARABIC TRIPLE DOT PUNCTUATION MARK..ARABIC Q
- {0x0620, 0x0, propertyUNASSIGNED}, //
- {0x0621, 0x063F, propertyPVALID}, // ARABIC LETTER HAMZA..ARABIC LETTER FARSI YEH
- {0x0640, 0x0, propertyDISALLOWED}, // ARABIC TATWEEL
- {0x0641, 0x065E, propertyPVALID}, // ARABIC LETTER FEH..ARABIC FATHA WITH TWO DOT
- {0x065F, 0x0, propertyUNASSIGNED}, //
- {0x0660, 0x0669, propertyCONTEXTO}, // ARABIC-INDIC DIGIT ZERO..ARABIC-INDIC DIGIT
- {0x066A, 0x066D, propertyDISALLOWED}, // ARABIC PERCENT SIGN..ARABIC FIVE POINTED STA
- {0x066E, 0x0674, propertyPVALID}, // ARABIC LETTER DOTLESS BEH..ARABIC LETTER HIG
- {0x0675, 0x0678, propertyDISALLOWED}, // ARABIC LETTER HIGH HAMZA ALEF..ARABIC LETTER
- {0x0679, 0x06D3, propertyPVALID}, // ARABIC LETTER TTEH..ARABIC LETTER YEH BARREE
- {0x06D4, 0x0, propertyDISALLOWED}, // ARABIC FULL STOP
- {0x06D5, 0x06DC, propertyPVALID}, // ARABIC LETTER AE..ARABIC SMALL HIGH SEEN
- {0x06DD, 0x06DE, propertyDISALLOWED}, // ARABIC END OF AYAH..ARABIC START OF RUB EL H
- {0x06DF, 0x06E8, propertyPVALID}, // ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL
- {0x06E9, 0x0, propertyDISALLOWED}, // ARABIC PLACE OF SAJDAH
- {0x06EA, 0x06EF, propertyPVALID}, // ARABIC EMPTY CENTRE LOW STOP..ARABIC LETTER
- {0x06F0, 0x06F9, propertyCONTEXTO}, // EXTENDED ARABIC-INDIC DIGIT ZERO..EXTENDED A
- {0x06FA, 0x06FF, propertyPVALID}, // ARABIC LETTER SHEEN WITH DOT BELOW..ARABIC L
- {0x0700, 0x070D, propertyDISALLOWED}, // SYRIAC END OF PARAGRAPH..SYRIAC HARKLEAN AST
- {0x070E, 0x0, propertyUNASSIGNED}, //
- {0x070F, 0x0, propertyDISALLOWED}, // SYRIAC ABBREVIATION MARK
- {0x0710, 0x074A, propertyPVALID}, // SYRIAC LETTER ALAPH..SYRIAC BARREKH
- {0x074B, 0x074C, propertyUNASSIGNED}, // ..
- {0x074D, 0x07B1, propertyPVALID}, // SYRIAC LETTER SOGDIAN ZHAIN..THAANA LETTER N
- {0x07B2, 0x07BF, propertyUNASSIGNED}, // ..
- {0x07C0, 0x07F5, propertyPVALID}, // NKO DIGIT ZERO..NKO LOW TONE APOSTROPHE
- {0x07F6, 0x07FA, propertyDISALLOWED}, // NKO SYMBOL OO DENNEN..NKO LAJANYALAN
- {0x07FB, 0x07FF, propertyUNASSIGNED}, // ..
- {0x0800, 0x082D, propertyPVALID}, // SAMARITAN LETTER ALAF..SAMARITAN MARK NEQUDA
- {0x082E, 0x082F, propertyUNASSIGNED}, // ..
- {0x0830, 0x083E, propertyDISALLOWED}, // SAMARITAN PUNCTUATION NEQUDAA..SAMARITAN PUN
- {0x083F, 0x08FF, propertyUNASSIGNED}, // ..
- {0x0900, 0x0939, propertyPVALID}, // DEVANAGARI SIGN INVERTED CANDRABINDU..DEVANA
- {0x093A, 0x093B, propertyUNASSIGNED}, // ..
- {0x093C, 0x094E, propertyPVALID}, // DEVANAGARI SIGN NUKTA..DEVANAGARI VOWEL SIGN
- {0x094F, 0x0, propertyUNASSIGNED}, //
- {0x0950, 0x0955, propertyPVALID}, // DEVANAGARI OM..DEVANAGARI VOWEL SIGN CANDRA
- {0x0956, 0x0957, propertyUNASSIGNED}, // ..
- {0x0958, 0x095F, propertyDISALLOWED}, // DEVANAGARI LETTER QA..DEVANAGARI LETTER YYA
- {0x0960, 0x0963, propertyPVALID}, // DEVANAGARI LETTER VOCALIC RR..DEVANAGARI VOW
- {0x0964, 0x0965, propertyDISALLOWED}, // DEVANAGARI DANDA..DEVANAGARI DOUBLE DANDA
- {0x0966, 0x096F, propertyPVALID}, // DEVANAGARI DIGIT ZERO..DEVANAGARI DIGIT NINE
- {0x0970, 0x0, propertyDISALLOWED}, // DEVANAGARI ABBREVIATION SIGN
- {0x0971, 0x0972, propertyPVALID}, // DEVANAGARI SIGN HIGH SPACING DOT..DEVANAGARI
- {0x0973, 0x0978, propertyUNASSIGNED}, // ..
- {0x0979, 0x097F, propertyPVALID}, // DEVANAGARI LETTER ZHA..DEVANAGARI LETTER BBA
- {0x0980, 0x0, propertyUNASSIGNED}, //
- {0x0981, 0x0983, propertyPVALID}, // BENGALI SIGN CANDRABINDU..BENGALI SIGN VISAR
- {0x0984, 0x0, propertyUNASSIGNED}, //
- {0x0985, 0x098C, propertyPVALID}, // BENGALI LETTER A..BENGALI LETTER VOCALIC L
- {0x098D, 0x098E, propertyUNASSIGNED}, // ..
- {0x098F, 0x0990, propertyPVALID}, // BENGALI LETTER E..BENGALI LETTER AI
- {0x0991, 0x0992, propertyUNASSIGNED}, // ..
- {0x0993, 0x09A8, propertyPVALID}, // BENGALI LETTER O..BENGALI LETTER NA
- {0x09A9, 0x0, propertyUNASSIGNED}, //
- {0x09AA, 0x09B0, propertyPVALID}, // BENGALI LETTER PA..BENGALI LETTER RA
- {0x09B1, 0x0, propertyUNASSIGNED}, //
- {0x09B2, 0x0, propertyPVALID}, // BENGALI LETTER LA
- {0x09B3, 0x09B5, propertyUNASSIGNED}, // ..
- {0x09B6, 0x09B9, propertyPVALID}, // BENGALI LETTER SHA..BENGALI LETTER HA
- {0x09BA, 0x09BB, propertyUNASSIGNED}, // ..
- {0x09BC, 0x09C4, propertyPVALID}, // BENGALI SIGN NUKTA..BENGALI VOWEL SIGN VOCAL
- {0x09C5, 0x09C6, propertyUNASSIGNED}, // ..
- {0x09C7, 0x09C8, propertyPVALID}, // BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI
- {0x09C9, 0x09CA, propertyUNASSIGNED}, // ..
- {0x09CB, 0x09CE, propertyPVALID}, // BENGALI VOWEL SIGN O..BENGALI LETTER KHANDA
- {0x09CF, 0x09D6, propertyUNASSIGNED}, // ..
- {0x09D7, 0x0, propertyPVALID}, // BENGALI AU LENGTH MARK
- {0x09D8, 0x09DB, propertyUNASSIGNED}, // ..
- {0x09DC, 0x09DD, propertyDISALLOWED}, // BENGALI LETTER RRA..BENGALI LETTER RHA
- {0x09DE, 0x0, propertyUNASSIGNED}, //
- {0x09DF, 0x0, propertyDISALLOWED}, // BENGALI LETTER YYA
- {0x09E0, 0x09E3, propertyPVALID}, // BENGALI LETTER VOCALIC RR..BENGALI VOWEL SIG
- {0x09E4, 0x09E5, propertyUNASSIGNED}, // ..
- {0x09E6, 0x09F1, propertyPVALID}, // BENGALI DIGIT ZERO..BENGALI LETTER RA WITH L
- {0x09F2, 0x09FB, propertyDISALLOWED}, // BENGALI RUPEE MARK..BENGALI GANDA MARK
- {0x09FC, 0x0A00, propertyUNASSIGNED}, // ..
- {0x0A01, 0x0A03, propertyPVALID}, // GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN VISA
- {0x0A04, 0x0, propertyUNASSIGNED}, //
- {0x0A05, 0x0A0A, propertyPVALID}, // GURMUKHI LETTER A..GURMUKHI LETTER UU
- {0x0A0B, 0x0A0E, propertyUNASSIGNED}, // ..
- {0x0A0F, 0x0A10, propertyPVALID}, // GURMUKHI LETTER EE..GURMUKHI LETTER AI
- {0x0A11, 0x0A12, propertyUNASSIGNED}, // ..
- {0x0A13, 0x0A28, propertyPVALID}, // GURMUKHI LETTER OO..GURMUKHI LETTER NA
- {0x0A29, 0x0, propertyUNASSIGNED}, //
- {0x0A2A, 0x0A30, propertyPVALID}, // GURMUKHI LETTER PA..GURMUKHI LETTER RA
- {0x0A31, 0x0, propertyUNASSIGNED}, //
- {0x0A32, 0x0, propertyPVALID}, // GURMUKHI LETTER LA
- {0x0A33, 0x0, propertyDISALLOWED}, // GURMUKHI LETTER LLA
- {0x0A34, 0x0, propertyUNASSIGNED}, //
- {0x0A35, 0x0, propertyPVALID}, // GURMUKHI LETTER VA
- {0x0A36, 0x0, propertyDISALLOWED}, // GURMUKHI LETTER SHA
- {0x0A37, 0x0, propertyUNASSIGNED}, //
- {0x0A38, 0x0A39, propertyPVALID}, // GURMUKHI LETTER SA..GURMUKHI LETTER HA
- {0x0A3A, 0x0A3B, propertyUNASSIGNED}, // ..
- {0x0A3C, 0x0, propertyPVALID}, // GURMUKHI SIGN NUKTA
- {0x0A3D, 0x0, propertyUNASSIGNED}, //
- {0x0A3E, 0x0A42, propertyPVALID}, // GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN
- {0x0A43, 0x0A46, propertyUNASSIGNED}, // ..
- {0x0A47, 0x0A48, propertyPVALID}, // GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN
- {0x0A49, 0x0A4A, propertyUNASSIGNED}, // ..
- {0x0A4B, 0x0A4D, propertyPVALID}, // GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA
- {0x0A4E, 0x0A50, propertyUNASSIGNED}, // ..
- {0x0A51, 0x0, propertyPVALID}, // GURMUKHI SIGN UDAAT
- {0x0A52, 0x0A58, propertyUNASSIGNED}, // ..
- {0x0A59, 0x0A5B, propertyDISALLOWED}, // GURMUKHI LETTER KHHA..GURMUKHI LETTER ZA
- {0x0A5C, 0x0, propertyPVALID}, // GURMUKHI LETTER RRA
- {0x0A5D, 0x0, propertyUNASSIGNED}, //
- {0x0A5E, 0x0, propertyDISALLOWED}, // GURMUKHI LETTER FA
- {0x0A5F, 0x0A65, propertyUNASSIGNED}, // ..
- {0x0A66, 0x0A75, propertyPVALID}, // GURMUKHI DIGIT ZERO..GURMUKHI SIGN YAKASH
- {0x0A76, 0x0A80, propertyUNASSIGNED}, // ..
- {0x0A81, 0x0A83, propertyPVALID}, // GUJARATI SIGN CANDRABINDU..GUJARATI SIGN VIS
- {0x0A84, 0x0, propertyUNASSIGNED}, //
- {0x0A85, 0x0A8D, propertyPVALID}, // GUJARATI LETTER A..GUJARATI VOWEL CANDRA E
- {0x0A8E, 0x0, propertyUNASSIGNED}, //
- {0x0A8F, 0x0A91, propertyPVALID}, // GUJARATI LETTER E..GUJARATI VOWEL CANDRA O
- {0x0A92, 0x0, propertyUNASSIGNED}, //
- {0x0A93, 0x0AA8, propertyPVALID}, // GUJARATI LETTER O..GUJARATI LETTER NA
- {0x0AA9, 0x0, propertyUNASSIGNED}, //
- {0x0AAA, 0x0AB0, propertyPVALID}, // GUJARATI LETTER PA..GUJARATI LETTER RA
- {0x0AB1, 0x0, propertyUNASSIGNED}, //
- {0x0AB2, 0x0AB3, propertyPVALID}, // GUJARATI LETTER LA..GUJARATI LETTER LLA
- {0x0AB4, 0x0, propertyUNASSIGNED}, //
- {0x0AB5, 0x0AB9, propertyPVALID}, // GUJARATI LETTER VA..GUJARATI LETTER HA
- {0x0ABA, 0x0ABB, propertyUNASSIGNED}, // ..
- {0x0ABC, 0x0AC5, propertyPVALID}, // GUJARATI SIGN NUKTA..GUJARATI VOWEL SIGN CAN
- {0x0AC6, 0x0, propertyUNASSIGNED}, //
- {0x0AC7, 0x0AC9, propertyPVALID}, // GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN C
- {0x0ACA, 0x0, propertyUNASSIGNED}, //
- {0x0ACB, 0x0ACD, propertyPVALID}, // GUJARATI VOWEL SIGN O..GUJARATI SIGN VIRAMA
- {0x0ACE, 0x0ACF, propertyUNASSIGNED}, // ..
- {0x0AD0, 0x0, propertyPVALID}, // GUJARATI OM
- {0x0AD1, 0x0ADF, propertyUNASSIGNED}, // ..
- {0x0AE0, 0x0AE3, propertyPVALID}, // GUJARATI LETTER VOCALIC RR..GUJARATI VOWEL S
- {0x0AE4, 0x0AE5, propertyUNASSIGNED}, // ..
- {0x0AE6, 0x0AEF, propertyPVALID}, // GUJARATI DIGIT ZERO..GUJARATI DIGIT NINE
- {0x0AF0, 0x0, propertyUNASSIGNED}, //
- {0x0AF1, 0x0, propertyDISALLOWED}, // GUJARATI RUPEE SIGN
- {0x0AF2, 0x0B00, propertyUNASSIGNED}, // ..
- {0x0B01, 0x0B03, propertyPVALID}, // ORIYA SIGN CANDRABINDU..ORIYA SIGN VISARGA
- {0x0B04, 0x0, propertyUNASSIGNED}, //
- {0x0B05, 0x0B0C, propertyPVALID}, // ORIYA LETTER A..ORIYA LETTER VOCALIC L
- {0x0B0D, 0x0B0E, propertyUNASSIGNED}, // ..
- {0x0B0F, 0x0B10, propertyPVALID}, // ORIYA LETTER E..ORIYA LETTER AI
- {0x0B11, 0x0B12, propertyUNASSIGNED}, // ..
- {0x0B13, 0x0B28, propertyPVALID}, // ORIYA LETTER O..ORIYA LETTER NA
- {0x0B29, 0x0, propertyUNASSIGNED}, //
- {0x0B2A, 0x0B30, propertyPVALID}, // ORIYA LETTER PA..ORIYA LETTER RA
- {0x0B31, 0x0, propertyUNASSIGNED}, //
- {0x0B32, 0x0B33, propertyPVALID}, // ORIYA LETTER LA..ORIYA LETTER LLA
- {0x0B34, 0x0, propertyUNASSIGNED}, //
- {0x0B35, 0x0B39, propertyPVALID}, // ORIYA LETTER VA..ORIYA LETTER HA
- {0x0B3A, 0x0B3B, propertyUNASSIGNED}, // ..
- {0x0B3C, 0x0B44, propertyPVALID}, // ORIYA SIGN NUKTA..ORIYA VOWEL SIGN VOCALIC R
- {0x0B45, 0x0B46, propertyUNASSIGNED}, // ..
- {0x0B47, 0x0B48, propertyPVALID}, // ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI
- {0x0B49, 0x0B4A, propertyUNASSIGNED}, // ..
- {0x0B4B, 0x0B4D, propertyPVALID}, // ORIYA VOWEL SIGN O..ORIYA SIGN VIRAMA
- {0x0B4E, 0x0B55, propertyUNASSIGNED}, // ..
- {0x0B56, 0x0B57, propertyPVALID}, // ORIYA AI LENGTH MARK..ORIYA AU LENGTH MARK
- {0x0B58, 0x0B5B, propertyUNASSIGNED}, // ..
- {0x0B5C, 0x0B5D, propertyDISALLOWED}, // ORIYA LETTER RRA..ORIYA LETTER RHA
- {0x0B5E, 0x0, propertyUNASSIGNED}, //
- {0x0B5F, 0x0B63, propertyPVALID}, // ORIYA LETTER YYA..ORIYA VOWEL SIGN VOCALIC L
- {0x0B64, 0x0B65, propertyUNASSIGNED}, // ..
- {0x0B66, 0x0B6F, propertyPVALID}, // ORIYA DIGIT ZERO..ORIYA DIGIT NINE
- {0x0B70, 0x0, propertyDISALLOWED}, // ORIYA ISSHAR
- {0x0B71, 0x0, propertyPVALID}, // ORIYA LETTER WA
- {0x0B72, 0x0B81, propertyUNASSIGNED}, // ..
- {0x0B82, 0x0B83, propertyPVALID}, // TAMIL SIGN ANUSVARA..TAMIL SIGN VISARGA
- {0x0B84, 0x0, propertyUNASSIGNED}, //
- {0x0B85, 0x0B8A, propertyPVALID}, // TAMIL LETTER A..TAMIL LETTER UU
- {0x0B8B, 0x0B8D, propertyUNASSIGNED}, // ..
- {0x0B8E, 0x0B90, propertyPVALID}, // TAMIL LETTER E..TAMIL LETTER AI
- {0x0B91, 0x0, propertyUNASSIGNED}, //
- {0x0B92, 0x0B95, propertyPVALID}, // TAMIL LETTER O..TAMIL LETTER KA
- {0x0B96, 0x0B98, propertyUNASSIGNED}, // ..
- {0x0B99, 0x0B9A, propertyPVALID}, // TAMIL LETTER NGA..TAMIL LETTER CA
- {0x0B9B, 0x0, propertyUNASSIGNED}, //
- {0x0B9C, 0x0, propertyPVALID}, // TAMIL LETTER JA
- {0x0B9D, 0x0, propertyUNASSIGNED}, //
- {0x0B9E, 0x0B9F, propertyPVALID}, // TAMIL LETTER NYA..TAMIL LETTER TTA
- {0x0BA0, 0x0BA2, propertyUNASSIGNED}, // ..
- {0x0BA3, 0x0BA4, propertyPVALID}, // TAMIL LETTER NNA..TAMIL LETTER TA
- {0x0BA5, 0x0BA7, propertyUNASSIGNED}, // ..
- {0x0BA8, 0x0BAA, propertyPVALID}, // TAMIL LETTER NA..TAMIL LETTER PA
- {0x0BAB, 0x0BAD, propertyUNASSIGNED}, // ..
- {0x0BAE, 0x0BB9, propertyPVALID}, // TAMIL LETTER MA..TAMIL LETTER HA
- {0x0BBA, 0x0BBD, propertyUNASSIGNED}, // ..
- {0x0BBE, 0x0BC2, propertyPVALID}, // TAMIL VOWEL SIGN AA..TAMIL VOWEL SIGN UU
- {0x0BC3, 0x0BC5, propertyUNASSIGNED}, // ..
- {0x0BC6, 0x0BC8, propertyPVALID}, // TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI
- {0x0BC9, 0x0, propertyUNASSIGNED}, //
- {0x0BCA, 0x0BCD, propertyPVALID}, // TAMIL VOWEL SIGN O..TAMIL SIGN VIRAMA
- {0x0BCE, 0x0BCF, propertyUNASSIGNED}, // ..
- {0x0BD0, 0x0, propertyPVALID}, // TAMIL OM
- {0x0BD1, 0x0BD6, propertyUNASSIGNED}, // ..
- {0x0BD7, 0x0, propertyPVALID}, // TAMIL AU LENGTH MARK
- {0x0BD8, 0x0BE5, propertyUNASSIGNED}, // ..
- {0x0BE6, 0x0BEF, propertyPVALID}, // TAMIL DIGIT ZERO..TAMIL DIGIT NINE
- {0x0BF0, 0x0BFA, propertyDISALLOWED}, // TAMIL NUMBER TEN..TAMIL NUMBER SIGN
- {0x0BFB, 0x0C00, propertyUNASSIGNED}, // ..
- {0x0C01, 0x0C03, propertyPVALID}, // TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA
- {0x0C04, 0x0, propertyUNASSIGNED}, //
- {0x0C05, 0x0C0C, propertyPVALID}, // TELUGU LETTER A..TELUGU LETTER VOCALIC L
- {0x0C0D, 0x0, propertyUNASSIGNED}, //
- {0x0C0E, 0x0C10, propertyPVALID}, // TELUGU LETTER E..TELUGU LETTER AI
- {0x0C11, 0x0, propertyUNASSIGNED}, //
- {0x0C12, 0x0C28, propertyPVALID}, // TELUGU LETTER O..TELUGU LETTER NA
- {0x0C29, 0x0, propertyUNASSIGNED}, //
- {0x0C2A, 0x0C33, propertyPVALID}, // TELUGU LETTER PA..TELUGU LETTER LLA
- {0x0C34, 0x0, propertyUNASSIGNED}, //
- {0x0C35, 0x0C39, propertyPVALID}, // TELUGU LETTER VA..TELUGU LETTER HA
- {0x0C3A, 0x0C3C, propertyUNASSIGNED}, // ..
- {0x0C3D, 0x0C44, propertyPVALID}, // TELUGU SIGN AVAGRAHA..TELUGU VOWEL SIGN VOCA
- {0x0C45, 0x0, propertyUNASSIGNED}, //
- {0x0C46, 0x0C48, propertyPVALID}, // TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI
- {0x0C49, 0x0, propertyUNASSIGNED}, //
- {0x0C4A, 0x0C4D, propertyPVALID}, // TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA
- {0x0C4E, 0x0C54, propertyUNASSIGNED}, // ..
- {0x0C55, 0x0C56, propertyPVALID}, // TELUGU LENGTH MARK..TELUGU AI LENGTH MARK
- {0x0C57, 0x0, propertyUNASSIGNED}, //
- {0x0C58, 0x0C59, propertyPVALID}, // TELUGU LETTER TSA..TELUGU LETTER DZA
- {0x0C5A, 0x0C5F, propertyUNASSIGNED}, // ..
- {0x0C60, 0x0C63, propertyPVALID}, // TELUGU LETTER VOCALIC RR..TELUGU VOWEL SIGN
- {0x0C64, 0x0C65, propertyUNASSIGNED}, // ..
- {0x0C66, 0x0C6F, propertyPVALID}, // TELUGU DIGIT ZERO..TELUGU DIGIT NINE
- {0x0C70, 0x0C77, propertyUNASSIGNED}, // ..
- {0x0C78, 0x0C7F, propertyDISALLOWED}, // TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF
- {0x0C80, 0x0C81, propertyUNASSIGNED}, // ..
- {0x0C82, 0x0C83, propertyPVALID}, // KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA
- {0x0C84, 0x0, propertyUNASSIGNED}, //
- {0x0C85, 0x0C8C, propertyPVALID}, // KANNADA LETTER A..KANNADA LETTER VOCALIC L
- {0x0C8D, 0x0, propertyUNASSIGNED}, //
- {0x0C8E, 0x0C90, propertyPVALID}, // KANNADA LETTER E..KANNADA LETTER AI
- {0x0C91, 0x0, propertyUNASSIGNED}, //
- {0x0C92, 0x0CA8, propertyPVALID}, // KANNADA LETTER O..KANNADA LETTER NA
- {0x0CA9, 0x0, propertyUNASSIGNED}, //
- {0x0CAA, 0x0CB3, propertyPVALID}, // KANNADA LETTER PA..KANNADA LETTER LLA
- {0x0CB4, 0x0, propertyUNASSIGNED}, //
- {0x0CB5, 0x0CB9, propertyPVALID}, // KANNADA LETTER VA..KANNADA LETTER HA
- {0x0CBA, 0x0CBB, propertyUNASSIGNED}, // ..
- {0x0CBC, 0x0CC4, propertyPVALID}, // KANNADA SIGN NUKTA..KANNADA VOWEL SIGN VOCAL
- {0x0CC5, 0x0, propertyUNASSIGNED}, //
- {0x0CC6, 0x0CC8, propertyPVALID}, // KANNADA VOWEL SIGN E..KANNADA VOWEL SIGN AI
- {0x0CC9, 0x0, propertyUNASSIGNED}, //
- {0x0CCA, 0x0CCD, propertyPVALID}, // KANNADA VOWEL SIGN O..KANNADA SIGN VIRAMA
- {0x0CCE, 0x0CD4, propertyUNASSIGNED}, // ..
- {0x0CD5, 0x0CD6, propertyPVALID}, // KANNADA LENGTH MARK..KANNADA AI LENGTH MARK
- {0x0CD7, 0x0CDD, propertyUNASSIGNED}, // ..
- {0x0CDE, 0x0, propertyPVALID}, // KANNADA LETTER FA
- {0x0CDF, 0x0, propertyUNASSIGNED}, //
- {0x0CE0, 0x0CE3, propertyPVALID}, // KANNADA LETTER VOCALIC RR..KANNADA VOWEL SIG
- {0x0CE4, 0x0CE5, propertyUNASSIGNED}, // ..
- {0x0CE6, 0x0CEF, propertyPVALID}, // KANNADA DIGIT ZERO..KANNADA DIGIT NINE
- {0x0CF0, 0x0, propertyUNASSIGNED}, //
- {0x0CF1, 0x0CF2, propertyDISALLOWED}, // KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADH
- {0x0CF3, 0x0D01, propertyUNASSIGNED}, // ..
- {0x0D02, 0x0D03, propertyPVALID}, // MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISA
- {0x0D04, 0x0, propertyUNASSIGNED}, //
- {0x0D05, 0x0D0C, propertyPVALID}, // MALAYALAM LETTER A..MALAYALAM LETTER VOCALIC
- {0x0D0D, 0x0, propertyUNASSIGNED}, //
- {0x0D0E, 0x0D10, propertyPVALID}, // MALAYALAM LETTER E..MALAYALAM LETTER AI
- {0x0D11, 0x0, propertyUNASSIGNED}, //
- {0x0D12, 0x0D28, propertyPVALID}, // MALAYALAM LETTER O..MALAYALAM LETTER NA
- {0x0D29, 0x0, propertyUNASSIGNED}, //
- {0x0D2A, 0x0D39, propertyPVALID}, // MALAYALAM LETTER PA..MALAYALAM LETTER HA
- {0x0D3A, 0x0D3C, propertyUNASSIGNED}, // ..
- {0x0D3D, 0x0D44, propertyPVALID}, // MALAYALAM SIGN AVAGRAHA..MALAYALAM VOWEL SIG
- {0x0D45, 0x0, propertyUNASSIGNED}, //
- {0x0D46, 0x0D48, propertyPVALID}, // MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN
- {0x0D49, 0x0, propertyUNASSIGNED}, //
- {0x0D4A, 0x0D4D, propertyPVALID}, // MALAYALAM VOWEL SIGN O..MALAYALAM SIGN VIRAM
- {0x0D4E, 0x0D56, propertyUNASSIGNED}, // ..
- {0x0D57, 0x0, propertyPVALID}, // MALAYALAM AU LENGTH MARK
- {0x0D58, 0x0D5F, propertyUNASSIGNED}, // ..
- {0x0D60, 0x0D63, propertyPVALID}, // MALAYALAM LETTER VOCALIC RR..MALAYALAM VOWEL
- {0x0D64, 0x0D65, propertyUNASSIGNED}, // ..
- {0x0D66, 0x0D6F, propertyPVALID}, // MALAYALAM DIGIT ZERO..MALAYALAM DIGIT NINE
- {0x0D70, 0x0D75, propertyDISALLOWED}, // MALAYALAM NUMBER TEN..MALAYALAM FRACTION THR
- {0x0D76, 0x0D78, propertyUNASSIGNED}, // ..
- {0x0D79, 0x0, propertyDISALLOWED}, // MALAYALAM DATE MARK
- {0x0D7A, 0x0D7F, propertyPVALID}, // MALAYALAM LETTER CHILLU NN..MALAYALAM LETTER
- {0x0D80, 0x0D81, propertyUNASSIGNED}, // ..
- {0x0D82, 0x0D83, propertyPVALID}, // SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARG
- {0x0D84, 0x0, propertyUNASSIGNED}, //
- {0x0D85, 0x0D96, propertyPVALID}, // SINHALA LETTER AYANNA..SINHALA LETTER AUYANN
- {0x0D97, 0x0D99, propertyUNASSIGNED}, // ..
- {0x0D9A, 0x0DB1, propertyPVALID}, // SINHALA LETTER ALPAPRAANA KAYANNA..SINHALA L
- {0x0DB2, 0x0, propertyUNASSIGNED}, //
- {0x0DB3, 0x0DBB, propertyPVALID}, // SINHALA LETTER SANYAKA DAYANNA..SINHALA LETT
- {0x0DBC, 0x0, propertyUNASSIGNED}, //
- {0x0DBD, 0x0, propertyPVALID}, // SINHALA LETTER DANTAJA LAYANNA
- {0x0DBE, 0x0DBF, propertyUNASSIGNED}, // ..
- {0x0DC0, 0x0DC6, propertyPVALID}, // SINHALA LETTER VAYANNA..SINHALA LETTER FAYAN
- {0x0DC7, 0x0DC9, propertyUNASSIGNED}, // ..
- {0x0DCA, 0x0, propertyPVALID}, // SINHALA SIGN AL-LAKUNA
- {0x0DCB, 0x0DCE, propertyUNASSIGNED}, // ..
- {0x0DCF, 0x0DD4, propertyPVALID}, // SINHALA VOWEL SIGN AELA-PILLA..SINHALA VOWEL
- {0x0DD5, 0x0, propertyUNASSIGNED}, //
- {0x0DD6, 0x0, propertyPVALID}, // SINHALA VOWEL SIGN DIGA PAA-PILLA
- {0x0DD7, 0x0, propertyUNASSIGNED}, //
- {0x0DD8, 0x0DDF, propertyPVALID}, // SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOW
- {0x0DE0, 0x0DF1, propertyUNASSIGNED}, // ..
- {0x0DF2, 0x0DF3, propertyPVALID}, // SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHAL
- {0x0DF4, 0x0, propertyDISALLOWED}, // SINHALA PUNCTUATION KUNDDALIYA
- {0x0DF5, 0x0E00, propertyUNASSIGNED}, // ..
- {0x0E01, 0x0E32, propertyPVALID}, // THAI CHARACTER KO KAI..THAI CHARACTER SARA A
- {0x0E33, 0x0, propertyDISALLOWED}, // THAI CHARACTER SARA AM
- {0x0E34, 0x0E3A, propertyPVALID}, // THAI CHARACTER SARA I..THAI CHARACTER PHINTH
- {0x0E3B, 0x0E3E, propertyUNASSIGNED}, // ..
- {0x0E3F, 0x0, propertyDISALLOWED}, // THAI CURRENCY SYMBOL BAHT
- {0x0E40, 0x0E4E, propertyPVALID}, // THAI CHARACTER SARA E..THAI CHARACTER YAMAKK
- {0x0E4F, 0x0, propertyDISALLOWED}, // THAI CHARACTER FONGMAN
- {0x0E50, 0x0E59, propertyPVALID}, // THAI DIGIT ZERO..THAI DIGIT NINE
- {0x0E5A, 0x0E5B, propertyDISALLOWED}, // THAI CHARACTER ANGKHANKHU..THAI CHARACTER KH
- {0x0E5C, 0x0E80, propertyUNASSIGNED}, // ..
- {0x0E81, 0x0E82, propertyPVALID}, // LAO LETTER KO..LAO LETTER KHO SUNG
- {0x0E83, 0x0, propertyUNASSIGNED}, //
- {0x0E84, 0x0, propertyPVALID}, // LAO LETTER KHO TAM
- {0x0E85, 0x0E86, propertyUNASSIGNED}, // ..
- {0x0E87, 0x0E88, propertyPVALID}, // LAO LETTER NGO..LAO LETTER CO
- {0x0E89, 0x0, propertyUNASSIGNED}, //