From 6b9e4fadbdfed4d486f2ebffd4e45fc0ef6e66d5 Mon Sep 17 00:00:00 2001 From: Yevhen Vydolob Date: Fri, 13 Jan 2023 15:53:10 +0200 Subject: [PATCH] Use 'crc' section for hosts file modification replace goodhosts with libhosty re-add 9 hosts per line limit perform clean only if crc section present add migration code for old hosts file Co-Authored-By: Christophe Fergeau <526310+cfergeau@users.noreply.github.com> --- cmd/admin-helper/clean.go | 2 +- go.mod | 4 +- go.sum | 21 +- pkg/api/mux.go | 2 +- pkg/hosts/hosts.go | 308 ++- pkg/hosts/hosts_test.go | 159 +- pkg/hosts/util.go | 52 + .../github.com/areYouLazy/libhosty/.gitignore | 5 + .../utfbom => areYouLazy/libhosty}/LICENSE | 4 +- .../github.com/areYouLazy/libhosty/README.md | 167 ++ vendor/github.com/areYouLazy/libhosty/TODO.md | 4 + .../areYouLazy/libhosty/coverage_badge.png | Bin 0 -> 2202 bytes .../github.com/areYouLazy/libhosty/errors.go | 38 + .../areYouLazy/libhosty/formatter.go | 33 + .../github.com/areYouLazy/libhosty/helper.go | 25 + .../areYouLazy/libhosty/libhosty.go | 846 ++++++++ .../github.com/areYouLazy/libhosty/parser.go | 112 ++ .../areYouLazy/libhosty/templates.go | 52 + .../asaskevich/govalidator/.gitignore | 15 - .../asaskevich/govalidator/.travis.yml | 12 - .../asaskevich/govalidator/CODE_OF_CONDUCT.md | 43 - .../asaskevich/govalidator/CONTRIBUTING.md | 63 - .../github.com/asaskevich/govalidator/LICENSE | 21 - .../asaskevich/govalidator/README.md | 622 ------ .../asaskevich/govalidator/arrays.go | 87 - .../asaskevich/govalidator/converter.go | 81 - .../github.com/asaskevich/govalidator/doc.go | 3 - .../asaskevich/govalidator/error.go | 47 - .../asaskevich/govalidator/numerics.go | 100 - .../asaskevich/govalidator/patterns.go | 113 -- .../asaskevich/govalidator/types.go | 656 ------ .../asaskevich/govalidator/utils.go | 270 --- .../asaskevich/govalidator/validator.go | 1769 ----------------- .../asaskevich/govalidator/wercker.yml | 15 - .../github.com/dimchansky/utfbom/.gitignore | 37 - .../github.com/dimchansky/utfbom/.travis.yml | 29 - vendor/github.com/dimchansky/utfbom/README.md | 66 - vendor/github.com/dimchansky/utfbom/utfbom.go | 192 -- .../github.com/goodhosts/hostsfile/.gitignore | 1 - vendor/github.com/goodhosts/hostsfile/LICENSE | 176 -- .../github.com/goodhosts/hostsfile/Makefile | 5 - .../github.com/goodhosts/hostsfile/hosts.go | 451 ----- .../goodhosts/hostsfile/hostsline.go | 108 - .../github.com/goodhosts/hostsfile/make.bat | 27 - .../github.com/goodhosts/hostsfile/slice.go | 55 - .../github.com/goodhosts/hostsfile/utils.go | 12 - .../goodhosts/hostsfile/utils_windows.go | 12 - vendor/modules.txt | 12 +- 48 files changed, 1745 insertions(+), 5189 deletions(-) create mode 100644 pkg/hosts/util.go create mode 100644 vendor/github.com/areYouLazy/libhosty/.gitignore rename vendor/github.com/{dimchansky/utfbom => areYouLazy/libhosty}/LICENSE (99%) create mode 100644 vendor/github.com/areYouLazy/libhosty/README.md create mode 100644 vendor/github.com/areYouLazy/libhosty/TODO.md create mode 100644 vendor/github.com/areYouLazy/libhosty/coverage_badge.png create mode 100644 vendor/github.com/areYouLazy/libhosty/errors.go create mode 100644 vendor/github.com/areYouLazy/libhosty/formatter.go create mode 100644 vendor/github.com/areYouLazy/libhosty/helper.go create mode 100644 vendor/github.com/areYouLazy/libhosty/libhosty.go create mode 100644 vendor/github.com/areYouLazy/libhosty/parser.go create mode 100644 vendor/github.com/areYouLazy/libhosty/templates.go delete mode 100644 vendor/github.com/asaskevich/govalidator/.gitignore delete mode 100644 vendor/github.com/asaskevich/govalidator/.travis.yml delete mode 100644 vendor/github.com/asaskevich/govalidator/CODE_OF_CONDUCT.md delete mode 100644 vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md delete mode 100644 vendor/github.com/asaskevich/govalidator/LICENSE delete mode 100644 vendor/github.com/asaskevich/govalidator/README.md delete mode 100644 vendor/github.com/asaskevich/govalidator/arrays.go delete mode 100644 vendor/github.com/asaskevich/govalidator/converter.go delete mode 100644 vendor/github.com/asaskevich/govalidator/doc.go delete mode 100644 vendor/github.com/asaskevich/govalidator/error.go delete mode 100644 vendor/github.com/asaskevich/govalidator/numerics.go delete mode 100644 vendor/github.com/asaskevich/govalidator/patterns.go delete mode 100644 vendor/github.com/asaskevich/govalidator/types.go delete mode 100644 vendor/github.com/asaskevich/govalidator/utils.go delete mode 100644 vendor/github.com/asaskevich/govalidator/validator.go delete mode 100644 vendor/github.com/asaskevich/govalidator/wercker.yml delete mode 100644 vendor/github.com/dimchansky/utfbom/.gitignore delete mode 100644 vendor/github.com/dimchansky/utfbom/.travis.yml delete mode 100644 vendor/github.com/dimchansky/utfbom/README.md delete mode 100644 vendor/github.com/dimchansky/utfbom/utfbom.go delete mode 100644 vendor/github.com/goodhosts/hostsfile/.gitignore delete mode 100644 vendor/github.com/goodhosts/hostsfile/LICENSE delete mode 100644 vendor/github.com/goodhosts/hostsfile/Makefile delete mode 100644 vendor/github.com/goodhosts/hostsfile/hosts.go delete mode 100644 vendor/github.com/goodhosts/hostsfile/hostsline.go delete mode 100644 vendor/github.com/goodhosts/hostsfile/make.bat delete mode 100644 vendor/github.com/goodhosts/hostsfile/slice.go delete mode 100644 vendor/github.com/goodhosts/hostsfile/utils.go delete mode 100644 vendor/github.com/goodhosts/hostsfile/utils_windows.go diff --git a/cmd/admin-helper/clean.go b/cmd/admin-helper/clean.go index 12e5768e..e806058d 100644 --- a/cmd/admin-helper/clean.go +++ b/cmd/admin-helper/clean.go @@ -24,5 +24,5 @@ func clean(args []string) error { if err != nil { return err } - return hosts.Clean(args) + return hosts.Clean() } diff --git a/go.mod b/go.mod index 91066fa0..3d9562b0 100644 --- a/go.mod +++ b/go.mod @@ -4,16 +4,14 @@ go 1.17 require ( github.com/Microsoft/go-winio v0.6.0 - github.com/goodhosts/hostsfile v0.1.1 + github.com/areYouLazy/libhosty v1.1.0 github.com/kardianos/service v1.2.2 github.com/spf13/cobra v1.6.1 github.com/stretchr/testify v1.8.1 ) require ( - github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dimchansky/utfbom v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kr/pretty v0.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect diff --git a/go.sum b/go.sum index 095c47c8..0b00d377 100644 --- a/go.sum +++ b/go.sum @@ -1,23 +1,11 @@ -github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2oc2f4tzPWic= -github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= -github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/codegangsta/cli v1.20.0/go.mod h1:/qJNoX69yVSKu5o4jLyXAENLRyk1uhi7zkbQ3slBdOA= -github.com/corpix/uarand v0.1.1 h1:RMr1TWc9F4n5jiPDzFHtmaUXLKLNUFK0SgCLo4BhX/U= -github.com/corpix/uarand v0.1.1/go.mod h1:SFKZvkcRoLqVRFZ4u25xPmp6m9ktANfbpXZ7SJ0/FNU= +github.com/areYouLazy/libhosty v1.1.0 h1:kO6UTk9z72cHW28A/V1kKi7C8iKQGqINiVGXp+05Eao= +github.com/areYouLazy/libhosty v1.1.0/go.mod h1:dV4ir3feRrTbWdcJ21mt3MeZlASg0sc8db6nimL9GOA= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= -github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= -github.com/goodhosts/hostsfile v0.1.1 h1:SqRUTFOshOCon0ZSXDrW1bkKZvs4+5pRgYFWySdaLno= -github.com/goodhosts/hostsfile v0.1.1/go.mod h1:lXcUP8xO4WR5vvuQ3F/N0bMQoclOtYKEEUnyY2jTusY= -github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428 h1:Mo9W14pwbO9VfRe+ygqZ8dFbPpoIK1HFrG/zjTuQ+nc= -github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGznybq1itEKXj6RYw9I71qK4kH+OGMjRC4KEo= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= @@ -28,8 +16,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -42,8 +28,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= @@ -88,7 +72,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/api/mux.go b/pkg/api/mux.go index 0b623905..35c62f91 100644 --- a/pkg/api/mux.go +++ b/pkg/api/mux.go @@ -57,7 +57,7 @@ func Mux(hosts *hosts.Hosts) http.Handler { http.Error(w, err.Error(), http.StatusBadRequest) return } - if err := hosts.Clean(req.Domains); err != nil { + if err := hosts.Clean(); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } diff --git a/pkg/hosts/hosts.go b/pkg/hosts/hosts.go index 0bfe6551..e0af6331 100644 --- a/pkg/hosts/hosts.go +++ b/pkg/hosts/hosts.go @@ -2,12 +2,14 @@ package hosts import ( "fmt" + "net" "os" "regexp" "sort" "strings" + "sync" - "github.com/goodhosts/hostsfile" + "github.com/areYouLazy/libhosty" ) const ( @@ -15,6 +17,11 @@ const ( dns1123SubdomainRegexp = `[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*` clusterDomain = ".crc.testing" appsDomain = ".apps-crc.testing" + + crcTemplate = `# Added by CRC +# End of CRC section +` + maxHostsInLine = 9 ) var ( @@ -23,20 +30,13 @@ var ( ) type Hosts struct { - File *hostsfile.Hosts + sync.Mutex + File *libhosty.HostsFile HostFilter func(string) bool } -func init() { - // goodhosts unconditionally uses this environment variable - // as an override for the hosts file to use. We don't want admin-helper - // to modify arbitrary file, so we have to unset it before calling into - // goodhosts. - os.Unsetenv("HOSTS_PATH") -} - func New() (*Hosts, error) { - file, err := hostsfile.NewHosts() + file, err := libhosty.Init() if err != nil { return nil, err } @@ -51,7 +51,41 @@ func defaultFilter(s string) bool { return clusterRegexp.MatchString(s) || appRegexp.MatchString(s) } -func (h *Hosts) Add(ip string, hosts []string) error { +func linesContain(lines []*libhosty.HostsFileLine, hostName string) bool { + for _, line := range lines { + for _, hn := range line.Hostnames { + if hn == hostName { + return true + } + + } + } + + return false +} + +func uniqueHostnames(lines []*libhosty.HostsFileLine, hosts []string) []string { + uniqueHosts := map[string]bool{} + + // Remove duplicate entries from `hosts` + for _, host := range hosts { + uniqueHosts[host] = true + } + + // Remove entries in `hosts` which are already present in the file + var hostEntries []string + for hostname := range uniqueHosts { + if !linesContain(lines, hostname) { + hostEntries = append(hostEntries, hostname) + } + } + + sort.Strings(hostEntries) + + return hostEntries +} + +func (h *Hosts) Add(ipRaw string, hosts []string) error { if err := h.verifyHosts(hosts); err != nil { return err } @@ -60,22 +94,74 @@ func (h *Hosts) Add(ip string, hosts []string) error { return err } - uniqueHosts := map[string]bool{} - for i := 0; i < len(hosts); i++ { - uniqueHosts[hosts[i]] = true + // parse ip to net.IP + ip := net.ParseIP(ipRaw) + if ip == nil { + return libhosty.ErrCannotParseIPAddress(ipRaw) } - var hostEntries []string - for key := range uniqueHosts { - hostEntries = append(hostEntries, key) + start, end, err := h.verifyCrcSection() + if err != nil { + return err } - sort.Strings(hostEntries) - - if err := h.File.Add(ip, hostEntries...); err != nil { + lines, err := h.findIP(start, end, ip) + if err != nil { return err } - return h.File.Flush() + + h.Lock() + defer h.Unlock() + + hostEntries := uniqueHostnames(lines, hosts) + + h.addNewHostEntries(hostEntries, lines, ip) + + return h.File.SaveHostsFile() +} + +func (h *Hosts) addNewHostEntries(hostEntries []string, lines []*libhosty.HostsFileLine, ip net.IP) { + var hostAdder HostAdder + + hostAdder.AppendHosts(hostEntries...) + + for _, line := range lines { + // This will append hosts from the hostAdder to fill the line up to 9 entries + // Lines over 9 entries will be truncated, and their extra + // entries will be added to hostAdder so that they can added to the next lines + hostAdder.FillLine(line) + } + + // Create new lines for entries left-over entries (entries which haven't been added existing lines) + hostsToAdd := hostAdder.PopN(maxHostsInLine) + for len(hostsToAdd) > 0 { + h.createAndAddHostsLine(ip, hostsToAdd, h.lastNonCommentLine()) + hostsToAdd = hostAdder.PopN(maxHostsInLine) + } +} + +func (h *Hosts) lastNonCommentLine() int { + _, end := h.findCrcSection() + return end - 1 +} + +func (h *Hosts) createAndAddHostsLine(ip net.IP, hosts []string, sectionStart int) { + hfl := libhosty.HostsFileLine{ + Type: libhosty.LineTypeAddress, + Address: ip, + Hostnames: hosts, + } + + // inserts to hosts + newHosts := make([]libhosty.HostsFileLine, 0) + newHosts = append(newHosts, h.File.HostsFileLines[:sectionStart+1]...) + newHosts = append(newHosts, hfl) + newLineNum := len(newHosts) - 1 + newHosts = append(newHosts, h.File.HostsFileLines[sectionStart+1:]...) + h.File.HostsFileLines = newHosts + + // generate raw version of the line + hfl.Raw = h.File.RenderHostsFileLine(newLineNum) } func (h *Hosts) Remove(hosts []string) error { @@ -92,60 +178,100 @@ func (h *Hosts) Remove(hosts []string) error { uniqueHosts[hosts[i]] = true } - var hostEntries []string + var hostEntries = make(map[string]struct{}, len(uniqueHosts)) + for key := range uniqueHosts { - hostEntries = append(hostEntries, key) + hostEntries[key] = struct{}{} } - for _, host := range hostEntries { - if err := h.File.RemoveByHostname(host); err != nil { - return err - } - } - return h.File.Flush() -} + start, end := h.findCrcSection() -func (h *Hosts) Clean(rawSuffixes []string) error { - if err := h.checkIsWritable(); err != nil { - return err - } + h.Lock() + defer h.Unlock() + // delete from CRC section + if start > 0 && end > 0 { + for i := start; i < end; i++ { + line := h.File.GetHostsFileLineByRow(i) + if line.Type == libhosty.LineTypeComment { + continue + } - var suffixes []string - for _, suffix := range rawSuffixes { - if !strings.HasPrefix(suffix, ".") { - return fmt.Errorf("suffix should start with a dot") + for hostIdx, hostname := range line.Hostnames { + if _, ok := hostEntries[hostname]; ok { + h.removeHostFromLine(line, hostIdx, i) + } + + } } - suffixes = append(suffixes, suffix) - } + } else { + // CRC section not present, delete hosts from entire file + for _, host := range hosts { + lineIdx, _, err := h.File.LookupByHostname(host) + if err != nil { + continue + } + + line := h.File.GetHostsFileLineByRow(lineIdx) - var toDelete []string - for _, line := range h.File.Lines { - for _, host := range line.Hosts { - for _, suffix := range suffixes { - if strings.HasSuffix(host, suffix) { - toDelete = append(toDelete, host) + for hostIdx, hostname := range line.Hostnames { + if hostname == host { + h.removeHostFromLine(line, hostIdx, lineIdx) break } + } + } } - if err := h.verifyHosts(toDelete); err != nil { + return h.File.SaveHostsFile() +} + +func (h *Hosts) removeHostFromLine(line *libhosty.HostsFileLine, hostIdx int, i int) { + if len(line.Hostnames) >= 1 { + line.Hostnames = append(line.Hostnames[:hostIdx], line.Hostnames[hostIdx+1:]...) + } + + // remove the line if there are no more hostnames (other than the actual one) + if len(line.Hostnames) < 1 { + h.File.RemoveHostsFileLineByRow(i) + } +} + +func (h *Hosts) Clean() error { + if err := h.checkIsWritable(); err != nil { return err } - for _, host := range toDelete { - if err := h.File.RemoveByHostname(host); err != nil { - return err - } + h.Lock() + defer h.Unlock() + + start, end := h.findCrcSection() + // no CRC section present + if start == -1 && end == -1 { + return nil + } + + var newHosts []libhosty.HostsFileLine + + newHosts = append(newHosts, h.File.HostsFileLines[:start-1]...) + newHosts = append(newHosts, h.File.HostsFileLines[end+1:]...) + h.File.HostsFileLines = newHosts + + _, _, emptyLineErr := h.File.AddEmptyFileLine() + if emptyLineErr != nil { + return emptyLineErr } - return h.File.Flush() + + return h.File.SaveHostsFile() } func (h *Hosts) checkIsWritable() error { - if !h.File.IsWritable() { + file, err := os.OpenFile(h.File.Config.FilePath, os.O_WRONLY, 0660) + if err != nil { return fmt.Errorf("host file not writable, try running with elevated privileges") } + defer file.Close() return nil } @@ -154,7 +280,17 @@ func (h *Hosts) Contains(ip, host string) bool { return false } - return h.File.Has(ip, host) + lines := h.File.GetHostsFileLinesByAddress(ip) + + for _, line := range lines { + for _, h := range line.Hostnames { + if h == host { + return true + } + } + } + + return false } func (h *Hosts) verifyHosts(hosts []string) error { @@ -165,3 +301,65 @@ func (h *Hosts) verifyHosts(hosts []string) error { } return nil } + +func (h *Hosts) verifyCrcSection() (int, int, error) { + + start, end := h.findCrcSection() + + if start > 0 && end > 0 { + return start, end, nil + } + + hfl, err := libhosty.ParseHostsFileAsString(crcTemplate) + if err != nil { + return -1, -1, err + } + + h.File.HostsFileLines = append(h.File.HostsFileLines, hfl...) + + start, end = h.findCrcSection() + + if start > 0 && end > 0 { + return start, end, nil + } + + return -1, -1, fmt.Errorf("can't add CRC section, check hosts file") +} + +func (h *Hosts) findCrcSection() (int, int) { + start := -1 + end := -1 + + for i, line := range h.File.HostsFileLines { + if line.Type == libhosty.LineTypeComment { + if strings.Contains(line.Raw, "Added by CRC") { + start = i + continue + } + + if strings.Contains(line.Raw, "End of CRC section") { + end = i + break + } + + } + } + + return start, end +} + +func (h *Hosts) findIP(start, end int, ip net.IP) ([]*libhosty.HostsFileLine, error) { + var result []*libhosty.HostsFileLine + for i := start; i < end; i++ { + line := h.File.GetHostsFileLineByRow(i) + if line.Type == libhosty.LineTypeComment { + continue + } + + if net.IP.Equal(line.Address, ip) { + result = append(result, line) + } + } + + return result, nil +} diff --git a/pkg/hosts/hosts_test.go b/pkg/hosts/hosts_test.go index e7291074..f079aa3d 100644 --- a/pkg/hosts/hosts_test.go +++ b/pkg/hosts/hosts_test.go @@ -1,15 +1,26 @@ package hosts import ( + "fmt" "os" "path/filepath" "runtime" + "strings" "testing" - "github.com/goodhosts/hostsfile" + "github.com/areYouLazy/libhosty" + "github.com/stretchr/testify/assert" ) +const ( + hostsTemplate = `# Do not remove the following line, or various programs +# that require network functionality will fail. +127.0.0.1 localhost.localdomain localhost +::1 localhost6.localdomain6 localhost6 +` +) + func TestAdd(t *testing.T) { dir, err := os.MkdirTemp("", "hosts") assert.NoError(t, err) @@ -25,7 +36,59 @@ func TestAdd(t *testing.T) { content, err := os.ReadFile(hostsFile) assert.NoError(t, err) - assert.Equal(t, "127.0.0.1 entry1 entry2 entry3"+eol()+"127.0.0.2 entry4"+eol(), string(content)) + assert.Equal(t, "127.0.0.1 entry1"+eol()+crcSection("127.0.0.1 entry1 entry2 entry3", "127.0.0.2 entry4")+eol(), string(content)) +} + +func TestAddMoreThen9Hosts(t *testing.T) { + dir, err := os.MkdirTemp("", "hosts") + assert.NoError(t, err) + defer os.RemoveAll(dir) + + hostsFile := filepath.Join(dir, "hosts") + assert.NoError(t, os.WriteFile(hostsFile, []byte(hostsTemplate), 0600)) + + host := hosts(t, hostsFile) + + assert.NoError(t, host.Add("127.0.0.1", []string{"entry1", "entry2", "entry3", "entry3", "entry4", "entry5", "entry6", "entry7", "entry8", "entry9", "entry10"})) + + content, err := os.ReadFile(hostsFile) + assert.NoError(t, err) + assert.Equal(t, hostsTemplate+eol()+crcSection("127.0.0.1 entry1 entry10 entry2 entry3 entry4 entry5 entry6 entry7 entry8", "127.0.0.1 entry9")+eol(), string(content)) +} + +func TestAddMoreThan18Hosts(t *testing.T) { + dir, err := os.MkdirTemp("", "hosts") + assert.NoError(t, err) + defer os.RemoveAll(dir) + + hostsFile := filepath.Join(dir, "hosts") + assert.NoError(t, os.WriteFile(hostsFile, []byte(hostsTemplate), 0600)) + + host := hosts(t, hostsFile) + + assert.NoError(t, host.Add("127.0.0.1", []string{"entry0"})) + assert.NoError(t, host.Add("127.0.0.1", []string{"entry1", "entry2", "entry3", "entry3", "entry4", "entry5", "entry6", "entry7", "entry8", "entry9", "entry10", "entry11", "entry12", "entry13", "entry14", "entry15", "entry16", "entry17", "entry18", "entry19", "entry20"})) + + content, err := os.ReadFile(hostsFile) + assert.NoError(t, err) + assert.Equal(t, hostsTemplate+eol()+crcSection("127.0.0.1 entry0 entry1 entry10 entry11 entry12 entry13 entry14 entry15 entry16", "127.0.0.1 entry17 entry18 entry19 entry2 entry20 entry3 entry4 entry5 entry6", "127.0.0.1 entry7 entry8 entry9")+eol(), string(content)) +} + +func TestAddMoreThen9HostsInMultipleLines(t *testing.T) { + dir, err := os.MkdirTemp("", "hosts") + assert.NoError(t, err) + defer os.RemoveAll(dir) + + hostsFile := filepath.Join(dir, "hosts") + assert.NoError(t, os.WriteFile(hostsFile, []byte(hostsTemplate+eol()+crcSection("127.0.0.1 entry1 entry10 entry2 entry3 entry4 entry5 entry6 entry7", "127.0.0.1 entry11 entry12 entry13 entry14 entry15 entry16 entry17 entry18")+eol()), 0600)) + + host := hosts(t, hostsFile) + + assert.NoError(t, host.Add("127.0.0.1", []string{"entry8", "entry9", "entry10"})) + + content, err := os.ReadFile(hostsFile) + assert.NoError(t, err) + assert.Equal(t, hostsTemplate+eol()+crcSection("127.0.0.1 entry1 entry10 entry2 entry3 entry4 entry5 entry6 entry7 entry8", "127.0.0.1 entry11 entry12 entry13 entry14 entry15 entry16 entry17 entry18 entry9")+eol(), string(content)) } func TestRemove(t *testing.T) { @@ -34,15 +97,16 @@ func TestRemove(t *testing.T) { defer os.RemoveAll(dir) hostsFile := filepath.Join(dir, "hosts") - assert.NoError(t, os.WriteFile(hostsFile, []byte(`127.0.0.1 entry1 entry2`), 0600)) + assert.NoError(t, os.WriteFile(hostsFile, []byte(hostsTemplate), 0600)) host := hosts(t, hostsFile) + assert.NoError(t, host.Add("127.0.0.1", []string{"entry1", "entry2"})) assert.NoError(t, host.Remove([]string{"entry2"})) content, err := os.ReadFile(hostsFile) assert.NoError(t, err) - assert.Equal(t, "127.0.0.1 entry1"+eol(), string(content)) + assert.Equal(t, hostsTemplate+eol()+crcSection("127.0.0.1 entry1")+eol(), string(content)) } func TestClean(t *testing.T) { @@ -51,15 +115,32 @@ func TestClean(t *testing.T) { defer os.RemoveAll(dir) hostsFile := filepath.Join(dir, "hosts") - assert.NoError(t, os.WriteFile(hostsFile, []byte(`127.0.0.1 entry1.suffix1 entry2.suffix2`), 0600)) + assert.NoError(t, os.WriteFile(hostsFile, []byte(hostsTemplate+eol()+crcSection("127.0.0.1 entry1.suffix1 entry2.suffix2")), 0600)) host := hosts(t, hostsFile) - assert.NoError(t, host.Clean([]string{".suffix1"})) + assert.NoError(t, host.Clean()) content, err := os.ReadFile(hostsFile) assert.NoError(t, err) - assert.Equal(t, "127.0.0.1 entry2.suffix2"+eol(), string(content)) + assert.Equal(t, hostsTemplate, string(content)) +} + +func TestCleanWithoutCrcSection(t *testing.T) { + dir, err := os.MkdirTemp("", "hosts") + assert.NoError(t, err) + defer os.RemoveAll(dir) + + hostsFile := filepath.Join(dir, "hosts") + assert.NoError(t, os.WriteFile(hostsFile, []byte(hostsTemplate), 0600)) + + host := hosts(t, hostsFile) + + assert.NoError(t, host.Clean()) + + content, err := os.ReadFile(hostsFile) + assert.NoError(t, err) + assert.Equal(t, hostsTemplate, string(content)) } func TestContains(t *testing.T) { @@ -85,7 +166,8 @@ func TestSuffixFilter(t *testing.T) { hostsFile := filepath.Join(dir, "hosts") assert.NoError(t, os.WriteFile(hostsFile, []byte(`127.0.0.1 localhost localhost.localdomain`), 0600)) - file, err := hostsfile.NewCustomHosts(hostsFile) + config, _ := libhosty.NewHostsFileConfig(hostsFile) + file, err := libhosty.InitWithConfig(config) assert.NoError(t, err) host := Hosts{ File: file, @@ -98,12 +180,63 @@ func TestSuffixFilter(t *testing.T) { assert.Error(t, host.Add("127.0.0.1", []string{"host.poison"})) assert.Error(t, host.Add("127.0.0.1", []string{"CAPITAL.crc.testing"})) assert.Error(t, host.Remove([]string{"localhost"})) - assert.NoError(t, host.Clean([]string{".crc.testing"})) - assert.Error(t, host.Clean([]string{".localdomain"})) + assert.NoError(t, host.Clean()) +} + +func TestAddMoreThan9HostsWithFullLine(t *testing.T) { + dir, err := os.MkdirTemp("", "hosts") + assert.NoError(t, err) + defer os.RemoveAll(dir) + + hostsFile := filepath.Join(dir, "hosts") + assert.NoError(t, os.WriteFile(hostsFile, []byte(hostsTemplate+eol()+crcSection("127.0.0.1 entry1 entry2 entry3 entry4 entry5 entry6 entry7 entry8 entry9")+eol()), 0600)) + + host := hosts(t, hostsFile) + + assert.NoError(t, host.Add("127.0.0.1", []string{"entry10"})) + + content, err := os.ReadFile(hostsFile) + assert.NoError(t, err) + assert.Equal(t, hostsTemplate+eol()+crcSection("127.0.0.1 entry1 entry2 entry3 entry4 entry5 entry6 entry7 entry8 entry9", "127.0.0.1 entry10")+eol(), string(content)) +} + +func TestAddMoreThan9HostsWithOverfullLine(t *testing.T) { + dir, err := os.MkdirTemp("", "hosts") + assert.NoError(t, err) + defer os.RemoveAll(dir) + + hostsFile := filepath.Join(dir, "hosts") + assert.NoError(t, os.WriteFile(hostsFile, []byte(hostsTemplate+eol()+crcSection("127.0.0.1 entry1 entry2 entry3 entry4 entry5 entry6 entry7 entry8 entry9 entry10")+eol()), 0600)) + + host := hosts(t, hostsFile) + + assert.NoError(t, host.Add("127.0.0.1", []string{"entry11"})) + + content, err := os.ReadFile(hostsFile) + assert.NoError(t, err) + assert.Equal(t, hostsTemplate+eol()+crcSection("127.0.0.1 entry1 entry2 entry3 entry4 entry5 entry6 entry7 entry8 entry9", "127.0.0.1 entry10 entry11")+eol(), string(content)) +} + +func TestRemoveOnOldHostFile(t *testing.T) { + dir, err := os.MkdirTemp("", "hosts") + assert.NoError(t, err) + defer os.RemoveAll(dir) + + hostsFile := filepath.Join(dir, "hosts") + assert.NoError(t, os.WriteFile(hostsFile, []byte(hostsTemplate+eol()+"127.0.0.1 entry1 entry2"), 0600)) + + host := hosts(t, hostsFile) + + assert.NoError(t, host.Remove([]string{"entry1", "entry2"})) + + content, err := os.ReadFile(hostsFile) + assert.NoError(t, err) + assert.Equal(t, hostsTemplate, string(content)) } func hosts(t *testing.T, hostsFile string) Hosts { - file, err := hostsfile.NewCustomHosts(hostsFile) + config, _ := libhosty.NewHostsFileConfig(hostsFile) + file, err := libhosty.InitWithConfig(config) assert.NoError(t, err) return Hosts{ File: file, @@ -119,3 +252,7 @@ func eol() string { } return "\n" } + +func crcSection(lines ...string) string { + return fmt.Sprintf("# Added by CRC"+eol()+"%s"+eol()+"# End of CRC section", strings.Join(lines, eol())) +} diff --git a/pkg/hosts/util.go b/pkg/hosts/util.go new file mode 100644 index 00000000..167534f7 --- /dev/null +++ b/pkg/hosts/util.go @@ -0,0 +1,52 @@ +package hosts + +import ( + "github.com/areYouLazy/libhosty" +) + +type HostAdder struct { + hostsToAdd []string +} + +func (adder *HostAdder) PrependHosts(hosts []string) { + adder.hostsToAdd = append(hosts, adder.hostsToAdd...) +} + +func (adder *HostAdder) AppendHosts(hosts ...string) { + adder.hostsToAdd = append(adder.hostsToAdd, hosts...) +} + +func (adder *HostAdder) AppendHost(host string) { + adder.hostsToAdd = append(adder.hostsToAdd, host) +} + +func (adder *HostAdder) Len() int { + return len(adder.hostsToAdd) +} + +func (adder *HostAdder) PopN(count int) []string { + if adder.Len() == 0 { + return []string{} + } + if adder.Len() < count { + count = adder.Len() + } + hosts := adder.hostsToAdd[:count] + adder.hostsToAdd = adder.hostsToAdd[count:] + + return hosts +} + +func (adder *HostAdder) FillLine(line *libhosty.HostsFileLine) { + if len(line.Hostnames) == maxHostsInLine { + return + } + if len(line.Hostnames) > maxHostsInLine { + adder.PrependHosts(line.Hostnames[maxHostsInLine:]) + line.Hostnames = line.Hostnames[0:maxHostsInLine] + return + } + + newHosts := adder.PopN(maxHostsInLine - len(line.Hostnames)) + line.Hostnames = append(line.Hostnames, newHosts...) +} diff --git a/vendor/github.com/areYouLazy/libhosty/.gitignore b/vendor/github.com/areYouLazy/libhosty/.gitignore new file mode 100644 index 00000000..d99b5875 --- /dev/null +++ b/vendor/github.com/areYouLazy/libhosty/.gitignore @@ -0,0 +1,5 @@ +# test coverage output +./coverage.out +./cover.out +coverage.out +cover.out diff --git a/vendor/github.com/dimchansky/utfbom/LICENSE b/vendor/github.com/areYouLazy/libhosty/LICENSE similarity index 99% rename from vendor/github.com/dimchansky/utfbom/LICENSE rename to vendor/github.com/areYouLazy/libhosty/LICENSE index 6279cb87..261eeb9e 100644 --- a/vendor/github.com/dimchansky/utfbom/LICENSE +++ b/vendor/github.com/areYouLazy/libhosty/LICENSE @@ -178,7 +178,7 @@ APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" + boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright (c) 2018-2020, Dmitrij Koniajev (dimchansky@gmail.com) + Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/vendor/github.com/areYouLazy/libhosty/README.md b/vendor/github.com/areYouLazy/libhosty/README.md new file mode 100644 index 00000000..e87c798b --- /dev/null +++ b/vendor/github.com/areYouLazy/libhosty/README.md @@ -0,0 +1,167 @@ +# libhosty + +[![made-with-Go](https://img.shields.io/badge/made%20with-Go-1f425f.svg)](http://golang.org) +[![Go Report Card](https://goreportcard.com/badge/github.com/areYouLazy/libhosty)](https://goreportcard.com/report/github.com/areYouLazy/libhosty) +[![Build and Test](https://github.com/areYouLazy/libhosty/actions/workflows/build-and-test.yml/badge.svg?branch=main&event=push)](https://github.com/areYouLazy/libhosty/actions/workflows/build-and-test.yml) +![gopherbadger-tag-do-not-edit](coverage_badge.png) +[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/areYouLazy/libhosty) + +## Description + +libhosty is a pure golang library to manipulate the hosts file. It is inspired by [txeh](https://github.com/txn2/txeh), with some enrichments. + +## Table of Contents + +* [Main Features](#Main-Features) +* [Installation](#Installation) +* [Usage](#Usage) +* [Contributing](#Contributing) + * [Templates](#Templates) +* [Credits](#Credits) +* [License](#License) + +## Main Features + +* Comment/Uncomment a line without removing it from the file +* Restore the default hosts file for your system +* Add/Remove Address lines +* Add/Remove Comment lines +* Add/Remove Empty lines +* Query by hostname +* Automatically handles duplicate entries + +## Installation + +Ensure you have go on your system + +```bash +> go version +go version go1.15.6 linux/amd64 +``` + +and pull the library + +```bash +> go get github.com/areYouLazy/libhosty +``` + +## Usage + +To use the library, just import it and call the `Init()` method. + +Note: This code doesn't handle errors for readability purposes, but you SHOULD! + +```go +package main + +import "github.com/areYouLazy/libhosty" + +func main() { + //you can define a custom config object + // and use it to initialize libhosty with a custom hosts file + // + //cnf, _ := libhosty.NewHostsFileConfig("/home/sonica/hosts-export.txt") + //hfl, _ := libhosty.InitWithConfig(cnf) + + //or initialize libhosty that will automatically try to loads + // then default hosts file for your OS + hfl, _ := libhosty.Init() + + //add an empty line + hfl.AddEmptyFileLine() + + //add a host with a comment + hfl.AddHostFileLine("12.12.12.12", "my.host.name", "comment on my hostname!") + + //add a comment + hfl.AddCommentFileLine("just a comment") + + //add an empty line + hfl.AddEmptyFileLine() + + //add another host without comment + hfl.AddHostsFileLine("13.13.13.13", "another.host.name", "") + + //add another fqdn to the previous ip + hfl.AddHostsFileLine("12.12.12.12", "second.host.name", "") + + // comment for host lines can be done by hostname, row line + // or IP (as net.IP or string) + // + // Comment the line with address 12.12.12.12 + // + // By-Row-Number + idx, _ := hfl.GetHostsFileLineByHostname("second.host.name") + hfl.CommentHostsFileLineByRow(idx) + // + // By-Hostname + hfl.CommentHostsFileLineByHostname("second.host.name") + // + // By-Address-As-IP + ip := net.ParseIP("12.12.12.12") + hfl.CommentHostsFileLineByIP(ip) + // + // By-Address-As-String + hfl.CommentHostsFileLineByAddress("12.12.12.12") + + // render the hosts file + fmt.Println(hfl.RenderHostsFile()) + + // write file to disk + hfl.SaveHostsFile() + + // or to a custom location + hfl.SaveHostsFileAs("/home/sonica/hosts-export.txt") + + // restore the original hosts file for linux + hfl.RestoreDefaultLinuxHostsFile() + + // render the hosts file + fmt.Println(hfl.RenderHostsFile()) + + // write to disk + hfl.SaveHostsFile() +} +``` + +The 1st `fmt.Println()` should output something like this (in a linux host) + +```console +# Do not remove the following line, or various programs +# that require network functionality will fail. +127.0.0.1 localhost.localdomain localhost +::1 localhost6.localdomain6 localhost6 + +# 12.12.12.12 my.host.name second.host.name #comment on my hostname! +# just a comment line + +13.13.13.13 another.host.name +``` + +While the 2nd `fmt.Println()` should output the default template for linux systems + +```console +# Do not remove the following line, or various programs +# that require network functionality will fail. +127.0.0.1 localhost.localdomain localhost +::1 localhost6.localdomain6 localhost6 + +``` + +If you handle errors properly, you'll notice that this example program will fail on the `SaveHostsFile()` call if started as a normal user, as editing the hosts file requires root privileges. This does not prevent libhosty from loading, managing, rendering and exporting the hosts file + +## Contributing + +Issues and PRs are more than welcome! + +### Templates + +If you find a hosts template (like the Docker one) that you think can be useful to have in this library feel free to open a Pull Request + +## Credits + +Project Contributors will be listed here + +## License + +Licenses under Apache License 2.0 diff --git a/vendor/github.com/areYouLazy/libhosty/TODO.md b/vendor/github.com/areYouLazy/libhosty/TODO.md new file mode 100644 index 00000000..136bb9f4 --- /dev/null +++ b/vendor/github.com/areYouLazy/libhosty/TODO.md @@ -0,0 +1,4 @@ +# TODO + +* Improve tests +* Improve comments diff --git a/vendor/github.com/areYouLazy/libhosty/coverage_badge.png b/vendor/github.com/areYouLazy/libhosty/coverage_badge.png new file mode 100644 index 0000000000000000000000000000000000000000..14cc422ee89fab91ac12e51487d025a1e7066146 GIT binary patch literal 2202 zcmV;L2xa$)P)Px-R7pfZRA_|qzpZDkUdB30U_xs%Qob>Fo&ra29wFf0hS`JuvfO|$y@t-F& z{{W&>LVVWS?=DqTRXqf(6GBXAQTH<@T003WAg!OGL?{Z7G0_Fx@0cw5NfH@~5W+ao zh1~C$EQTU5DwC#|`xui+!v}``A269T9Me!)mdVP>A}uYAsHiA9J3Bdd?i?R}_#s}e z_ns2TvP^b%Hfd>Tn9XLcU%yUORTVWgHUDc56OPd|R3t4$v$m0L$s$5AbJbN(>6e)R zyg9E5t04t|(l0Z)>Z%XM$&$jWbIO@#Ucwh$A9L{Qp95f2B6&5roL^kYB_RAEYZ=qf zO`A4hu~^u#V+YO6%~&iJGBPq)uwVhTwYB%u^bptf?b~T>Z6z))4u``*eSJM{xBH&B zCL2c6@MrNa5vNIF!|xvF+ReYCL}-Xg(E;E%`WSxq03U4nEB@O9BjO)1FT?qrn~Zno z@aw0JGRLrhCf5ZDFDVAUR>SX-R#-32cyx*!i5V*<;ls(!C)|O?AS2? z3Q=KYpA`mo5QNQBlG1dijv)MxC7|T$tW(upP7O>&uGJ50N zup~_xF)uPjJ2+7qyf!@E=N^DCLDYN|A;5QgfO)1RxVw94)?P%5X3Q;KGemgjm_B_v zZEbA?0s&N2MUo_BS;k;65Ed4O(P(7z=FJ>BbciRPe3HwTFH>Ax{JpspMZs(~6B!v9 zlBcSw3cKBoBuNM%SiE>KpMU;28jXg+!b0}$-OCeCJi)GAyC^CuLRHmZvs$f|yu3W# zdFLI35M*X%l9rZ6W@aW04GpYayB3qlL{U)@ufP5}xw*L*jmBWjdcA(k9Ak|isd0Gv z`^d59^ZUnI`R${%EPW{Ru6aWy4|gLgz?Vj{bD?*^c{x|;Ln&(ql0NK;c25fKr*_uhLv^UO2obUHj9&j_wS zATWY!IILD{NFKM_%{SkCLuzU&7cN{NJw2VnhYypKl*G)LGbt}GAHij@Sh#lW8UQCw zoFF11B6u^Dl$5Z1`Etx=Ga8MCy1F_N5)!Dducx)OmGJOzjvYJ3>eZ_eLSVDm#?3K= zJ9`rS8jMrS^z^t`{$?VnGk(H>tarK6bB-==`>=^1#K?HbPR{1$<`G=OF&2+Tqv6b%Go+`d)6vmESXdY>EiFVvMRD=sMM_Fa z?h;#7RbcSu84}xOvthT}dH(t5X>V`GZnv|3{rX@It#)t=)a&)Z7{A{?ZjPWYcl6uQ zdkul$)YnJRr}$`a)zEh9A52ve==5B_L+O!QE)p2$4D!e8lZcZODRb?j?9q?edNLPT zBlE(_U0nJu>rTrC`wq2qZ``;+b#*nRrKMP{R>H%>v0AOnm@xw(1dWZ2Bqt}6nwpAE zr{n3TpXTb-tMv8tjmmZQ>{&`nOPM`;Hma%;7Z*oyaWUV1`|X%K=g*&K?%cVoS+j=I zr%$7*DvgbeBqSt|pPx^Bd_1wSvBbv4q9{sGoS~H|gg}xcd_Esqt(K&uBsOl`2tY$a z11TvfBqt{aa}OCbUXHQGZK;PZTK>qo`32~dFc!^RMyzQ%EnQ9HZn?w9rrA3d_EtEiHU64 zvIUF9!dG8?#lC&}LjIg_xm;}6uz{5;S7J7sX=`hvqN0M@+FFv5l0xzbAt)>?WYwxw zY}~k!zP>&N1_rQLENtDn6|2=6+~)K1^8tAO{r6e7Ze1{Ue}6wEB_%9fx|FW2E^2CO zu-RCJqQ>RXmot@2d&pkJ8j^P;X<-Gn{EIU_}G1u}4|LXlG2haS9 zOW}V&)=Iqc88Y+aq?RE!6)Mzy5bUF+M1F|gR^?GqSoxz74lgWglDBp`qQ53?%!wCxu z8<(f2r-$C&ULJnm`LG~gAuoBjCQ0$B(8849|n!DGPH?8n;~ z80Lb+Lw4WD*r+TPJpcdz07*qoM6N<$f-#*n-2eap literal 0 HcmV?d00001 diff --git a/vendor/github.com/areYouLazy/libhosty/errors.go b/vendor/github.com/areYouLazy/libhosty/errors.go new file mode 100644 index 00000000..d4fae1a1 --- /dev/null +++ b/vendor/github.com/areYouLazy/libhosty/errors.go @@ -0,0 +1,38 @@ +package libhosty + +import ( + "errors" + "fmt" +) + +//ErrNotAnAddressLine used when operating on a non-address line for operation +// related to address lines, such as comment/uncomment +var ErrNotAnAddressLine = errors.New("this line is not of type ADDRESS") + +//ErrUncommentableLine used when try to comment a line that cannot be commented +var ErrUncommentableLine = errors.New("this line cannot be commented") + +//ErrAlredyCommentedLine used when try to comment an alredy commented line +var ErrAlredyCommentedLine = errors.New("this line is alredy commented") + +//ErrAlredyUncommentedLine used when try to uncomment an alredy uncommented line +var ErrAlredyUncommentedLine = errors.New("this line is alredy uncommented") + +//ErrAddressNotFound used when provided address is not found +var ErrAddressNotFound = errors.New("cannot find a line with given address") + +//ErrHostnameNotFound used when provided hostname is not found +var ErrHostnameNotFound = errors.New("cannot find a line with given hostname") + +//ErrUnknown used when we don't know what's happened +var ErrUnknown = errors.New("unknown error") + +//ErrCannotParseIPAddress used when unable to parse given ip address +func ErrCannotParseIPAddress(ip string) error { + return fmt.Errorf("cannot parse IP Address: %s", ip) +} + +//ErrUnrecognizedOS used when unable to recognize OS +func ErrUnrecognizedOS(os string) error { + return fmt.Errorf("unrecognized OS: %s", os) +} diff --git a/vendor/github.com/areYouLazy/libhosty/formatter.go b/vendor/github.com/areYouLazy/libhosty/formatter.go new file mode 100644 index 00000000..994e5bd3 --- /dev/null +++ b/vendor/github.com/areYouLazy/libhosty/formatter.go @@ -0,0 +1,33 @@ +package libhosty + +import ( + "fmt" + "strings" +) + +// lineFormatter return a readable form for the given HostsFileLine object +func lineFormatter(hfl HostsFileLine) string { + + // returns raw if we don't need to edit the line + // this is for UNKNOWN, EMPTY and COMMENT linetypes + if hfl.Type < LineTypeAddress { + return hfl.Raw + } + + // check if it's a commented line + if hfl.IsCommented { + // check if there's a comment for that line + if len(hfl.Comment) > 0 { + return fmt.Sprintf("# %-16s %s #%s", hfl.Address, strings.Join(hfl.Hostnames, " "), hfl.Comment) + } + + return fmt.Sprintf("# %-16s %s", hfl.Address, strings.Join(hfl.Hostnames, " ")) + } + + // return the actual hosts entry + if len(hfl.Comment) > 0 { + return fmt.Sprintf("%-16s %s #%s", hfl.Address, strings.Join(hfl.Hostnames, " "), hfl.Comment) + } + + return fmt.Sprintf("%-16s %s", hfl.Address, strings.Join(hfl.Hostnames, " ")) +} diff --git a/vendor/github.com/areYouLazy/libhosty/helper.go b/vendor/github.com/areYouLazy/libhosty/helper.go new file mode 100644 index 00000000..705916af --- /dev/null +++ b/vendor/github.com/areYouLazy/libhosty/helper.go @@ -0,0 +1,25 @@ +package libhosty + +//RestoreDefaultWindowsHostsFile loads the default windows hosts file +func (h *HostsFile) RestoreDefaultWindowsHostsFile() { + hfl, _ := ParseHostsFileAsString(windowsHostsTemplate) + h.HostsFileLines = hfl +} + +//RestoreDefaultLinuxHostsFile loads the default linux hosts file +func (h *HostsFile) RestoreDefaultLinuxHostsFile() { + hfl, _ := ParseHostsFileAsString(linuxHostsTemplate) + h.HostsFileLines = hfl +} + +//RestoreDefaultDarwinHostsFile loads the default darwin hosts file +func (h *HostsFile) RestoreDefaultDarwinHostsFile() { + hfl, _ := ParseHostsFileAsString(darwinHostsTemplate) + h.HostsFileLines = hfl +} + +//AddDockerDesktopTemplate adds the dockerDesktopTemplate to the actual hostsFile +func (h *HostsFile) AddDockerDesktopTemplate() { + hfl, _ := ParseHostsFileAsString(dockerDesktopTemplate) + h.HostsFileLines = append(h.HostsFileLines, hfl...) +} diff --git a/vendor/github.com/areYouLazy/libhosty/libhosty.go b/vendor/github.com/areYouLazy/libhosty/libhosty.go new file mode 100644 index 00000000..3504e8b8 --- /dev/null +++ b/vendor/github.com/areYouLazy/libhosty/libhosty.go @@ -0,0 +1,846 @@ +//Package libhosty is a pure golang library to manipulate the hosts file +package libhosty + +import ( + "io/ioutil" + "net" + "os" + "regexp" + "runtime" + "strings" + "sync" +) + +const ( + //Version exposes library version + Version = "2.0" +) + +const ( + // defines default path for windows os + windowsFilePath = "C:\\Windows\\System32\\drivers\\etc\\" + + // defines default path for linux os + unixFilePath = "/etc/" + + // defines default filename + hostsFileName = "hosts" +) + +//LineType define a safe type for line type enumeration +type LineType int + +const ( + //LineTypeUnknown defines unknown lines + LineTypeUnknown LineType = 0 + + //LineTypeEmpty defines empty lines + LineTypeEmpty LineType = 10 + + //LineTypeComment defines comment lines (starts with #) + LineTypeComment LineType = 20 + + //LineTypeAddress defines address lines (actual hosts lines) + LineTypeAddress LineType = 30 +) + +//HostsFileConfig defines parameters to find hosts file. +// FilePath is the absolute path of the hosts file (filename included) +type HostsFileConfig struct { + FilePath string +} + +//HostsFileLine holds hosts file lines data +type HostsFileLine struct { + //Number is the original line number + Number int + + //LineType defines the line type + Type LineType + + //Address is a net.IP representation of the address + Address net.IP + + //Parts is a slice of the line splitted by '#' + Parts []string + + //Hostnames is a slice of hostnames for the relative IP + Hostnames []string + + //Raw is the raw representation of the line, as it is in the hosts file + Raw string + + //Comment is the comment part of the line (if present in an ADDRESS line) + Comment string + + //IsCommented to know if the current ADDRESS line is commented out (starts with '#') + IsCommented bool + + //trimed is a trimed version (no spaces before and after) of the line + trimed string +} + +//HostsFile is a reference for the hosts file configuration and lines +type HostsFile struct { + sync.Mutex + + //Config reference to a HostsConfig object + Config *HostsFileConfig + + //HostsFileLines slice of HostsFileLine objects + HostsFileLines []HostsFileLine +} + +//InitWithConfig returns a new instance of a hostsfile. +// InitWithConfig is meant to be used with a custom conf file +// however InitWithConfig() will fallback to Init() if conf is nill +// You should use Init() to load hosts file from default location +func InitWithConfig(conf *HostsFileConfig) (*HostsFile, error) { + var config *HostsFileConfig + var err error + + if conf != nil { + config = conf + } else { + return Init() + } + + // allocate a new HostsFile object + hf := &HostsFile{ + // use default configuration + Config: config, + + // allocate a new slice of HostsFileLine objects + HostsFileLines: make([]HostsFileLine, 0), + } + + // parse the hosts file and load file lines + hf.HostsFileLines, err = ParseHostsFile(hf.Config.FilePath) + if err != nil { + return nil, err + } + + //return HostsFile + return hf, nil +} + +//Init returns a new instance of a hostsfile. +func Init() (*HostsFile, error) { + // initialize hostsConfig + config, err := NewHostsFileConfig("") + if err != nil { + return nil, err + } + + // allocate a new HostsFile object + hf := &HostsFile{ + // use default configuration + Config: config, + + // allocate a new slice of HostsFileLine objects + HostsFileLines: make([]HostsFileLine, 0), + } + + // parse the hosts file and load file lines + hf.HostsFileLines, err = ParseHostsFile(hf.Config.FilePath) + if err != nil { + return nil, err + } + + //return HostsFile + return hf, nil +} + +//NewHostsFileConfig loads hosts file based on environment. +// NewHostsFileConfig initialize the default file path based +// on the OS or from a given location if a custom path is provided +func NewHostsFileConfig(path string) (*HostsFileConfig, error) { + // allocate hostsConfig + var hc *HostsFileConfig + + // ensure custom path exists + // https://stackoverflow.com/questions/12518876/how-to-check-if-a-file-exists-in-go + if fh, err := os.Stat(path); err == nil { + // eusure custom path points to a file (not a directory) + if !fh.IsDir() { + hc = &HostsFileConfig{ + FilePath: path, + } + } + } else { + // check os to construct default path + switch runtime.GOOS { + case "windows": + hc = &HostsFileConfig{ + FilePath: windowsFilePath + hostsFileName, + } + default: + hc = &HostsFileConfig{ + FilePath: unixFilePath + hostsFileName, + } + } + } + + return hc, nil +} + +//GetHostsFileLines returns every address row +func (h *HostsFile) GetHostsFileLines() []*HostsFileLine { + var hfl []*HostsFileLine + + for idx := range h.HostsFileLines { + if h.HostsFileLines[idx].Type == LineTypeAddress { + hfl = append(hfl, h.GetHostsFileLineByRow(idx)) + } + } + + return hfl +} + +//GetHostsFileLineByRow returns a ponter to the given HostsFileLine row +func (h *HostsFile) GetHostsFileLineByRow(row int) *HostsFileLine { + return &h.HostsFileLines[row] +} + +//GetHostsFileLineByIP returns the index of the line and a ponter to the given HostsFileLine line +func (h *HostsFile) GetHostsFileLineByIP(ip net.IP) (int, *HostsFileLine) { + if ip == nil { + return -1, nil + } + + for idx := range h.HostsFileLines { + if net.IP.Equal(ip, h.HostsFileLines[idx].Address) { + return idx, &h.HostsFileLines[idx] + } + } + + return -1, nil +} + +func (h *HostsFile) GetHostsFileLinesByIP(ip net.IP) []*HostsFileLine { + if ip == nil { + return nil + } + + hfl := make([]*HostsFileLine, 0) + + for idx := range h.HostsFileLines { + if net.IP.Equal(ip, h.HostsFileLines[idx].Address) { + hfl = append(hfl, &h.HostsFileLines[idx]) + } + } + + return hfl +} + +//GetHostsFileLineByAddress returns the index of the line and a ponter to the given HostsFileLine line +func (h *HostsFile) GetHostsFileLineByAddress(address string) (int, *HostsFileLine) { + ip := net.ParseIP(address) + return h.GetHostsFileLineByIP(ip) +} + +func (h *HostsFile) GetHostsFileLinesByAddress(address string) []*HostsFileLine { + ip := net.ParseIP(address) + return h.GetHostsFileLinesByIP(ip) +} + +//GetHostsFileLineByHostname returns the index of the line and a ponter to the given HostsFileLine line +func (h *HostsFile) GetHostsFileLineByHostname(hostname string) (int, *HostsFileLine) { + for idx := range h.HostsFileLines { + for _, hn := range h.HostsFileLines[idx].Hostnames { + if hn == hostname { + return idx, &h.HostsFileLines[idx] + } + } + } + + return -1, nil +} + +func (h *HostsFile) GetHostsFileLinesByHostname(hostname string) []*HostsFileLine { + hfl := make([]*HostsFileLine, 0) + + for idx := range h.HostsFileLines { + for _, hn := range h.HostsFileLines[idx].Hostnames { + if hn == hostname { + hfl = append(hfl, &h.HostsFileLines[idx]) + continue + } + } + } + + return hfl +} + +func (h *HostsFile) GetHostsFileLinesByHostnameAsRegexp(hostname string) []*HostsFileLine { + hfl := make([]*HostsFileLine, 0) + + reg := regexp.MustCompile(hostname) + + for idx := range h.HostsFileLines { + for _, hn := range h.HostsFileLines[idx].Hostnames { + if reg.MatchString(hn) { + hfl = append(hfl, &h.HostsFileLines[idx]) + continue + } + } + } + + return hfl +} + +//RenderHostsFile render and returns the hosts file with the lineFormatter() routine +func (h *HostsFile) RenderHostsFile() string { + // allocate a buffer for file lines + var sliceBuffer []string + + // iterate HostsFileLines and popolate the buffer with formatted lines + for _, l := range h.HostsFileLines { + sliceBuffer = append(sliceBuffer, lineFormatter(l)) + } + + // strings.Join() prevent the last line from being a new blank line + // as opposite to a for loop with fmt.Printf(buffer + '\n') + return strings.Join(sliceBuffer, "\n") +} + +//RenderHostsFileLine render and returns the given hosts line with the lineFormatter() routine +func (h *HostsFile) RenderHostsFileLine(row int) string { + // iterate to find the row to render + if len(h.HostsFileLines) > row { + return lineFormatter(h.HostsFileLines[row]) + } + + return "" +} + +//SaveHostsFile write hosts file to configured path. +// error is not nil if something goes wrong +func (h *HostsFile) SaveHostsFile() error { + return h.SaveHostsFileAs(h.Config.FilePath) +} + +//SaveHostsFileAs write hosts file to the given path. +// error is not nil if something goes wrong +func (h *HostsFile) SaveHostsFileAs(path string) error { + // render the file as a byte slice + dataBytes := []byte(h.RenderHostsFile()) + + // write file to disk + err := ioutil.WriteFile(path, dataBytes, 0644) + if err != nil { + return err + } + + return nil +} + +//RemoveHostsFileLineByRow remove row at given index from HostsFileLines +func (h *HostsFile) RemoveHostsFileLineByRow(row int) { + // prevent out-of-index + if row < len(h.HostsFileLines) { + h.Lock() + h.HostsFileLines = append(h.HostsFileLines[:row], h.HostsFileLines[row+1:]...) + h.Unlock() + } +} + +func (h *HostsFile) RemoveHostsFileLineByIP(ip net.IP) { + for idx := len(h.HostsFileLines) - 1; idx >= 0; idx-- { + if net.IP.Equal(ip, h.HostsFileLines[idx].Address) { + h.RemoveHostsFileLineByRow(idx) + return + } + } +} + +func (h *HostsFile) RemoveHostsFileLinesByIP(ip net.IP) { + for idx := len(h.HostsFileLines) - 1; idx >= 0; idx-- { + if net.IP.Equal(ip, h.HostsFileLines[idx].Address) { + h.RemoveHostsFileLineByRow(idx) + } + } +} + +func (h *HostsFile) RemoveHostsFileLineByAddress(address string) { + ip := net.ParseIP(address) + + h.RemoveHostsFileLineByIP(ip) +} + +func (h *HostsFile) RemoveHostsFileLinesByAddress(address string) { + ip := net.ParseIP(address) + + h.RemoveHostsFileLinesByIP(ip) +} + +func (h *HostsFile) RemoveHostsFileLineByHostname(hostname string) { + for idx := len(h.HostsFileLines) - 1; idx >= 0; idx-- { + if h.HostsFileLines[idx].Type == LineTypeAddress { + for _, hn := range h.HostsFileLines[idx].Hostnames { + if hn == hostname { + h.RemoveHostsFileLineByRow(idx) + return + } + } + } + } +} + +func (h *HostsFile) RemoveHostsFileLinesByHostnameAsRegexp(hostname string) { + reg := regexp.MustCompile(hostname) + + for idx := len(h.HostsFileLines) - 1; idx >= 0; idx-- { + for _, hn := range h.HostsFileLines[idx].Hostnames { + if reg.MatchString(hn) { + h.RemoveHostsFileLineByRow(idx) + continue + } + } + } +} + +func (h *HostsFile) RemoveHostsFileLinesByHostname(hostname string) { + for idx := len(h.HostsFileLines) - 1; idx >= 0; idx-- { + if h.HostsFileLines[idx].Type == LineTypeAddress { + for _, hn := range h.HostsFileLines[idx].Hostnames { + if hn == hostname { + h.RemoveHostsFileLineByRow(idx) + continue + } + } + } + } +} + +//LookupByHostname check if the given fqdn exists. +// if yes, it returns the index of the address and the associated address. +// error is not nil if something goes wrong +func (h *HostsFile) LookupByHostname(hostname string) (int, net.IP, error) { + for idx, hfl := range h.HostsFileLines { + for _, hn := range hfl.Hostnames { + if hn == hostname { + return idx, h.HostsFileLines[idx].Address, nil + } + } + } + + return -1, nil, ErrHostnameNotFound +} + +//AddHostsFileLineRaw add the given ip/fqdn/comment pair +// this is different from AddHostFileLine because it does not take care of duplicates +// this just append the new entry to the hosts file +func (h *HostsFile) AddHostsFileLineRaw(ipRaw, fqdnRaw, comment string) (int, *HostsFileLine, error) { + // hostname to lowercase + hostname := strings.ToLower(fqdnRaw) + // parse ip to net.IP + ip := net.ParseIP(ipRaw) + + // if we have a valid IP + if ip != nil { + // create a new hosts line + hfl := HostsFileLine{ + Type: LineTypeAddress, + Address: ip, + Hostnames: []string{hostname}, + Comment: comment, + IsCommented: false, + } + + // append to hosts + h.HostsFileLines = append(h.HostsFileLines, hfl) + + // get index + idx := len(h.HostsFileLines) - 1 + + // return created entry + return idx, &h.HostsFileLines[idx], nil + } + + // return error + return -1, nil, ErrCannotParseIPAddress(ipRaw) +} + +//AddHostsFileLine add the given ip/fqdn/comment pair, cleanup is done for previous entry. +// it returns the index of the edited (created) line and a pointer to the hostsfileline object. +// error is not nil if something goes wrong +func (h *HostsFile) AddHostsFileLine(ipRaw, fqdnRaw, comment string) (int, *HostsFileLine, error) { + // hostname to lowercase + hostname := strings.ToLower(fqdnRaw) + // parse ip to net.IP + ip := net.ParseIP(ipRaw) + + // if we have a valid IP + if ip != nil { + //check if we alredy have the fqdn + if idx, addr, err := h.LookupByHostname(hostname); err == nil { + //if actual ip is the same as the given one, we are done + if net.IP.Equal(addr, ip) { + // handle comment + if comment != "" { + // just replace the current comment with the new one + h.HostsFileLines[idx].Comment = comment + } + return idx, &h.HostsFileLines[idx], nil + } + + //if address is different, we need to remove the hostname from the previous entry + for hostIdx, hn := range h.HostsFileLines[idx].Hostnames { + if hn == hostname { + if len(h.HostsFileLines[idx].Hostnames) > 1 { + h.Lock() + h.HostsFileLines[idx].Hostnames = append(h.HostsFileLines[idx].Hostnames[:hostIdx], h.HostsFileLines[idx].Hostnames[hostIdx+1:]...) + h.Unlock() + } + + //remove the line if there are no more hostnames (other than the actual one) + if len(h.HostsFileLines[idx].Hostnames) <= 1 { + h.RemoveHostsFileLineByRow(idx) + } + } + } + } + + //if we alredy have the address, just add the hostname to that line + for idx, hfl := range h.HostsFileLines { + if net.IP.Equal(hfl.Address, ip) { + h.Lock() + h.HostsFileLines[idx].Hostnames = append(h.HostsFileLines[idx].Hostnames, hostname) + h.Unlock() + + // handle comment + if comment != "" { + // just replace the current comment with the new one + h.HostsFileLines[idx].Comment = comment + } + + // return edited entry + return idx, &h.HostsFileLines[idx], nil + } + } + + // at this point we need to create new host line + hfl := HostsFileLine{ + Type: LineTypeAddress, + Address: ip, + Hostnames: []string{hostname}, + Comment: comment, + IsCommented: false, + } + + // generate raw version of the line + hfl.Raw = lineFormatter(hfl) + + // append to hosts + h.HostsFileLines = append(h.HostsFileLines, hfl) + + // get index + idx := len(h.HostsFileLines) - 1 + + // return created entry + return idx, &h.HostsFileLines[idx], nil + } + + // return error + return -1, nil, ErrCannotParseIPAddress(ipRaw) +} + +//AddCommentFileLine adds a new line of type comment with the given comment. +// it returns the index of the edited (created) line and a pointer to the hostsfileline object. +// error is not nil if something goes wrong +func (h *HostsFile) AddCommentFileLine(comment string) (int, *HostsFileLine, error) { + h.Lock() + defer h.Unlock() + + hfl := HostsFileLine{ + Type: LineTypeComment, + Raw: "# " + comment, + Comment: comment, + } + + hfl.Raw = lineFormatter(hfl) + + h.HostsFileLines = append(h.HostsFileLines, hfl) + idx := len(h.HostsFileLines) - 1 + return idx, &h.HostsFileLines[idx], nil +} + +//AddEmptyFileLine adds a new line of type empty. +// it returns the index of the edited (created) line and a pointer to the hostsfileline object. +// error is not nil if something goes wrong +func (h *HostsFile) AddEmptyFileLine() (int, *HostsFileLine, error) { + h.Lock() + defer h.Unlock() + + hfl := HostsFileLine{ + Type: LineTypeEmpty, + Raw: "", + } + + h.HostsFileLines = append(h.HostsFileLines, hfl) + idx := len(h.HostsFileLines) - 1 + return idx, &h.HostsFileLines[idx], nil +} + +//CommentHostsFileLineByRow set the IsCommented bit for the given row to true +func (h *HostsFile) CommentHostsFileLineByRow(row int) error { + h.Lock() + defer h.Unlock() + + if len(h.HostsFileLines) > row { + if h.HostsFileLines[row].Type == LineTypeAddress { + if !h.HostsFileLines[row].IsCommented { + h.HostsFileLines[row].IsCommented = true + + h.HostsFileLines[row].Raw = h.RenderHostsFileLine(row) + return nil + } + + return ErrAlredyCommentedLine + } + + return ErrNotAnAddressLine + } + + return ErrUnknown +} + +//CommentHostsFileLineByIP set the IsCommented bit for the given address to true +func (h *HostsFile) CommentHostsFileLineByIP(ip net.IP) error { + h.Lock() + defer h.Unlock() + + for idx := range h.HostsFileLines { + if net.IP.Equal(ip, h.HostsFileLines[idx].Address) { + if !h.HostsFileLines[idx].IsCommented { + h.HostsFileLines[idx].IsCommented = true + + h.HostsFileLines[idx].Raw = h.RenderHostsFileLine(idx) + return nil + } + + return ErrAlredyCommentedLine + } + } + + return ErrAddressNotFound +} + +func (h *HostsFile) CommentHostsFileLinesByIP(ip net.IP) { + h.Lock() + defer h.Unlock() + + for idx := range h.HostsFileLines { + if net.IP.Equal(ip, h.HostsFileLines[idx].Address) { + if !h.HostsFileLines[idx].IsCommented { + h.HostsFileLines[idx].IsCommented = true + + h.HostsFileLines[idx].Raw = h.RenderHostsFileLine(idx) + } + } + } +} + +//CommentHostsFileLineByAddress set the IsCommented bit for the given address as string to true +func (h *HostsFile) CommentHostsFileLineByAddress(address string) error { + ip := net.ParseIP(address) + + return h.CommentHostsFileLineByIP(ip) +} + +func (h *HostsFile) CommentHostsFileLinesByAddress(address string) { + ip := net.ParseIP(address) + h.CommentHostsFileLinesByIP(ip) +} + +//CommentHostsFileLineByHostname set the IsCommented bit for the given hostname to true +func (h *HostsFile) CommentHostsFileLineByHostname(hostname string) error { + h.Lock() + defer h.Unlock() + + for idx := range h.HostsFileLines { + for _, hn := range h.HostsFileLines[idx].Hostnames { + if hn == hostname { + if !h.HostsFileLines[idx].IsCommented { + h.HostsFileLines[idx].IsCommented = true + + h.HostsFileLines[idx].Raw = h.RenderHostsFileLine(idx) + return nil + } + + return ErrAlredyCommentedLine + } + } + } + + return ErrHostnameNotFound +} + +func (h *HostsFile) CommentHostsFileLinesByHostname(hostname string) { + h.Lock() + defer h.Unlock() + + for idx := range h.HostsFileLines { + for _, hn := range h.HostsFileLines[idx].Hostnames { + if hn == hostname { + if !h.HostsFileLines[idx].IsCommented { + h.HostsFileLines[idx].IsCommented = true + + h.HostsFileLines[idx].Raw = h.RenderHostsFileLine(idx) + } + } + } + } +} + +func (h *HostsFile) CommentHostsFileLinesByHostnameAsRegexp(hostname string) { + h.Lock() + defer h.Unlock() + + reg := regexp.MustCompile(hostname) + + for idx := range h.HostsFileLines { + for _, hn := range h.HostsFileLines[idx].Hostnames { + if reg.MatchString(hn) { + if !h.HostsFileLines[idx].IsCommented { + h.HostsFileLines[idx].IsCommented = true + + h.HostsFileLines[idx].Raw = h.RenderHostsFileLine(idx) + continue + } + } + } + } +} + +//UncommentHostsFileLineByRow set the IsCommented bit for the given row to false +func (h *HostsFile) UncommentHostsFileLineByRow(row int) error { + h.Lock() + defer h.Unlock() + + if len(h.HostsFileLines) > row { + if h.HostsFileLines[row].Type == LineTypeAddress { + if h.HostsFileLines[row].IsCommented { + h.HostsFileLines[row].IsCommented = false + + h.HostsFileLines[row].Raw = h.RenderHostsFileLine(row) + return nil + } + + return ErrAlredyUncommentedLine + } + + return ErrNotAnAddressLine + } + + return ErrUnknown +} + +//UncommentHostsFileLineByIP set the IsCommented bit for the given address to false +func (h *HostsFile) UncommentHostsFileLineByIP(ip net.IP) error { + h.Lock() + defer h.Unlock() + + for idx, hfl := range h.HostsFileLines { + if net.IP.Equal(ip, hfl.Address) { + if h.HostsFileLines[idx].IsCommented { + h.HostsFileLines[idx].IsCommented = false + + h.HostsFileLines[idx].Raw = h.RenderHostsFileLine(idx) + return nil + } + + return ErrAlredyUncommentedLine + } + } + + return ErrNotAnAddressLine +} + +func (h *HostsFile) UncommentHostsFileLinesByIP(ip net.IP) { + h.Lock() + defer h.Unlock() + + for idx := range h.HostsFileLines { + if net.IP.Equal(ip, h.HostsFileLines[idx].Address) { + if h.HostsFileLines[idx].IsCommented { + h.HostsFileLines[idx].IsCommented = false + + h.HostsFileLines[idx].Raw = h.RenderHostsFileLine(idx) + } + } + } +} + +//UncommentHostsFileLineByAddress set the IsCommented bit for the given address as string to false +func (h *HostsFile) UncommentHostsFileLineByAddress(address string) error { + ip := net.ParseIP(address) + + return h.UncommentHostsFileLineByIP(ip) +} + +func (h *HostsFile) UncommentHostsFileLinesByAddress(address string) { + ip := net.ParseIP(address) + h.UncommentHostsFileLinesByIP(ip) +} + +//UncommentHostsFileLineByHostname set the IsCommented bit for the given hostname to false +func (h *HostsFile) UncommentHostsFileLineByHostname(hostname string) error { + h.Lock() + defer h.Unlock() + + for idx := range h.HostsFileLines { + for _, hn := range h.HostsFileLines[idx].Hostnames { + if hn == hostname { + if h.HostsFileLines[idx].IsCommented { + h.HostsFileLines[idx].IsCommented = false + + h.HostsFileLines[idx].Raw = h.RenderHostsFileLine(idx) + return nil + } + + return ErrAlredyUncommentedLine + } + } + } + + return ErrHostnameNotFound +} + +func (h *HostsFile) UncommentHostsFileLinesByHostname(hostname string) { + h.Lock() + defer h.Unlock() + + for idx := range h.HostsFileLines { + for _, hn := range h.HostsFileLines[idx].Hostnames { + if hn == hostname { + if h.HostsFileLines[idx].IsCommented { + h.HostsFileLines[idx].IsCommented = false + + h.HostsFileLines[idx].Raw = h.RenderHostsFileLine(idx) + } + } + } + } +} + +func (h *HostsFile) UncommentHostsFileLinesByHostnameAsRegexp(hostname string) { + h.Lock() + defer h.Unlock() + + reg := regexp.MustCompile(hostname) + + for idx := range h.HostsFileLines { + for _, hn := range h.HostsFileLines[idx].Hostnames { + if reg.MatchString(hn) { + if h.HostsFileLines[idx].IsCommented { + h.HostsFileLines[idx].IsCommented = false + + h.HostsFileLines[idx].Raw = h.RenderHostsFileLine(idx) + continue + } + } + } + } +} diff --git a/vendor/github.com/areYouLazy/libhosty/parser.go b/vendor/github.com/areYouLazy/libhosty/parser.go new file mode 100644 index 00000000..9f090d09 --- /dev/null +++ b/vendor/github.com/areYouLazy/libhosty/parser.go @@ -0,0 +1,112 @@ +package libhosty + +import ( + "io/ioutil" + "net" + "strings" +) + +//ParseHostsFile parse a hosts file from the given location. +// error is not nil if something goes wrong +func ParseHostsFile(path string) ([]HostsFileLine, error) { + byteData, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + return parser(byteData) +} + +//ParseHostsFileAsString parse a hosts file from a given string. +// error is not nil if something goes wrong +func ParseHostsFileAsString(stringData string) ([]HostsFileLine, error) { + bytesData := []byte(stringData) + return parser(bytesData) +} + +func parser(bytesData []byte) ([]HostsFileLine, error) { + byteDataNormalized := strings.Replace(string(bytesData), "\r\n", "\n", -1) + fileLines := strings.Split(byteDataNormalized, "\n") + hostsFileLines := make([]HostsFileLine, len(fileLines)) + + // trim leading an trailing whitespace + for i, l := range fileLines { + curLine := &hostsFileLines[i] + curLine.Number = i + curLine.Raw = l + + // trim line + curLine.trimed = strings.TrimSpace(l) + + // check if it's an empty line + if curLine.trimed == "" { + curLine.Type = LineTypeEmpty + continue + } + + // check if line starts with a # + if strings.HasPrefix(curLine.trimed, "#") { + // this can be a comment or a commented host line + // so remove the 1st char (#), trim spaces + // and try to parse the line as a host line + noCommentLine := strings.TrimPrefix(curLine.trimed, "#") + tmpParts := strings.Fields(strings.TrimSpace(noCommentLine)) + + // check what we have + switch len(tmpParts) { + case 0: + // empty line, comment line + curLine.Type = LineTypeComment + continue + default: + // non-empty line, try to parse as address + address := net.ParseIP(tmpParts[0]) + + // if address is nil this line is a comment + if address == nil { + curLine.Type = LineTypeComment + continue + } + } + + // otherwise it is a commented line so let's try to parse it as a normal line + curLine.IsCommented = true + curLine.trimed = noCommentLine + } + + // not a comment or empty line so try to parse it + // check if it contains a comment + curLineSplit := strings.SplitN(curLine.trimed, "#", 2) + if len(curLineSplit) > 1 { + // trim spaces from comments + curLine.Comment = strings.TrimSpace(curLineSplit[1]) + } + + curLine.trimed = curLineSplit[0] + curLine.Parts = strings.Fields(curLine.trimed) + + if len(curLine.Parts) > 1 { + // parse address to ensure we have a valid address line + tmpIP := net.ParseIP(curLine.Parts[0]) + if tmpIP != nil { + + curLine.Type = LineTypeAddress + curLine.Address = tmpIP + // lower case all + for _, p := range curLine.Parts[1:] { + curLine.Hostnames = append(curLine.Hostnames, strings.ToLower(p)) + } + + continue + } + } + + // if we can't figure out what this line is mark it as unknown + curLine.Type = LineTypeUnknown + } + + // normalize slice + hostsFileLines = hostsFileLines[:] + + return hostsFileLines, nil +} diff --git a/vendor/github.com/areYouLazy/libhosty/templates.go b/vendor/github.com/areYouLazy/libhosty/templates.go new file mode 100644 index 00000000..aa0bcacd --- /dev/null +++ b/vendor/github.com/areYouLazy/libhosty/templates.go @@ -0,0 +1,52 @@ +package libhosty + +// linuxHostsTemplate defines default linux hosts file +const linuxHostsTemplate = `# Do not remove the following line, or various programs +# that require network functionality will fail. +127.0.0.1 localhost.localdomain localhost +::1 localhost6.localdomain6 localhost6 +` + +// windowsHostsTemplate defines default windows hosts file +const windowsHostsTemplate = `# Copyright (c) 1993-2006 Microsoft Corp. +# +# This is a sample HOSTS file used by Microsoft TCP/IP for Windows. +# +# This file contains the mappings of IP addresses to host names. Each +# entry should be kept on an individual line. The IP address should +# be placed in the first column followed by the corresponding host name. +# The IP address and the host name should be separated by at least one +# space. +# +# Additionally, comments (such as these) may be inserted on individual +# lines or following the machine name denoted by a '#' symbol. +# +# For example: +# +# 102.54.94.97 rhino.acme.com # source server +# 38.25.63.10 x.acme.com # x client host +# localhost name resolution is handle within DNS itself. +# 127.0.0.1 localhost +# ::1 localhost +` + +// darwinHostsTemplate defines default darwin hosts file +const darwinHostsTemplate = `## +# Host Database +# +# localhost is used to configure the loopback interface +# when the system is booting. Do not change this entry +## +127.0.0.1 localhost +255.255.255.255 broadcasthost::1 localhost + +::1 localhost +fe80::1%lo0 localhost +` + +// dockerDesktopTemplate defines docker desktop hosts entry +const dockerDesktopTemplate = `# Added by Docker Desktop +# To allow the same kube context to work on the host and the container: +127.0.0.1 kubernetes.docker.internal +# End of section +` diff --git a/vendor/github.com/asaskevich/govalidator/.gitignore b/vendor/github.com/asaskevich/govalidator/.gitignore deleted file mode 100644 index 8d69a941..00000000 --- a/vendor/github.com/asaskevich/govalidator/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -bin/ -.idea/ -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - diff --git a/vendor/github.com/asaskevich/govalidator/.travis.yml b/vendor/github.com/asaskevich/govalidator/.travis.yml deleted file mode 100644 index bb83c667..00000000 --- a/vendor/github.com/asaskevich/govalidator/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: go -dist: xenial -go: - - '1.10' - - '1.11' - - '1.12' - - '1.13' - - 'tip' - -script: - - go test -coverpkg=./... -coverprofile=coverage.info -timeout=5s - - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/asaskevich/govalidator/CODE_OF_CONDUCT.md b/vendor/github.com/asaskevich/govalidator/CODE_OF_CONDUCT.md deleted file mode 100644 index 4b462b0d..00000000 --- a/vendor/github.com/asaskevich/govalidator/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,43 +0,0 @@ -# Contributor Code of Conduct - -This project adheres to [The Code Manifesto](http://codemanifesto.com) -as its guidelines for contributor interactions. - -## The Code Manifesto - -We want to work in an ecosystem that empowers developers to reach their -potential — one that encourages growth and effective collaboration. A space -that is safe for all. - -A space such as this benefits everyone that participates in it. It encourages -new developers to enter our field. It is through discussion and collaboration -that we grow, and through growth that we improve. - -In the effort to create such a place, we hold to these values: - -1. **Discrimination limits us.** This includes discrimination on the basis of - race, gender, sexual orientation, gender identity, age, nationality, - technology and any other arbitrary exclusion of a group of people. -2. **Boundaries honor us.** Your comfort levels are not everyone’s comfort - levels. Remember that, and if brought to your attention, heed it. -3. **We are our biggest assets.** None of us were born masters of our trade. - Each of us has been helped along the way. Return that favor, when and where - you can. -4. **We are resources for the future.** As an extension of #3, share what you - know. Make yourself a resource to help those that come after you. -5. **Respect defines us.** Treat others as you wish to be treated. Make your - discussions, criticisms and debates from a position of respectfulness. Ask - yourself, is it true? Is it necessary? Is it constructive? Anything less is - unacceptable. -6. **Reactions require grace.** Angry responses are valid, but abusive language - and vindictive actions are toxic. When something happens that offends you, - handle it assertively, but be respectful. Escalate reasonably, and try to - allow the offender an opportunity to explain themselves, and possibly - correct the issue. -7. **Opinions are just that: opinions.** Each and every one of us, due to our - background and upbringing, have varying opinions. That is perfectly - acceptable. Remember this: if you respect your own opinions, you should - respect the opinions of others. -8. **To err is human.** You might not intend it, but mistakes do happen and - contribute to build experience. Tolerate honest mistakes, and don't - hesitate to apologize if you make one yourself. diff --git a/vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md b/vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md deleted file mode 100644 index 7ed268a1..00000000 --- a/vendor/github.com/asaskevich/govalidator/CONTRIBUTING.md +++ /dev/null @@ -1,63 +0,0 @@ -#### Support -If you do have a contribution to the package, feel free to create a Pull Request or an Issue. - -#### What to contribute -If you don't know what to do, there are some features and functions that need to be done - -- [ ] Refactor code -- [ ] Edit docs and [README](https://github.com/asaskevich/govalidator/README.md): spellcheck, grammar and typo check -- [ ] Create actual list of contributors and projects that currently using this package -- [ ] Resolve [issues and bugs](https://github.com/asaskevich/govalidator/issues) -- [ ] Update actual [list of functions](https://github.com/asaskevich/govalidator#list-of-functions) -- [ ] Update [list of validators](https://github.com/asaskevich/govalidator#validatestruct-2) that available for `ValidateStruct` and add new -- [ ] Implement new validators: `IsFQDN`, `IsIMEI`, `IsPostalCode`, `IsISIN`, `IsISRC` etc -- [x] Implement [validation by maps](https://github.com/asaskevich/govalidator/issues/224) -- [ ] Implement fuzzing testing -- [ ] Implement some struct/map/array utilities -- [ ] Implement map/array validation -- [ ] Implement benchmarking -- [ ] Implement batch of examples -- [ ] Look at forks for new features and fixes - -#### Advice -Feel free to create what you want, but keep in mind when you implement new features: -- Code must be clear and readable, names of variables/constants clearly describes what they are doing -- Public functions must be documented and described in source file and added to README.md to the list of available functions -- There are must be unit-tests for any new functions and improvements - -## Financial contributions - -We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/govalidator). -Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed. - - -## Credits - - -### Contributors - -Thank you to all the people who have already contributed to govalidator! - - - -### Backers - -Thank you to all our backers! [[Become a backer](https://opencollective.com/govalidator#backer)] - - - - -### Sponsors - -Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/govalidator#sponsor)) - - - - - - - - - - - \ No newline at end of file diff --git a/vendor/github.com/asaskevich/govalidator/LICENSE b/vendor/github.com/asaskevich/govalidator/LICENSE deleted file mode 100644 index cacba910..00000000 --- a/vendor/github.com/asaskevich/govalidator/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014-2020 Alex Saskevich - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -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. \ No newline at end of file diff --git a/vendor/github.com/asaskevich/govalidator/README.md b/vendor/github.com/asaskevich/govalidator/README.md deleted file mode 100644 index 2c3fc35e..00000000 --- a/vendor/github.com/asaskevich/govalidator/README.md +++ /dev/null @@ -1,622 +0,0 @@ -govalidator -=========== -[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/asaskevich/govalidator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![GoDoc](https://godoc.org/github.com/asaskevich/govalidator?status.png)](https://godoc.org/github.com/asaskevich/govalidator) -[![Build Status](https://travis-ci.org/asaskevich/govalidator.svg?branch=master)](https://travis-ci.org/asaskevich/govalidator) -[![Coverage](https://codecov.io/gh/asaskevich/govalidator/branch/master/graph/badge.svg)](https://codecov.io/gh/asaskevich/govalidator) [![Go Report Card](https://goreportcard.com/badge/github.com/asaskevich/govalidator)](https://goreportcard.com/report/github.com/asaskevich/govalidator) [![GoSearch](http://go-search.org/badge?id=github.com%2Fasaskevich%2Fgovalidator)](http://go-search.org/view?id=github.com%2Fasaskevich%2Fgovalidator) [![Backers on Open Collective](https://opencollective.com/govalidator/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/govalidator/sponsors/badge.svg)](#sponsors) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fasaskevich%2Fgovalidator.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fasaskevich%2Fgovalidator?ref=badge_shield) - -A package of validators and sanitizers for strings, structs and collections. Based on [validator.js](https://github.com/chriso/validator.js). - -#### Installation -Make sure that Go is installed on your computer. -Type the following command in your terminal: - - go get github.com/asaskevich/govalidator - -or you can get specified release of the package with `gopkg.in`: - - go get gopkg.in/asaskevich/govalidator.v10 - -After it the package is ready to use. - - -#### Import package in your project -Add following line in your `*.go` file: -```go -import "github.com/asaskevich/govalidator" -``` -If you are unhappy to use long `govalidator`, you can do something like this: -```go -import ( - valid "github.com/asaskevich/govalidator" -) -``` - -#### Activate behavior to require all fields have a validation tag by default -`SetFieldsRequiredByDefault` causes validation to fail when struct fields do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`). A good place to activate this is a package init function or the main() function. - -`SetNilPtrAllowedByRequired` causes validation to pass when struct fields marked by `required` are set to nil. This is disabled by default for consistency, but some packages that need to be able to determine between `nil` and `zero value` state can use this. If disabled, both `nil` and `zero` values cause validation errors. - -```go -import "github.com/asaskevich/govalidator" - -func init() { - govalidator.SetFieldsRequiredByDefault(true) -} -``` - -Here's some code to explain it: -```go -// this struct definition will fail govalidator.ValidateStruct() (and the field values do not matter): -type exampleStruct struct { - Name string `` - Email string `valid:"email"` -} - -// this, however, will only fail when Email is empty or an invalid email address: -type exampleStruct2 struct { - Name string `valid:"-"` - Email string `valid:"email"` -} - -// lastly, this will only fail when Email is an invalid email address but not when it's empty: -type exampleStruct2 struct { - Name string `valid:"-"` - Email string `valid:"email,optional"` -} -``` - -#### Recent breaking changes (see [#123](https://github.com/asaskevich/govalidator/pull/123)) -##### Custom validator function signature -A context was added as the second parameter, for structs this is the object being validated – this makes dependent validation possible. -```go -import "github.com/asaskevich/govalidator" - -// old signature -func(i interface{}) bool - -// new signature -func(i interface{}, o interface{}) bool -``` - -##### Adding a custom validator -This was changed to prevent data races when accessing custom validators. -```go -import "github.com/asaskevich/govalidator" - -// before -govalidator.CustomTypeTagMap["customByteArrayValidator"] = func(i interface{}, o interface{}) bool { - // ... -} - -// after -govalidator.CustomTypeTagMap.Set("customByteArrayValidator", func(i interface{}, o interface{}) bool { - // ... -}) -``` - -#### List of functions: -```go -func Abs(value float64) float64 -func BlackList(str, chars string) string -func ByteLength(str string, params ...string) bool -func CamelCaseToUnderscore(str string) string -func Contains(str, substring string) bool -func Count(array []interface{}, iterator ConditionIterator) int -func Each(array []interface{}, iterator Iterator) -func ErrorByField(e error, field string) string -func ErrorsByField(e error) map[string]string -func Filter(array []interface{}, iterator ConditionIterator) []interface{} -func Find(array []interface{}, iterator ConditionIterator) interface{} -func GetLine(s string, index int) (string, error) -func GetLines(s string) []string -func HasLowerCase(str string) bool -func HasUpperCase(str string) bool -func HasWhitespace(str string) bool -func HasWhitespaceOnly(str string) bool -func InRange(value interface{}, left interface{}, right interface{}) bool -func InRangeFloat32(value, left, right float32) bool -func InRangeFloat64(value, left, right float64) bool -func InRangeInt(value, left, right interface{}) bool -func IsASCII(str string) bool -func IsAlpha(str string) bool -func IsAlphanumeric(str string) bool -func IsBase64(str string) bool -func IsByteLength(str string, min, max int) bool -func IsCIDR(str string) bool -func IsCRC32(str string) bool -func IsCRC32b(str string) bool -func IsCreditCard(str string) bool -func IsDNSName(str string) bool -func IsDataURI(str string) bool -func IsDialString(str string) bool -func IsDivisibleBy(str, num string) bool -func IsEmail(str string) bool -func IsExistingEmail(email string) bool -func IsFilePath(str string) (bool, int) -func IsFloat(str string) bool -func IsFullWidth(str string) bool -func IsHalfWidth(str string) bool -func IsHash(str string, algorithm string) bool -func IsHexadecimal(str string) bool -func IsHexcolor(str string) bool -func IsHost(str string) bool -func IsIP(str string) bool -func IsIPv4(str string) bool -func IsIPv6(str string) bool -func IsISBN(str string, version int) bool -func IsISBN10(str string) bool -func IsISBN13(str string) bool -func IsISO3166Alpha2(str string) bool -func IsISO3166Alpha3(str string) bool -func IsISO4217(str string) bool -func IsISO693Alpha2(str string) bool -func IsISO693Alpha3b(str string) bool -func IsIn(str string, params ...string) bool -func IsInRaw(str string, params ...string) bool -func IsInt(str string) bool -func IsJSON(str string) bool -func IsLatitude(str string) bool -func IsLongitude(str string) bool -func IsLowerCase(str string) bool -func IsMAC(str string) bool -func IsMD4(str string) bool -func IsMD5(str string) bool -func IsMagnetURI(str string) bool -func IsMongoID(str string) bool -func IsMultibyte(str string) bool -func IsNatural(value float64) bool -func IsNegative(value float64) bool -func IsNonNegative(value float64) bool -func IsNonPositive(value float64) bool -func IsNotNull(str string) bool -func IsNull(str string) bool -func IsNumeric(str string) bool -func IsPort(str string) bool -func IsPositive(value float64) bool -func IsPrintableASCII(str string) bool -func IsRFC3339(str string) bool -func IsRFC3339WithoutZone(str string) bool -func IsRGBcolor(str string) bool -func IsRegex(str string) bool -func IsRequestURI(rawurl string) bool -func IsRequestURL(rawurl string) bool -func IsRipeMD128(str string) bool -func IsRipeMD160(str string) bool -func IsRsaPub(str string, params ...string) bool -func IsRsaPublicKey(str string, keylen int) bool -func IsSHA1(str string) bool -func IsSHA256(str string) bool -func IsSHA384(str string) bool -func IsSHA512(str string) bool -func IsSSN(str string) bool -func IsSemver(str string) bool -func IsTiger128(str string) bool -func IsTiger160(str string) bool -func IsTiger192(str string) bool -func IsTime(str string, format string) bool -func IsType(v interface{}, params ...string) bool -func IsURL(str string) bool -func IsUTFDigit(str string) bool -func IsUTFLetter(str string) bool -func IsUTFLetterNumeric(str string) bool -func IsUTFNumeric(str string) bool -func IsUUID(str string) bool -func IsUUIDv3(str string) bool -func IsUUIDv4(str string) bool -func IsUUIDv5(str string) bool -func IsULID(str string) bool -func IsUnixTime(str string) bool -func IsUpperCase(str string) bool -func IsVariableWidth(str string) bool -func IsWhole(value float64) bool -func LeftTrim(str, chars string) string -func Map(array []interface{}, iterator ResultIterator) []interface{} -func Matches(str, pattern string) bool -func MaxStringLength(str string, params ...string) bool -func MinStringLength(str string, params ...string) bool -func NormalizeEmail(str string) (string, error) -func PadBoth(str string, padStr string, padLen int) string -func PadLeft(str string, padStr string, padLen int) string -func PadRight(str string, padStr string, padLen int) string -func PrependPathToErrors(err error, path string) error -func Range(str string, params ...string) bool -func RemoveTags(s string) string -func ReplacePattern(str, pattern, replace string) string -func Reverse(s string) string -func RightTrim(str, chars string) string -func RuneLength(str string, params ...string) bool -func SafeFileName(str string) string -func SetFieldsRequiredByDefault(value bool) -func SetNilPtrAllowedByRequired(value bool) -func Sign(value float64) float64 -func StringLength(str string, params ...string) bool -func StringMatches(s string, params ...string) bool -func StripLow(str string, keepNewLines bool) string -func ToBoolean(str string) (bool, error) -func ToFloat(str string) (float64, error) -func ToInt(value interface{}) (res int64, err error) -func ToJSON(obj interface{}) (string, error) -func ToString(obj interface{}) string -func Trim(str, chars string) string -func Truncate(str string, length int, ending string) string -func TruncatingErrorf(str string, args ...interface{}) error -func UnderscoreToCamelCase(s string) string -func ValidateMap(inputMap map[string]interface{}, validationMap map[string]interface{}) (bool, error) -func ValidateStruct(s interface{}) (bool, error) -func WhiteList(str, chars string) string -type ConditionIterator -type CustomTypeValidator -type Error -func (e Error) Error() string -type Errors -func (es Errors) Error() string -func (es Errors) Errors() []error -type ISO3166Entry -type ISO693Entry -type InterfaceParamValidator -type Iterator -type ParamValidator -type ResultIterator -type UnsupportedTypeError -func (e *UnsupportedTypeError) Error() string -type Validator -``` - -#### Examples -###### IsURL -```go -println(govalidator.IsURL(`http://user@pass:domain.com/path/page`)) -``` -###### IsType -```go -println(govalidator.IsType("Bob", "string")) -println(govalidator.IsType(1, "int")) -i := 1 -println(govalidator.IsType(&i, "*int")) -``` - -IsType can be used through the tag `type` which is essential for map validation: -```go -type User struct { - Name string `valid:"type(string)"` - Age int `valid:"type(int)"` - Meta interface{} `valid:"type(string)"` -} -result, err := govalidator.ValidateStruct(User{"Bob", 20, "meta"}) -if err != nil { - println("error: " + err.Error()) -} -println(result) -``` -###### ToString -```go -type User struct { - FirstName string - LastName string -} - -str := govalidator.ToString(&User{"John", "Juan"}) -println(str) -``` -###### Each, Map, Filter, Count for slices -Each iterates over the slice/array and calls Iterator for every item -```go -data := []interface{}{1, 2, 3, 4, 5} -var fn govalidator.Iterator = func(value interface{}, index int) { - println(value.(int)) -} -govalidator.Each(data, fn) -``` -```go -data := []interface{}{1, 2, 3, 4, 5} -var fn govalidator.ResultIterator = func(value interface{}, index int) interface{} { - return value.(int) * 3 -} -_ = govalidator.Map(data, fn) // result = []interface{}{1, 6, 9, 12, 15} -``` -```go -data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} -var fn govalidator.ConditionIterator = func(value interface{}, index int) bool { - return value.(int)%2 == 0 -} -_ = govalidator.Filter(data, fn) // result = []interface{}{2, 4, 6, 8, 10} -_ = govalidator.Count(data, fn) // result = 5 -``` -###### ValidateStruct [#2](https://github.com/asaskevich/govalidator/pull/2) -If you want to validate structs, you can use tag `valid` for any field in your structure. All validators used with this field in one tag are separated by comma. If you want to skip validation, place `-` in your tag. If you need a validator that is not on the list below, you can add it like this: -```go -govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool { - return str == "duck" -}) -``` -For completely custom validators (interface-based), see below. - -Here is a list of available validators for struct fields (validator - used function): -```go -"email": IsEmail, -"url": IsURL, -"dialstring": IsDialString, -"requrl": IsRequestURL, -"requri": IsRequestURI, -"alpha": IsAlpha, -"utfletter": IsUTFLetter, -"alphanum": IsAlphanumeric, -"utfletternum": IsUTFLetterNumeric, -"numeric": IsNumeric, -"utfnumeric": IsUTFNumeric, -"utfdigit": IsUTFDigit, -"hexadecimal": IsHexadecimal, -"hexcolor": IsHexcolor, -"rgbcolor": IsRGBcolor, -"lowercase": IsLowerCase, -"uppercase": IsUpperCase, -"int": IsInt, -"float": IsFloat, -"null": IsNull, -"uuid": IsUUID, -"uuidv3": IsUUIDv3, -"uuidv4": IsUUIDv4, -"uuidv5": IsUUIDv5, -"creditcard": IsCreditCard, -"isbn10": IsISBN10, -"isbn13": IsISBN13, -"json": IsJSON, -"multibyte": IsMultibyte, -"ascii": IsASCII, -"printableascii": IsPrintableASCII, -"fullwidth": IsFullWidth, -"halfwidth": IsHalfWidth, -"variablewidth": IsVariableWidth, -"base64": IsBase64, -"datauri": IsDataURI, -"ip": IsIP, -"port": IsPort, -"ipv4": IsIPv4, -"ipv6": IsIPv6, -"dns": IsDNSName, -"host": IsHost, -"mac": IsMAC, -"latitude": IsLatitude, -"longitude": IsLongitude, -"ssn": IsSSN, -"semver": IsSemver, -"rfc3339": IsRFC3339, -"rfc3339WithoutZone": IsRFC3339WithoutZone, -"ISO3166Alpha2": IsISO3166Alpha2, -"ISO3166Alpha3": IsISO3166Alpha3, -"ulid": IsULID, -``` -Validators with parameters - -```go -"range(min|max)": Range, -"length(min|max)": ByteLength, -"runelength(min|max)": RuneLength, -"stringlength(min|max)": StringLength, -"matches(pattern)": StringMatches, -"in(string1|string2|...|stringN)": IsIn, -"rsapub(keylength)" : IsRsaPub, -"minstringlength(int): MinStringLength, -"maxstringlength(int): MaxStringLength, -``` -Validators with parameters for any type - -```go -"type(type)": IsType, -``` - -And here is small example of usage: -```go -type Post struct { - Title string `valid:"alphanum,required"` - Message string `valid:"duck,ascii"` - Message2 string `valid:"animal(dog)"` - AuthorIP string `valid:"ipv4"` - Date string `valid:"-"` -} -post := &Post{ - Title: "My Example Post", - Message: "duck", - Message2: "dog", - AuthorIP: "123.234.54.3", -} - -// Add your own struct validation tags -govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool { - return str == "duck" -}) - -// Add your own struct validation tags with parameter -govalidator.ParamTagMap["animal"] = govalidator.ParamValidator(func(str string, params ...string) bool { - species := params[0] - return str == species -}) -govalidator.ParamTagRegexMap["animal"] = regexp.MustCompile("^animal\\((\\w+)\\)$") - -result, err := govalidator.ValidateStruct(post) -if err != nil { - println("error: " + err.Error()) -} -println(result) -``` -###### ValidateMap [#2](https://github.com/asaskevich/govalidator/pull/338) -If you want to validate maps, you can use the map to be validated and a validation map that contain the same tags used in ValidateStruct, both maps have to be in the form `map[string]interface{}` - -So here is small example of usage: -```go -var mapTemplate = map[string]interface{}{ - "name":"required,alpha", - "family":"required,alpha", - "email":"required,email", - "cell-phone":"numeric", - "address":map[string]interface{}{ - "line1":"required,alphanum", - "line2":"alphanum", - "postal-code":"numeric", - }, -} - -var inputMap = map[string]interface{}{ - "name":"Bob", - "family":"Smith", - "email":"foo@bar.baz", - "address":map[string]interface{}{ - "line1":"", - "line2":"", - "postal-code":"", - }, -} - -result, err := govalidator.ValidateMap(inputMap, mapTemplate) -if err != nil { - println("error: " + err.Error()) -} -println(result) -``` - -###### WhiteList -```go -// Remove all characters from string ignoring characters between "a" and "z" -println(govalidator.WhiteList("a3a43a5a4a3a2a23a4a5a4a3a4", "a-z") == "aaaaaaaaaaaa") -``` - -###### Custom validation functions -Custom validation using your own domain specific validators is also available - here's an example of how to use it: -```go -import "github.com/asaskevich/govalidator" - -type CustomByteArray [6]byte // custom types are supported and can be validated - -type StructWithCustomByteArray struct { - ID CustomByteArray `valid:"customByteArrayValidator,customMinLengthValidator"` // multiple custom validators are possible as well and will be evaluated in sequence - Email string `valid:"email"` - CustomMinLength int `valid:"-"` -} - -govalidator.CustomTypeTagMap.Set("customByteArrayValidator", func(i interface{}, context interface{}) bool { - switch v := context.(type) { // you can type switch on the context interface being validated - case StructWithCustomByteArray: - // you can check and validate against some other field in the context, - // return early or not validate against the context at all – your choice - case SomeOtherType: - // ... - default: - // expecting some other type? Throw/panic here or continue - } - - switch v := i.(type) { // type switch on the struct field being validated - case CustomByteArray: - for _, e := range v { // this validator checks that the byte array is not empty, i.e. not all zeroes - if e != 0 { - return true - } - } - } - return false -}) -govalidator.CustomTypeTagMap.Set("customMinLengthValidator", func(i interface{}, context interface{}) bool { - switch v := context.(type) { // this validates a field against the value in another field, i.e. dependent validation - case StructWithCustomByteArray: - return len(v.ID) >= v.CustomMinLength - } - return false -}) -``` - -###### Loop over Error() -By default .Error() returns all errors in a single String. To access each error you can do this: -```go - if err != nil { - errs := err.(govalidator.Errors).Errors() - for _, e := range errs { - fmt.Println(e.Error()) - } - } -``` - -###### Custom error messages -Custom error messages are supported via annotations by adding the `~` separator - here's an example of how to use it: -```go -type Ticket struct { - Id int64 `json:"id"` - FirstName string `json:"firstname" valid:"required~First name is blank"` -} -``` - -#### Notes -Documentation is available here: [godoc.org](https://godoc.org/github.com/asaskevich/govalidator). -Full information about code coverage is also available here: [govalidator on gocover.io](http://gocover.io/github.com/asaskevich/govalidator). - -#### Support -If you do have a contribution to the package, feel free to create a Pull Request or an Issue. - -#### What to contribute -If you don't know what to do, there are some features and functions that need to be done - -- [ ] Refactor code -- [ ] Edit docs and [README](https://github.com/asaskevich/govalidator/README.md): spellcheck, grammar and typo check -- [ ] Create actual list of contributors and projects that currently using this package -- [ ] Resolve [issues and bugs](https://github.com/asaskevich/govalidator/issues) -- [ ] Update actual [list of functions](https://github.com/asaskevich/govalidator#list-of-functions) -- [ ] Update [list of validators](https://github.com/asaskevich/govalidator#validatestruct-2) that available for `ValidateStruct` and add new -- [ ] Implement new validators: `IsFQDN`, `IsIMEI`, `IsPostalCode`, `IsISIN`, `IsISRC` etc -- [x] Implement [validation by maps](https://github.com/asaskevich/govalidator/issues/224) -- [ ] Implement fuzzing testing -- [ ] Implement some struct/map/array utilities -- [ ] Implement map/array validation -- [ ] Implement benchmarking -- [ ] Implement batch of examples -- [ ] Look at forks for new features and fixes - -#### Advice -Feel free to create what you want, but keep in mind when you implement new features: -- Code must be clear and readable, names of variables/constants clearly describes what they are doing -- Public functions must be documented and described in source file and added to README.md to the list of available functions -- There are must be unit-tests for any new functions and improvements - -## Credits -### Contributors - -This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. - -#### Special thanks to [contributors](https://github.com/asaskevich/govalidator/graphs/contributors) -* [Daniel Lohse](https://github.com/annismckenzie) -* [Attila Oláh](https://github.com/attilaolah) -* [Daniel Korner](https://github.com/Dadie) -* [Steven Wilkin](https://github.com/stevenwilkin) -* [Deiwin Sarjas](https://github.com/deiwin) -* [Noah Shibley](https://github.com/slugmobile) -* [Nathan Davies](https://github.com/nathj07) -* [Matt Sanford](https://github.com/mzsanford) -* [Simon ccl1115](https://github.com/ccl1115) - - - - -### Backers - -Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/govalidator#backer)] - - - - -### Sponsors - -Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/govalidator#sponsor)] - - - - - - - - - - - - - - - -## License -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fasaskevich%2Fgovalidator.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fasaskevich%2Fgovalidator?ref=badge_large) diff --git a/vendor/github.com/asaskevich/govalidator/arrays.go b/vendor/github.com/asaskevich/govalidator/arrays.go deleted file mode 100644 index 3e1da7cb..00000000 --- a/vendor/github.com/asaskevich/govalidator/arrays.go +++ /dev/null @@ -1,87 +0,0 @@ -package govalidator - -// Iterator is the function that accepts element of slice/array and its index -type Iterator func(interface{}, int) - -// ResultIterator is the function that accepts element of slice/array and its index and returns any result -type ResultIterator func(interface{}, int) interface{} - -// ConditionIterator is the function that accepts element of slice/array and its index and returns boolean -type ConditionIterator func(interface{}, int) bool - -// ReduceIterator is the function that accepts two element of slice/array and returns result of merging those values -type ReduceIterator func(interface{}, interface{}) interface{} - -// Some validates that any item of array corresponds to ConditionIterator. Returns boolean. -func Some(array []interface{}, iterator ConditionIterator) bool { - res := false - for index, data := range array { - res = res || iterator(data, index) - } - return res -} - -// Every validates that every item of array corresponds to ConditionIterator. Returns boolean. -func Every(array []interface{}, iterator ConditionIterator) bool { - res := true - for index, data := range array { - res = res && iterator(data, index) - } - return res -} - -// Reduce boils down a list of values into a single value by ReduceIterator -func Reduce(array []interface{}, iterator ReduceIterator, initialValue interface{}) interface{} { - for _, data := range array { - initialValue = iterator(initialValue, data) - } - return initialValue -} - -// Each iterates over the slice and apply Iterator to every item -func Each(array []interface{}, iterator Iterator) { - for index, data := range array { - iterator(data, index) - } -} - -// Map iterates over the slice and apply ResultIterator to every item. Returns new slice as a result. -func Map(array []interface{}, iterator ResultIterator) []interface{} { - var result = make([]interface{}, len(array)) - for index, data := range array { - result[index] = iterator(data, index) - } - return result -} - -// Find iterates over the slice and apply ConditionIterator to every item. Returns first item that meet ConditionIterator or nil otherwise. -func Find(array []interface{}, iterator ConditionIterator) interface{} { - for index, data := range array { - if iterator(data, index) { - return data - } - } - return nil -} - -// Filter iterates over the slice and apply ConditionIterator to every item. Returns new slice. -func Filter(array []interface{}, iterator ConditionIterator) []interface{} { - var result = make([]interface{}, 0) - for index, data := range array { - if iterator(data, index) { - result = append(result, data) - } - } - return result -} - -// Count iterates over the slice and apply ConditionIterator to every item. Returns count of items that meets ConditionIterator. -func Count(array []interface{}, iterator ConditionIterator) int { - count := 0 - for index, data := range array { - if iterator(data, index) { - count = count + 1 - } - } - return count -} diff --git a/vendor/github.com/asaskevich/govalidator/converter.go b/vendor/github.com/asaskevich/govalidator/converter.go deleted file mode 100644 index d68e990f..00000000 --- a/vendor/github.com/asaskevich/govalidator/converter.go +++ /dev/null @@ -1,81 +0,0 @@ -package govalidator - -import ( - "encoding/json" - "fmt" - "reflect" - "strconv" -) - -// ToString convert the input to a string. -func ToString(obj interface{}) string { - res := fmt.Sprintf("%v", obj) - return res -} - -// ToJSON convert the input to a valid JSON string -func ToJSON(obj interface{}) (string, error) { - res, err := json.Marshal(obj) - if err != nil { - res = []byte("") - } - return string(res), err -} - -// ToFloat convert the input string to a float, or 0.0 if the input is not a float. -func ToFloat(value interface{}) (res float64, err error) { - val := reflect.ValueOf(value) - - switch value.(type) { - case int, int8, int16, int32, int64: - res = float64(val.Int()) - case uint, uint8, uint16, uint32, uint64: - res = float64(val.Uint()) - case float32, float64: - res = val.Float() - case string: - res, err = strconv.ParseFloat(val.String(), 64) - if err != nil { - res = 0 - } - default: - err = fmt.Errorf("ToInt: unknown interface type %T", value) - res = 0 - } - - return -} - -// ToInt convert the input string or any int type to an integer type 64, or 0 if the input is not an integer. -func ToInt(value interface{}) (res int64, err error) { - val := reflect.ValueOf(value) - - switch value.(type) { - case int, int8, int16, int32, int64: - res = val.Int() - case uint, uint8, uint16, uint32, uint64: - res = int64(val.Uint()) - case float32, float64: - res = int64(val.Float()) - case string: - if IsInt(val.String()) { - res, err = strconv.ParseInt(val.String(), 0, 64) - if err != nil { - res = 0 - } - } else { - err = fmt.Errorf("ToInt: invalid numeric format %g", value) - res = 0 - } - default: - err = fmt.Errorf("ToInt: unknown interface type %T", value) - res = 0 - } - - return -} - -// ToBoolean convert the input string to a boolean. -func ToBoolean(str string) (bool, error) { - return strconv.ParseBool(str) -} diff --git a/vendor/github.com/asaskevich/govalidator/doc.go b/vendor/github.com/asaskevich/govalidator/doc.go deleted file mode 100644 index 55dce62d..00000000 --- a/vendor/github.com/asaskevich/govalidator/doc.go +++ /dev/null @@ -1,3 +0,0 @@ -package govalidator - -// A package of validators and sanitizers for strings, structures and collections. diff --git a/vendor/github.com/asaskevich/govalidator/error.go b/vendor/github.com/asaskevich/govalidator/error.go deleted file mode 100644 index 1da2336f..00000000 --- a/vendor/github.com/asaskevich/govalidator/error.go +++ /dev/null @@ -1,47 +0,0 @@ -package govalidator - -import ( - "sort" - "strings" -) - -// Errors is an array of multiple errors and conforms to the error interface. -type Errors []error - -// Errors returns itself. -func (es Errors) Errors() []error { - return es -} - -func (es Errors) Error() string { - var errs []string - for _, e := range es { - errs = append(errs, e.Error()) - } - sort.Strings(errs) - return strings.Join(errs, ";") -} - -// Error encapsulates a name, an error and whether there's a custom error message or not. -type Error struct { - Name string - Err error - CustomErrorMessageExists bool - - // Validator indicates the name of the validator that failed - Validator string - Path []string -} - -func (e Error) Error() string { - if e.CustomErrorMessageExists { - return e.Err.Error() - } - - errName := e.Name - if len(e.Path) > 0 { - errName = strings.Join(append(e.Path, e.Name), ".") - } - - return errName + ": " + e.Err.Error() -} diff --git a/vendor/github.com/asaskevich/govalidator/numerics.go b/vendor/github.com/asaskevich/govalidator/numerics.go deleted file mode 100644 index 5041d9e8..00000000 --- a/vendor/github.com/asaskevich/govalidator/numerics.go +++ /dev/null @@ -1,100 +0,0 @@ -package govalidator - -import ( - "math" -) - -// Abs returns absolute value of number -func Abs(value float64) float64 { - return math.Abs(value) -} - -// Sign returns signum of number: 1 in case of value > 0, -1 in case of value < 0, 0 otherwise -func Sign(value float64) float64 { - if value > 0 { - return 1 - } else if value < 0 { - return -1 - } else { - return 0 - } -} - -// IsNegative returns true if value < 0 -func IsNegative(value float64) bool { - return value < 0 -} - -// IsPositive returns true if value > 0 -func IsPositive(value float64) bool { - return value > 0 -} - -// IsNonNegative returns true if value >= 0 -func IsNonNegative(value float64) bool { - return value >= 0 -} - -// IsNonPositive returns true if value <= 0 -func IsNonPositive(value float64) bool { - return value <= 0 -} - -// InRangeInt returns true if value lies between left and right border -func InRangeInt(value, left, right interface{}) bool { - value64, _ := ToInt(value) - left64, _ := ToInt(left) - right64, _ := ToInt(right) - if left64 > right64 { - left64, right64 = right64, left64 - } - return value64 >= left64 && value64 <= right64 -} - -// InRangeFloat32 returns true if value lies between left and right border -func InRangeFloat32(value, left, right float32) bool { - if left > right { - left, right = right, left - } - return value >= left && value <= right -} - -// InRangeFloat64 returns true if value lies between left and right border -func InRangeFloat64(value, left, right float64) bool { - if left > right { - left, right = right, left - } - return value >= left && value <= right -} - -// InRange returns true if value lies between left and right border, generic type to handle int, float32, float64 and string. -// All types must the same type. -// False if value doesn't lie in range or if it incompatible or not comparable -func InRange(value interface{}, left interface{}, right interface{}) bool { - switch value.(type) { - case int: - intValue, _ := ToInt(value) - intLeft, _ := ToInt(left) - intRight, _ := ToInt(right) - return InRangeInt(intValue, intLeft, intRight) - case float32, float64: - intValue, _ := ToFloat(value) - intLeft, _ := ToFloat(left) - intRight, _ := ToFloat(right) - return InRangeFloat64(intValue, intLeft, intRight) - case string: - return value.(string) >= left.(string) && value.(string) <= right.(string) - default: - return false - } -} - -// IsWhole returns true if value is whole number -func IsWhole(value float64) bool { - return math.Remainder(value, 1) == 0 -} - -// IsNatural returns true if value is natural number (positive and whole) -func IsNatural(value float64) bool { - return IsWhole(value) && IsPositive(value) -} diff --git a/vendor/github.com/asaskevich/govalidator/patterns.go b/vendor/github.com/asaskevich/govalidator/patterns.go deleted file mode 100644 index bafc3765..00000000 --- a/vendor/github.com/asaskevich/govalidator/patterns.go +++ /dev/null @@ -1,113 +0,0 @@ -package govalidator - -import "regexp" - -// Basic regular expressions for validating strings -const ( - Email string = "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$" - CreditCard string = "^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$" - ISBN10 string = "^(?:[0-9]{9}X|[0-9]{10})$" - ISBN13 string = "^(?:[0-9]{13})$" - UUID3 string = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$" - UUID4 string = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" - UUID5 string = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" - UUID string = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" - Alpha string = "^[a-zA-Z]+$" - Alphanumeric string = "^[a-zA-Z0-9]+$" - Numeric string = "^[0-9]+$" - Int string = "^(?:[-+]?(?:0|[1-9][0-9]*))$" - Float string = "^(?:[-+]?(?:[0-9]+))?(?:\\.[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$" - Hexadecimal string = "^[0-9a-fA-F]+$" - Hexcolor string = "^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" - RGBcolor string = "^rgb\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*\\)$" - ASCII string = "^[\x00-\x7F]+$" - Multibyte string = "[^\x00-\x7F]" - FullWidth string = "[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]" - HalfWidth string = "[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]" - Base64 string = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$" - PrintableASCII string = "^[\x20-\x7E]+$" - DataURI string = "^data:.+\\/(.+);base64$" - MagnetURI string = "^magnet:\\?xt=urn:[a-zA-Z0-9]+:[a-zA-Z0-9]{32,40}&dn=.+&tr=.+$" - Latitude string = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$" - Longitude string = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$" - DNSName string = `^([a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*[\._]?$` - IP string = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))` - URLSchema string = `((ftp|tcp|udp|wss?|https?):\/\/)` - URLUsername string = `(\S+(:\S*)?@)` - URLPath string = `((\/|\?|#)[^\s]*)` - URLPort string = `(:(\d{1,5}))` - URLIP string = `([1-9]\d?|1\d\d|2[01]\d|22[0-3]|24\d|25[0-5])(\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-5]))` - URLSubdomain string = `((www\.)|([a-zA-Z0-9]+([-_\.]?[a-zA-Z0-9])*[a-zA-Z0-9]\.[a-zA-Z0-9]+))` - URL = `^` + URLSchema + `?` + URLUsername + `?` + `((` + URLIP + `|(\[` + IP + `\])|(([a-zA-Z0-9]([a-zA-Z0-9-_]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|(` + URLSubdomain + `?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))\.?` + URLPort + `?` + URLPath + `?$` - SSN string = `^\d{3}[- ]?\d{2}[- ]?\d{4}$` - WinPath string = `^[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$` - UnixPath string = `^(/[^/\x00]*)+/?$` - WinARPath string = `^(?:(?:[a-zA-Z]:|\\\\[a-z0-9_.$●-]+\\[a-z0-9_.$●-]+)\\|\\?[^\\/:*?"<>|\r\n]+\\?)(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$` - UnixARPath string = `^((\.{0,2}/)?([^/\x00]*))+/?$` - Semver string = "^v?(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(-(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\\+[0-9a-zA-Z-]+(\\.[0-9a-zA-Z-]+)*)?$" - tagName string = "valid" - hasLowerCase string = ".*[[:lower:]]" - hasUpperCase string = ".*[[:upper:]]" - hasWhitespace string = ".*[[:space:]]" - hasWhitespaceOnly string = "^[[:space:]]+$" - IMEI string = "^[0-9a-f]{14}$|^\\d{15}$|^\\d{18}$" - IMSI string = "^\\d{14,15}$" - E164 string = `^\+?[1-9]\d{1,14}$` -) - -// Used by IsFilePath func -const ( - // Unknown is unresolved OS type - Unknown = iota - // Win is Windows type - Win - // Unix is *nix OS types - Unix -) - -var ( - userRegexp = regexp.MustCompile("^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+$") - hostRegexp = regexp.MustCompile("^[^\\s]+\\.[^\\s]+$") - userDotRegexp = regexp.MustCompile("(^[.]{1})|([.]{1}$)|([.]{2,})") - rxEmail = regexp.MustCompile(Email) - rxCreditCard = regexp.MustCompile(CreditCard) - rxISBN10 = regexp.MustCompile(ISBN10) - rxISBN13 = regexp.MustCompile(ISBN13) - rxUUID3 = regexp.MustCompile(UUID3) - rxUUID4 = regexp.MustCompile(UUID4) - rxUUID5 = regexp.MustCompile(UUID5) - rxUUID = regexp.MustCompile(UUID) - rxAlpha = regexp.MustCompile(Alpha) - rxAlphanumeric = regexp.MustCompile(Alphanumeric) - rxNumeric = regexp.MustCompile(Numeric) - rxInt = regexp.MustCompile(Int) - rxFloat = regexp.MustCompile(Float) - rxHexadecimal = regexp.MustCompile(Hexadecimal) - rxHexcolor = regexp.MustCompile(Hexcolor) - rxRGBcolor = regexp.MustCompile(RGBcolor) - rxASCII = regexp.MustCompile(ASCII) - rxPrintableASCII = regexp.MustCompile(PrintableASCII) - rxMultibyte = regexp.MustCompile(Multibyte) - rxFullWidth = regexp.MustCompile(FullWidth) - rxHalfWidth = regexp.MustCompile(HalfWidth) - rxBase64 = regexp.MustCompile(Base64) - rxDataURI = regexp.MustCompile(DataURI) - rxMagnetURI = regexp.MustCompile(MagnetURI) - rxLatitude = regexp.MustCompile(Latitude) - rxLongitude = regexp.MustCompile(Longitude) - rxDNSName = regexp.MustCompile(DNSName) - rxURL = regexp.MustCompile(URL) - rxSSN = regexp.MustCompile(SSN) - rxWinPath = regexp.MustCompile(WinPath) - rxUnixPath = regexp.MustCompile(UnixPath) - rxARWinPath = regexp.MustCompile(WinARPath) - rxARUnixPath = regexp.MustCompile(UnixARPath) - rxSemver = regexp.MustCompile(Semver) - rxHasLowerCase = regexp.MustCompile(hasLowerCase) - rxHasUpperCase = regexp.MustCompile(hasUpperCase) - rxHasWhitespace = regexp.MustCompile(hasWhitespace) - rxHasWhitespaceOnly = regexp.MustCompile(hasWhitespaceOnly) - rxIMEI = regexp.MustCompile(IMEI) - rxIMSI = regexp.MustCompile(IMSI) - rxE164 = regexp.MustCompile(E164) -) diff --git a/vendor/github.com/asaskevich/govalidator/types.go b/vendor/github.com/asaskevich/govalidator/types.go deleted file mode 100644 index c573abb5..00000000 --- a/vendor/github.com/asaskevich/govalidator/types.go +++ /dev/null @@ -1,656 +0,0 @@ -package govalidator - -import ( - "reflect" - "regexp" - "sort" - "sync" -) - -// Validator is a wrapper for a validator function that returns bool and accepts string. -type Validator func(str string) bool - -// CustomTypeValidator is a wrapper for validator functions that returns bool and accepts any type. -// The second parameter should be the context (in the case of validating a struct: the whole object being validated). -type CustomTypeValidator func(i interface{}, o interface{}) bool - -// ParamValidator is a wrapper for validator functions that accept additional parameters. -type ParamValidator func(str string, params ...string) bool - -// InterfaceParamValidator is a wrapper for functions that accept variants parameters for an interface value -type InterfaceParamValidator func(in interface{}, params ...string) bool -type tagOptionsMap map[string]tagOption - -func (t tagOptionsMap) orderedKeys() []string { - var keys []string - for k := range t { - keys = append(keys, k) - } - - sort.Slice(keys, func(a, b int) bool { - return t[keys[a]].order < t[keys[b]].order - }) - - return keys -} - -type tagOption struct { - name string - customErrorMessage string - order int -} - -// UnsupportedTypeError is a wrapper for reflect.Type -type UnsupportedTypeError struct { - Type reflect.Type -} - -// stringValues is a slice of reflect.Value holding *reflect.StringValue. -// It implements the methods to sort by string. -type stringValues []reflect.Value - -// InterfaceParamTagMap is a map of functions accept variants parameters for an interface value -var InterfaceParamTagMap = map[string]InterfaceParamValidator{ - "type": IsType, -} - -// InterfaceParamTagRegexMap maps interface param tags to their respective regexes. -var InterfaceParamTagRegexMap = map[string]*regexp.Regexp{ - "type": regexp.MustCompile(`^type\((.*)\)$`), -} - -// ParamTagMap is a map of functions accept variants parameters -var ParamTagMap = map[string]ParamValidator{ - "length": ByteLength, - "range": Range, - "runelength": RuneLength, - "stringlength": StringLength, - "matches": StringMatches, - "in": IsInRaw, - "rsapub": IsRsaPub, - "minstringlength": MinStringLength, - "maxstringlength": MaxStringLength, -} - -// ParamTagRegexMap maps param tags to their respective regexes. -var ParamTagRegexMap = map[string]*regexp.Regexp{ - "range": regexp.MustCompile("^range\\((\\d+)\\|(\\d+)\\)$"), - "length": regexp.MustCompile("^length\\((\\d+)\\|(\\d+)\\)$"), - "runelength": regexp.MustCompile("^runelength\\((\\d+)\\|(\\d+)\\)$"), - "stringlength": regexp.MustCompile("^stringlength\\((\\d+)\\|(\\d+)\\)$"), - "in": regexp.MustCompile(`^in\((.*)\)`), - "matches": regexp.MustCompile(`^matches\((.+)\)$`), - "rsapub": regexp.MustCompile("^rsapub\\((\\d+)\\)$"), - "minstringlength": regexp.MustCompile("^minstringlength\\((\\d+)\\)$"), - "maxstringlength": regexp.MustCompile("^maxstringlength\\((\\d+)\\)$"), -} - -type customTypeTagMap struct { - validators map[string]CustomTypeValidator - - sync.RWMutex -} - -func (tm *customTypeTagMap) Get(name string) (CustomTypeValidator, bool) { - tm.RLock() - defer tm.RUnlock() - v, ok := tm.validators[name] - return v, ok -} - -func (tm *customTypeTagMap) Set(name string, ctv CustomTypeValidator) { - tm.Lock() - defer tm.Unlock() - tm.validators[name] = ctv -} - -// CustomTypeTagMap is a map of functions that can be used as tags for ValidateStruct function. -// Use this to validate compound or custom types that need to be handled as a whole, e.g. -// `type UUID [16]byte` (this would be handled as an array of bytes). -var CustomTypeTagMap = &customTypeTagMap{validators: make(map[string]CustomTypeValidator)} - -// TagMap is a map of functions, that can be used as tags for ValidateStruct function. -var TagMap = map[string]Validator{ - "email": IsEmail, - "url": IsURL, - "dialstring": IsDialString, - "requrl": IsRequestURL, - "requri": IsRequestURI, - "alpha": IsAlpha, - "utfletter": IsUTFLetter, - "alphanum": IsAlphanumeric, - "utfletternum": IsUTFLetterNumeric, - "numeric": IsNumeric, - "utfnumeric": IsUTFNumeric, - "utfdigit": IsUTFDigit, - "hexadecimal": IsHexadecimal, - "hexcolor": IsHexcolor, - "rgbcolor": IsRGBcolor, - "lowercase": IsLowerCase, - "uppercase": IsUpperCase, - "int": IsInt, - "float": IsFloat, - "null": IsNull, - "notnull": IsNotNull, - "uuid": IsUUID, - "uuidv3": IsUUIDv3, - "uuidv4": IsUUIDv4, - "uuidv5": IsUUIDv5, - "creditcard": IsCreditCard, - "isbn10": IsISBN10, - "isbn13": IsISBN13, - "json": IsJSON, - "multibyte": IsMultibyte, - "ascii": IsASCII, - "printableascii": IsPrintableASCII, - "fullwidth": IsFullWidth, - "halfwidth": IsHalfWidth, - "variablewidth": IsVariableWidth, - "base64": IsBase64, - "datauri": IsDataURI, - "ip": IsIP, - "port": IsPort, - "ipv4": IsIPv4, - "ipv6": IsIPv6, - "dns": IsDNSName, - "host": IsHost, - "mac": IsMAC, - "latitude": IsLatitude, - "longitude": IsLongitude, - "ssn": IsSSN, - "semver": IsSemver, - "rfc3339": IsRFC3339, - "rfc3339WithoutZone": IsRFC3339WithoutZone, - "ISO3166Alpha2": IsISO3166Alpha2, - "ISO3166Alpha3": IsISO3166Alpha3, - "ISO4217": IsISO4217, - "IMEI": IsIMEI, - "ulid": IsULID, -} - -// ISO3166Entry stores country codes -type ISO3166Entry struct { - EnglishShortName string - FrenchShortName string - Alpha2Code string - Alpha3Code string - Numeric string -} - -//ISO3166List based on https://www.iso.org/obp/ui/#search/code/ Code Type "Officially Assigned Codes" -var ISO3166List = []ISO3166Entry{ - {"Afghanistan", "Afghanistan (l')", "AF", "AFG", "004"}, - {"Albania", "Albanie (l')", "AL", "ALB", "008"}, - {"Antarctica", "Antarctique (l')", "AQ", "ATA", "010"}, - {"Algeria", "Algérie (l')", "DZ", "DZA", "012"}, - {"American Samoa", "Samoa américaines (les)", "AS", "ASM", "016"}, - {"Andorra", "Andorre (l')", "AD", "AND", "020"}, - {"Angola", "Angola (l')", "AO", "AGO", "024"}, - {"Antigua and Barbuda", "Antigua-et-Barbuda", "AG", "ATG", "028"}, - {"Azerbaijan", "Azerbaïdjan (l')", "AZ", "AZE", "031"}, - {"Argentina", "Argentine (l')", "AR", "ARG", "032"}, - {"Australia", "Australie (l')", "AU", "AUS", "036"}, - {"Austria", "Autriche (l')", "AT", "AUT", "040"}, - {"Bahamas (the)", "Bahamas (les)", "BS", "BHS", "044"}, - {"Bahrain", "Bahreïn", "BH", "BHR", "048"}, - {"Bangladesh", "Bangladesh (le)", "BD", "BGD", "050"}, - {"Armenia", "Arménie (l')", "AM", "ARM", "051"}, - {"Barbados", "Barbade (la)", "BB", "BRB", "052"}, - {"Belgium", "Belgique (la)", "BE", "BEL", "056"}, - {"Bermuda", "Bermudes (les)", "BM", "BMU", "060"}, - {"Bhutan", "Bhoutan (le)", "BT", "BTN", "064"}, - {"Bolivia (Plurinational State of)", "Bolivie (État plurinational de)", "BO", "BOL", "068"}, - {"Bosnia and Herzegovina", "Bosnie-Herzégovine (la)", "BA", "BIH", "070"}, - {"Botswana", "Botswana (le)", "BW", "BWA", "072"}, - {"Bouvet Island", "Bouvet (l'Île)", "BV", "BVT", "074"}, - {"Brazil", "Brésil (le)", "BR", "BRA", "076"}, - {"Belize", "Belize (le)", "BZ", "BLZ", "084"}, - {"British Indian Ocean Territory (the)", "Indien (le Territoire britannique de l'océan)", "IO", "IOT", "086"}, - {"Solomon Islands", "Salomon (Îles)", "SB", "SLB", "090"}, - {"Virgin Islands (British)", "Vierges britanniques (les Îles)", "VG", "VGB", "092"}, - {"Brunei Darussalam", "Brunéi Darussalam (le)", "BN", "BRN", "096"}, - {"Bulgaria", "Bulgarie (la)", "BG", "BGR", "100"}, - {"Myanmar", "Myanmar (le)", "MM", "MMR", "104"}, - {"Burundi", "Burundi (le)", "BI", "BDI", "108"}, - {"Belarus", "Bélarus (le)", "BY", "BLR", "112"}, - {"Cambodia", "Cambodge (le)", "KH", "KHM", "116"}, - {"Cameroon", "Cameroun (le)", "CM", "CMR", "120"}, - {"Canada", "Canada (le)", "CA", "CAN", "124"}, - {"Cabo Verde", "Cabo Verde", "CV", "CPV", "132"}, - {"Cayman Islands (the)", "Caïmans (les Îles)", "KY", "CYM", "136"}, - {"Central African Republic (the)", "République centrafricaine (la)", "CF", "CAF", "140"}, - {"Sri Lanka", "Sri Lanka", "LK", "LKA", "144"}, - {"Chad", "Tchad (le)", "TD", "TCD", "148"}, - {"Chile", "Chili (le)", "CL", "CHL", "152"}, - {"China", "Chine (la)", "CN", "CHN", "156"}, - {"Taiwan (Province of China)", "Taïwan (Province de Chine)", "TW", "TWN", "158"}, - {"Christmas Island", "Christmas (l'Île)", "CX", "CXR", "162"}, - {"Cocos (Keeling) Islands (the)", "Cocos (les Îles)/ Keeling (les Îles)", "CC", "CCK", "166"}, - {"Colombia", "Colombie (la)", "CO", "COL", "170"}, - {"Comoros (the)", "Comores (les)", "KM", "COM", "174"}, - {"Mayotte", "Mayotte", "YT", "MYT", "175"}, - {"Congo (the)", "Congo (le)", "CG", "COG", "178"}, - {"Congo (the Democratic Republic of the)", "Congo (la République démocratique du)", "CD", "COD", "180"}, - {"Cook Islands (the)", "Cook (les Îles)", "CK", "COK", "184"}, - {"Costa Rica", "Costa Rica (le)", "CR", "CRI", "188"}, - {"Croatia", "Croatie (la)", "HR", "HRV", "191"}, - {"Cuba", "Cuba", "CU", "CUB", "192"}, - {"Cyprus", "Chypre", "CY", "CYP", "196"}, - {"Czech Republic (the)", "tchèque (la République)", "CZ", "CZE", "203"}, - {"Benin", "Bénin (le)", "BJ", "BEN", "204"}, - {"Denmark", "Danemark (le)", "DK", "DNK", "208"}, - {"Dominica", "Dominique (la)", "DM", "DMA", "212"}, - {"Dominican Republic (the)", "dominicaine (la République)", "DO", "DOM", "214"}, - {"Ecuador", "Équateur (l')", "EC", "ECU", "218"}, - {"El Salvador", "El Salvador", "SV", "SLV", "222"}, - {"Equatorial Guinea", "Guinée équatoriale (la)", "GQ", "GNQ", "226"}, - {"Ethiopia", "Éthiopie (l')", "ET", "ETH", "231"}, - {"Eritrea", "Érythrée (l')", "ER", "ERI", "232"}, - {"Estonia", "Estonie (l')", "EE", "EST", "233"}, - {"Faroe Islands (the)", "Féroé (les Îles)", "FO", "FRO", "234"}, - {"Falkland Islands (the) [Malvinas]", "Falkland (les Îles)/Malouines (les Îles)", "FK", "FLK", "238"}, - {"South Georgia and the South Sandwich Islands", "Géorgie du Sud-et-les Îles Sandwich du Sud (la)", "GS", "SGS", "239"}, - {"Fiji", "Fidji (les)", "FJ", "FJI", "242"}, - {"Finland", "Finlande (la)", "FI", "FIN", "246"}, - {"Åland Islands", "Åland(les Îles)", "AX", "ALA", "248"}, - {"France", "France (la)", "FR", "FRA", "250"}, - {"French Guiana", "Guyane française (la )", "GF", "GUF", "254"}, - {"French Polynesia", "Polynésie française (la)", "PF", "PYF", "258"}, - {"French Southern Territories (the)", "Terres australes françaises (les)", "TF", "ATF", "260"}, - {"Djibouti", "Djibouti", "DJ", "DJI", "262"}, - {"Gabon", "Gabon (le)", "GA", "GAB", "266"}, - {"Georgia", "Géorgie (la)", "GE", "GEO", "268"}, - {"Gambia (the)", "Gambie (la)", "GM", "GMB", "270"}, - {"Palestine, State of", "Palestine, État de", "PS", "PSE", "275"}, - {"Germany", "Allemagne (l')", "DE", "DEU", "276"}, - {"Ghana", "Ghana (le)", "GH", "GHA", "288"}, - {"Gibraltar", "Gibraltar", "GI", "GIB", "292"}, - {"Kiribati", "Kiribati", "KI", "KIR", "296"}, - {"Greece", "Grèce (la)", "GR", "GRC", "300"}, - {"Greenland", "Groenland (le)", "GL", "GRL", "304"}, - {"Grenada", "Grenade (la)", "GD", "GRD", "308"}, - {"Guadeloupe", "Guadeloupe (la)", "GP", "GLP", "312"}, - {"Guam", "Guam", "GU", "GUM", "316"}, - {"Guatemala", "Guatemala (le)", "GT", "GTM", "320"}, - {"Guinea", "Guinée (la)", "GN", "GIN", "324"}, - {"Guyana", "Guyana (le)", "GY", "GUY", "328"}, - {"Haiti", "Haïti", "HT", "HTI", "332"}, - {"Heard Island and McDonald Islands", "Heard-et-Îles MacDonald (l'Île)", "HM", "HMD", "334"}, - {"Holy See (the)", "Saint-Siège (le)", "VA", "VAT", "336"}, - {"Honduras", "Honduras (le)", "HN", "HND", "340"}, - {"Hong Kong", "Hong Kong", "HK", "HKG", "344"}, - {"Hungary", "Hongrie (la)", "HU", "HUN", "348"}, - {"Iceland", "Islande (l')", "IS", "ISL", "352"}, - {"India", "Inde (l')", "IN", "IND", "356"}, - {"Indonesia", "Indonésie (l')", "ID", "IDN", "360"}, - {"Iran (Islamic Republic of)", "Iran (République Islamique d')", "IR", "IRN", "364"}, - {"Iraq", "Iraq (l')", "IQ", "IRQ", "368"}, - {"Ireland", "Irlande (l')", "IE", "IRL", "372"}, - {"Israel", "Israël", "IL", "ISR", "376"}, - {"Italy", "Italie (l')", "IT", "ITA", "380"}, - {"Côte d'Ivoire", "Côte d'Ivoire (la)", "CI", "CIV", "384"}, - {"Jamaica", "Jamaïque (la)", "JM", "JAM", "388"}, - {"Japan", "Japon (le)", "JP", "JPN", "392"}, - {"Kazakhstan", "Kazakhstan (le)", "KZ", "KAZ", "398"}, - {"Jordan", "Jordanie (la)", "JO", "JOR", "400"}, - {"Kenya", "Kenya (le)", "KE", "KEN", "404"}, - {"Korea (the Democratic People's Republic of)", "Corée (la République populaire démocratique de)", "KP", "PRK", "408"}, - {"Korea (the Republic of)", "Corée (la République de)", "KR", "KOR", "410"}, - {"Kuwait", "Koweït (le)", "KW", "KWT", "414"}, - {"Kyrgyzstan", "Kirghizistan (le)", "KG", "KGZ", "417"}, - {"Lao People's Democratic Republic (the)", "Lao, République démocratique populaire", "LA", "LAO", "418"}, - {"Lebanon", "Liban (le)", "LB", "LBN", "422"}, - {"Lesotho", "Lesotho (le)", "LS", "LSO", "426"}, - {"Latvia", "Lettonie (la)", "LV", "LVA", "428"}, - {"Liberia", "Libéria (le)", "LR", "LBR", "430"}, - {"Libya", "Libye (la)", "LY", "LBY", "434"}, - {"Liechtenstein", "Liechtenstein (le)", "LI", "LIE", "438"}, - {"Lithuania", "Lituanie (la)", "LT", "LTU", "440"}, - {"Luxembourg", "Luxembourg (le)", "LU", "LUX", "442"}, - {"Macao", "Macao", "MO", "MAC", "446"}, - {"Madagascar", "Madagascar", "MG", "MDG", "450"}, - {"Malawi", "Malawi (le)", "MW", "MWI", "454"}, - {"Malaysia", "Malaisie (la)", "MY", "MYS", "458"}, - {"Maldives", "Maldives (les)", "MV", "MDV", "462"}, - {"Mali", "Mali (le)", "ML", "MLI", "466"}, - {"Malta", "Malte", "MT", "MLT", "470"}, - {"Martinique", "Martinique (la)", "MQ", "MTQ", "474"}, - {"Mauritania", "Mauritanie (la)", "MR", "MRT", "478"}, - {"Mauritius", "Maurice", "MU", "MUS", "480"}, - {"Mexico", "Mexique (le)", "MX", "MEX", "484"}, - {"Monaco", "Monaco", "MC", "MCO", "492"}, - {"Mongolia", "Mongolie (la)", "MN", "MNG", "496"}, - {"Moldova (the Republic of)", "Moldova , République de", "MD", "MDA", "498"}, - {"Montenegro", "Monténégro (le)", "ME", "MNE", "499"}, - {"Montserrat", "Montserrat", "MS", "MSR", "500"}, - {"Morocco", "Maroc (le)", "MA", "MAR", "504"}, - {"Mozambique", "Mozambique (le)", "MZ", "MOZ", "508"}, - {"Oman", "Oman", "OM", "OMN", "512"}, - {"Namibia", "Namibie (la)", "NA", "NAM", "516"}, - {"Nauru", "Nauru", "NR", "NRU", "520"}, - {"Nepal", "Népal (le)", "NP", "NPL", "524"}, - {"Netherlands (the)", "Pays-Bas (les)", "NL", "NLD", "528"}, - {"Curaçao", "Curaçao", "CW", "CUW", "531"}, - {"Aruba", "Aruba", "AW", "ABW", "533"}, - {"Sint Maarten (Dutch part)", "Saint-Martin (partie néerlandaise)", "SX", "SXM", "534"}, - {"Bonaire, Sint Eustatius and Saba", "Bonaire, Saint-Eustache et Saba", "BQ", "BES", "535"}, - {"New Caledonia", "Nouvelle-Calédonie (la)", "NC", "NCL", "540"}, - {"Vanuatu", "Vanuatu (le)", "VU", "VUT", "548"}, - {"New Zealand", "Nouvelle-Zélande (la)", "NZ", "NZL", "554"}, - {"Nicaragua", "Nicaragua (le)", "NI", "NIC", "558"}, - {"Niger (the)", "Niger (le)", "NE", "NER", "562"}, - {"Nigeria", "Nigéria (le)", "NG", "NGA", "566"}, - {"Niue", "Niue", "NU", "NIU", "570"}, - {"Norfolk Island", "Norfolk (l'Île)", "NF", "NFK", "574"}, - {"Norway", "Norvège (la)", "NO", "NOR", "578"}, - {"Northern Mariana Islands (the)", "Mariannes du Nord (les Îles)", "MP", "MNP", "580"}, - {"United States Minor Outlying Islands (the)", "Îles mineures éloignées des États-Unis (les)", "UM", "UMI", "581"}, - {"Micronesia (Federated States of)", "Micronésie (États fédérés de)", "FM", "FSM", "583"}, - {"Marshall Islands (the)", "Marshall (Îles)", "MH", "MHL", "584"}, - {"Palau", "Palaos (les)", "PW", "PLW", "585"}, - {"Pakistan", "Pakistan (le)", "PK", "PAK", "586"}, - {"Panama", "Panama (le)", "PA", "PAN", "591"}, - {"Papua New Guinea", "Papouasie-Nouvelle-Guinée (la)", "PG", "PNG", "598"}, - {"Paraguay", "Paraguay (le)", "PY", "PRY", "600"}, - {"Peru", "Pérou (le)", "PE", "PER", "604"}, - {"Philippines (the)", "Philippines (les)", "PH", "PHL", "608"}, - {"Pitcairn", "Pitcairn", "PN", "PCN", "612"}, - {"Poland", "Pologne (la)", "PL", "POL", "616"}, - {"Portugal", "Portugal (le)", "PT", "PRT", "620"}, - {"Guinea-Bissau", "Guinée-Bissau (la)", "GW", "GNB", "624"}, - {"Timor-Leste", "Timor-Leste (le)", "TL", "TLS", "626"}, - {"Puerto Rico", "Porto Rico", "PR", "PRI", "630"}, - {"Qatar", "Qatar (le)", "QA", "QAT", "634"}, - {"Réunion", "Réunion (La)", "RE", "REU", "638"}, - {"Romania", "Roumanie (la)", "RO", "ROU", "642"}, - {"Russian Federation (the)", "Russie (la Fédération de)", "RU", "RUS", "643"}, - {"Rwanda", "Rwanda (le)", "RW", "RWA", "646"}, - {"Saint Barthélemy", "Saint-Barthélemy", "BL", "BLM", "652"}, - {"Saint Helena, Ascension and Tristan da Cunha", "Sainte-Hélène, Ascension et Tristan da Cunha", "SH", "SHN", "654"}, - {"Saint Kitts and Nevis", "Saint-Kitts-et-Nevis", "KN", "KNA", "659"}, - {"Anguilla", "Anguilla", "AI", "AIA", "660"}, - {"Saint Lucia", "Sainte-Lucie", "LC", "LCA", "662"}, - {"Saint Martin (French part)", "Saint-Martin (partie française)", "MF", "MAF", "663"}, - {"Saint Pierre and Miquelon", "Saint-Pierre-et-Miquelon", "PM", "SPM", "666"}, - {"Saint Vincent and the Grenadines", "Saint-Vincent-et-les Grenadines", "VC", "VCT", "670"}, - {"San Marino", "Saint-Marin", "SM", "SMR", "674"}, - {"Sao Tome and Principe", "Sao Tomé-et-Principe", "ST", "STP", "678"}, - {"Saudi Arabia", "Arabie saoudite (l')", "SA", "SAU", "682"}, - {"Senegal", "Sénégal (le)", "SN", "SEN", "686"}, - {"Serbia", "Serbie (la)", "RS", "SRB", "688"}, - {"Seychelles", "Seychelles (les)", "SC", "SYC", "690"}, - {"Sierra Leone", "Sierra Leone (la)", "SL", "SLE", "694"}, - {"Singapore", "Singapour", "SG", "SGP", "702"}, - {"Slovakia", "Slovaquie (la)", "SK", "SVK", "703"}, - {"Viet Nam", "Viet Nam (le)", "VN", "VNM", "704"}, - {"Slovenia", "Slovénie (la)", "SI", "SVN", "705"}, - {"Somalia", "Somalie (la)", "SO", "SOM", "706"}, - {"South Africa", "Afrique du Sud (l')", "ZA", "ZAF", "710"}, - {"Zimbabwe", "Zimbabwe (le)", "ZW", "ZWE", "716"}, - {"Spain", "Espagne (l')", "ES", "ESP", "724"}, - {"South Sudan", "Soudan du Sud (le)", "SS", "SSD", "728"}, - {"Sudan (the)", "Soudan (le)", "SD", "SDN", "729"}, - {"Western Sahara*", "Sahara occidental (le)*", "EH", "ESH", "732"}, - {"Suriname", "Suriname (le)", "SR", "SUR", "740"}, - {"Svalbard and Jan Mayen", "Svalbard et l'Île Jan Mayen (le)", "SJ", "SJM", "744"}, - {"Swaziland", "Swaziland (le)", "SZ", "SWZ", "748"}, - {"Sweden", "Suède (la)", "SE", "SWE", "752"}, - {"Switzerland", "Suisse (la)", "CH", "CHE", "756"}, - {"Syrian Arab Republic", "République arabe syrienne (la)", "SY", "SYR", "760"}, - {"Tajikistan", "Tadjikistan (le)", "TJ", "TJK", "762"}, - {"Thailand", "Thaïlande (la)", "TH", "THA", "764"}, - {"Togo", "Togo (le)", "TG", "TGO", "768"}, - {"Tokelau", "Tokelau (les)", "TK", "TKL", "772"}, - {"Tonga", "Tonga (les)", "TO", "TON", "776"}, - {"Trinidad and Tobago", "Trinité-et-Tobago (la)", "TT", "TTO", "780"}, - {"United Arab Emirates (the)", "Émirats arabes unis (les)", "AE", "ARE", "784"}, - {"Tunisia", "Tunisie (la)", "TN", "TUN", "788"}, - {"Turkey", "Turquie (la)", "TR", "TUR", "792"}, - {"Turkmenistan", "Turkménistan (le)", "TM", "TKM", "795"}, - {"Turks and Caicos Islands (the)", "Turks-et-Caïcos (les Îles)", "TC", "TCA", "796"}, - {"Tuvalu", "Tuvalu (les)", "TV", "TUV", "798"}, - {"Uganda", "Ouganda (l')", "UG", "UGA", "800"}, - {"Ukraine", "Ukraine (l')", "UA", "UKR", "804"}, - {"Macedonia (the former Yugoslav Republic of)", "Macédoine (l'ex‑République yougoslave de)", "MK", "MKD", "807"}, - {"Egypt", "Égypte (l')", "EG", "EGY", "818"}, - {"United Kingdom of Great Britain and Northern Ireland (the)", "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord (le)", "GB", "GBR", "826"}, - {"Guernsey", "Guernesey", "GG", "GGY", "831"}, - {"Jersey", "Jersey", "JE", "JEY", "832"}, - {"Isle of Man", "Île de Man", "IM", "IMN", "833"}, - {"Tanzania, United Republic of", "Tanzanie, République-Unie de", "TZ", "TZA", "834"}, - {"United States of America (the)", "États-Unis d'Amérique (les)", "US", "USA", "840"}, - {"Virgin Islands (U.S.)", "Vierges des États-Unis (les Îles)", "VI", "VIR", "850"}, - {"Burkina Faso", "Burkina Faso (le)", "BF", "BFA", "854"}, - {"Uruguay", "Uruguay (l')", "UY", "URY", "858"}, - {"Uzbekistan", "Ouzbékistan (l')", "UZ", "UZB", "860"}, - {"Venezuela (Bolivarian Republic of)", "Venezuela (République bolivarienne du)", "VE", "VEN", "862"}, - {"Wallis and Futuna", "Wallis-et-Futuna", "WF", "WLF", "876"}, - {"Samoa", "Samoa (le)", "WS", "WSM", "882"}, - {"Yemen", "Yémen (le)", "YE", "YEM", "887"}, - {"Zambia", "Zambie (la)", "ZM", "ZMB", "894"}, -} - -// ISO4217List is the list of ISO currency codes -var ISO4217List = []string{ - "AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", - "BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BOV", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD", - "CAD", "CDF", "CHE", "CHF", "CHW", "CLF", "CLP", "CNY", "COP", "COU", "CRC", "CUC", "CUP", "CVE", "CZK", - "DJF", "DKK", "DOP", "DZD", - "EGP", "ERN", "ETB", "EUR", - "FJD", "FKP", - "GBP", "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD", - "HKD", "HNL", "HRK", "HTG", "HUF", - "IDR", "ILS", "INR", "IQD", "IRR", "ISK", - "JMD", "JOD", "JPY", - "KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT", - "LAK", "LBP", "LKR", "LRD", "LSL", "LYD", - "MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MUR", "MVR", "MWK", "MXN", "MXV", "MYR", "MZN", - "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", - "OMR", - "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG", - "QAR", - "RON", "RSD", "RUB", "RWF", - "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SRD", "SSP", "STD", "STN", "SVC", "SYP", "SZL", - "THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS", - "UAH", "UGX", "USD", "USN", "UYI", "UYU", "UYW", "UZS", - "VEF", "VES", "VND", "VUV", - "WST", - "XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "XSU", "XTS", "XUA", "XXX", - "YER", - "ZAR", "ZMW", "ZWL", -} - -// ISO693Entry stores ISO language codes -type ISO693Entry struct { - Alpha3bCode string - Alpha2Code string - English string -} - -//ISO693List based on http://data.okfn.org/data/core/language-codes/r/language-codes-3b2.json -var ISO693List = []ISO693Entry{ - {Alpha3bCode: "aar", Alpha2Code: "aa", English: "Afar"}, - {Alpha3bCode: "abk", Alpha2Code: "ab", English: "Abkhazian"}, - {Alpha3bCode: "afr", Alpha2Code: "af", English: "Afrikaans"}, - {Alpha3bCode: "aka", Alpha2Code: "ak", English: "Akan"}, - {Alpha3bCode: "alb", Alpha2Code: "sq", English: "Albanian"}, - {Alpha3bCode: "amh", Alpha2Code: "am", English: "Amharic"}, - {Alpha3bCode: "ara", Alpha2Code: "ar", English: "Arabic"}, - {Alpha3bCode: "arg", Alpha2Code: "an", English: "Aragonese"}, - {Alpha3bCode: "arm", Alpha2Code: "hy", English: "Armenian"}, - {Alpha3bCode: "asm", Alpha2Code: "as", English: "Assamese"}, - {Alpha3bCode: "ava", Alpha2Code: "av", English: "Avaric"}, - {Alpha3bCode: "ave", Alpha2Code: "ae", English: "Avestan"}, - {Alpha3bCode: "aym", Alpha2Code: "ay", English: "Aymara"}, - {Alpha3bCode: "aze", Alpha2Code: "az", English: "Azerbaijani"}, - {Alpha3bCode: "bak", Alpha2Code: "ba", English: "Bashkir"}, - {Alpha3bCode: "bam", Alpha2Code: "bm", English: "Bambara"}, - {Alpha3bCode: "baq", Alpha2Code: "eu", English: "Basque"}, - {Alpha3bCode: "bel", Alpha2Code: "be", English: "Belarusian"}, - {Alpha3bCode: "ben", Alpha2Code: "bn", English: "Bengali"}, - {Alpha3bCode: "bih", Alpha2Code: "bh", English: "Bihari languages"}, - {Alpha3bCode: "bis", Alpha2Code: "bi", English: "Bislama"}, - {Alpha3bCode: "bos", Alpha2Code: "bs", English: "Bosnian"}, - {Alpha3bCode: "bre", Alpha2Code: "br", English: "Breton"}, - {Alpha3bCode: "bul", Alpha2Code: "bg", English: "Bulgarian"}, - {Alpha3bCode: "bur", Alpha2Code: "my", English: "Burmese"}, - {Alpha3bCode: "cat", Alpha2Code: "ca", English: "Catalan; Valencian"}, - {Alpha3bCode: "cha", Alpha2Code: "ch", English: "Chamorro"}, - {Alpha3bCode: "che", Alpha2Code: "ce", English: "Chechen"}, - {Alpha3bCode: "chi", Alpha2Code: "zh", English: "Chinese"}, - {Alpha3bCode: "chu", Alpha2Code: "cu", English: "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic"}, - {Alpha3bCode: "chv", Alpha2Code: "cv", English: "Chuvash"}, - {Alpha3bCode: "cor", Alpha2Code: "kw", English: "Cornish"}, - {Alpha3bCode: "cos", Alpha2Code: "co", English: "Corsican"}, - {Alpha3bCode: "cre", Alpha2Code: "cr", English: "Cree"}, - {Alpha3bCode: "cze", Alpha2Code: "cs", English: "Czech"}, - {Alpha3bCode: "dan", Alpha2Code: "da", English: "Danish"}, - {Alpha3bCode: "div", Alpha2Code: "dv", English: "Divehi; Dhivehi; Maldivian"}, - {Alpha3bCode: "dut", Alpha2Code: "nl", English: "Dutch; Flemish"}, - {Alpha3bCode: "dzo", Alpha2Code: "dz", English: "Dzongkha"}, - {Alpha3bCode: "eng", Alpha2Code: "en", English: "English"}, - {Alpha3bCode: "epo", Alpha2Code: "eo", English: "Esperanto"}, - {Alpha3bCode: "est", Alpha2Code: "et", English: "Estonian"}, - {Alpha3bCode: "ewe", Alpha2Code: "ee", English: "Ewe"}, - {Alpha3bCode: "fao", Alpha2Code: "fo", English: "Faroese"}, - {Alpha3bCode: "fij", Alpha2Code: "fj", English: "Fijian"}, - {Alpha3bCode: "fin", Alpha2Code: "fi", English: "Finnish"}, - {Alpha3bCode: "fre", Alpha2Code: "fr", English: "French"}, - {Alpha3bCode: "fry", Alpha2Code: "fy", English: "Western Frisian"}, - {Alpha3bCode: "ful", Alpha2Code: "ff", English: "Fulah"}, - {Alpha3bCode: "geo", Alpha2Code: "ka", English: "Georgian"}, - {Alpha3bCode: "ger", Alpha2Code: "de", English: "German"}, - {Alpha3bCode: "gla", Alpha2Code: "gd", English: "Gaelic; Scottish Gaelic"}, - {Alpha3bCode: "gle", Alpha2Code: "ga", English: "Irish"}, - {Alpha3bCode: "glg", Alpha2Code: "gl", English: "Galician"}, - {Alpha3bCode: "glv", Alpha2Code: "gv", English: "Manx"}, - {Alpha3bCode: "gre", Alpha2Code: "el", English: "Greek, Modern (1453-)"}, - {Alpha3bCode: "grn", Alpha2Code: "gn", English: "Guarani"}, - {Alpha3bCode: "guj", Alpha2Code: "gu", English: "Gujarati"}, - {Alpha3bCode: "hat", Alpha2Code: "ht", English: "Haitian; Haitian Creole"}, - {Alpha3bCode: "hau", Alpha2Code: "ha", English: "Hausa"}, - {Alpha3bCode: "heb", Alpha2Code: "he", English: "Hebrew"}, - {Alpha3bCode: "her", Alpha2Code: "hz", English: "Herero"}, - {Alpha3bCode: "hin", Alpha2Code: "hi", English: "Hindi"}, - {Alpha3bCode: "hmo", Alpha2Code: "ho", English: "Hiri Motu"}, - {Alpha3bCode: "hrv", Alpha2Code: "hr", English: "Croatian"}, - {Alpha3bCode: "hun", Alpha2Code: "hu", English: "Hungarian"}, - {Alpha3bCode: "ibo", Alpha2Code: "ig", English: "Igbo"}, - {Alpha3bCode: "ice", Alpha2Code: "is", English: "Icelandic"}, - {Alpha3bCode: "ido", Alpha2Code: "io", English: "Ido"}, - {Alpha3bCode: "iii", Alpha2Code: "ii", English: "Sichuan Yi; Nuosu"}, - {Alpha3bCode: "iku", Alpha2Code: "iu", English: "Inuktitut"}, - {Alpha3bCode: "ile", Alpha2Code: "ie", English: "Interlingue; Occidental"}, - {Alpha3bCode: "ina", Alpha2Code: "ia", English: "Interlingua (International Auxiliary Language Association)"}, - {Alpha3bCode: "ind", Alpha2Code: "id", English: "Indonesian"}, - {Alpha3bCode: "ipk", Alpha2Code: "ik", English: "Inupiaq"}, - {Alpha3bCode: "ita", Alpha2Code: "it", English: "Italian"}, - {Alpha3bCode: "jav", Alpha2Code: "jv", English: "Javanese"}, - {Alpha3bCode: "jpn", Alpha2Code: "ja", English: "Japanese"}, - {Alpha3bCode: "kal", Alpha2Code: "kl", English: "Kalaallisut; Greenlandic"}, - {Alpha3bCode: "kan", Alpha2Code: "kn", English: "Kannada"}, - {Alpha3bCode: "kas", Alpha2Code: "ks", English: "Kashmiri"}, - {Alpha3bCode: "kau", Alpha2Code: "kr", English: "Kanuri"}, - {Alpha3bCode: "kaz", Alpha2Code: "kk", English: "Kazakh"}, - {Alpha3bCode: "khm", Alpha2Code: "km", English: "Central Khmer"}, - {Alpha3bCode: "kik", Alpha2Code: "ki", English: "Kikuyu; Gikuyu"}, - {Alpha3bCode: "kin", Alpha2Code: "rw", English: "Kinyarwanda"}, - {Alpha3bCode: "kir", Alpha2Code: "ky", English: "Kirghiz; Kyrgyz"}, - {Alpha3bCode: "kom", Alpha2Code: "kv", English: "Komi"}, - {Alpha3bCode: "kon", Alpha2Code: "kg", English: "Kongo"}, - {Alpha3bCode: "kor", Alpha2Code: "ko", English: "Korean"}, - {Alpha3bCode: "kua", Alpha2Code: "kj", English: "Kuanyama; Kwanyama"}, - {Alpha3bCode: "kur", Alpha2Code: "ku", English: "Kurdish"}, - {Alpha3bCode: "lao", Alpha2Code: "lo", English: "Lao"}, - {Alpha3bCode: "lat", Alpha2Code: "la", English: "Latin"}, - {Alpha3bCode: "lav", Alpha2Code: "lv", English: "Latvian"}, - {Alpha3bCode: "lim", Alpha2Code: "li", English: "Limburgan; Limburger; Limburgish"}, - {Alpha3bCode: "lin", Alpha2Code: "ln", English: "Lingala"}, - {Alpha3bCode: "lit", Alpha2Code: "lt", English: "Lithuanian"}, - {Alpha3bCode: "ltz", Alpha2Code: "lb", English: "Luxembourgish; Letzeburgesch"}, - {Alpha3bCode: "lub", Alpha2Code: "lu", English: "Luba-Katanga"}, - {Alpha3bCode: "lug", Alpha2Code: "lg", English: "Ganda"}, - {Alpha3bCode: "mac", Alpha2Code: "mk", English: "Macedonian"}, - {Alpha3bCode: "mah", Alpha2Code: "mh", English: "Marshallese"}, - {Alpha3bCode: "mal", Alpha2Code: "ml", English: "Malayalam"}, - {Alpha3bCode: "mao", Alpha2Code: "mi", English: "Maori"}, - {Alpha3bCode: "mar", Alpha2Code: "mr", English: "Marathi"}, - {Alpha3bCode: "may", Alpha2Code: "ms", English: "Malay"}, - {Alpha3bCode: "mlg", Alpha2Code: "mg", English: "Malagasy"}, - {Alpha3bCode: "mlt", Alpha2Code: "mt", English: "Maltese"}, - {Alpha3bCode: "mon", Alpha2Code: "mn", English: "Mongolian"}, - {Alpha3bCode: "nau", Alpha2Code: "na", English: "Nauru"}, - {Alpha3bCode: "nav", Alpha2Code: "nv", English: "Navajo; Navaho"}, - {Alpha3bCode: "nbl", Alpha2Code: "nr", English: "Ndebele, South; South Ndebele"}, - {Alpha3bCode: "nde", Alpha2Code: "nd", English: "Ndebele, North; North Ndebele"}, - {Alpha3bCode: "ndo", Alpha2Code: "ng", English: "Ndonga"}, - {Alpha3bCode: "nep", Alpha2Code: "ne", English: "Nepali"}, - {Alpha3bCode: "nno", Alpha2Code: "nn", English: "Norwegian Nynorsk; Nynorsk, Norwegian"}, - {Alpha3bCode: "nob", Alpha2Code: "nb", English: "Bokmål, Norwegian; Norwegian Bokmål"}, - {Alpha3bCode: "nor", Alpha2Code: "no", English: "Norwegian"}, - {Alpha3bCode: "nya", Alpha2Code: "ny", English: "Chichewa; Chewa; Nyanja"}, - {Alpha3bCode: "oci", Alpha2Code: "oc", English: "Occitan (post 1500); Provençal"}, - {Alpha3bCode: "oji", Alpha2Code: "oj", English: "Ojibwa"}, - {Alpha3bCode: "ori", Alpha2Code: "or", English: "Oriya"}, - {Alpha3bCode: "orm", Alpha2Code: "om", English: "Oromo"}, - {Alpha3bCode: "oss", Alpha2Code: "os", English: "Ossetian; Ossetic"}, - {Alpha3bCode: "pan", Alpha2Code: "pa", English: "Panjabi; Punjabi"}, - {Alpha3bCode: "per", Alpha2Code: "fa", English: "Persian"}, - {Alpha3bCode: "pli", Alpha2Code: "pi", English: "Pali"}, - {Alpha3bCode: "pol", Alpha2Code: "pl", English: "Polish"}, - {Alpha3bCode: "por", Alpha2Code: "pt", English: "Portuguese"}, - {Alpha3bCode: "pus", Alpha2Code: "ps", English: "Pushto; Pashto"}, - {Alpha3bCode: "que", Alpha2Code: "qu", English: "Quechua"}, - {Alpha3bCode: "roh", Alpha2Code: "rm", English: "Romansh"}, - {Alpha3bCode: "rum", Alpha2Code: "ro", English: "Romanian; Moldavian; Moldovan"}, - {Alpha3bCode: "run", Alpha2Code: "rn", English: "Rundi"}, - {Alpha3bCode: "rus", Alpha2Code: "ru", English: "Russian"}, - {Alpha3bCode: "sag", Alpha2Code: "sg", English: "Sango"}, - {Alpha3bCode: "san", Alpha2Code: "sa", English: "Sanskrit"}, - {Alpha3bCode: "sin", Alpha2Code: "si", English: "Sinhala; Sinhalese"}, - {Alpha3bCode: "slo", Alpha2Code: "sk", English: "Slovak"}, - {Alpha3bCode: "slv", Alpha2Code: "sl", English: "Slovenian"}, - {Alpha3bCode: "sme", Alpha2Code: "se", English: "Northern Sami"}, - {Alpha3bCode: "smo", Alpha2Code: "sm", English: "Samoan"}, - {Alpha3bCode: "sna", Alpha2Code: "sn", English: "Shona"}, - {Alpha3bCode: "snd", Alpha2Code: "sd", English: "Sindhi"}, - {Alpha3bCode: "som", Alpha2Code: "so", English: "Somali"}, - {Alpha3bCode: "sot", Alpha2Code: "st", English: "Sotho, Southern"}, - {Alpha3bCode: "spa", Alpha2Code: "es", English: "Spanish; Castilian"}, - {Alpha3bCode: "srd", Alpha2Code: "sc", English: "Sardinian"}, - {Alpha3bCode: "srp", Alpha2Code: "sr", English: "Serbian"}, - {Alpha3bCode: "ssw", Alpha2Code: "ss", English: "Swati"}, - {Alpha3bCode: "sun", Alpha2Code: "su", English: "Sundanese"}, - {Alpha3bCode: "swa", Alpha2Code: "sw", English: "Swahili"}, - {Alpha3bCode: "swe", Alpha2Code: "sv", English: "Swedish"}, - {Alpha3bCode: "tah", Alpha2Code: "ty", English: "Tahitian"}, - {Alpha3bCode: "tam", Alpha2Code: "ta", English: "Tamil"}, - {Alpha3bCode: "tat", Alpha2Code: "tt", English: "Tatar"}, - {Alpha3bCode: "tel", Alpha2Code: "te", English: "Telugu"}, - {Alpha3bCode: "tgk", Alpha2Code: "tg", English: "Tajik"}, - {Alpha3bCode: "tgl", Alpha2Code: "tl", English: "Tagalog"}, - {Alpha3bCode: "tha", Alpha2Code: "th", English: "Thai"}, - {Alpha3bCode: "tib", Alpha2Code: "bo", English: "Tibetan"}, - {Alpha3bCode: "tir", Alpha2Code: "ti", English: "Tigrinya"}, - {Alpha3bCode: "ton", Alpha2Code: "to", English: "Tonga (Tonga Islands)"}, - {Alpha3bCode: "tsn", Alpha2Code: "tn", English: "Tswana"}, - {Alpha3bCode: "tso", Alpha2Code: "ts", English: "Tsonga"}, - {Alpha3bCode: "tuk", Alpha2Code: "tk", English: "Turkmen"}, - {Alpha3bCode: "tur", Alpha2Code: "tr", English: "Turkish"}, - {Alpha3bCode: "twi", Alpha2Code: "tw", English: "Twi"}, - {Alpha3bCode: "uig", Alpha2Code: "ug", English: "Uighur; Uyghur"}, - {Alpha3bCode: "ukr", Alpha2Code: "uk", English: "Ukrainian"}, - {Alpha3bCode: "urd", Alpha2Code: "ur", English: "Urdu"}, - {Alpha3bCode: "uzb", Alpha2Code: "uz", English: "Uzbek"}, - {Alpha3bCode: "ven", Alpha2Code: "ve", English: "Venda"}, - {Alpha3bCode: "vie", Alpha2Code: "vi", English: "Vietnamese"}, - {Alpha3bCode: "vol", Alpha2Code: "vo", English: "Volapük"}, - {Alpha3bCode: "wel", Alpha2Code: "cy", English: "Welsh"}, - {Alpha3bCode: "wln", Alpha2Code: "wa", English: "Walloon"}, - {Alpha3bCode: "wol", Alpha2Code: "wo", English: "Wolof"}, - {Alpha3bCode: "xho", Alpha2Code: "xh", English: "Xhosa"}, - {Alpha3bCode: "yid", Alpha2Code: "yi", English: "Yiddish"}, - {Alpha3bCode: "yor", Alpha2Code: "yo", English: "Yoruba"}, - {Alpha3bCode: "zha", Alpha2Code: "za", English: "Zhuang; Chuang"}, - {Alpha3bCode: "zul", Alpha2Code: "zu", English: "Zulu"}, -} diff --git a/vendor/github.com/asaskevich/govalidator/utils.go b/vendor/github.com/asaskevich/govalidator/utils.go deleted file mode 100644 index f4c30f82..00000000 --- a/vendor/github.com/asaskevich/govalidator/utils.go +++ /dev/null @@ -1,270 +0,0 @@ -package govalidator - -import ( - "errors" - "fmt" - "html" - "math" - "path" - "regexp" - "strings" - "unicode" - "unicode/utf8" -) - -// Contains checks if the string contains the substring. -func Contains(str, substring string) bool { - return strings.Contains(str, substring) -} - -// Matches checks if string matches the pattern (pattern is regular expression) -// In case of error return false -func Matches(str, pattern string) bool { - match, _ := regexp.MatchString(pattern, str) - return match -} - -// LeftTrim trims characters from the left side of the input. -// If second argument is empty, it will remove leading spaces. -func LeftTrim(str, chars string) string { - if chars == "" { - return strings.TrimLeftFunc(str, unicode.IsSpace) - } - r, _ := regexp.Compile("^[" + chars + "]+") - return r.ReplaceAllString(str, "") -} - -// RightTrim trims characters from the right side of the input. -// If second argument is empty, it will remove trailing spaces. -func RightTrim(str, chars string) string { - if chars == "" { - return strings.TrimRightFunc(str, unicode.IsSpace) - } - r, _ := regexp.Compile("[" + chars + "]+$") - return r.ReplaceAllString(str, "") -} - -// Trim trims characters from both sides of the input. -// If second argument is empty, it will remove spaces. -func Trim(str, chars string) string { - return LeftTrim(RightTrim(str, chars), chars) -} - -// WhiteList removes characters that do not appear in the whitelist. -func WhiteList(str, chars string) string { - pattern := "[^" + chars + "]+" - r, _ := regexp.Compile(pattern) - return r.ReplaceAllString(str, "") -} - -// BlackList removes characters that appear in the blacklist. -func BlackList(str, chars string) string { - pattern := "[" + chars + "]+" - r, _ := regexp.Compile(pattern) - return r.ReplaceAllString(str, "") -} - -// StripLow removes characters with a numerical value < 32 and 127, mostly control characters. -// If keep_new_lines is true, newline characters are preserved (\n and \r, hex 0xA and 0xD). -func StripLow(str string, keepNewLines bool) string { - chars := "" - if keepNewLines { - chars = "\x00-\x09\x0B\x0C\x0E-\x1F\x7F" - } else { - chars = "\x00-\x1F\x7F" - } - return BlackList(str, chars) -} - -// ReplacePattern replaces regular expression pattern in string -func ReplacePattern(str, pattern, replace string) string { - r, _ := regexp.Compile(pattern) - return r.ReplaceAllString(str, replace) -} - -// Escape replaces <, >, & and " with HTML entities. -var Escape = html.EscapeString - -func addSegment(inrune, segment []rune) []rune { - if len(segment) == 0 { - return inrune - } - if len(inrune) != 0 { - inrune = append(inrune, '_') - } - inrune = append(inrune, segment...) - return inrune -} - -// UnderscoreToCamelCase converts from underscore separated form to camel case form. -// Ex.: my_func => MyFunc -func UnderscoreToCamelCase(s string) string { - return strings.Replace(strings.Title(strings.Replace(strings.ToLower(s), "_", " ", -1)), " ", "", -1) -} - -// CamelCaseToUnderscore converts from camel case form to underscore separated form. -// Ex.: MyFunc => my_func -func CamelCaseToUnderscore(str string) string { - var output []rune - var segment []rune - for _, r := range str { - - // not treat number as separate segment - if !unicode.IsLower(r) && string(r) != "_" && !unicode.IsNumber(r) { - output = addSegment(output, segment) - segment = nil - } - segment = append(segment, unicode.ToLower(r)) - } - output = addSegment(output, segment) - return string(output) -} - -// Reverse returns reversed string -func Reverse(s string) string { - r := []rune(s) - for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 { - r[i], r[j] = r[j], r[i] - } - return string(r) -} - -// GetLines splits string by "\n" and return array of lines -func GetLines(s string) []string { - return strings.Split(s, "\n") -} - -// GetLine returns specified line of multiline string -func GetLine(s string, index int) (string, error) { - lines := GetLines(s) - if index < 0 || index >= len(lines) { - return "", errors.New("line index out of bounds") - } - return lines[index], nil -} - -// RemoveTags removes all tags from HTML string -func RemoveTags(s string) string { - return ReplacePattern(s, "<[^>]*>", "") -} - -// SafeFileName returns safe string that can be used in file names -func SafeFileName(str string) string { - name := strings.ToLower(str) - name = path.Clean(path.Base(name)) - name = strings.Trim(name, " ") - separators, err := regexp.Compile(`[ &_=+:]`) - if err == nil { - name = separators.ReplaceAllString(name, "-") - } - legal, err := regexp.Compile(`[^[:alnum:]-.]`) - if err == nil { - name = legal.ReplaceAllString(name, "") - } - for strings.Contains(name, "--") { - name = strings.Replace(name, "--", "-", -1) - } - return name -} - -// NormalizeEmail canonicalize an email address. -// The local part of the email address is lowercased for all domains; the hostname is always lowercased and -// the local part of the email address is always lowercased for hosts that are known to be case-insensitive (currently only GMail). -// Normalization follows special rules for known providers: currently, GMail addresses have dots removed in the local part and -// are stripped of tags (e.g. some.one+tag@gmail.com becomes someone@gmail.com) and all @googlemail.com addresses are -// normalized to @gmail.com. -func NormalizeEmail(str string) (string, error) { - if !IsEmail(str) { - return "", fmt.Errorf("%s is not an email", str) - } - parts := strings.Split(str, "@") - parts[0] = strings.ToLower(parts[0]) - parts[1] = strings.ToLower(parts[1]) - if parts[1] == "gmail.com" || parts[1] == "googlemail.com" { - parts[1] = "gmail.com" - parts[0] = strings.Split(ReplacePattern(parts[0], `\.`, ""), "+")[0] - } - return strings.Join(parts, "@"), nil -} - -// Truncate a string to the closest length without breaking words. -func Truncate(str string, length int, ending string) string { - var aftstr, befstr string - if len(str) > length { - words := strings.Fields(str) - before, present := 0, 0 - for i := range words { - befstr = aftstr - before = present - aftstr = aftstr + words[i] + " " - present = len(aftstr) - if present > length && i != 0 { - if (length - before) < (present - length) { - return Trim(befstr, " /\\.,\"'#!?&@+-") + ending - } - return Trim(aftstr, " /\\.,\"'#!?&@+-") + ending - } - } - } - - return str -} - -// PadLeft pads left side of a string if size of string is less then indicated pad length -func PadLeft(str string, padStr string, padLen int) string { - return buildPadStr(str, padStr, padLen, true, false) -} - -// PadRight pads right side of a string if size of string is less then indicated pad length -func PadRight(str string, padStr string, padLen int) string { - return buildPadStr(str, padStr, padLen, false, true) -} - -// PadBoth pads both sides of a string if size of string is less then indicated pad length -func PadBoth(str string, padStr string, padLen int) string { - return buildPadStr(str, padStr, padLen, true, true) -} - -// PadString either left, right or both sides. -// Note that padding string can be unicode and more then one character -func buildPadStr(str string, padStr string, padLen int, padLeft bool, padRight bool) string { - - // When padded length is less then the current string size - if padLen < utf8.RuneCountInString(str) { - return str - } - - padLen -= utf8.RuneCountInString(str) - - targetLen := padLen - - targetLenLeft := targetLen - targetLenRight := targetLen - if padLeft && padRight { - targetLenLeft = padLen / 2 - targetLenRight = padLen - targetLenLeft - } - - strToRepeatLen := utf8.RuneCountInString(padStr) - - repeatTimes := int(math.Ceil(float64(targetLen) / float64(strToRepeatLen))) - repeatedString := strings.Repeat(padStr, repeatTimes) - - leftSide := "" - if padLeft { - leftSide = repeatedString[0:targetLenLeft] - } - - rightSide := "" - if padRight { - rightSide = repeatedString[0:targetLenRight] - } - - return leftSide + str + rightSide -} - -// TruncatingErrorf removes extra args from fmt.Errorf if not formatted in the str object -func TruncatingErrorf(str string, args ...interface{}) error { - n := strings.Count(str, "%s") - return fmt.Errorf(str, args[:n]...) -} diff --git a/vendor/github.com/asaskevich/govalidator/validator.go b/vendor/github.com/asaskevich/govalidator/validator.go deleted file mode 100644 index 46ecfc84..00000000 --- a/vendor/github.com/asaskevich/govalidator/validator.go +++ /dev/null @@ -1,1769 +0,0 @@ -// Package govalidator is package of validators and sanitizers for strings, structs and collections. -package govalidator - -import ( - "bytes" - "crypto/rsa" - "crypto/x509" - "encoding/base64" - "encoding/json" - "encoding/pem" - "fmt" - "io/ioutil" - "net" - "net/url" - "reflect" - "regexp" - "sort" - "strconv" - "strings" - "time" - "unicode" - "unicode/utf8" -) - -var ( - fieldsRequiredByDefault bool - nilPtrAllowedByRequired = false - notNumberRegexp = regexp.MustCompile("[^0-9]+") - whiteSpacesAndMinus = regexp.MustCompile(`[\s-]+`) - paramsRegexp = regexp.MustCompile(`\(.*\)$`) -) - -const maxURLRuneCount = 2083 -const minURLRuneCount = 3 -const rfc3339WithoutZone = "2006-01-02T15:04:05" - -// SetFieldsRequiredByDefault causes validation to fail when struct fields -// do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`). -// This struct definition will fail govalidator.ValidateStruct() (and the field values do not matter): -// type exampleStruct struct { -// Name string `` -// Email string `valid:"email"` -// This, however, will only fail when Email is empty or an invalid email address: -// type exampleStruct2 struct { -// Name string `valid:"-"` -// Email string `valid:"email"` -// Lastly, this will only fail when Email is an invalid email address but not when it's empty: -// type exampleStruct2 struct { -// Name string `valid:"-"` -// Email string `valid:"email,optional"` -func SetFieldsRequiredByDefault(value bool) { - fieldsRequiredByDefault = value -} - -// SetNilPtrAllowedByRequired causes validation to pass for nil ptrs when a field is set to required. -// The validation will still reject ptr fields in their zero value state. Example with this enabled: -// type exampleStruct struct { -// Name *string `valid:"required"` -// With `Name` set to "", this will be considered invalid input and will cause a validation error. -// With `Name` set to nil, this will be considered valid by validation. -// By default this is disabled. -func SetNilPtrAllowedByRequired(value bool) { - nilPtrAllowedByRequired = value -} - -// IsEmail checks if the string is an email. -func IsEmail(str string) bool { - // TODO uppercase letters are not supported - return rxEmail.MatchString(str) -} - -// IsExistingEmail checks if the string is an email of existing domain -func IsExistingEmail(email string) bool { - - if len(email) < 6 || len(email) > 254 { - return false - } - at := strings.LastIndex(email, "@") - if at <= 0 || at > len(email)-3 { - return false - } - user := email[:at] - host := email[at+1:] - if len(user) > 64 { - return false - } - switch host { - case "localhost", "example.com": - return true - } - if userDotRegexp.MatchString(user) || !userRegexp.MatchString(user) || !hostRegexp.MatchString(host) { - return false - } - if _, err := net.LookupMX(host); err != nil { - if _, err := net.LookupIP(host); err != nil { - return false - } - } - - return true -} - -// IsURL checks if the string is an URL. -func IsURL(str string) bool { - if str == "" || utf8.RuneCountInString(str) >= maxURLRuneCount || len(str) <= minURLRuneCount || strings.HasPrefix(str, ".") { - return false - } - strTemp := str - if strings.Contains(str, ":") && !strings.Contains(str, "://") { - // support no indicated urlscheme but with colon for port number - // http:// is appended so url.Parse will succeed, strTemp used so it does not impact rxURL.MatchString - strTemp = "http://" + str - } - u, err := url.Parse(strTemp) - if err != nil { - return false - } - if strings.HasPrefix(u.Host, ".") { - return false - } - if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) { - return false - } - return rxURL.MatchString(str) -} - -// IsRequestURL checks if the string rawurl, assuming -// it was received in an HTTP request, is a valid -// URL confirm to RFC 3986 -func IsRequestURL(rawurl string) bool { - url, err := url.ParseRequestURI(rawurl) - if err != nil { - return false //Couldn't even parse the rawurl - } - if len(url.Scheme) == 0 { - return false //No Scheme found - } - return true -} - -// IsRequestURI checks if the string rawurl, assuming -// it was received in an HTTP request, is an -// absolute URI or an absolute path. -func IsRequestURI(rawurl string) bool { - _, err := url.ParseRequestURI(rawurl) - return err == nil -} - -// IsAlpha checks if the string contains only letters (a-zA-Z). Empty string is valid. -func IsAlpha(str string) bool { - if IsNull(str) { - return true - } - return rxAlpha.MatchString(str) -} - -//IsUTFLetter checks if the string contains only unicode letter characters. -//Similar to IsAlpha but for all languages. Empty string is valid. -func IsUTFLetter(str string) bool { - if IsNull(str) { - return true - } - - for _, c := range str { - if !unicode.IsLetter(c) { - return false - } - } - return true - -} - -// IsAlphanumeric checks if the string contains only letters and numbers. Empty string is valid. -func IsAlphanumeric(str string) bool { - if IsNull(str) { - return true - } - return rxAlphanumeric.MatchString(str) -} - -// IsUTFLetterNumeric checks if the string contains only unicode letters and numbers. Empty string is valid. -func IsUTFLetterNumeric(str string) bool { - if IsNull(str) { - return true - } - for _, c := range str { - if !unicode.IsLetter(c) && !unicode.IsNumber(c) { //letters && numbers are ok - return false - } - } - return true - -} - -// IsNumeric checks if the string contains only numbers. Empty string is valid. -func IsNumeric(str string) bool { - if IsNull(str) { - return true - } - return rxNumeric.MatchString(str) -} - -// IsUTFNumeric checks if the string contains only unicode numbers of any kind. -// Numbers can be 0-9 but also Fractions ¾,Roman Ⅸ and Hangzhou 〩. Empty string is valid. -func IsUTFNumeric(str string) bool { - if IsNull(str) { - return true - } - if strings.IndexAny(str, "+-") > 0 { - return false - } - if len(str) > 1 { - str = strings.TrimPrefix(str, "-") - str = strings.TrimPrefix(str, "+") - } - for _, c := range str { - if !unicode.IsNumber(c) { //numbers && minus sign are ok - return false - } - } - return true - -} - -// IsUTFDigit checks if the string contains only unicode radix-10 decimal digits. Empty string is valid. -func IsUTFDigit(str string) bool { - if IsNull(str) { - return true - } - if strings.IndexAny(str, "+-") > 0 { - return false - } - if len(str) > 1 { - str = strings.TrimPrefix(str, "-") - str = strings.TrimPrefix(str, "+") - } - for _, c := range str { - if !unicode.IsDigit(c) { //digits && minus sign are ok - return false - } - } - return true - -} - -// IsHexadecimal checks if the string is a hexadecimal number. -func IsHexadecimal(str string) bool { - return rxHexadecimal.MatchString(str) -} - -// IsHexcolor checks if the string is a hexadecimal color. -func IsHexcolor(str string) bool { - return rxHexcolor.MatchString(str) -} - -// IsRGBcolor checks if the string is a valid RGB color in form rgb(RRR, GGG, BBB). -func IsRGBcolor(str string) bool { - return rxRGBcolor.MatchString(str) -} - -// IsLowerCase checks if the string is lowercase. Empty string is valid. -func IsLowerCase(str string) bool { - if IsNull(str) { - return true - } - return str == strings.ToLower(str) -} - -// IsUpperCase checks if the string is uppercase. Empty string is valid. -func IsUpperCase(str string) bool { - if IsNull(str) { - return true - } - return str == strings.ToUpper(str) -} - -// HasLowerCase checks if the string contains at least 1 lowercase. Empty string is valid. -func HasLowerCase(str string) bool { - if IsNull(str) { - return true - } - return rxHasLowerCase.MatchString(str) -} - -// HasUpperCase checks if the string contains as least 1 uppercase. Empty string is valid. -func HasUpperCase(str string) bool { - if IsNull(str) { - return true - } - return rxHasUpperCase.MatchString(str) -} - -// IsInt checks if the string is an integer. Empty string is valid. -func IsInt(str string) bool { - if IsNull(str) { - return true - } - return rxInt.MatchString(str) -} - -// IsFloat checks if the string is a float. -func IsFloat(str string) bool { - return str != "" && rxFloat.MatchString(str) -} - -// IsDivisibleBy checks if the string is a number that's divisible by another. -// If second argument is not valid integer or zero, it's return false. -// Otherwise, if first argument is not valid integer or zero, it's return true (Invalid string converts to zero). -func IsDivisibleBy(str, num string) bool { - f, _ := ToFloat(str) - p := int64(f) - q, _ := ToInt(num) - if q == 0 { - return false - } - return (p == 0) || (p%q == 0) -} - -// IsNull checks if the string is null. -func IsNull(str string) bool { - return len(str) == 0 -} - -// IsNotNull checks if the string is not null. -func IsNotNull(str string) bool { - return !IsNull(str) -} - -// HasWhitespaceOnly checks the string only contains whitespace -func HasWhitespaceOnly(str string) bool { - return len(str) > 0 && rxHasWhitespaceOnly.MatchString(str) -} - -// HasWhitespace checks if the string contains any whitespace -func HasWhitespace(str string) bool { - return len(str) > 0 && rxHasWhitespace.MatchString(str) -} - -// IsByteLength checks if the string's length (in bytes) falls in a range. -func IsByteLength(str string, min, max int) bool { - return len(str) >= min && len(str) <= max -} - -// IsUUIDv3 checks if the string is a UUID version 3. -func IsUUIDv3(str string) bool { - return rxUUID3.MatchString(str) -} - -// IsUUIDv4 checks if the string is a UUID version 4. -func IsUUIDv4(str string) bool { - return rxUUID4.MatchString(str) -} - -// IsUUIDv5 checks if the string is a UUID version 5. -func IsUUIDv5(str string) bool { - return rxUUID5.MatchString(str) -} - -// IsUUID checks if the string is a UUID (version 3, 4 or 5). -func IsUUID(str string) bool { - return rxUUID.MatchString(str) -} - -// Byte to index table for O(1) lookups when unmarshaling. -// We use 0xFF as sentinel value for invalid indexes. -var ulidDec = [...]byte{ - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, - 0x0F, 0x10, 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, 0x15, 0xFF, - 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, 0x1D, 0x1E, - 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, - 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, - 0x15, 0xFF, 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, - 0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -} - -// EncodedSize is the length of a text encoded ULID. -const ulidEncodedSize = 26 - -// IsULID checks if the string is a ULID. -// -// Implementation got from: -// https://github.com/oklog/ulid (Apache-2.0 License) -// -func IsULID(str string) bool { - // Check if a base32 encoded ULID is the right length. - if len(str) != ulidEncodedSize { - return false - } - - // Check if all the characters in a base32 encoded ULID are part of the - // expected base32 character set. - if ulidDec[str[0]] == 0xFF || - ulidDec[str[1]] == 0xFF || - ulidDec[str[2]] == 0xFF || - ulidDec[str[3]] == 0xFF || - ulidDec[str[4]] == 0xFF || - ulidDec[str[5]] == 0xFF || - ulidDec[str[6]] == 0xFF || - ulidDec[str[7]] == 0xFF || - ulidDec[str[8]] == 0xFF || - ulidDec[str[9]] == 0xFF || - ulidDec[str[10]] == 0xFF || - ulidDec[str[11]] == 0xFF || - ulidDec[str[12]] == 0xFF || - ulidDec[str[13]] == 0xFF || - ulidDec[str[14]] == 0xFF || - ulidDec[str[15]] == 0xFF || - ulidDec[str[16]] == 0xFF || - ulidDec[str[17]] == 0xFF || - ulidDec[str[18]] == 0xFF || - ulidDec[str[19]] == 0xFF || - ulidDec[str[20]] == 0xFF || - ulidDec[str[21]] == 0xFF || - ulidDec[str[22]] == 0xFF || - ulidDec[str[23]] == 0xFF || - ulidDec[str[24]] == 0xFF || - ulidDec[str[25]] == 0xFF { - return false - } - - // Check if the first character in a base32 encoded ULID will overflow. This - // happens because the base32 representation encodes 130 bits, while the - // ULID is only 128 bits. - // - // See https://github.com/oklog/ulid/issues/9 for details. - if str[0] > '7' { - return false - } - return true -} - -// IsCreditCard checks if the string is a credit card. -func IsCreditCard(str string) bool { - sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "") - if !rxCreditCard.MatchString(sanitized) { - return false - } - var sum int64 - var digit string - var tmpNum int64 - var shouldDouble bool - for i := len(sanitized) - 1; i >= 0; i-- { - digit = sanitized[i:(i + 1)] - tmpNum, _ = ToInt(digit) - if shouldDouble { - tmpNum *= 2 - if tmpNum >= 10 { - sum += (tmpNum % 10) + 1 - } else { - sum += tmpNum - } - } else { - sum += tmpNum - } - shouldDouble = !shouldDouble - } - - return sum%10 == 0 -} - -// IsISBN10 checks if the string is an ISBN version 10. -func IsISBN10(str string) bool { - return IsISBN(str, 10) -} - -// IsISBN13 checks if the string is an ISBN version 13. -func IsISBN13(str string) bool { - return IsISBN(str, 13) -} - -// IsISBN checks if the string is an ISBN (version 10 or 13). -// If version value is not equal to 10 or 13, it will be checks both variants. -func IsISBN(str string, version int) bool { - sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "") - var checksum int32 - var i int32 - if version == 10 { - if !rxISBN10.MatchString(sanitized) { - return false - } - for i = 0; i < 9; i++ { - checksum += (i + 1) * int32(sanitized[i]-'0') - } - if sanitized[9] == 'X' { - checksum += 10 * 10 - } else { - checksum += 10 * int32(sanitized[9]-'0') - } - if checksum%11 == 0 { - return true - } - return false - } else if version == 13 { - if !rxISBN13.MatchString(sanitized) { - return false - } - factor := []int32{1, 3} - for i = 0; i < 12; i++ { - checksum += factor[i%2] * int32(sanitized[i]-'0') - } - return (int32(sanitized[12]-'0'))-((10-(checksum%10))%10) == 0 - } - return IsISBN(str, 10) || IsISBN(str, 13) -} - -// IsJSON checks if the string is valid JSON (note: uses json.Unmarshal). -func IsJSON(str string) bool { - var js json.RawMessage - return json.Unmarshal([]byte(str), &js) == nil -} - -// IsMultibyte checks if the string contains one or more multibyte chars. Empty string is valid. -func IsMultibyte(str string) bool { - if IsNull(str) { - return true - } - return rxMultibyte.MatchString(str) -} - -// IsASCII checks if the string contains ASCII chars only. Empty string is valid. -func IsASCII(str string) bool { - if IsNull(str) { - return true - } - return rxASCII.MatchString(str) -} - -// IsPrintableASCII checks if the string contains printable ASCII chars only. Empty string is valid. -func IsPrintableASCII(str string) bool { - if IsNull(str) { - return true - } - return rxPrintableASCII.MatchString(str) -} - -// IsFullWidth checks if the string contains any full-width chars. Empty string is valid. -func IsFullWidth(str string) bool { - if IsNull(str) { - return true - } - return rxFullWidth.MatchString(str) -} - -// IsHalfWidth checks if the string contains any half-width chars. Empty string is valid. -func IsHalfWidth(str string) bool { - if IsNull(str) { - return true - } - return rxHalfWidth.MatchString(str) -} - -// IsVariableWidth checks if the string contains a mixture of full and half-width chars. Empty string is valid. -func IsVariableWidth(str string) bool { - if IsNull(str) { - return true - } - return rxHalfWidth.MatchString(str) && rxFullWidth.MatchString(str) -} - -// IsBase64 checks if a string is base64 encoded. -func IsBase64(str string) bool { - return rxBase64.MatchString(str) -} - -// IsFilePath checks is a string is Win or Unix file path and returns it's type. -func IsFilePath(str string) (bool, int) { - if rxWinPath.MatchString(str) { - //check windows path limit see: - // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath - if len(str[3:]) > 32767 { - return false, Win - } - return true, Win - } else if rxUnixPath.MatchString(str) { - return true, Unix - } - return false, Unknown -} - -//IsWinFilePath checks both relative & absolute paths in Windows -func IsWinFilePath(str string) bool { - if rxARWinPath.MatchString(str) { - //check windows path limit see: - // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath - if len(str[3:]) > 32767 { - return false - } - return true - } - return false -} - -//IsUnixFilePath checks both relative & absolute paths in Unix -func IsUnixFilePath(str string) bool { - if rxARUnixPath.MatchString(str) { - return true - } - return false -} - -// IsDataURI checks if a string is base64 encoded data URI such as an image -func IsDataURI(str string) bool { - dataURI := strings.Split(str, ",") - if !rxDataURI.MatchString(dataURI[0]) { - return false - } - return IsBase64(dataURI[1]) -} - -// IsMagnetURI checks if a string is valid magnet URI -func IsMagnetURI(str string) bool { - return rxMagnetURI.MatchString(str) -} - -// IsISO3166Alpha2 checks if a string is valid two-letter country code -func IsISO3166Alpha2(str string) bool { - for _, entry := range ISO3166List { - if str == entry.Alpha2Code { - return true - } - } - return false -} - -// IsISO3166Alpha3 checks if a string is valid three-letter country code -func IsISO3166Alpha3(str string) bool { - for _, entry := range ISO3166List { - if str == entry.Alpha3Code { - return true - } - } - return false -} - -// IsISO693Alpha2 checks if a string is valid two-letter language code -func IsISO693Alpha2(str string) bool { - for _, entry := range ISO693List { - if str == entry.Alpha2Code { - return true - } - } - return false -} - -// IsISO693Alpha3b checks if a string is valid three-letter language code -func IsISO693Alpha3b(str string) bool { - for _, entry := range ISO693List { - if str == entry.Alpha3bCode { - return true - } - } - return false -} - -// IsDNSName will validate the given string as a DNS name -func IsDNSName(str string) bool { - if str == "" || len(strings.Replace(str, ".", "", -1)) > 255 { - // constraints already violated - return false - } - return !IsIP(str) && rxDNSName.MatchString(str) -} - -// IsHash checks if a string is a hash of type algorithm. -// Algorithm is one of ['md4', 'md5', 'sha1', 'sha256', 'sha384', 'sha512', 'ripemd128', 'ripemd160', 'tiger128', 'tiger160', 'tiger192', 'crc32', 'crc32b'] -func IsHash(str string, algorithm string) bool { - var len string - algo := strings.ToLower(algorithm) - - if algo == "crc32" || algo == "crc32b" { - len = "8" - } else if algo == "md5" || algo == "md4" || algo == "ripemd128" || algo == "tiger128" { - len = "32" - } else if algo == "sha1" || algo == "ripemd160" || algo == "tiger160" { - len = "40" - } else if algo == "tiger192" { - len = "48" - } else if algo == "sha3-224" { - len = "56" - } else if algo == "sha256" || algo == "sha3-256" { - len = "64" - } else if algo == "sha384" || algo == "sha3-384" { - len = "96" - } else if algo == "sha512" || algo == "sha3-512" { - len = "128" - } else { - return false - } - - return Matches(str, "^[a-f0-9]{"+len+"}$") -} - -// IsSHA3224 checks is a string is a SHA3-224 hash. Alias for `IsHash(str, "sha3-224")` -func IsSHA3224(str string) bool { - return IsHash(str, "sha3-224") -} - -// IsSHA3256 checks is a string is a SHA3-256 hash. Alias for `IsHash(str, "sha3-256")` -func IsSHA3256(str string) bool { - return IsHash(str, "sha3-256") -} - -// IsSHA3384 checks is a string is a SHA3-384 hash. Alias for `IsHash(str, "sha3-384")` -func IsSHA3384(str string) bool { - return IsHash(str, "sha3-384") -} - -// IsSHA3512 checks is a string is a SHA3-512 hash. Alias for `IsHash(str, "sha3-512")` -func IsSHA3512(str string) bool { - return IsHash(str, "sha3-512") -} - -// IsSHA512 checks is a string is a SHA512 hash. Alias for `IsHash(str, "sha512")` -func IsSHA512(str string) bool { - return IsHash(str, "sha512") -} - -// IsSHA384 checks is a string is a SHA384 hash. Alias for `IsHash(str, "sha384")` -func IsSHA384(str string) bool { - return IsHash(str, "sha384") -} - -// IsSHA256 checks is a string is a SHA256 hash. Alias for `IsHash(str, "sha256")` -func IsSHA256(str string) bool { - return IsHash(str, "sha256") -} - -// IsTiger192 checks is a string is a Tiger192 hash. Alias for `IsHash(str, "tiger192")` -func IsTiger192(str string) bool { - return IsHash(str, "tiger192") -} - -// IsTiger160 checks is a string is a Tiger160 hash. Alias for `IsHash(str, "tiger160")` -func IsTiger160(str string) bool { - return IsHash(str, "tiger160") -} - -// IsRipeMD160 checks is a string is a RipeMD160 hash. Alias for `IsHash(str, "ripemd160")` -func IsRipeMD160(str string) bool { - return IsHash(str, "ripemd160") -} - -// IsSHA1 checks is a string is a SHA-1 hash. Alias for `IsHash(str, "sha1")` -func IsSHA1(str string) bool { - return IsHash(str, "sha1") -} - -// IsTiger128 checks is a string is a Tiger128 hash. Alias for `IsHash(str, "tiger128")` -func IsTiger128(str string) bool { - return IsHash(str, "tiger128") -} - -// IsRipeMD128 checks is a string is a RipeMD128 hash. Alias for `IsHash(str, "ripemd128")` -func IsRipeMD128(str string) bool { - return IsHash(str, "ripemd128") -} - -// IsCRC32 checks is a string is a CRC32 hash. Alias for `IsHash(str, "crc32")` -func IsCRC32(str string) bool { - return IsHash(str, "crc32") -} - -// IsCRC32b checks is a string is a CRC32b hash. Alias for `IsHash(str, "crc32b")` -func IsCRC32b(str string) bool { - return IsHash(str, "crc32b") -} - -// IsMD5 checks is a string is a MD5 hash. Alias for `IsHash(str, "md5")` -func IsMD5(str string) bool { - return IsHash(str, "md5") -} - -// IsMD4 checks is a string is a MD4 hash. Alias for `IsHash(str, "md4")` -func IsMD4(str string) bool { - return IsHash(str, "md4") -} - -// IsDialString validates the given string for usage with the various Dial() functions -func IsDialString(str string) bool { - if h, p, err := net.SplitHostPort(str); err == nil && h != "" && p != "" && (IsDNSName(h) || IsIP(h)) && IsPort(p) { - return true - } - - return false -} - -// IsIP checks if a string is either IP version 4 or 6. Alias for `net.ParseIP` -func IsIP(str string) bool { - return net.ParseIP(str) != nil -} - -// IsPort checks if a string represents a valid port -func IsPort(str string) bool { - if i, err := strconv.Atoi(str); err == nil && i > 0 && i < 65536 { - return true - } - return false -} - -// IsIPv4 checks if the string is an IP version 4. -func IsIPv4(str string) bool { - ip := net.ParseIP(str) - return ip != nil && strings.Contains(str, ".") -} - -// IsIPv6 checks if the string is an IP version 6. -func IsIPv6(str string) bool { - ip := net.ParseIP(str) - return ip != nil && strings.Contains(str, ":") -} - -// IsCIDR checks if the string is an valid CIDR notiation (IPV4 & IPV6) -func IsCIDR(str string) bool { - _, _, err := net.ParseCIDR(str) - return err == nil -} - -// IsMAC checks if a string is valid MAC address. -// Possible MAC formats: -// 01:23:45:67:89:ab -// 01:23:45:67:89:ab:cd:ef -// 01-23-45-67-89-ab -// 01-23-45-67-89-ab-cd-ef -// 0123.4567.89ab -// 0123.4567.89ab.cdef -func IsMAC(str string) bool { - _, err := net.ParseMAC(str) - return err == nil -} - -// IsHost checks if the string is a valid IP (both v4 and v6) or a valid DNS name -func IsHost(str string) bool { - return IsIP(str) || IsDNSName(str) -} - -// IsMongoID checks if the string is a valid hex-encoded representation of a MongoDB ObjectId. -func IsMongoID(str string) bool { - return rxHexadecimal.MatchString(str) && (len(str) == 24) -} - -// IsLatitude checks if a string is valid latitude. -func IsLatitude(str string) bool { - return rxLatitude.MatchString(str) -} - -// IsLongitude checks if a string is valid longitude. -func IsLongitude(str string) bool { - return rxLongitude.MatchString(str) -} - -// IsIMEI checks if a string is valid IMEI -func IsIMEI(str string) bool { - return rxIMEI.MatchString(str) -} - -// IsIMSI checks if a string is valid IMSI -func IsIMSI(str string) bool { - if !rxIMSI.MatchString(str) { - return false - } - - mcc, err := strconv.ParseInt(str[0:3], 10, 32) - if err != nil { - return false - } - - switch mcc { - case 202, 204, 206, 208, 212, 213, 214, 216, 218, 219: - case 220, 221, 222, 226, 228, 230, 231, 232, 234, 235: - case 238, 240, 242, 244, 246, 247, 248, 250, 255, 257: - case 259, 260, 262, 266, 268, 270, 272, 274, 276, 278: - case 280, 282, 283, 284, 286, 288, 289, 290, 292, 293: - case 294, 295, 297, 302, 308, 310, 311, 312, 313, 314: - case 315, 316, 330, 332, 334, 338, 340, 342, 344, 346: - case 348, 350, 352, 354, 356, 358, 360, 362, 363, 364: - case 365, 366, 368, 370, 372, 374, 376, 400, 401, 402: - case 404, 405, 406, 410, 412, 413, 414, 415, 416, 417: - case 418, 419, 420, 421, 422, 424, 425, 426, 427, 428: - case 429, 430, 431, 432, 434, 436, 437, 438, 440, 441: - case 450, 452, 454, 455, 456, 457, 460, 461, 466, 467: - case 470, 472, 502, 505, 510, 514, 515, 520, 525, 528: - case 530, 536, 537, 539, 540, 541, 542, 543, 544, 545: - case 546, 547, 548, 549, 550, 551, 552, 553, 554, 555: - case 602, 603, 604, 605, 606, 607, 608, 609, 610, 611: - case 612, 613, 614, 615, 616, 617, 618, 619, 620, 621: - case 622, 623, 624, 625, 626, 627, 628, 629, 630, 631: - case 632, 633, 634, 635, 636, 637, 638, 639, 640, 641: - case 642, 643, 645, 646, 647, 648, 649, 650, 651, 652: - case 653, 654, 655, 657, 658, 659, 702, 704, 706, 708: - case 710, 712, 714, 716, 722, 724, 730, 732, 734, 736: - case 738, 740, 742, 744, 746, 748, 750, 995: - return true - default: - return false - } - return true -} - -// IsRsaPublicKey checks if a string is valid public key with provided length -func IsRsaPublicKey(str string, keylen int) bool { - bb := bytes.NewBufferString(str) - pemBytes, err := ioutil.ReadAll(bb) - if err != nil { - return false - } - block, _ := pem.Decode(pemBytes) - if block != nil && block.Type != "PUBLIC KEY" { - return false - } - var der []byte - - if block != nil { - der = block.Bytes - } else { - der, err = base64.StdEncoding.DecodeString(str) - if err != nil { - return false - } - } - - key, err := x509.ParsePKIXPublicKey(der) - if err != nil { - return false - } - pubkey, ok := key.(*rsa.PublicKey) - if !ok { - return false - } - bitlen := len(pubkey.N.Bytes()) * 8 - return bitlen == int(keylen) -} - -// IsRegex checks if a give string is a valid regex with RE2 syntax or not -func IsRegex(str string) bool { - if _, err := regexp.Compile(str); err == nil { - return true - } - return false -} - -func toJSONName(tag string) string { - if tag == "" { - return "" - } - - // JSON name always comes first. If there's no options then split[0] is - // JSON name, if JSON name is not set, then split[0] is an empty string. - split := strings.SplitN(tag, ",", 2) - - name := split[0] - - // However it is possible that the field is skipped when - // (de-)serializing from/to JSON, in which case assume that there is no - // tag name to use - if name == "-" { - return "" - } - return name -} - -func prependPathToErrors(err error, path string) error { - switch err2 := err.(type) { - case Error: - err2.Path = append([]string{path}, err2.Path...) - return err2 - case Errors: - errors := err2.Errors() - for i, err3 := range errors { - errors[i] = prependPathToErrors(err3, path) - } - return err2 - } - return err -} - -// ValidateArray performs validation according to condition iterator that validates every element of the array -func ValidateArray(array []interface{}, iterator ConditionIterator) bool { - return Every(array, iterator) -} - -// ValidateMap use validation map for fields. -// result will be equal to `false` if there are any errors. -// s is the map containing the data to be validated. -// m is the validation map in the form: -// map[string]interface{}{"name":"required,alpha","address":map[string]interface{}{"line1":"required,alphanum"}} -func ValidateMap(s map[string]interface{}, m map[string]interface{}) (bool, error) { - if s == nil { - return true, nil - } - result := true - var err error - var errs Errors - var index int - val := reflect.ValueOf(s) - for key, value := range s { - presentResult := true - validator, ok := m[key] - if !ok { - presentResult = false - var err error - err = fmt.Errorf("all map keys has to be present in the validation map; got %s", key) - err = prependPathToErrors(err, key) - errs = append(errs, err) - } - valueField := reflect.ValueOf(value) - mapResult := true - typeResult := true - structResult := true - resultField := true - switch subValidator := validator.(type) { - case map[string]interface{}: - var err error - if v, ok := value.(map[string]interface{}); !ok { - mapResult = false - err = fmt.Errorf("map validator has to be for the map type only; got %s", valueField.Type().String()) - err = prependPathToErrors(err, key) - errs = append(errs, err) - } else { - mapResult, err = ValidateMap(v, subValidator) - if err != nil { - mapResult = false - err = prependPathToErrors(err, key) - errs = append(errs, err) - } - } - case string: - if (valueField.Kind() == reflect.Struct || - (valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) && - subValidator != "-" { - var err error - structResult, err = ValidateStruct(valueField.Interface()) - if err != nil { - err = prependPathToErrors(err, key) - errs = append(errs, err) - } - } - resultField, err = typeCheck(valueField, reflect.StructField{ - Name: key, - PkgPath: "", - Type: val.Type(), - Tag: reflect.StructTag(fmt.Sprintf("%s:%q", tagName, subValidator)), - Offset: 0, - Index: []int{index}, - Anonymous: false, - }, val, nil) - if err != nil { - errs = append(errs, err) - } - case nil: - // already handlerd when checked before - default: - typeResult = false - err = fmt.Errorf("map validator has to be either map[string]interface{} or string; got %s", valueField.Type().String()) - err = prependPathToErrors(err, key) - errs = append(errs, err) - } - result = result && presentResult && typeResult && resultField && structResult && mapResult - index++ - } - // checks required keys - requiredResult := true - for key, value := range m { - if schema, ok := value.(string); ok { - tags := parseTagIntoMap(schema) - if required, ok := tags["required"]; ok { - if _, ok := s[key]; !ok { - requiredResult = false - if required.customErrorMessage != "" { - err = Error{key, fmt.Errorf(required.customErrorMessage), true, "required", []string{}} - } else { - err = Error{key, fmt.Errorf("required field missing"), false, "required", []string{}} - } - errs = append(errs, err) - } - } - } - } - - if len(errs) > 0 { - err = errs - } - return result && requiredResult, err -} - -// ValidateStruct use tags for fields. -// result will be equal to `false` if there are any errors. -// todo currently there is no guarantee that errors will be returned in predictable order (tests may to fail) -func ValidateStruct(s interface{}) (bool, error) { - if s == nil { - return true, nil - } - result := true - var err error - val := reflect.ValueOf(s) - if val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr { - val = val.Elem() - } - // we only accept structs - if val.Kind() != reflect.Struct { - return false, fmt.Errorf("function only accepts structs; got %s", val.Kind()) - } - var errs Errors - for i := 0; i < val.NumField(); i++ { - valueField := val.Field(i) - typeField := val.Type().Field(i) - if typeField.PkgPath != "" { - continue // Private field - } - structResult := true - if valueField.Kind() == reflect.Interface { - valueField = valueField.Elem() - } - if (valueField.Kind() == reflect.Struct || - (valueField.Kind() == reflect.Ptr && valueField.Elem().Kind() == reflect.Struct)) && - typeField.Tag.Get(tagName) != "-" { - var err error - structResult, err = ValidateStruct(valueField.Interface()) - if err != nil { - err = prependPathToErrors(err, typeField.Name) - errs = append(errs, err) - } - } - resultField, err2 := typeCheck(valueField, typeField, val, nil) - if err2 != nil { - - // Replace structure name with JSON name if there is a tag on the variable - jsonTag := toJSONName(typeField.Tag.Get("json")) - if jsonTag != "" { - switch jsonError := err2.(type) { - case Error: - jsonError.Name = jsonTag - err2 = jsonError - case Errors: - for i2, err3 := range jsonError { - switch customErr := err3.(type) { - case Error: - customErr.Name = jsonTag - jsonError[i2] = customErr - } - } - - err2 = jsonError - } - } - - errs = append(errs, err2) - } - result = result && resultField && structResult - } - if len(errs) > 0 { - err = errs - } - return result, err -} - -// ValidateStructAsync performs async validation of the struct and returns results through the channels -func ValidateStructAsync(s interface{}) (<-chan bool, <-chan error) { - res := make(chan bool) - errors := make(chan error) - - go func() { - defer close(res) - defer close(errors) - - isValid, isFailed := ValidateStruct(s) - - res <- isValid - errors <- isFailed - }() - - return res, errors -} - -// ValidateMapAsync performs async validation of the map and returns results through the channels -func ValidateMapAsync(s map[string]interface{}, m map[string]interface{}) (<-chan bool, <-chan error) { - res := make(chan bool) - errors := make(chan error) - - go func() { - defer close(res) - defer close(errors) - - isValid, isFailed := ValidateMap(s, m) - - res <- isValid - errors <- isFailed - }() - - return res, errors -} - -// parseTagIntoMap parses a struct tag `valid:required~Some error message,length(2|3)` into map[string]string{"required": "Some error message", "length(2|3)": ""} -func parseTagIntoMap(tag string) tagOptionsMap { - optionsMap := make(tagOptionsMap) - options := strings.Split(tag, ",") - - for i, option := range options { - option = strings.TrimSpace(option) - - validationOptions := strings.Split(option, "~") - if !isValidTag(validationOptions[0]) { - continue - } - if len(validationOptions) == 2 { - optionsMap[validationOptions[0]] = tagOption{validationOptions[0], validationOptions[1], i} - } else { - optionsMap[validationOptions[0]] = tagOption{validationOptions[0], "", i} - } - } - return optionsMap -} - -func isValidTag(s string) bool { - if s == "" { - return false - } - for _, c := range s { - switch { - case strings.ContainsRune("\\'\"!#$%&()*+-./:<=>?@[]^_{|}~ ", c): - // Backslash and quote chars are reserved, but - // otherwise any punctuation chars are allowed - // in a tag name. - default: - if !unicode.IsLetter(c) && !unicode.IsDigit(c) { - return false - } - } - } - return true -} - -// IsSSN will validate the given string as a U.S. Social Security Number -func IsSSN(str string) bool { - if str == "" || len(str) != 11 { - return false - } - return rxSSN.MatchString(str) -} - -// IsSemver checks if string is valid semantic version -func IsSemver(str string) bool { - return rxSemver.MatchString(str) -} - -// IsType checks if interface is of some type -func IsType(v interface{}, params ...string) bool { - if len(params) == 1 { - typ := params[0] - return strings.Replace(reflect.TypeOf(v).String(), " ", "", -1) == strings.Replace(typ, " ", "", -1) - } - return false -} - -// IsTime checks if string is valid according to given format -func IsTime(str string, format string) bool { - _, err := time.Parse(format, str) - return err == nil -} - -// IsUnixTime checks if string is valid unix timestamp value -func IsUnixTime(str string) bool { - if _, err := strconv.Atoi(str); err == nil { - return true - } - return false -} - -// IsRFC3339 checks if string is valid timestamp value according to RFC3339 -func IsRFC3339(str string) bool { - return IsTime(str, time.RFC3339) -} - -// IsRFC3339WithoutZone checks if string is valid timestamp value according to RFC3339 which excludes the timezone. -func IsRFC3339WithoutZone(str string) bool { - return IsTime(str, rfc3339WithoutZone) -} - -// IsISO4217 checks if string is valid ISO currency code -func IsISO4217(str string) bool { - for _, currency := range ISO4217List { - if str == currency { - return true - } - } - - return false -} - -// ByteLength checks string's length -func ByteLength(str string, params ...string) bool { - if len(params) == 2 { - min, _ := ToInt(params[0]) - max, _ := ToInt(params[1]) - return len(str) >= int(min) && len(str) <= int(max) - } - - return false -} - -// RuneLength checks string's length -// Alias for StringLength -func RuneLength(str string, params ...string) bool { - return StringLength(str, params...) -} - -// IsRsaPub checks whether string is valid RSA key -// Alias for IsRsaPublicKey -func IsRsaPub(str string, params ...string) bool { - if len(params) == 1 { - len, _ := ToInt(params[0]) - return IsRsaPublicKey(str, int(len)) - } - - return false -} - -// StringMatches checks if a string matches a given pattern. -func StringMatches(s string, params ...string) bool { - if len(params) == 1 { - pattern := params[0] - return Matches(s, pattern) - } - return false -} - -// StringLength checks string's length (including multi byte strings) -func StringLength(str string, params ...string) bool { - - if len(params) == 2 { - strLength := utf8.RuneCountInString(str) - min, _ := ToInt(params[0]) - max, _ := ToInt(params[1]) - return strLength >= int(min) && strLength <= int(max) - } - - return false -} - -// MinStringLength checks string's minimum length (including multi byte strings) -func MinStringLength(str string, params ...string) bool { - - if len(params) == 1 { - strLength := utf8.RuneCountInString(str) - min, _ := ToInt(params[0]) - return strLength >= int(min) - } - - return false -} - -// MaxStringLength checks string's maximum length (including multi byte strings) -func MaxStringLength(str string, params ...string) bool { - - if len(params) == 1 { - strLength := utf8.RuneCountInString(str) - max, _ := ToInt(params[0]) - return strLength <= int(max) - } - - return false -} - -// Range checks string's length -func Range(str string, params ...string) bool { - if len(params) == 2 { - value, _ := ToFloat(str) - min, _ := ToFloat(params[0]) - max, _ := ToFloat(params[1]) - return InRange(value, min, max) - } - - return false -} - -// IsInRaw checks if string is in list of allowed values -func IsInRaw(str string, params ...string) bool { - if len(params) == 1 { - rawParams := params[0] - - parsedParams := strings.Split(rawParams, "|") - - return IsIn(str, parsedParams...) - } - - return false -} - -// IsIn checks if string str is a member of the set of strings params -func IsIn(str string, params ...string) bool { - for _, param := range params { - if str == param { - return true - } - } - - return false -} - -func checkRequired(v reflect.Value, t reflect.StructField, options tagOptionsMap) (bool, error) { - if nilPtrAllowedByRequired { - k := v.Kind() - if (k == reflect.Ptr || k == reflect.Interface) && v.IsNil() { - return true, nil - } - } - - if requiredOption, isRequired := options["required"]; isRequired { - if len(requiredOption.customErrorMessage) > 0 { - return false, Error{t.Name, fmt.Errorf(requiredOption.customErrorMessage), true, "required", []string{}} - } - return false, Error{t.Name, fmt.Errorf("non zero value required"), false, "required", []string{}} - } else if _, isOptional := options["optional"]; fieldsRequiredByDefault && !isOptional { - return false, Error{t.Name, fmt.Errorf("Missing required field"), false, "required", []string{}} - } - // not required and empty is valid - return true, nil -} - -func typeCheck(v reflect.Value, t reflect.StructField, o reflect.Value, options tagOptionsMap) (isValid bool, resultErr error) { - if !v.IsValid() { - return false, nil - } - - tag := t.Tag.Get(tagName) - - // checks if the field should be ignored - switch tag { - case "": - if v.Kind() != reflect.Slice && v.Kind() != reflect.Map { - if !fieldsRequiredByDefault { - return true, nil - } - return false, Error{t.Name, fmt.Errorf("All fields are required to at least have one validation defined"), false, "required", []string{}} - } - case "-": - return true, nil - } - - isRootType := false - if options == nil { - isRootType = true - options = parseTagIntoMap(tag) - } - - if isEmptyValue(v) { - // an empty value is not validated, checks only required - isValid, resultErr = checkRequired(v, t, options) - for key := range options { - delete(options, key) - } - return isValid, resultErr - } - - var customTypeErrors Errors - optionsOrder := options.orderedKeys() - for _, validatorName := range optionsOrder { - validatorStruct := options[validatorName] - if validatefunc, ok := CustomTypeTagMap.Get(validatorName); ok { - delete(options, validatorName) - - if result := validatefunc(v.Interface(), o.Interface()); !result { - if len(validatorStruct.customErrorMessage) > 0 { - customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: TruncatingErrorf(validatorStruct.customErrorMessage, fmt.Sprint(v), validatorName), CustomErrorMessageExists: true, Validator: stripParams(validatorName)}) - continue - } - customTypeErrors = append(customTypeErrors, Error{Name: t.Name, Err: fmt.Errorf("%s does not validate as %s", fmt.Sprint(v), validatorName), CustomErrorMessageExists: false, Validator: stripParams(validatorName)}) - } - } - } - - if len(customTypeErrors.Errors()) > 0 { - return false, customTypeErrors - } - - if isRootType { - // Ensure that we've checked the value by all specified validators before report that the value is valid - defer func() { - delete(options, "optional") - delete(options, "required") - - if isValid && resultErr == nil && len(options) != 0 { - optionsOrder := options.orderedKeys() - for _, validator := range optionsOrder { - isValid = false - resultErr = Error{t.Name, fmt.Errorf( - "The following validator is invalid or can't be applied to the field: %q", validator), false, stripParams(validator), []string{}} - return - } - } - }() - } - - for _, validatorSpec := range optionsOrder { - validatorStruct := options[validatorSpec] - var negate bool - validator := validatorSpec - customMsgExists := len(validatorStruct.customErrorMessage) > 0 - - // checks whether the tag looks like '!something' or 'something' - if validator[0] == '!' { - validator = validator[1:] - negate = true - } - - // checks for interface param validators - for key, value := range InterfaceParamTagRegexMap { - ps := value.FindStringSubmatch(validator) - if len(ps) == 0 { - continue - } - - validatefunc, ok := InterfaceParamTagMap[key] - if !ok { - continue - } - - delete(options, validatorSpec) - - field := fmt.Sprint(v) - if result := validatefunc(v.Interface(), ps[1:]...); (!result && !negate) || (result && negate) { - if customMsgExists { - return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}} - } - if negate { - return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} - } - return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} - } - } - } - - switch v.Kind() { - case reflect.Bool, - reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, - reflect.Float32, reflect.Float64, - reflect.String: - // for each tag option checks the map of validator functions - for _, validatorSpec := range optionsOrder { - validatorStruct := options[validatorSpec] - var negate bool - validator := validatorSpec - customMsgExists := len(validatorStruct.customErrorMessage) > 0 - - // checks whether the tag looks like '!something' or 'something' - if validator[0] == '!' { - validator = validator[1:] - negate = true - } - - // checks for param validators - for key, value := range ParamTagRegexMap { - ps := value.FindStringSubmatch(validator) - if len(ps) == 0 { - continue - } - - validatefunc, ok := ParamTagMap[key] - if !ok { - continue - } - - delete(options, validatorSpec) - - switch v.Kind() { - case reflect.String, - reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, - reflect.Float32, reflect.Float64: - - field := fmt.Sprint(v) // make value into string, then validate with regex - if result := validatefunc(field, ps[1:]...); (!result && !negate) || (result && negate) { - if customMsgExists { - return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}} - } - if negate { - return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} - } - return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} - } - default: - // type not yet supported, fail - return false, Error{t.Name, fmt.Errorf("Validator %s doesn't support kind %s", validator, v.Kind()), false, stripParams(validatorSpec), []string{}} - } - } - - if validatefunc, ok := TagMap[validator]; ok { - delete(options, validatorSpec) - - switch v.Kind() { - case reflect.String, - reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, - reflect.Float32, reflect.Float64: - field := fmt.Sprint(v) // make value into string, then validate with regex - if result := validatefunc(field); !result && !negate || result && negate { - if customMsgExists { - return false, Error{t.Name, TruncatingErrorf(validatorStruct.customErrorMessage, field, validator), customMsgExists, stripParams(validatorSpec), []string{}} - } - if negate { - return false, Error{t.Name, fmt.Errorf("%s does validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} - } - return false, Error{t.Name, fmt.Errorf("%s does not validate as %s", field, validator), customMsgExists, stripParams(validatorSpec), []string{}} - } - default: - //Not Yet Supported Types (Fail here!) - err := fmt.Errorf("Validator %s doesn't support kind %s for value %v", validator, v.Kind(), v) - return false, Error{t.Name, err, false, stripParams(validatorSpec), []string{}} - } - } - } - return true, nil - case reflect.Map: - if v.Type().Key().Kind() != reflect.String { - return false, &UnsupportedTypeError{v.Type()} - } - var sv stringValues - sv = v.MapKeys() - sort.Sort(sv) - result := true - for i, k := range sv { - var resultItem bool - var err error - if v.MapIndex(k).Kind() != reflect.Struct { - resultItem, err = typeCheck(v.MapIndex(k), t, o, options) - if err != nil { - return false, err - } - } else { - resultItem, err = ValidateStruct(v.MapIndex(k).Interface()) - if err != nil { - err = prependPathToErrors(err, t.Name+"."+sv[i].Interface().(string)) - return false, err - } - } - result = result && resultItem - } - return result, nil - case reflect.Slice, reflect.Array: - result := true - for i := 0; i < v.Len(); i++ { - var resultItem bool - var err error - if v.Index(i).Kind() != reflect.Struct { - resultItem, err = typeCheck(v.Index(i), t, o, options) - if err != nil { - return false, err - } - } else { - resultItem, err = ValidateStruct(v.Index(i).Interface()) - if err != nil { - err = prependPathToErrors(err, t.Name+"."+strconv.Itoa(i)) - return false, err - } - } - result = result && resultItem - } - return result, nil - case reflect.Interface: - // If the value is an interface then encode its element - if v.IsNil() { - return true, nil - } - return ValidateStruct(v.Interface()) - case reflect.Ptr: - // If the value is a pointer then checks its element - if v.IsNil() { - return true, nil - } - return typeCheck(v.Elem(), t, o, options) - case reflect.Struct: - return true, nil - default: - return false, &UnsupportedTypeError{v.Type()} - } -} - -func stripParams(validatorString string) string { - return paramsRegexp.ReplaceAllString(validatorString, "") -} - -// isEmptyValue checks whether value empty or not -func isEmptyValue(v reflect.Value) bool { - switch v.Kind() { - case reflect.String, reflect.Array: - return v.Len() == 0 - case reflect.Map, reflect.Slice: - return v.Len() == 0 || v.IsNil() - case reflect.Bool: - return !v.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - case reflect.Float32, reflect.Float64: - return v.Float() == 0 - case reflect.Interface, reflect.Ptr: - return v.IsNil() - } - - return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) -} - -// ErrorByField returns error for specified field of the struct -// validated by ValidateStruct or empty string if there are no errors -// or this field doesn't exists or doesn't have any errors. -func ErrorByField(e error, field string) string { - if e == nil { - return "" - } - return ErrorsByField(e)[field] -} - -// ErrorsByField returns map of errors of the struct validated -// by ValidateStruct or empty map if there are no errors. -func ErrorsByField(e error) map[string]string { - m := make(map[string]string) - if e == nil { - return m - } - // prototype for ValidateStruct - - switch e := e.(type) { - case Error: - m[e.Name] = e.Err.Error() - case Errors: - for _, item := range e.Errors() { - n := ErrorsByField(item) - for k, v := range n { - m[k] = v - } - } - } - - return m -} - -// Error returns string equivalent for reflect.Type -func (e *UnsupportedTypeError) Error() string { - return "validator: unsupported type: " + e.Type.String() -} - -func (sv stringValues) Len() int { return len(sv) } -func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] } -func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) } -func (sv stringValues) get(i int) string { return sv[i].String() } - -func IsE164(str string) bool { - return rxE164.MatchString(str) -} diff --git a/vendor/github.com/asaskevich/govalidator/wercker.yml b/vendor/github.com/asaskevich/govalidator/wercker.yml deleted file mode 100644 index bc5f7b08..00000000 --- a/vendor/github.com/asaskevich/govalidator/wercker.yml +++ /dev/null @@ -1,15 +0,0 @@ -box: golang -build: - steps: - - setup-go-workspace - - - script: - name: go get - code: | - go version - go get -t ./... - - - script: - name: go test - code: | - go test -race -v ./... diff --git a/vendor/github.com/dimchansky/utfbom/.gitignore b/vendor/github.com/dimchansky/utfbom/.gitignore deleted file mode 100644 index d7ec5ceb..00000000 --- a/vendor/github.com/dimchansky/utfbom/.gitignore +++ /dev/null @@ -1,37 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.dll -*.so -*.dylib -*.o -*.a - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.prof - -# Test binary, build with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 -.glide/ - -# Gogland -.idea/ \ No newline at end of file diff --git a/vendor/github.com/dimchansky/utfbom/.travis.yml b/vendor/github.com/dimchansky/utfbom/.travis.yml deleted file mode 100644 index 19312ee3..00000000 --- a/vendor/github.com/dimchansky/utfbom/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -language: go -sudo: false - -go: - - 1.10.x - - 1.11.x - - 1.12.x - - 1.13.x - - 1.14.x - - 1.15.x - -cache: - directories: - - $HOME/.cache/go-build - - $HOME/gopath/pkg/mod - -env: - global: - - GO111MODULE=on - -before_install: - - go get github.com/mattn/goveralls - - go get golang.org/x/tools/cmd/cover - - go get golang.org/x/tools/cmd/goimports - - go get golang.org/x/lint/golint -script: - - gofiles=$(find ./ -name '*.go') && [ -z "$gofiles" ] || unformatted=$(goimports -l $gofiles) && [ -z "$unformatted" ] || (echo >&2 "Go files must be formatted with gofmt. Following files has problem:\n $unformatted" && false) - - golint ./... # This won't break the build, just show warnings - - $HOME/gopath/bin/goveralls -service=travis-ci diff --git a/vendor/github.com/dimchansky/utfbom/README.md b/vendor/github.com/dimchansky/utfbom/README.md deleted file mode 100644 index 8ece2800..00000000 --- a/vendor/github.com/dimchansky/utfbom/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# utfbom [![Godoc](https://godoc.org/github.com/dimchansky/utfbom?status.png)](https://godoc.org/github.com/dimchansky/utfbom) [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Build Status](https://travis-ci.org/dimchansky/utfbom.svg?branch=master)](https://travis-ci.org/dimchansky/utfbom) [![Go Report Card](https://goreportcard.com/badge/github.com/dimchansky/utfbom)](https://goreportcard.com/report/github.com/dimchansky/utfbom) [![Coverage Status](https://coveralls.io/repos/github/dimchansky/utfbom/badge.svg?branch=master)](https://coveralls.io/github/dimchansky/utfbom?branch=master) - -The package utfbom implements the detection of the BOM (Unicode Byte Order Mark) and removing as necessary. It can also return the encoding detected by the BOM. - -## Installation - - go get -u github.com/dimchansky/utfbom - -## Example - -```go -package main - -import ( - "bytes" - "fmt" - "io/ioutil" - - "github.com/dimchansky/utfbom" -) - -func main() { - trySkip([]byte("\xEF\xBB\xBFhello")) - trySkip([]byte("hello")) -} - -func trySkip(byteData []byte) { - fmt.Println("Input:", byteData) - - // just skip BOM - output, err := ioutil.ReadAll(utfbom.SkipOnly(bytes.NewReader(byteData))) - if err != nil { - fmt.Println(err) - return - } - fmt.Println("ReadAll with BOM skipping", output) - - // skip BOM and detect encoding - sr, enc := utfbom.Skip(bytes.NewReader(byteData)) - fmt.Printf("Detected encoding: %s\n", enc) - output, err = ioutil.ReadAll(sr) - if err != nil { - fmt.Println(err) - return - } - fmt.Println("ReadAll with BOM detection and skipping", output) - fmt.Println() -} -``` - -Output: - -``` -$ go run main.go -Input: [239 187 191 104 101 108 108 111] -ReadAll with BOM skipping [104 101 108 108 111] -Detected encoding: UTF8 -ReadAll with BOM detection and skipping [104 101 108 108 111] - -Input: [104 101 108 108 111] -ReadAll with BOM skipping [104 101 108 108 111] -Detected encoding: Unknown -ReadAll with BOM detection and skipping [104 101 108 108 111] -``` - - diff --git a/vendor/github.com/dimchansky/utfbom/utfbom.go b/vendor/github.com/dimchansky/utfbom/utfbom.go deleted file mode 100644 index 77a303e5..00000000 --- a/vendor/github.com/dimchansky/utfbom/utfbom.go +++ /dev/null @@ -1,192 +0,0 @@ -// Package utfbom implements the detection of the BOM (Unicode Byte Order Mark) and removing as necessary. -// It wraps an io.Reader object, creating another object (Reader) that also implements the io.Reader -// interface but provides automatic BOM checking and removing as necessary. -package utfbom - -import ( - "errors" - "io" -) - -// Encoding is type alias for detected UTF encoding. -type Encoding int - -// Constants to identify detected UTF encodings. -const ( - // Unknown encoding, returned when no BOM was detected - Unknown Encoding = iota - - // UTF8, BOM bytes: EF BB BF - UTF8 - - // UTF-16, big-endian, BOM bytes: FE FF - UTF16BigEndian - - // UTF-16, little-endian, BOM bytes: FF FE - UTF16LittleEndian - - // UTF-32, big-endian, BOM bytes: 00 00 FE FF - UTF32BigEndian - - // UTF-32, little-endian, BOM bytes: FF FE 00 00 - UTF32LittleEndian -) - -// String returns a user-friendly string representation of the encoding. Satisfies fmt.Stringer interface. -func (e Encoding) String() string { - switch e { - case UTF8: - return "UTF8" - case UTF16BigEndian: - return "UTF16BigEndian" - case UTF16LittleEndian: - return "UTF16LittleEndian" - case UTF32BigEndian: - return "UTF32BigEndian" - case UTF32LittleEndian: - return "UTF32LittleEndian" - default: - return "Unknown" - } -} - -const maxConsecutiveEmptyReads = 100 - -// Skip creates Reader which automatically detects BOM (Unicode Byte Order Mark) and removes it as necessary. -// It also returns the encoding detected by the BOM. -// If the detected encoding is not needed, you can call the SkipOnly function. -func Skip(rd io.Reader) (*Reader, Encoding) { - // Is it already a Reader? - b, ok := rd.(*Reader) - if ok { - return b, Unknown - } - - enc, left, err := detectUtf(rd) - return &Reader{ - rd: rd, - buf: left, - err: err, - }, enc -} - -// SkipOnly creates Reader which automatically detects BOM (Unicode Byte Order Mark) and removes it as necessary. -func SkipOnly(rd io.Reader) *Reader { - r, _ := Skip(rd) - return r -} - -// Reader implements automatic BOM (Unicode Byte Order Mark) checking and -// removing as necessary for an io.Reader object. -type Reader struct { - rd io.Reader // reader provided by the client - buf []byte // buffered data - err error // last error -} - -// Read is an implementation of io.Reader interface. -// The bytes are taken from the underlying Reader, but it checks for BOMs, removing them as necessary. -func (r *Reader) Read(p []byte) (n int, err error) { - if len(p) == 0 { - return 0, nil - } - - if r.buf == nil { - if r.err != nil { - return 0, r.readErr() - } - - return r.rd.Read(p) - } - - // copy as much as we can - n = copy(p, r.buf) - r.buf = nilIfEmpty(r.buf[n:]) - return n, nil -} - -func (r *Reader) readErr() error { - err := r.err - r.err = nil - return err -} - -var errNegativeRead = errors.New("utfbom: reader returned negative count from Read") - -func detectUtf(rd io.Reader) (enc Encoding, buf []byte, err error) { - buf, err = readBOM(rd) - - if len(buf) >= 4 { - if isUTF32BigEndianBOM4(buf) { - return UTF32BigEndian, nilIfEmpty(buf[4:]), err - } - if isUTF32LittleEndianBOM4(buf) { - return UTF32LittleEndian, nilIfEmpty(buf[4:]), err - } - } - - if len(buf) > 2 && isUTF8BOM3(buf) { - return UTF8, nilIfEmpty(buf[3:]), err - } - - if (err != nil && err != io.EOF) || (len(buf) < 2) { - return Unknown, nilIfEmpty(buf), err - } - - if isUTF16BigEndianBOM2(buf) { - return UTF16BigEndian, nilIfEmpty(buf[2:]), err - } - if isUTF16LittleEndianBOM2(buf) { - return UTF16LittleEndian, nilIfEmpty(buf[2:]), err - } - - return Unknown, nilIfEmpty(buf), err -} - -func readBOM(rd io.Reader) (buf []byte, err error) { - const maxBOMSize = 4 - var bom [maxBOMSize]byte // used to read BOM - - // read as many bytes as possible - for nEmpty, n := 0, 0; err == nil && len(buf) < maxBOMSize; buf = bom[:len(buf)+n] { - if n, err = rd.Read(bom[len(buf):]); n < 0 { - panic(errNegativeRead) - } - if n > 0 { - nEmpty = 0 - } else { - nEmpty++ - if nEmpty >= maxConsecutiveEmptyReads { - err = io.ErrNoProgress - } - } - } - return -} - -func isUTF32BigEndianBOM4(buf []byte) bool { - return buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0xFE && buf[3] == 0xFF -} - -func isUTF32LittleEndianBOM4(buf []byte) bool { - return buf[0] == 0xFF && buf[1] == 0xFE && buf[2] == 0x00 && buf[3] == 0x00 -} - -func isUTF8BOM3(buf []byte) bool { - return buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF -} - -func isUTF16BigEndianBOM2(buf []byte) bool { - return buf[0] == 0xFE && buf[1] == 0xFF -} - -func isUTF16LittleEndianBOM2(buf []byte) bool { - return buf[0] == 0xFF && buf[1] == 0xFE -} - -func nilIfEmpty(buf []byte) (res []byte) { - if len(buf) > 0 { - res = buf - } - return -} diff --git a/vendor/github.com/goodhosts/hostsfile/.gitignore b/vendor/github.com/goodhosts/hostsfile/.gitignore deleted file mode 100644 index 723ef36f..00000000 --- a/vendor/github.com/goodhosts/hostsfile/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.idea \ No newline at end of file diff --git a/vendor/github.com/goodhosts/hostsfile/LICENSE b/vendor/github.com/goodhosts/hostsfile/LICENSE deleted file mode 100644 index d9a10c0d..00000000 --- a/vendor/github.com/goodhosts/hostsfile/LICENSE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - 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. - - END OF TERMS AND CONDITIONS diff --git a/vendor/github.com/goodhosts/hostsfile/Makefile b/vendor/github.com/goodhosts/hostsfile/Makefile deleted file mode 100644 index 74b42960..00000000 --- a/vendor/github.com/goodhosts/hostsfile/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -ci: - @goimports -l . || (goimports -d . && exit 1) - @golangci-lint run - @go test -v . -.DEFAULT_GOAL := ci \ No newline at end of file diff --git a/vendor/github.com/goodhosts/hostsfile/hosts.go b/vendor/github.com/goodhosts/hostsfile/hosts.go deleted file mode 100644 index dfda77ae..00000000 --- a/vendor/github.com/goodhosts/hostsfile/hosts.go +++ /dev/null @@ -1,451 +0,0 @@ -package hostsfile - -import ( - "bufio" - "bytes" - "fmt" - "net" - "os" - "path/filepath" - "sort" - "sync" - - "github.com/asaskevich/govalidator" - "github.com/dimchansky/utfbom" -) - -type lookup struct { - sync.RWMutex - l map[string][]int -} - -type Hosts struct { - Path string - Lines []HostsLine - ips lookup - hosts lookup -} - -// NewHosts return a new instance of ``Hosts`` using the default hosts file path. -func NewHosts() (*Hosts, error) { - osHostsFilePath := os.ExpandEnv(filepath.FromSlash(HostsFilePath)) - - if env, isset := os.LookupEnv("HOSTS_PATH"); isset && len(env) > 0 { - osHostsFilePath = os.ExpandEnv(filepath.FromSlash(env)) - } - - return NewCustomHosts(osHostsFilePath) -} - -// NewCustomHosts return a new instance of ``Hosts`` using a custom hosts file path. -func NewCustomHosts(osHostsFilePath string) (*Hosts, error) { - hosts := &Hosts{ - Path: osHostsFilePath, - ips: lookup{l: make(map[string][]int)}, - hosts: lookup{l: make(map[string][]int)}, - } - - if err := hosts.Load(); err != nil { - return hosts, err - } - - return hosts, nil -} - -// IsWritable return ```true``` if hosts file is writable. -func (h *Hosts) IsWritable() bool { - file, err := os.OpenFile(h.Path, os.O_WRONLY, 0660) - if err != nil { - return false - } - defer file.Close() - return true -} - -// Load the hosts file into ```l.Lines```. -// ```Load()``` is called by ```NewHosts()``` and ```Hosts.Flush()``` so you -// generally you won't need to call this yourself. -func (h *Hosts) Load() error { - file, err := os.Open(h.Path) - if err != nil { - return err - } - defer file.Close() - - // if you're reloading from disk confirm you refresh the hash maps and lines - if len(h.Lines) != 0 { - h.ips = lookup{l: make(map[string][]int)} - h.hosts = lookup{l: make(map[string][]int)} - h.Lines = []HostsLine{} - } - - scanner := bufio.NewScanner(utfbom.SkipOnly(file)) - for scanner.Scan() { - hl := NewHostsLine(scanner.Text()) - h.Lines = append(h.Lines, hl) - pos := len(h.Lines) - 1 - h.addIpPosition(hl.IP, pos) - for _, host := range hl.Hosts { - h.addHostPositions(host, pos) - } - } - - return scanner.Err() -} - -// Flush any changes made to hosts file. -func (h *Hosts) Flush() error { - h.preFlushClean() - file, err := os.Create(h.Path) - if err != nil { - return err - } - - defer file.Close() - - w := bufio.NewWriter(file) - for _, line := range h.Lines { - if _, err := fmt.Fprintf(w, "%s%s", line.ToRaw(), eol); err != nil { - return err - } - } - - err = w.Flush() - if err != nil { - return err - } - - return h.Load() -} - -// AddRaw takes a line from a hosts file and parses/adds the HostsLine -func (h *Hosts) AddRaw(raw ...string) error { - for _, r := range raw { - nl := NewHostsLine(r) - if nl.IP != "" && net.ParseIP(nl.IP) == nil { - return fmt.Errorf("%q is an invalid IP address", nl.IP) - } - - for _, host := range nl.Hosts { - if !govalidator.IsDNSName(host) { - return fmt.Errorf("hostname is not a valid dns name: %s", host) - } - } - - h.Lines = append(h.Lines, nl) - pos := len(h.Lines) - 1 - h.addIpPosition(nl.IP, pos) - for _, host := range nl.Hosts { - h.addHostPositions(host, pos) - } - } - - return nil -} - -// Add an entry to the hosts file. -func (h *Hosts) Add(ip string, hosts ...string) error { - if net.ParseIP(ip) == nil { - return fmt.Errorf("%q is an invalid IP address", ip) - } - - position := h.getIpPositions(ip) - if len(position) == 0 { - nl := HostsLine{ - Raw: buildRawLine(ip, hosts), - IP: ip, - Hosts: hosts, - } - h.Lines = append(h.Lines, nl) - pos := len(h.Lines) - 1 - h.addIpPosition(ip, pos) - for _, host := range nl.Hosts { - h.addHostPositions(host, pos) - } - } else { - // add new host to the first one we find - hostsCopy := h.Lines[position[0]].Hosts - for _, addHost := range hosts { - if h.Has(ip, addHost) { - // this combo already exists - continue - } - - if !govalidator.IsDNSName(addHost) { - return fmt.Errorf("hostname is not a valid dns name: %s", addHost) - } - if itemInSliceString(addHost, hostsCopy) { - continue // host exists for ip already - } - - hostsCopy = append(hostsCopy, addHost) - h.addHostPositions(addHost, position[0]) - } - h.Lines[position[0]].Hosts = hostsCopy - h.Lines[position[0]].Raw = h.Lines[position[0]].ToRaw() // reset raw - } - - return nil -} - -func (h *Hosts) Clear() { - h.Lines = []HostsLine{} -} - -// Clean merge duplicate ips and hosts per ip -func (h *Hosts) Clean() { - h.RemoveDuplicateIps() - h.RemoveDuplicateHosts() - h.SortHosts() - h.SortByIp() - h.HostsPerLine(HostsPerLine) -} - -// Has return a bool if ip/host combo in hosts file. -func (h *Hosts) Has(ip string, host string) bool { - ippos := h.getIpPositions(ip) - hostpos := h.getHostPositions(host) - for _, pos := range ippos { - if itemInSliceInt(pos, hostpos) { - // if ip and host have matching lookup positions we have a combo match - return true - } - } - - return false -} - -// HasHostname return a bool if hostname in hosts file. -func (h *Hosts) HasHostname(host string) bool { - return len(h.getHostPositions(host)) > 0 -} - -func (h *Hosts) HasIp(ip string) bool { - return len(h.getIpPositions(ip)) > 0 -} - -// Remove an entry from the hosts file. -func (h *Hosts) Remove(ip string, hosts ...string) error { - var outputLines []HostsLine - if net.ParseIP(ip) == nil { - return fmt.Errorf("%q is an invalid IP address", ip) - } - - for _, line := range h.Lines { - // Bad lines or comments just get readded. - if line.Err != nil || line.IsComment() || line.IP != ip { - outputLines = append(outputLines, line) - continue - } - - var newHosts []string - for _, checkHost := range line.Hosts { - if !itemInSliceString(checkHost, hosts) { - newHosts = append(newHosts, checkHost) - } - } - - // If hosts is empty, skip the line completely. - if len(newHosts) > 0 { - newLineRaw := line.IP - - for _, host := range newHosts { - newLineRaw = fmt.Sprintf("%s %s", newLineRaw, host) - } - newLine := NewHostsLine(newLineRaw) - outputLines = append(outputLines, newLine) - } - } - - h.Lines = outputLines - return nil -} - -// RemoveByHostname remove entries by hostname from the hosts file. -func (h *Hosts) RemoveByHostname(host string) error { - for _, p := range h.getHostPositions(host) { - line := &h.Lines[p] - if len(line.Hosts) > 0 { - line.Hosts = removeFromSliceString(host, line.Hosts) - line.RegenRaw() - } - h.removeHostPositions(host, p) - } - - return nil -} - -func (h *Hosts) RemoveByIp(ip string) error { - pos := h.getIpPositions(ip) - for len(pos) > 0 { - for _, p := range pos { - h.removeByPosition(p) - } - } - - return nil -} - -func (h *Hosts) RemoveDuplicateIps() { - ipCount := make(map[string]int) - for _, line := range h.Lines { - ipCount[line.IP]++ - } - for ip, count := range ipCount { - if count > 1 { - h.combineIp(ip) - } - } -} - -func (h *Hosts) RemoveDuplicateHosts() { - for pos, line := range h.Lines { - line.RemoveDuplicateHosts() - h.Lines[pos] = line - } -} - -func (h *Hosts) SortHosts() { - for pos, line := range h.Lines { - line.SortHosts() - h.Lines[pos] = line - } -} - -// SortByIp convert to net.IP and byte.Compare -func (h *Hosts) SortByIp() { - sortedIps := make([]net.IP, 0, len(h.Lines)) - for _, l := range h.Lines { - sortedIps = append(sortedIps, net.ParseIP(l.IP)) - } - sort.Slice(sortedIps, func(i, j int) bool { - return bytes.Compare(sortedIps[i], sortedIps[j]) < 0 - }) - - var sortedLines []HostsLine - for _, ip := range sortedIps { - for _, l := range h.Lines { - if ip.String() == l.IP { - sortedLines = append(sortedLines, l) - } - } - } - h.Lines = sortedLines -} - -func (h *Hosts) HostsPerLine(count int) { - // restacks everything into 1 ip again so we can do the split, do this even if count is -1 so it can reset the slice - h.RemoveDuplicateIps() - if count <= 0 { - return - } - var newLines []HostsLine - for _, line := range h.Lines { - if len(line.Hosts) <= count { - newLines = append(newLines, line) - continue - } - - for i := 0; i < len(line.Hosts); i += count { - lineCopy := line - end := len(line.Hosts) - if end > i+count { - end = i + count - } - - lineCopy.Hosts = line.Hosts[i:end] - lineCopy.Raw = lineCopy.ToRaw() - newLines = append(newLines, lineCopy) - } - } - h.Lines = newLines -} - -func (h *Hosts) combineIp(ip string) { - newLine := HostsLine{ - IP: ip, - } - - linesCopy := make([]HostsLine, len(h.Lines)) - copy(linesCopy, h.Lines) - for _, line := range linesCopy { - if line.IP == ip { - newLine.Combine(line) - } - } - newLine.SortHosts() - h.removeIp(ip) - h.Lines = append(h.Lines, newLine) -} - -func (h *Hosts) removeByPosition(pos int) { - if pos == 0 && len(h.Lines) == 1 { - h.Clear() - return - } - if pos == len(h.Lines) { - h.Lines = h.Lines[:pos-1] - return - } - h.Lines = append(h.Lines[:pos], h.Lines[pos+1:]...) -} - -func (h *Hosts) removeIp(ip string) { - var newLines []HostsLine - for _, line := range h.Lines { - if line.IP != ip { - newLines = append(newLines, line) - } - } - - h.Lines = newLines -} - -func (h *Hosts) getHostPositions(host string) []int { - h.hosts.RLock() - defer h.hosts.RUnlock() - i, ok := h.hosts.l[host] - if ok { - return i - } - return []int{} -} - -func (h *Hosts) addHostPositions(host string, pos int) { - h.hosts.Lock() - defer h.hosts.Unlock() - h.hosts.l[host] = append(h.hosts.l[host], pos) -} - -func (h *Hosts) removeHostPositions(host string, pos int) { - h.hosts.Lock() - defer h.hosts.Unlock() - positions := h.hosts.l[host] - h.hosts.l[host] = removeFromSliceInt(pos, positions) -} - -func (h *Hosts) getIpPositions(ip string) []int { - h.ips.RLock() - defer h.ips.RUnlock() - i, ok := h.ips.l[ip] - if ok { - return i - } - - return []int{} -} - -func (h *Hosts) addIpPosition(ip string, pos int) { - h.ips.Lock() - defer h.ips.Unlock() - h.ips.l[ip] = append(h.ips.l[ip], pos) -} - -func buildRawLine(ip string, hosts []string) string { - output := ip - for _, host := range hosts { - output = fmt.Sprintf("%s %s", output, host) - } - - return output -} diff --git a/vendor/github.com/goodhosts/hostsfile/hostsline.go b/vendor/github.com/goodhosts/hostsfile/hostsline.go deleted file mode 100644 index aa9f26fb..00000000 --- a/vendor/github.com/goodhosts/hostsfile/hostsline.go +++ /dev/null @@ -1,108 +0,0 @@ -package hostsfile - -import ( - "fmt" - "net" - "sort" - "strings" -) - -type HostsLine struct { - IP string - Hosts []string - Raw string - Err error - Comment string -} - -const commentChar string = "#" - -// NewHostsLine return a new instance of ```HostsLine```. -func NewHostsLine(raw string) HostsLine { - output := HostsLine{Raw: raw} - if output.IsComment() { //whole line is comment - return output - } - - if output.HasComment() { //trailing comment - commentSplit := strings.Split(output.Raw, commentChar) - raw = commentSplit[0] - output.Comment = commentSplit[1] - } - - fields := strings.Fields(raw) - if len(fields) == 0 { - return output - } - - rawIP := fields[0] - if net.ParseIP(rawIP) == nil { - output.Err = fmt.Errorf("bad hosts line: %q", raw) - } - - output.IP = rawIP - output.Hosts = fields[1:] - - return output -} - -func (l *HostsLine) ToRaw() string { - var comment string - if l.IsComment() { //Whole line is comment - return l.Raw - } - - if l.Comment != "" { - comment = fmt.Sprintf(" %s%s", commentChar, l.Comment) - } - - return fmt.Sprintf("%s %s%s", l.IP, strings.Join(l.Hosts, " "), comment) -} - -func (l *HostsLine) RemoveDuplicateHosts() { - unique := make(map[string]struct{}) - for _, h := range l.Hosts { - unique[h] = struct{}{} - } - - l.Hosts = []string{} - for k := range unique { - l.Hosts = append(l.Hosts, k) - } - l.Raw = l.ToRaw() -} - -func (l *HostsLine) Combine(hostline HostsLine) { - l.Hosts = append(l.Hosts, hostline.Hosts...) - if l.Comment == "" { - l.Comment = hostline.Comment - } else { - l.Comment = fmt.Sprintf("%s %s", l.Comment, hostline.Comment) - } - l.Raw = l.ToRaw() -} - -func (l *HostsLine) SortHosts() { - sort.Strings(l.Hosts) - l.Raw = l.ToRaw() -} - -func (l *HostsLine) IsComment() bool { - return strings.HasPrefix(strings.TrimSpace(l.Raw), commentChar) -} - -func (l *HostsLine) HasComment() bool { - return strings.Contains(l.Raw, commentChar) -} - -func (l *HostsLine) IsValid() bool { - return l.IP != "" -} - -func (l *HostsLine) IsMalformed() bool { - return l.Err != nil -} - -func (l *HostsLine) RegenRaw() { - l.Raw = fmt.Sprintf("%s %s", l.IP, strings.Join(l.Hosts, " ")) -} diff --git a/vendor/github.com/goodhosts/hostsfile/make.bat b/vendor/github.com/goodhosts/hostsfile/make.bat deleted file mode 100644 index c4f75661..00000000 --- a/vendor/github.com/goodhosts/hostsfile/make.bat +++ /dev/null @@ -1,27 +0,0 @@ -@echo off -SETLOCAL ENABLEDELAYEDEXPANSION -set LF=^ - -echo %1% -if "%1%"=="" ( - set cmd=ci -) else ( - set cmd=%1% -) - -if "%cmd%" == "ci" ( - for /F %%i in ('goimports -l .') do ( - set "line=%%i" - set goimports=%goimports%!line!!LF! - ) - - if not "!goimports!" == "" ( - goimports -d . - goto :eof - ) - - golangci-lint run || goto :eof - go test -v . || goto :eof - - goto :eof -) diff --git a/vendor/github.com/goodhosts/hostsfile/slice.go b/vendor/github.com/goodhosts/hostsfile/slice.go deleted file mode 100644 index aace3e84..00000000 --- a/vendor/github.com/goodhosts/hostsfile/slice.go +++ /dev/null @@ -1,55 +0,0 @@ -package hostsfile - -func itemInSliceString(item string, list []string) bool { - for _, i := range list { - if i == item { - return true - } - } - return false -} - -func itemInSliceInt(item int, list []int) bool { - for _, i := range list { - if i == item { - return true - } - } - return false -} - -func removeFromSliceString(s string, slice []string) []string { - pos := findPositionInSliceString(s, slice) - for pos > -1 { - slice = append(slice[:pos], slice[pos+1:]...) - pos = findPositionInSliceString(s, slice) - } - return slice -} - -func findPositionInSliceString(s string, slice []string) int { - for index, v := range slice { - if v == s { - return index - } - } - return -1 -} - -func removeFromSliceInt(s int, slice []int) []int { - pos := findPositionInSliceInt(s, slice) - for pos > -1 { - slice = append(slice[:pos], slice[pos+1:]...) - pos = findPositionInSliceInt(s, slice) - } - return slice -} - -func findPositionInSliceInt(s int, slice []int) int { - for index, v := range slice { - if v == s { - return index - } - } - return -1 -} diff --git a/vendor/github.com/goodhosts/hostsfile/utils.go b/vendor/github.com/goodhosts/hostsfile/utils.go deleted file mode 100644 index 46cc58c4..00000000 --- a/vendor/github.com/goodhosts/hostsfile/utils.go +++ /dev/null @@ -1,12 +0,0 @@ -//go:build !windows -//+build !windows - -package hostsfile - -var ( - HostsPerLine = -1 // unlimited - HostsFilePath = "/etc/hosts" - eol = "\n" -) - -func (h *Hosts) preFlushClean() {} // no op diff --git a/vendor/github.com/goodhosts/hostsfile/utils_windows.go b/vendor/github.com/goodhosts/hostsfile/utils_windows.go deleted file mode 100644 index 43e32081..00000000 --- a/vendor/github.com/goodhosts/hostsfile/utils_windows.go +++ /dev/null @@ -1,12 +0,0 @@ -package hostsfile - -var ( - HostsPerLine = 9 - HostsFilePath = "${SystemRoot}/System32/drivers/etc/hosts" - eol = "\r\n" -) - -func (h *Hosts) preFlushClean() { - // need to force hosts per line always on windows see https://github.com/goodhosts/hostsfile/issues/18 - h.HostsPerLine(HostsPerLine) -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 91a343f5..dc40558b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -3,18 +3,12 @@ github.com/Microsoft/go-winio github.com/Microsoft/go-winio/internal/socket github.com/Microsoft/go-winio/pkg/guid -# github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d -## explicit; go 1.13 -github.com/asaskevich/govalidator +# github.com/areYouLazy/libhosty v1.1.0 +## explicit; go 1.16 +github.com/areYouLazy/libhosty # github.com/davecgh/go-spew v1.1.1 ## explicit github.com/davecgh/go-spew/spew -# github.com/dimchansky/utfbom v1.1.1 -## explicit -github.com/dimchansky/utfbom -# github.com/goodhosts/hostsfile v0.1.1 -## explicit; go 1.17 -github.com/goodhosts/hostsfile # github.com/inconshreveable/mousetrap v1.1.0 ## explicit; go 1.18 github.com/inconshreveable/mousetrap