From 34cc47c01067ff28f78b73ff89bfb50534dc1bc8 Mon Sep 17 00:00:00 2001 From: Janos Guljas Date: Sun, 1 Nov 2020 15:33:19 +0100 Subject: [PATCH] Initial commit --- .github/ISSUE_TEMPLATE/bug_report.md | 37 + .github/workflows/go.yml | 49 + .github/workflows/release.yml | 30 + .gitignore | 30 + .goreleaser.yml | 44 + AUTHORS | 3 + CONTRIBUTING.md | 22 + LICENSE | 27 + Makefile | 55 + README.md | 426 +++ cmd/compromised/config.go | 65 + cmd/compromised/config/compromised.go | 91 + cmd/compromised/config/config.go | 34 + cmd/compromised/debug_dump.go | 29 + cmd/compromised/debug_dump_windows.go | 18 + cmd/compromised/help.go | 58 + cmd/compromised/index_passwords.go | 60 + cmd/compromised/main.go | 81 + cmd/compromised/start.go | 195 ++ cmd/compromised/status.go | 25 + cmd/compromised/stop.go | 25 + cmd/compromised/version.go | 16 + doc.go | 7 + go.mod | 21 + go.sum | 602 ++++ pkg/api/api.go | 49 + pkg/api/api_test.go | 101 + pkg/api/doc.go | 8 + pkg/api/export_test.go | 8 + pkg/api/handlers.go | 59 + pkg/api/metrics.go | 41 + pkg/api/routers_test.go | 45 + pkg/api/routes.go | 58 + pkg/api/server.go | 60 + pkg/api/server_test.go | 118 + pkg/approxcount/approxcount.go | 46 + pkg/approxcount/approxcount_test.go | 104 + pkg/metrics/metrics.go | 31 + pkg/metrics/metrics_test.go | 69 + pkg/passwords/file/file.go | 70 + pkg/passwords/file/index.go | 378 +++ pkg/passwords/file/metrics.go | 43 + pkg/passwords/file/service.go | 176 + pkg/passwords/file/service_test.go | 146 + .../pwned-passwords-sha1-ordered-by-hash.txt | 2862 +++++++++++++++++ pkg/passwords/http/export_test.go | 8 + pkg/passwords/http/service.go | 132 + pkg/passwords/http/service_test.go | 104 + pkg/passwords/mock/service.go | 34 + pkg/passwords/passwords.go | 13 + version.go | 18 + 51 files changed, 6831 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/workflows/go.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 .goreleaser.yml create mode 100644 AUTHORS create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 cmd/compromised/config.go create mode 100644 cmd/compromised/config/compromised.go create mode 100644 cmd/compromised/config/config.go create mode 100644 cmd/compromised/debug_dump.go create mode 100644 cmd/compromised/debug_dump_windows.go create mode 100644 cmd/compromised/help.go create mode 100644 cmd/compromised/index_passwords.go create mode 100644 cmd/compromised/main.go create mode 100644 cmd/compromised/start.go create mode 100644 cmd/compromised/status.go create mode 100644 cmd/compromised/stop.go create mode 100644 cmd/compromised/version.go create mode 100644 doc.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 pkg/api/api.go create mode 100644 pkg/api/api_test.go create mode 100644 pkg/api/doc.go create mode 100644 pkg/api/export_test.go create mode 100644 pkg/api/handlers.go create mode 100644 pkg/api/metrics.go create mode 100644 pkg/api/routers_test.go create mode 100644 pkg/api/routes.go create mode 100644 pkg/api/server.go create mode 100644 pkg/api/server_test.go create mode 100644 pkg/approxcount/approxcount.go create mode 100644 pkg/approxcount/approxcount_test.go create mode 100644 pkg/metrics/metrics.go create mode 100644 pkg/metrics/metrics_test.go create mode 100644 pkg/passwords/file/file.go create mode 100644 pkg/passwords/file/index.go create mode 100644 pkg/passwords/file/metrics.go create mode 100644 pkg/passwords/file/service.go create mode 100644 pkg/passwords/file/service_test.go create mode 100644 pkg/passwords/file/testdata/pwned-passwords-sha1-ordered-by-hash.txt create mode 100644 pkg/passwords/http/export_test.go create mode 100644 pkg/passwords/http/service.go create mode 100644 pkg/passwords/http/service_test.go create mode 100644 pkg/passwords/mock/service.go create mode 100644 pkg/passwords/passwords.go create mode 100644 version.go diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..1257116 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,37 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. macOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Server (please complete the following information):** + - Provider: [e.g. AWS EC2] + - OS: [e.g. Debian] + - Version [e.g. 10.6] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..775d132 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,49 @@ +name: Go +on: [push, pull_request] +jobs: + + build: + name: Build + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + + steps: + + - name: Set up Go + uses: actions/setup-go@v1 + with: + go-version: 1.15 + + - name: Set up env + run: | + echo "::set-env name=GOPATH::$(go env GOPATH)" + echo "::add-path::$(go env GOPATH)/bin" + shell: bash + + - name: Checkout + uses: actions/checkout@v1 + with: + fetch-depth: 1 + + - name: Cache Go modules + uses: actions/cache@v1 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.OS }}-build-${{ env.cache-name }}- + ${{ runner.OS }}-build- + ${{ runner.OS }}- + + - name: Lint + if: matrix.os == 'ubuntu-latest' + run: make lint + + - name: Vet + if: matrix.os == 'ubuntu-latest' + run: make vet + + - name: Test + run: make test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..9c20786 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,30 @@ +name: Release + +on: + push: + branches-ignore: + - '**' + tags: + - 'v*.*.*' + +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + + - name: Checkout + uses: actions/checkout@v1 + + - name: Set up Go + uses: actions/setup-go@v1 + with: + go-version: 1.15 + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v1 + with: + version: latest + args: release --rm-dist + key: ${{ secrets.YOUR_PRIVATE_KEY }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..13d4502 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +/dist +/.idea +/.vscode + +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +.DS_Store diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..7dedf63 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,44 @@ +project_name: compromised + +builds: + - main: ./cmd/compromised/main.go + + binary: compromised + + flags: + - -v + - -trimpath + + ldflags: + - -s -w -X resenje.org/compromised.version={{.Version}} -X resenje.org/compromised.commit={{.ShortCommit}} + + env: + - CGO_ENABLED=0 + + goos: + - darwin + - linux + - windows + + goarch: + - amd64 + - 386 + - arm64 + - arm + + ignore: + - goos: darwin + goarch: 386 + - goos: darwin + goarch: arm64 + - goos: darwin + goarch: arm + - goos: windows + goarch: arm64 + - goos: windows + goarch: arm + +archives: + - name_template: "{{ tolower .ProjectName }}-{{ tolower .Os }}-{{ tolower .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" + + format: binary diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..ec88a7c --- /dev/null +++ b/AUTHORS @@ -0,0 +1,3 @@ +# This is the official list of Compromised service authors for copyright purposes. + +Janoš Guljaš diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..9decfec --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,22 @@ +# How to contribute + +We'd love to accept your patches and contributions to this project. There are just a few small guidelines you need to follow. + +1. Code should be `go fmt` formatted. +2. Exported types, constants, variables and functions should be documented. +3. Changes must be covered with tests. +4. All tests must pass constantly `go test .`. + +## Versioning + +Compromised service follows semantic versioning. New functionality should be accompanied by increment to the minor version number. + +## Releasing + +Any code which is complete, tested, reviewed, and merged to master can be released. + +1. Update the `version` number in `version.go`. +2. Make a pull request with these changes. +3. Once the pull request has been merged, visit [https://github.com/janos/compromised/releases](https://github.com/janos/compromised/release) and click `Draft a new release`. +4. Update the `Tag version` and `Release title` field with the new Comprmised CLI version. Be sure the version has a `v` prefixed in both places, e.g. `v1.25.0`. +5. Publish the release. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..55af8ae --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2020, Compromised AUTHORS. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of this project nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4609f2c --- /dev/null +++ b/Makefile @@ -0,0 +1,55 @@ +# Copyright (c) 2020, Compromised AUTHORS. +# All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +GO ?= go +GOLANGCI_LINT ?= $$($(GO) env GOPATH)/bin/golangci-lint +GOLANGCI_LINT_VERSION ?= v1.32.1 + +COMMIT ?= "$(shell git describe --long --dirty --always --match "" || true)" +LDFLAGS ?= -s -w -X resenje.org/compromised.commit="$(COMMIT)" + +.PHONY: all +all: binary lint vet test + +.PHONY: develop +develop: binary run + +.PHONY: binary +binary: export CGO_ENABLED=0 +binary: dist FORCE + $(GO) version + $(GO) build -trimpath -ldflags "$(LDFLAGS)" -o dist/compromised ./cmd/compromised + +dist: + mkdir $@ + +.PHONY: lint +lint: linter + $(GOLANGCI_LINT) run + +.PHONY: linter +linter: + test -f $(GOLANGCI_LINT) || curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $$($(GO) env GOPATH)/bin $(GOLANGCI_LINT_VERSION) + +.PHONY: vet +vet: + $(GO) vet ./... + +.PHONY: test +test: + $(GO) build -trimpath -ldflags "$(LDFLAGS)" ./... + $(GO) test -race -v ./... + +.PHONY: run +run: + ./dist/compromised config + ./dist/compromised + +.PHONY: clean +clean: + $(GO) clean + rm -rf dist/ + +FORCE: diff --git a/README.md b/README.md new file mode 100644 index 0000000..d6206ab --- /dev/null +++ b/README.md @@ -0,0 +1,426 @@ +# Compromised + +[![Go](https://github.com/janos/compromised/workflows/Go/badge.svg)](https://github.com/janos/compromised/actions) +[![PkgGoDev](https://pkg.go.dev/badge/resenje.org/compromised)](https://pkg.go.dev/resenje.org/compromised) +[![NewReleases](https://newreleases.io/badge.svg)](https://newreleases.io/github/janos/compromised) + +**Validate if a password has already been compromised with on-premises service.** + +This service is meant for people and organizations that want to protect their users from using already compromised passwords without exposing any information (password hash or even a part of it) to a third-party service, such is https://haveibeenpwned.com/. The same dataset is used as on haveibeenpwned, but only locally, as it provides the complete dataset do be downloaded. + +This service is created for and used in production by [NewReleases](https://newreleases.io). + +For any online service, NIST [SP 800-63B guidelines](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-63b.pdf) state that user-provided passwords should be checked against existing data breaches. + +This service provides a CLI interface to run an HTTP API service to validate if a specific password has been compromised and how many times. + +Its initial setup is not trivial as it requires a database to be generated from a publicly available data collection, while providing various options to reduce the database size. + +## Installation + +Compromised service binaries have no external dependencies and can just be copied and executed locally. + +Binary downloads of the Compromised service can be found on the [Releases page](https://github.com/janos/compromised/releases/latest). + +To install on Linux: + +```sh +wget https://github.com/janos/compromised/releases/latest/download/compromised-linux-amd64 -O /usr/local/bin/compromised +chmod +x /usr/local/bin/compromised +``` + +You may need additional privileges to write to `/usr/local/bin`, but the file can be saved at any location that you want. + +Supported operating systems and architectures: + +- macOS 64bit `darwin-amd64` +- Linux 64bit `linux-amd64` +- Linux 32bit `linux-386` +- Linux ARM 64bit `linux-arm64` +- Linux ARM 32bit `linux-armv6` +- Windows 64bit `windows-amd64` +- Windows 32bit `windows-386` + +This tool is implemented using the [Go programming language](https://golang.org) and can also be installed by issuing a `go get` command: + +```sh +go get -u resenje.org/compromised/cmd/compromised +``` + +## Usage + +This service does not distribute any passwords or password hashes. It relies on the validity of data provided by https://haveibeenpwned.com/Passwords and provides command to generate a searchable database from that data. + +It provides an HTTP server with a JSON-encoded API endpoint to be used to validate if a password has been compromised and how many times. + +In order to use the service it is required to generate the database and then start the service by loading the database. + +### Getting help + +Descriptions of available commands and flags can be printed with: + +```sh +compromised -h +``` + +``` +USAGE + + compromised [options...] [command] + + Executing the program without specifying a command will start a process in + the foreground and log all messages to stderr. + +COMMANDS + + daemon + Start program in the background. + + stop + Stop program that runs in the background. + + status + Display status of a running process. + + config + Print configuration that program will load on start. This command is + dependent of -config-dir option value. + + debug-dump + Send to a running process USR1 signal to log debug information in the log. + + index-passwords + Generate passwords database from pwned passwords sha1 file. + + version + Print version to Stdout. + +OPTIONS + + -config-dir string + Directory that contains configuration files. + -h Show program usage. +``` + +And flags of the `index-passwords` command: + +```sh +compromised index-passwords -h +``` + +``` +USAGE + + index-passwords [input filename] [output directory] + +OPTIONS + + -h Show program usage. + -hash-counting string + Store approximate hash counts. Possible values: exact, approx, none. (default "exact") + -min-hash-count uint + Skip hashes with counts lower than specified with this flag. (default 1) + -shard-count int + Split hashes into a several files. Possible values: 1, 2, 4, 8, 16, 32, 64, 128, 256. (default 32) +``` + +### Indexing password hashes + +Download Pwned passwords SHA1 ordered by hash 7z file from https://haveibeenpwned.com/Passwords. This file is several gigabytes long (version 6 is 10.1GB) so make sure that you have enough disk space. + +```sh +wget https://downloads.pwnedpasswords.com/passwords/pwned-passwords-sha1-ordered-by-hash-v6.7z +``` + +Extract a textual file from the downloaded 7z archive. This file is roughly twice in size of 7z archive that contains it, around 24G for version 6. Feel free to remove the 7z archive. + +Generate the database with the following command: + +```sh +compromised index-passwords \ + pwned-passwords-sha1-ordered-by-hash-v6.txt \ + compromised-passwords-db +``` + +This command will read the content of `pwned-passwords-sha1-ordered-by-hash-v6.txt` file (make sure that you enter the correct path to it) and store indexes in fast searchable database in `compromised-passwords-db` directory. Command `index-passwords` will create the directory itself and it will stop execution if it already exists. It is expected that the database size is around 12GB. + +By default, all hashes are stored and indexed into 32 files called shards. It is possible to reduce the database size with two optional CLI flags `--hash-counting` and `--min-hash-count`. + +For example: + +```sh +compromised index-passwords \ + --hash-counting approx \ + --min-hash-count 10 \ + pwned-passwords-sha1-ordered-by-hash-v6.txt \ + compromised-passwords +``` + +Flag `--hash-counting` with `approx` value stores approximate hash counts by having exact values for very small values of to around 17 and with the larger values less precise (with variance of around 5%), but close enough to make an estimation on password popularity. With this option, the complete database is 9.7GB large. + +Flag `--hash-counting` with `none` value does not store hash counts and API always returns 1 for count of compromised passwords. With this option, the complete database is 9.3GB large. + +Flag `--min-hash-count` receives a numerical value which filters out all password hashes which have less number of compromisations than specified. This way it is possible to reduce the size of the database by excluding less frequently used passwords. For example by `--min-hash-count 2` only excluding passwords with count 1, the database size is reduced to 7.6GB, or with `--min-hash-count 5` to 1.9GB, or with `--min-hash-count 10` to 800MB. + +You can combine these two options according to available capacity and the level of security and information that you want to provide. + +### Configuration + +Service configuration is stored in configuration file `compromised.yaml` in `/etc/compromised` directory by default. You can change the directory with `--config-dir` flag: + +```sh +compromised --config-dir /data/config/compromised +``` + +All available options and their default values can be printed with: + +```sh +compromised config +``` + +``` +# compromised +--- +listen: :8080 +listen-internal: 127.0.0.1:6060 +headers: + Server: compromised/0.1.0-6ed439e-dirty + X-Frame-Options: SAMEORIGIN +passwords-db: "" +log-dir: "" +log-level: DEBUG +syslog-facility: "" +syslog-tag: compromised +syslog-network: "" +syslog-address: "" +access-log-level: DEBUG +access-syslog-facility: "" +access-syslog-tag: compromised-access +daemon-log-file: daemon.log +daemon-log-file-mode: "644" +pid-file: /var/folders/l4/tn9ytbgs5xx76lshwgx5bj1w0000gn/T/compromised.pid + +# config directories +--- +- /etc/compromised +- /Users/janos/Library/Application Support/compromised +``` + +#### Environment variables + +The service can be configured with environment variables as well. Variable names can be constructed based on the keys in configuration files. + +For variables in `compromised.yaml`, capitalize all letters, replace `-` with `_` and prepend `COMPROMISED_` prefix. For example, to set `passwords-db`, the environment variable is `COMPROMISED_PASSWORDS_DB`: + +```sh +COMPROMISED_PASSWORDS_DB=/path/to/passwrods-db compromised +``` + +### Starting the service + +Executing the program without specifying a command will start a process in the foreground and log all messages to stderr: + +```sh +compromised +``` + +Service requires `passwords-db` directory to be specified: + +```sh +cat /etc/compromised/compromised.yaml +``` + +```yaml +passwords-db: /data/storage/compromised/passwrods +``` + +To write logs to files on local filesystem: + +```sh +cat /etc/compromised/compromised.yaml +``` + +```yaml +passwords-db: /data/storage/compromised/passwrods +log-dir: /data/log/compromised +``` + +Paths in configuration files are given only as examples. + +### Running in the background + +The service can be run in the background and managed by itself with commands: + +```sh +compromised daemon +``` + +```sh +compromised status +``` + +```sh +compromised stop +``` + +Or you can choose a process manager to manage it. For example this is a systemd service file: + +``` +[Unit] +Description=Compromised +After=network.target + +[Service] +ExecStart=/usr/local/bin/compromised +ExecStop=/bin/kill $MAINPID +KillMode=none +Restart=on-failure +RestartPreventExitStatus=255 +LimitNOFILE=65536 +PrivateTmp=true +NoNewPrivileges=true + +[Install] +WantedBy=default.target +``` + +### Using the API + +In order to minimize the exposure of passwords that are checked, only SHA1 hash of a password is accepted by the API. + +First calculate the hash (use printf, not echo as echo is appending new line): + +```sh +printf 12345678 | sha1 +``` + +``` +7c222fb2927d828af22f592134e8932480637c0d +``` + +Then make an HTTP request like this one. + +```sh +curl http://localhost:8080/v1/passwords/7c222fb2927d828af22f592134e8932480637c0d +``` + +```json +{"compromised":true,"count":2996082} +``` + +Make sure that the port is the same as you configured it for the `listen` option. + +Of if you choose a very strong password: + +```sh +printf "my not compromised password" | sha1sum +``` + +``` +d391477a0849048fc28e62850a25518d72afd013 +``` + +Then the HTTP response will look like this: + +```sh +curl http://localhost:8080/v1/passwords/d391477a0849048fc28e62850a25518d72afd013 +``` + +```json +{"compromised":false} +``` + +### Internal API + +Beside the main API, there is another API endpoint, by default available on port `6060` only on `localhost` which exposes some of the internal information about the service: + +- Prometheus metrics `http://localhost:6060/metrics` +- Most basic health check endpoint `http://localhost:6060/status` +- Most basic JSON health check endpoint `http://localhost:6060/api/status` +- Go pprof `http://localhost:6060/debug/pprof/` + +Internal API can be disabled with an empty value for `listen-internal` configuration option in `/etc/compromised/compromised.yaml`: + +```yaml +listen-internal: "" +``` + +## Using the Go library + +As this service is written in the Go programming language, an HTTP client package is provided, but also a package that allows loading the database in your own application if you do not want to manage the `compromised` service. + +### HTTP Client + +```go +package main + +import ( + "contex" + "crypto/sha1" + "fmt" + + httppasswords "resenje.org/compromised/pkg/passwords/http" +) + +func main() { + // url with host and port where compromised service is listening + s, err := httppasswords.New("http://localhost:8080", nil) + if err != nil { + panic(err) + } + + c, err := s.IsPasswordCompromised(contex.Background(), sha1.Sum([]byte("my password"))) + if err != nil { + panic(err) + } + + fmt.Println("this password has been compromised", c, "times") +} +``` + +### Embed DB + +```go +package main + +import ( + "contex" + "crypto/sha1" + "fmt" + + filepasswords "resenje.org/compromised/pkg/passwords/file" +) + +func main() { + s, err := filepasswords.New("/path/to/passwords-db") + if err != nil { + panic(err) + } + defer s.Close() + + c, err := s.IsPasswordCompromised(contex.Background(), sha1.Sum([]byte("my password"))) + if err != nil { + panic(err) + } + + fmt.Println("this password has been compromised", c, "times") +} +``` + +## Versioning + +To see the current version of the binary, execute: + +```sh +compromised version +``` + +Each version is tagged and the version is updated accordingly in `version.go` file. + +## Contributing + +Read the [contribution guidelines](CONTRIBUTING.md). + +## License + +This application is distributed under the BSD-style license found in the [LICENSE](LICENSE) file. diff --git a/cmd/compromised/config.go b/cmd/compromised/config.go new file mode 100644 index 0000000..db88432 --- /dev/null +++ b/cmd/compromised/config.go @@ -0,0 +1,65 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "resenje.org/x/application" + + "resenje.org/compromised/cmd/compromised/config" +) + +var ( + // Initialize configurations with default values. + options = config.NewCompromisedOptions() +) + +func init() { + // Register options in config. + cfg.Register(config.Name, options) +} + +var cfg = application.NewConfig(config.Name) + +func configCmd() { + // Print loaded configuration. + fmt.Print(cfg.String()) +} + +func updateConfig() { + if *configDir == "" { + *configDir = os.Getenv(strings.ToUpper(config.Name) + "_CONFIGDIR") + } + if *configDir == "" { + *configDir = config.Dir + } + + cfg.Dirs = []string{ + *configDir, + } + if d, err := os.UserConfigDir(); err == nil { + cfg.Dirs = append(cfg.Dirs, filepath.Join(d, config.Name)) + } + if err := cfg.Load(); err != nil { + fmt.Fprintln(os.Stderr, "Error: ", err) + os.Exit(2) + } +} + +func verifyAndPrepareConfig() { + if err := cfg.VerifyAndPrepare(); err != nil { + fmt.Fprintln(os.Stderr, "Error:", err) + if e, ok := err.(*application.HelpError); ok { + fmt.Println() + fmt.Println(e.Help) + } + os.Exit(2) + } +} diff --git a/cmd/compromised/config/compromised.go b/cmd/compromised/config/compromised.go new file mode 100644 index 0000000..9b3f722 --- /dev/null +++ b/cmd/compromised/config/compromised.go @@ -0,0 +1,91 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package config + +import ( + "net" + "os" + "path/filepath" + + "resenje.org/compromised" + "resenje.org/logging" + "resenje.org/marshal" +) + +// CompromisedOptions defines parameters related to service's core functionality. +type CompromisedOptions struct { + // HTTP + Listen string `json:"listen" yaml:"listen" envconfig:"LISTEN"` + ListenInternal string `json:"listen-internal" yaml:"listen-internal" envconfig:"LISTEN_INTERNAL"` + Headers map[string]string `json:"headers" yaml:"headers" envconfig:"HEADERS"` + // Passwords + PasswordsDB string `json:"passwords-db" yaml:"passwords-db" envconfig:"PASSWORDS_DB"` + // Logging + LogDir string `json:"log-dir" yaml:"log-dir" envconfig:"LOG_DIR"` + LogLevel logging.Level `json:"log-level" yaml:"log-level" envconfig:"LOG_LEVEL"` + SyslogFacility logging.SyslogFacility `json:"syslog-facility" yaml:"syslog-facility" envconfig:"SYSLOG_FACILITY"` + SyslogTag string `json:"syslog-tag" yaml:"syslog-tag" envconfig:"SYSLOG_TAG"` + SyslogNetwork string `json:"syslog-network" yaml:"syslog-network" envconfig:"SYSLOG_NETWORK"` + SyslogAddress string `json:"syslog-address" yaml:"syslog-address" envconfig:"SYSLOG_ADDRESS"` + AccessLogLevel logging.Level `json:"access-log-level" yaml:"access-log-level" envconfig:"ACCESS_LOG_LEVEL"` + AccessSyslogFacility logging.SyslogFacility `json:"access-syslog-facility" yaml:"access-syslog-facility" envconfig:"ACCESS_SYSLOG_FACILITY"` + AccessSyslogTag string `json:"access-syslog-tag" yaml:"access-syslog-tag" envconfig:"ACCESS_SYSLOG_TAG"` + // Daemon + DaemonLogFileName string `json:"daemon-log-file" yaml:"daemon-log-file" envconfig:"DAEMON_LOG_FILE"` + DaemonLogFileMode marshal.Mode `json:"daemon-log-file-mode" yaml:"daemon-log-file-mode" envconfig:"DAEMON_LOG_FILE_MODE"` + PidFileName string `json:"pid-file" yaml:"pid-file" envconfig:"PID_FILE"` +} + +// NewCompromisedOptions initializes CompromisedOptions with default values. +func NewCompromisedOptions() *CompromisedOptions { + return &CompromisedOptions{ + Listen: ":8080", + ListenInternal: "127.0.0.1:6060", + Headers: map[string]string{ + "Server": Name + "/" + compromised.Version, + "X-Frame-Options": "SAMEORIGIN", + }, + PasswordsDB: "", + LogDir: "", + LogLevel: logging.DEBUG, + SyslogFacility: "", + SyslogTag: Name, + SyslogNetwork: "", + SyslogAddress: "", + AccessLogLevel: logging.DEBUG, + AccessSyslogFacility: "", + AccessSyslogTag: Name + "-access", + DaemonLogFileName: "daemon.log", + DaemonLogFileMode: 0644, + PidFileName: filepath.Join(os.TempDir(), Name+".pid"), + } +} + +// VerifyAndPrepare implements application.Options interface. +func (o *CompromisedOptions) VerifyAndPrepare() (err error) { + ln, err := net.Listen("tcp", o.Listen) + if err != nil { + return + } + ln.Close() + ln, err = net.Listen("tcp", o.ListenInternal) + if err != nil { + return + } + ln.Close() + + for _, dir := range []string{ + filepath.Dir(o.PidFileName), + o.LogDir, + } { + if dir != "" { + if err := os.MkdirAll(dir, 0777); err != nil { + return err + } + } + } + return +} diff --git a/cmd/compromised/config/config.go b/cmd/compromised/config/config.go new file mode 100644 index 0000000..9e8424c --- /dev/null +++ b/cmd/compromised/config/config.go @@ -0,0 +1,34 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package config holds project and service related data and structures +// that define optional parameters for different parts of the service. +package config + +import ( + "os" + "path/filepath" +) + +var ( + // Name is the name of the service. + Name = "compromised" + + // BaseDir is the directory where the service's executable is located. + BaseDir = func() string { + path, err := os.Executable() + if err != nil { + panic(err) + } + path, err = filepath.EvalSymlinks(path) + if err != nil { + panic(err) + } + return filepath.Dir(path) + }() + + // Dir is default directory where configuration files are located. + Dir = "/etc/compromised" +) diff --git a/cmd/compromised/debug_dump.go b/cmd/compromised/debug_dump.go new file mode 100644 index 0000000..a1416c3 --- /dev/null +++ b/cmd/compromised/debug_dump.go @@ -0,0 +1,29 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !windows + +package main + +import ( + "fmt" + "os" + "syscall" + + "resenje.org/daemon" +) + +func debugDumpCmd() { + // Send SIGUSR1 signal to a daemonized process. + // Service is able to receive the signal and dump debugging + // information to files or stderr. + err := (&daemon.Daemon{ + PidFileName: options.PidFileName, + }).Signal(syscall.SIGUSR1) + if err != nil { + fmt.Fprintln(os.Stderr, "Error:", err) + os.Exit(2) + } +} diff --git a/cmd/compromised/debug_dump_windows.go b/cmd/compromised/debug_dump_windows.go new file mode 100644 index 0000000..2ccf76f --- /dev/null +++ b/cmd/compromised/debug_dump_windows.go @@ -0,0 +1,18 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package main + +import ( + "fmt" + "os" +) + +func debugDumpCmd() { + fmt.Fprintln(os.Stderr, "Debug dump is not supported on Windows") + os.Exit(2) +} diff --git a/cmd/compromised/help.go b/cmd/compromised/help.go new file mode 100644 index 0000000..943f009 --- /dev/null +++ b/cmd/compromised/help.go @@ -0,0 +1,58 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "os" +) + +var ( + usage = `USAGE + + %s [options...] [command] + + Executing the program without specifying a command will start a process in + the foreground and log all messages to stderr. + +COMMANDS + + daemon + Start program in the background. + + stop + Stop program that runs in the background. + + status + Display status of a running process. + + config + Print configuration that program will load on start. This command is + dependent of -config-dir option value. + + debug-dump + Send to a running process USR1 signal to log debug information in the log. + + index-passwords + Generate passwords database from pwned passwords sha1 file. + + version + Print version to Stdout. + +OPTIONS + +` +) + +func helpCmd() { + cli.Usage() +} + +func helpUnknownCmd(cmd string) { + fmt.Fprintln(os.Stderr, "unknown command:", cmd) + cli.Usage() + os.Exit(2) +} diff --git a/cmd/compromised/index_passwords.go b/cmd/compromised/index_passwords.go new file mode 100644 index 0000000..380545c --- /dev/null +++ b/cmd/compromised/index_passwords.go @@ -0,0 +1,60 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "flag" + "fmt" + "os" + + filepasswords "resenje.org/compromised/pkg/passwords/file" +) + +func indexPasswordsCmd() { + cli := flag.NewFlagSet("index-passwords", flag.ExitOnError) + + minHashCount := cli.Uint64("min-hash-count", 1, "Skip hashes with counts lower than specified with this flag.") + shardCount := cli.Int("shard-count", 32, "Split hashes into a several files. Possible values: 1, 2, 4, 8, 16, 32, 64, 128, 256.") + hashCounting := cli.String("hash-counting", "exact", "Store approximate hash counts. Possible values: exact, approx, none.") + + help := cli.Bool("h", false, "Show program usage.") + + cli.Usage = func() { + fmt.Fprintf(os.Stderr, `USAGE + + index-passwords [input filename] [output directory] + +OPTIONS + +`) + cli.PrintDefaults() + } + + if err := cli.Parse(os.Args[2:]); err != nil { + cli.Usage() + os.Exit(2) + } + + if *help { + cli.Usage() + return + } + + if cli.NArg() != 2 { + fmt.Fprintln(os.Stderr, "index-passwords command requires two arguments: input filename and output directory") + cli.Usage() + os.Exit(2) + } + + if err := filepasswords.Index(cli.Arg(0), cli.Arg(1), &filepasswords.IndexOptions{ + MinHashCount: *minHashCount, + ShardCount: *shardCount, + HashCounting: filepasswords.HashCounting(*hashCounting), + }); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(2) + } +} diff --git a/cmd/compromised/main.go b/cmd/compromised/main.go new file mode 100644 index 0000000..4fec63b --- /dev/null +++ b/cmd/compromised/main.go @@ -0,0 +1,81 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package compromised creates executable for the compromised program. +// +// Configuration loading, validation and initialization of all required +// services for server to function is done in this package. It integrates +// all server dependencies into a form of a single executable. + +package main + +import ( + "flag" + "fmt" + "os" +) + +var ( + cli = flag.NewFlagSet(os.Args[0], flag.ExitOnError) + + configDir = cli.String("config-dir", "", "Directory that contains configuration files.") + help = cli.Bool("h", false, "Show program usage.") +) + +func main() { + cli.Usage = func() { + fmt.Fprintf(os.Stderr, usage, os.Args[0]) + cli.PrintDefaults() + } + + if err := cli.Parse(os.Args[1:]); err != nil { + helpCmd() + return + } + + cmd := cli.Arg(0) + + if *help && cmd == "" { + helpCmd() + return + } + + switch cmd { + case "index-passwords": + indexPasswordsCmd() + return + + case "version": + versionCmd() + return + + } + + updateConfig() + + switch cmd { + case "", "daemon": + verifyAndPrepareConfig() + startCmd(cmd == "daemon") + + case "stop": + stopCmd() + + case "status": + statusCmd() + + case "debug-dump": + debugDumpCmd() + + case "config": + configCmd() + + case "index-passwords": + indexPasswordsCmd() + + default: + helpUnknownCmd(cmd) + } +} diff --git a/cmd/compromised/start.go b/cmd/compromised/start.go new file mode 100644 index 0000000..592597d --- /dev/null +++ b/cmd/compromised/start.go @@ -0,0 +1,195 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "context" + "fmt" + "io" + "os" + "sync" + "syscall" + "time" + + loggingpromethues "resenje.org/logging/prometheus" + "resenje.org/recovery" + "resenje.org/web/server" + "resenje.org/x/application" + + "resenje.org/compromised" + "resenje.org/compromised/cmd/compromised/config" + "resenje.org/compromised/pkg/api" + "resenje.org/compromised/pkg/metrics" + filepasswords "resenje.org/compromised/pkg/passwords/file" +) + +func startCmd(daemon bool) { + if options.PasswordsDB == "" { + fmt.Fprintln(os.Stderr, `Passwords database is not configured. + +Download Pwned passwords SHA1 ordered by hash from https://haveibeenpwned.com/Passwords and execute index-database command to generate a database. + +Refer to https://resenje.org/compromised documentation.`) + os.Exit(2) + return + } + + // Initialize the application with loaded options. + app, err := application.NewApp( + config.Name, + application.AppOptions{ + LogDir: options.LogDir, + PidFileName: options.PidFileName, + DaemonLogFileName: options.DaemonLogFileName, + DaemonLogFileMode: options.DaemonLogFileMode.FileMode(), + }) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(2) + } + + // Functions that will be called in parallel on application shutdown. + var shutdownFuncs []func() error + + // Setup logging. + loggers := application.NewLoggers( + application.WithForcedWriter(func() io.Writer { + if options.LogDir == "" { + return os.Stderr + } + return nil + }()), + ) + loggingCounter := loggingpromethues.NewCounter(&loggingpromethues.CounterOptions{ + Namespace: metrics.Namespace, + }) + logger := loggers.NewLogger("default", options.LogLevel, + application.NewTimedFileHandler(options.LogDir, config.Name), + application.NewSyslogHandler( + options.SyslogFacility, + options.SyslogTag, + options.SyslogNetwork, + options.SyslogAddress, + ), + loggingpromethues.NewHandler(loggingCounter, options.LogLevel), + ) + application.SetStdLogger() + accessLogger := loggers.NewLogger("access", options.AccessLogLevel, + application.NewTimedFileHandler(options.LogDir, "access"), + application.NewSyslogHandler( + options.AccessSyslogFacility, + options.AccessSyslogTag, + options.SyslogNetwork, + options.SyslogAddress, + ), + ) + + // Log application version on start + app.Functions = append(app.Functions, func() (err error) { + logger.Infof("version: %v", compromised.Version) + return nil + }) + + // Recovery service provides unified way of logging and notifying + // panic events. + recoveryService := &recovery.Service{ + Version: compromised.Version, + } + + // Initialize server. + srv, err := server.New(server.Options{ + Name: config.Name, + Version: compromised.Version, + ListenInternal: options.ListenInternal, + Logger: logger, + RecoveryService: recoveryService, + }) + if err != nil { + fmt.Fprintln(os.Stderr, "server:", err) + os.Exit(2) + } + srv.WithMetrics(loggingCounter.Metrics()...) + + passwordsService, err := filepasswords.New(options.PasswordsDB) + if err != nil { + fmt.Fprintln(os.Stderr, "passwords service:", err) + os.Exit(2) + } + srv.WithMetrics(passwordsService.Metrics()...) + shutdownFuncs = append(shutdownFuncs, passwordsService.Close) + + srvOptions := server.HTTPOptions{ + Name: config.Name, + Listen: options.Listen, + } + + apiHandler, err := api.New(api.Options{ + Version: compromised.Version, + Headers: options.Headers, + Logger: logger, + AccessLogger: accessLogger, + RecoveryService: recoveryService, + PasswordsService: passwordsService, + }) + if err != nil { + fmt.Fprintln(os.Stderr, "api:", err) + os.Exit(2) + } + srv.WithMetrics(apiHandler.Metrics()...) + srvOptions.SetHandler(apiHandler) + + // Configure main HTTP web server. + if err := srv.WithHTTP(srvOptions); err != nil { + fmt.Fprintln(os.Stderr, "configure", srvOptions.Name, "server:", err) + os.Exit(2) + } + + // Start web server. + app.Functions = append(app.Functions, srv.Serve) + + // Define shutdown function. + app.ShutdownFunc = func() error { + // Shutdown web server. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + srv.Shutdown(ctx) + cancel() + + // Shutdown all services in parallel. + var wg sync.WaitGroup + for _, shutdown := range shutdownFuncs { + wg.Add(1) + go func(shutdown func() error) { + defer wg.Done() + if err := shutdown(); err != nil { + logger.Errorf("shutting down: %v", err) + } + }(shutdown) + } + done := make(chan struct{}) + go func() { + defer close(done) + wg.Wait() + }() + select { + case <-time.After(10 * time.Second): + case <-done: + } + return nil + } + + // Put the process in the background only if the Pid is not 1 + // (for example in docker) and the command is `daemon`. + if syscall.Getpid() != 1 && daemon { + app.Daemonize() + } + + // Finally start the server. + // This is a blocking function. + if err := app.Start(logger); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(2) + } +} diff --git a/cmd/compromised/status.go b/cmd/compromised/status.go new file mode 100644 index 0000000..39d86ef --- /dev/null +++ b/cmd/compromised/status.go @@ -0,0 +1,25 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "os" + + "resenje.org/daemon" +) + +func statusCmd() { + // Use daemon.Daemon to obtain status information and print it. + pid, err := (&daemon.Daemon{ + PidFileName: options.PidFileName, + }).Status() + if err != nil { + fmt.Fprintln(os.Stderr, "Not running:", err) + os.Exit(2) + } + fmt.Println("Running: PID", pid) +} diff --git a/cmd/compromised/stop.go b/cmd/compromised/stop.go new file mode 100644 index 0000000..593d50f --- /dev/null +++ b/cmd/compromised/stop.go @@ -0,0 +1,25 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "os" + + "resenje.org/daemon" + "resenje.org/x/application" +) + +func stopCmd() { + err := application.StopDaemon(daemon.Daemon{ + PidFileName: options.PidFileName, + }) + if err != nil { + fmt.Fprintln(os.Stderr, "Error:", err) + os.Exit(2) + } + fmt.Println("Stopped") +} diff --git a/cmd/compromised/version.go b/cmd/compromised/version.go new file mode 100644 index 0000000..67023b6 --- /dev/null +++ b/cmd/compromised/version.go @@ -0,0 +1,16 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + + "resenje.org/compromised" +) + +func versionCmd() { + fmt.Println(compromised.Version) +} diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..ef97cba --- /dev/null +++ b/doc.go @@ -0,0 +1,7 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package compromised is the Compromised service implementation. +package compromised diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c0c1ccb --- /dev/null +++ b/go.mod @@ -0,0 +1,21 @@ +module resenje.org/compromised + +go 1.15 + +require ( + github.com/gorilla/handlers v1.5.1 + github.com/gorilla/mux v1.8.0 + github.com/prometheus/client_golang v1.8.0 + golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect + golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 // indirect + golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 // indirect + golang.org/x/text v0.3.4 // indirect + gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect + resenje.org/daemon v0.1.2 + resenje.org/jsonhttp v0.2.0 + resenje.org/logging v0.1.7 + resenje.org/marshal v0.1.1 + resenje.org/recovery v0.1.1 + resenje.org/web v0.5.0 + resenje.org/x v0.2.4 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..032e6d1 --- /dev/null +++ b/go.sum @@ -0,0 +1,602 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= +dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= +dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= +dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= +dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= +git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= +github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v1.5.0/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lucas-clemente/quic-go v0.18.0/go.mod h1:yXttHsSNxQi8AWijC/vLP+OJczXqzHSOcJrM5ITUlCg= +github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/marten-seemann/qpack v0.2.0/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= +github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= +github.com/marten-seemann/qtls-go1-15 v0.1.0/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.8.0 h1:zvJNkoCFAnYFNC24FV8nW4JdRJ3GIFcLbg65lL/JDcw= +github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.13.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4= +github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= +github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= +github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= +github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= +github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= +github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= +github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= +github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= +github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= +github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= +github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= +github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= +github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= +github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= +github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= +github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= +github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= +golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 h1:HlFl4V6pEMziuLXyRkm5BIYq1y1GAbb02pRlWvI54OM= +golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk= +gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +resenje.org/daemon v0.1.2 h1:aalIaDzo8YRMm9wiYb/fc8CPAsZc/rerc6mmzshABLg= +resenje.org/daemon v0.1.2/go.mod h1:mF5JRpH3EbrxI9WoeKY78e6PqSsbBtX9jAQL5vj/GBA= +resenje.org/email v0.1.3 h1:sOnX0nnJPQjvhceYUVO9p2ZiWe5qurmC38rZRccMhJ8= +resenje.org/email v0.1.3/go.mod h1:OhAVLRG3vqd9NSgayN3pAgzxTmc2B6mAefgShZvEgf0= +resenje.org/jsonhttp v0.2.0 h1:muOrhvgScixgVKAlYFcB8CKNxH95gnsqrf61P0Mpze0= +resenje.org/jsonhttp v0.2.0/go.mod h1:EDyeguyTWj2fU3D3SCE0qNTgthzyEkHYLM1uu0uikHU= +resenje.org/logging v0.1.5/go.mod h1:1IdoCm3+UwYfsplxDGV2pHCkUrLlQzlWwp4r28XfPx4= +resenje.org/logging v0.1.6/go.mod h1:2YqLTlqQnaI225kfv+W9jajFicf6KYskQApr2sTlCBc= +resenje.org/logging v0.1.7 h1:KUNlErIBNXgiAWkfioXZDX27RLk2WpfgTwLl6989lPo= +resenje.org/logging v0.1.7/go.mod h1:HRMBfynFd/bqNROK2r5JXDtamHQOlZS5VWT0VxpoH2Q= +resenje.org/marshal v0.1.1 h1:ok/SWjtD10u0juBj9FRzPq93KtVjNAwEp9wZc6ZGwE4= +resenje.org/marshal v0.1.1/go.mod h1:P7Cla6Ju5CFvW4Y8JbRgWX1Hcy4L1w4qcCsyadO7G94= +resenje.org/recovery v0.1.1 h1:/mXWD5S97BKF2B1/GxRUnWRuX2ZK4/4vSO70vsO1IYc= +resenje.org/recovery v0.1.1/go.mod h1:3S6aCVKMJEWsSAb61oZTteaiqkIfQPTr1RdiWnRbhME= +resenje.org/web v0.5.0 h1:PQuGFU97mQ3Sna5XHa+kAL9EsHm+B/OU1DKS3gKeqRM= +resenje.org/web v0.5.0/go.mod h1:haitTvrR/4bV+0sBmLqS/OQ3rhBekxQh0g/+P/Y3hfQ= +resenje.org/x v0.2.4 h1:nr0uymRRpSG/dhAYXfGvaJwiGIZSE9fLqmJy1kfvBBA= +resenje.org/x v0.2.4/go.mod h1:1b2Xpo29FRc3IMvg/u46/IyjySl5IjvtuSjXTA/AOnk= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= +sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= +sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/pkg/api/api.go b/pkg/api/api.go new file mode 100644 index 0000000..0b04c9b --- /dev/null +++ b/pkg/api/api.go @@ -0,0 +1,49 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package api + +import ( + "encoding/hex" + "net/http" + + "github.com/gorilla/mux" + "resenje.org/jsonhttp" +) + +type passwordResponse struct { + Compromised bool `json:"compromised"` + Count uint64 `json:"count,omitempty"` +} + +func (s *server) passwordHandler(w http.ResponseWriter, r *http.Request) { + hash := mux.Vars(r)["hash"] + + if len(hash) != 40 { + jsonhttp.NotFound(w, nil) + return + } + + slice, err := hex.DecodeString(hash) + if err != nil { + jsonhttp.NotFound(w, nil) + return + } + + var sum [20]byte + copy(sum[:], slice) + + count, err := s.PasswordsService.IsPasswordCompromised(r.Context(), sum) + if err != nil { + s.Logger.Errorf("api password handler: is password %x compromised: %v", sum, err) + jsonhttp.InternalServerError(w, nil) + return + } + + jsonhttp.OK(w, passwordResponse{ + Compromised: count > 0, + Count: count, + }) +} diff --git a/pkg/api/api_test.go b/pkg/api/api_test.go new file mode 100644 index 0000000..46a44f5 --- /dev/null +++ b/pkg/api/api_test.go @@ -0,0 +1,101 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package api_test + +import ( + "context" + "encoding/hex" + "errors" + "net/http" + "testing" + + "resenje.org/compromised/pkg/api" + mockpasswords "resenje.org/compromised/pkg/passwords/mock" + "resenje.org/jsonhttp" +) + +func TestPassword_notCompromised(t *testing.T) { + sum := [20]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19} + var gotSum [20]byte + c := newTestServer(t, testServerOptions{ + PasswordsService: mockpasswords.New(func(_ context.Context, s [20]byte) (uint64, error) { + gotSum = s + return 0, nil + }), + }) + + var r api.PasswordResponse + testResponseUnmarshal(t, c, http.MethodGet, "/v1/passwords/"+hex.EncodeToString(sum[:]), nil, http.StatusOK, &r) + + if gotSum != sum { + t.Errorf("got sum %v, want %v", gotSum, sum) + } + + if r.Compromised { + t.Error("want not compromised") + } + + if r.Count != 0 { + t.Errorf("got count %v, want 0", r.Count) + } +} + +func TestPassword_compromised(t *testing.T) { + sum := [20]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19} + var gotSum [20]byte + c := newTestServer(t, testServerOptions{ + PasswordsService: mockpasswords.New(func(_ context.Context, s [20]byte) (uint64, error) { + gotSum = s + return 10, nil + }), + }) + + var r api.PasswordResponse + testResponseUnmarshal(t, c, http.MethodGet, "/v1/passwords/"+hex.EncodeToString(sum[:]), nil, http.StatusOK, &r) + + if gotSum != sum { + t.Errorf("got sum %v, want %v", gotSum, sum) + } + + if !r.Compromised { + t.Error("want compromised") + } + + if r.Count != 10 { + t.Errorf("got count %v, want 10", r.Count) + } +} + +func TestPassword_shortSum(t *testing.T) { + c := newTestServer(t, testServerOptions{}) + + testResponseDirect(t, c, http.MethodGet, "/v1/passwords/1234", nil, http.StatusNotFound, jsonhttp.StatusResponse{ + Code: http.StatusNotFound, + Message: http.StatusText(http.StatusNotFound), + }) +} + +func TestPassword_invalidSum(t *testing.T) { + c := newTestServer(t, testServerOptions{}) + + testResponseDirect(t, c, http.MethodGet, "/v1/passwords/g1234567890abcdef1234567890abcdef1234567", nil, http.StatusNotFound, jsonhttp.StatusResponse{ + Code: http.StatusNotFound, + Message: http.StatusText(http.StatusNotFound), + }) +} + +func TestPassword_error(t *testing.T) { + c := newTestServer(t, testServerOptions{ + PasswordsService: mockpasswords.New(func(_ context.Context, s [20]byte) (uint64, error) { + return 0, errors.New("test error") + }), + }) + + testResponseDirect(t, c, http.MethodGet, "/v1/passwords/01234567890abcdef1234567890abcdef1234567", nil, http.StatusInternalServerError, jsonhttp.StatusResponse{ + Code: http.StatusInternalServerError, + Message: http.StatusText(http.StatusInternalServerError), + }) +} diff --git a/pkg/api/doc.go b/pkg/api/doc.go new file mode 100644 index 0000000..771e50e --- /dev/null +++ b/pkg/api/doc.go @@ -0,0 +1,8 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package api provides a service that handles HTTP requests using JSON-encoded +// messages. +package api diff --git a/pkg/api/export_test.go b/pkg/api/export_test.go new file mode 100644 index 0000000..4852fea --- /dev/null +++ b/pkg/api/export_test.go @@ -0,0 +1,8 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package api + +type PasswordResponse = passwordResponse diff --git a/pkg/api/handlers.go b/pkg/api/handlers.go new file mode 100644 index 0000000..3e2496a --- /dev/null +++ b/pkg/api/handlers.go @@ -0,0 +1,59 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package api + +import ( + "net/http" + "time" + + "resenje.org/jsonhttp" + "resenje.org/web" + accessLog "resenje.org/web/log/access" + "resenje.org/web/recovery" +) + +func (s *server) accessLogHandler(h http.Handler) http.Handler { + return accessLog.NewHandler(h, s.AccessLogger) +} + +func (s *server) jsonRecoveryHandler(h http.Handler) http.Handler { + return recovery.New(h, + recovery.WithLabel(s.Version), + recovery.WithLogFunc(s.Logger.Errorf), + recovery.WithPanicResponse(`{"message":"Internal Server Error","code":500}`, "application/json; charset=utf-8"), + ) +} + +func (s *server) pageviewMetricsHandler(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + s.metrics.PageviewCount.Inc() + h.ServeHTTP(w, r) + s.metrics.ResponseDuration.Observe(time.Since(start).Seconds()) + }) +} + +func jsonMaxBodyBytesHandler(h http.Handler) http.Handler { + return web.MaxBodyBytesHandler{ + Handler: h, + Limit: 2 * 1024 * 1024, + BodyFunc: func(r *http.Request) (string, error) { + return `{"message":"Request Entity Too Large","code":413}`, nil + }, + ContentType: "application/json; charset=utf-8", + ErrorHandler: nil, + } +} + +func jsonNotFoundHandler(w http.ResponseWriter, r *http.Request) { + jsonhttp.NotFound(w, nil) +} + +type jsonMethodHandler map[string]http.Handler + +func (h jsonMethodHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + web.HandleMethods(h, `{"message":"Method Not Allowed","code":405}`, "application/json; charset=utf-8", w, r) +} diff --git a/pkg/api/metrics.go b/pkg/api/metrics.go new file mode 100644 index 0000000..002224d --- /dev/null +++ b/pkg/api/metrics.go @@ -0,0 +1,41 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package api + +import ( + "github.com/prometheus/client_golang/prometheus" + m "resenje.org/compromised/pkg/metrics" +) + +type metrics struct { + // declare metrics + // must be exported to register them automatically using reflect + PageviewCount prometheus.Counter + ResponseDuration prometheus.Histogram +} + +func newMetrics() metrics { + subsystem := "api" + + return metrics{ + PageviewCount: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: m.Namespace, + Subsystem: subsystem, + Name: "request_count", + Help: "Number of API requests.", + }), + ResponseDuration: prometheus.NewHistogram(prometheus.HistogramOpts{ + Namespace: m.Namespace, + Subsystem: subsystem, + Name: "response_duration_seconds", + Help: "Histogram of API response durations.", + Buckets: []float64{0.01, 0.1, 0.25, 0.5, 1, 2.5, 5, 10}, + }), + } +} +func (s *server) Metrics() (cs []prometheus.Collector) { + return m.PrometheusCollectorsFromFields(s.metrics) +} diff --git a/pkg/api/routers_test.go b/pkg/api/routers_test.go new file mode 100644 index 0000000..f84eb50 --- /dev/null +++ b/pkg/api/routers_test.go @@ -0,0 +1,45 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package api_test + +import ( + "io/ioutil" + "net/http" + "testing" + + "resenje.org/jsonhttp" +) + +func TestRoot(t *testing.T) { + c := newTestServer(t, testServerOptions{}) + + testResponseDirect(t, c, http.MethodGet, "/", nil, http.StatusNotFound, jsonhttp.StatusResponse{ + Code: http.StatusNotFound, + Message: http.StatusText(http.StatusNotFound), + }) +} + +func TestRobotsTxt(t *testing.T) { + c := newTestServer(t, testServerOptions{}) + + resp, err := request(c, http.MethodGet, "/robots.txt", nil) + if err != nil { + t.Fatal(err) + } + defer resp.Body.Close() + + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatal(err) + } + + got := string(b) + want := "User-agent: *\nDisallow: /\n" + + if got != want { + t.Errorf("got response %q, want %q", got, want) + } +} diff --git a/pkg/api/routes.go b/pkg/api/routes.go new file mode 100644 index 0000000..796e6e0 --- /dev/null +++ b/pkg/api/routes.go @@ -0,0 +1,58 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package api + +import ( + "fmt" + "net/http" + + "github.com/gorilla/handlers" + "github.com/gorilla/mux" + "resenje.org/web" +) + +func newRouter(s *server) http.Handler { + // Top level router + baseRouter := http.NewServeMux() + + baseRouter.HandleFunc("/robots.txt", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "User-agent: *\nDisallow: /") + }) + + // Default route for all unknown paths + baseRouter.HandleFunc("/", jsonNotFoundHandler) + + // API v1 router + baseRouter.Handle("/v1/", newV1Router(s)) + + // Final handler + return web.ChainHandlers( + handlers.CompressHandler, + s.jsonRecoveryHandler, + s.accessLogHandler, + func(h http.Handler) http.Handler { + return web.NewSetHeadersHandler(h, s.Headers) + }, + web.FinalHandler(baseRouter), + ) +} + +func newV1Router(s *server) http.Handler { + r := mux.NewRouter().StrictSlash(true) + r.UseEncodedPath() + r.NotFoundHandler = http.HandlerFunc(jsonNotFoundHandler) + + r.Handle("/v1/passwords/{hash}", jsonMethodHandler{ + "GET": http.HandlerFunc(s.passwordHandler), + }) + + return web.ChainHandlers( + jsonMaxBodyBytesHandler, + s.pageviewMetricsHandler, + web.NoCacheHeadersHandler, + web.FinalHandler(r), + ) +} diff --git a/pkg/api/server.go b/pkg/api/server.go new file mode 100644 index 0000000..c6d19b0 --- /dev/null +++ b/pkg/api/server.go @@ -0,0 +1,60 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package api + +import ( + "net/http" + + "github.com/prometheus/client_golang/prometheus" + "resenje.org/compromised/pkg/passwords" + "resenje.org/logging" + "resenje.org/recovery" +) + +// Handler is an http.Handler with exposed metrics. +type Handler interface { + http.Handler + Metrics() (cs []prometheus.Collector) +} + +type server struct { + Options + + handler http.Handler + metrics metrics +} + +// Options structure contains optional properties for the Handler. +type Options struct { + Version string + Headers map[string]string + + Logger *logging.Logger + AccessLogger *logging.Logger + + RecoveryService *recovery.Service + + PasswordsService passwords.Service +} + +// New initializes a new Handler with provided options. +func New(o Options) (h Handler, err error) { + if o.Version == "" { + o.Version = "0" + } + s := &server{ + Options: o, + metrics: newMetrics(), + } + + s.handler = newRouter(s) + + return s, nil +} + +func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + s.handler.ServeHTTP(w, r) +} diff --git a/pkg/api/server_test.go b/pkg/api/server_test.go new file mode 100644 index 0000000..dd9a8cd --- /dev/null +++ b/pkg/api/server_test.go @@ -0,0 +1,118 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package api_test + +import ( + "bytes" + "encoding/json" + "io" + "io/ioutil" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "resenje.org/compromised" + "resenje.org/compromised/pkg/api" + "resenje.org/compromised/pkg/passwords" + "resenje.org/logging" + "resenje.org/recovery" + "resenje.org/web" +) + +type testServerOptions struct { + PasswordsService passwords.Service +} + +func newTestServer(t *testing.T, o testServerOptions) *http.Client { + version := "0.1.0-test" + logger, _ := logging.GetLogger("default") + + s, err := api.New(api.Options{ + Version: version, + Logger: logger, + AccessLogger: logger, + RecoveryService: &recovery.Service{ + Version: compromised.Version, + }, + PasswordsService: o.PasswordsService, + }) + if err != nil { + t.Fatal(err) + } + + ts := httptest.NewServer(s) + t.Cleanup(ts.Close) + + httpClient := &http.Client{ + Transport: web.RoundTripperFunc(func(r *http.Request) (*http.Response, error) { + u, err := url.Parse(ts.URL + r.URL.String()) + if err != nil { + return nil, err + } + r.URL = u + return ts.Client().Transport.RoundTrip(r) + }), + } + + return httpClient +} + +func testResponseUnmarshal(t *testing.T, client *http.Client, method, url string, body io.Reader, responseCode int, response interface{}) { + t.Helper() + + resp, err := request(client, method, url, body) + if err != nil { + t.Fatal(err) + } + defer resp.Body.Close() + + if resp.StatusCode != responseCode { + t.Fatalf("got response status %s, want %v %s", resp.Status, responseCode, http.StatusText(responseCode)) + } + + if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { + t.Fatal(err) + } +} + +func testResponseDirect(t *testing.T, client *http.Client, method, url string, body io.Reader, responseCode int, response interface{}) { + t.Helper() + + resp, err := request(client, method, url, body) + if err != nil { + t.Fatal(err) + } + defer resp.Body.Close() + + if resp.StatusCode != responseCode { + t.Fatalf("got response status %s, want %v %s", resp.Status, responseCode, http.StatusText(responseCode)) + } + + gotBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatal(err) + } + wantBytes, err := json.Marshal(response) + if err != nil { + t.Fatal(err) + } + + got := string(bytes.TrimSpace(gotBytes)) + want := string(wantBytes) + + if got != want { + t.Fatalf("got response %s, want %s", got, want) + } +} + +func request(client *http.Client, method, url string, body io.Reader) (resp *http.Response, err error) { + req, err := http.NewRequest(method, url, body) + if err != nil { + return nil, err + } + return client.Do(req) +} diff --git a/pkg/approxcount/approxcount.go b/pkg/approxcount/approxcount.go new file mode 100644 index 0000000..9f2a121 --- /dev/null +++ b/pkg/approxcount/approxcount.go @@ -0,0 +1,46 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package approxcount + +import ( + "fmt" + "math" +) + +// Encoder encodes and decodes integer values to and from 8 bytes with precision +// degrading logarithmically with higher values. Encoder must be aware of +// maximal possible value and starts always from zero. +// +// Minimal value that can be encoded is 1. +type Encoder struct { + max uint64 + c float64 +} + +// NewEncoder creates a new Encoder with up to max value able to encode. +func NewEncoder(max uint64) (*Encoder, error) { + if max < 1 { + return nil, fmt.Errorf("invalid max value %v", max) + } + return &Encoder{ + max: max, + c: 255 / math.Log(float64(max)), + }, nil +} + +// Encode returns an approximation of integer in 8 bytes format. +func (e *Encoder) Encode(value uint64) uint8 { + if value > e.max || value < 1 { + panic("overflow") + } + + return uint8(math.Round(math.Log(float64(value)) * e.c)) +} + +// Decode returns an approximated integer from 8 bytes. +func (e *Encoder) Decode(encoded uint8) uint64 { + return uint64(math.Round(math.Exp(float64(encoded) / e.c))) +} diff --git a/pkg/approxcount/approxcount_test.go b/pkg/approxcount/approxcount_test.go new file mode 100644 index 0000000..ba666cf --- /dev/null +++ b/pkg/approxcount/approxcount_test.go @@ -0,0 +1,104 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package approxcount_test + +import ( + "math" + "strconv" + "testing" + + "resenje.org/compromised/pkg/approxcount" +) + +func TestEncoder(t *testing.T) { + for _, tc := range []struct { + max uint64 + values []uint64 + want []uint64 + }{ + { + max: 1, + values: []uint64{1}, + want: []uint64{1}, + }, + { + max: 2, + values: []uint64{1, 2}, + want: []uint64{1, 2}, + }, + { + max: 10, + values: []uint64{1, 2, 3, 9, 10}, + want: []uint64{1, 2, 3, 9, 10}, + }, + { + max: 254, + values: []uint64{1, 2, 3, 253, 254}, + want: []uint64{1, 2, 3, 254, 254}, + }, + { + max: 255, + values: []uint64{1, 2, 254, 255}, + want: []uint64{1, 2, 255, 255}, + }, + { + max: 256, + values: []uint64{1, 53, 54, 55, 56, 57, 253, 254, 255, 256}, + want: []uint64{1, 53, 53, 55, 56, 57, 250, 256, 256, 256}, + }, + { + max: 23597311, + values: []uint64{1, 14, 15, 16, 17, 18, 1000, 23597310, 23597311}, + want: []uint64{1, 14, 15, 16, 18, 18, 1016, 23597311, 23597311}, + }, + { + max: math.MaxUint32, + values: []uint64{1, 7, 8, 9, 10, 11, 1000000, math.MaxUint32 - 1, math.MaxUint32}, + want: []uint64{1, 7, 8, 9, 10, 11, 1014925, math.MaxUint32, math.MaxUint32}, + }, + { + max: math.MaxUint64, + values: []uint64{1, 7, 8, 9, 10, 11, 1000000, math.MaxUint64 - 1, math.MaxUint64}, + want: []uint64{1, 7, 8, 10, 10, 11, 930374, 18446744073709524992, 18446744073709524992}, + }, + } { + t.Run(strconv.FormatUint(tc.max, 10), func(t *testing.T) { + e, err := approxcount.NewEncoder(tc.max) + if err != nil { + t.Fatal(err) + } + for i, value := range tc.values { + t.Run(strconv.FormatUint(value, 10), func(t *testing.T) { + want := tc.want[i] + encoded := e.Encode(value) + got := e.Decode(encoded) + if got != want { + t.Errorf("got %v, want %v for %v (encoded %v)", got, want, value, encoded) + } + }) + } + }) + } +} + +func TestEncoderPanic(t *testing.T) { + defer func() { + err := recover() + if err == nil { + t.Fatal("got no panic") + } + want := "overflow" + if err != want { + t.Fatalf("got panic error message %q, want %q", err, want) + } + }() + + e, err := approxcount.NewEncoder(100) + if err != nil { + t.Fatal(err) + } + e.Encode(101) +} diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go new file mode 100644 index 0000000..52a1045 --- /dev/null +++ b/pkg/metrics/metrics.go @@ -0,0 +1,31 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package metrics + +import ( + "reflect" + + "github.com/prometheus/client_golang/prometheus" +) + +var Namespace = "compromised" + +type Collector interface { + Metrics() []prometheus.Collector +} + +func PrometheusCollectorsFromFields(i interface{}) (cs []prometheus.Collector) { + v := reflect.Indirect(reflect.ValueOf(i)) + for i := 0; i < v.NumField(); i++ { + if !v.Field(i).CanInterface() { + continue + } + if u, ok := v.Field(i).Interface().(prometheus.Collector); ok { + cs = append(cs, u) + } + } + return cs +} diff --git a/pkg/metrics/metrics_test.go b/pkg/metrics/metrics_test.go new file mode 100644 index 0000000..89ddaef --- /dev/null +++ b/pkg/metrics/metrics_test.go @@ -0,0 +1,69 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package metrics_test + +import ( + "strings" + "testing" + + "github.com/prometheus/client_golang/prometheus" + + "resenje.org/compromised/pkg/metrics" +) + +func TestPrometheusCollectorsFromFields(t *testing.T) { + s := newService() + collectors := metrics.PrometheusCollectorsFromFields(s) + + if l := len(collectors); l != 2 { + t.Fatalf("got %v collectors %+v, want 2", l, collectors) + } + + m1 := collectors[0].(prometheus.Metric).Desc().String() + if !strings.Contains(m1, metrics.Namespace+"_api_request_count") { + t.Errorf("unexpected metric %s", m1) + } + + m2 := collectors[1].(prometheus.Metric).Desc().String() + if !strings.Contains(m2, metrics.Namespace+"_api_response_duration_seconds") { + t.Errorf("unexpected metric %s", m2) + } +} + +type service struct { + // valid metrics + RequestCount prometheus.Counter + ResponseDuration prometheus.Histogram + // invalid metrics + unexportedCount prometheus.Counter + UninitializedCount prometheus.Counter +} + +func newService() *service { + subsystem := "api" + + return &service{ + RequestCount: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: metrics.Namespace, + Subsystem: subsystem, + Name: "request_count", + Help: "Number of API requests.", + }), + ResponseDuration: prometheus.NewHistogram(prometheus.HistogramOpts{ + Namespace: metrics.Namespace, + Subsystem: subsystem, + Name: "response_duration_seconds", + Help: "Histogram of API response durations.", + Buckets: []float64{0.01, 0.1, 0.25, 0.5, 1, 2.5, 5, 10}, + }), + unexportedCount: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: metrics.Namespace, + Subsystem: subsystem, + Name: "unexported_count", + Help: "This metrics should not be discoverable by metrics.PrometheusCollectorsFromFields.", + }), + } +} diff --git a/pkg/passwords/file/file.go b/pkg/passwords/file/file.go new file mode 100644 index 0000000..278ecb9 --- /dev/null +++ b/pkg/passwords/file/file.go @@ -0,0 +1,70 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package file + +import ( + "crypto/sha1" + "fmt" + "strconv" +) + +const ( + version = 1 + supportedHash = "sha1" + defaultShardCount = 32 + maxShardCount = 256 + + partitionSize = 3 // uint24 size in bytes + indexLocationEncodedSize = 4 // uint32 size in bytes + indexReadSize = indexLocationEncodedSize * 2 + hashRemainderSize = sha1.Size - partitionSize + maxUint24 = 1<<24 - 1 +) + +var validShardCounts = []int{1, 2, 4, 8, 16, 32, 64, 128, 256} + +type meta struct { + Version int `json:"version"` + Hash string `json:"hash"` + Count uint64 `json:"count"` + MinHashCount uint64 `json:"min_hash_count"` + MaxHashCount uint64 `json:"max_hash_count"` + ShardCount int `json:"shard_count"` + CountDecoder string `json:"count_decoder"` +} + +func isShardCountValid(v int) bool { + for _, c := range validShardCounts { + if v == c { + return true + } + } + return false +} + +func getShard(b, shardCount int) int { + d := maxShardCount / shardCount + return b / d +} + +func getShardFilename(shard, shardCount int) string { + if shardCount == 1 { + return "hashes.db" + } + if shardCount > 36 { + n := strconv.FormatUint(uint64(shard), 36) + if len(n) == 1 { + n = "0" + n + } + return fmt.Sprintf("hashes-%s.db", n) + } + return fmt.Sprintf("hashes-%s.db", strconv.FormatUint(uint64(shard), 36)) +} + +func uint24(b []byte) uint64 { + _ = b[2] // bounds check hint to compiler + return uint64(b[2]) | uint64(b[1])<<8 | uint64(b[0])<<16 +} diff --git a/pkg/passwords/file/index.go b/pkg/passwords/file/index.go new file mode 100644 index 0000000..9a484d7 --- /dev/null +++ b/pkg/passwords/file/index.go @@ -0,0 +1,378 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package file + +import ( + "bufio" + "encoding/binary" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "time" + + "resenje.org/compromised/pkg/approxcount" +) + +// IndexOptions holds oprional parameter for indexing pwned passwords data. +type IndexOptions struct { + // MinHashCount filters out hashes with lower compromised counts from indexing. + MinHashCount uint64 + // ShardCount specifies the number of files into which hashes should be stored. + // Allowed values are 1, 2, 4, 8, 16, 32, 64, 128 and 256. + ShardCount int + // HashCounting specifies if hashes compromised count should be exact, + // approximate or none in order to have more compact database. + HashCounting HashCounting + // LogFunc can be specified as a custom receiver of log messages. + LogFunc func(string, ...interface{}) +} + +// HashCounting enumerates hash counting types. +type HashCounting string + +var ( + // HashCountingExact stores counts as they are. + HashCountingExact HashCounting = "exact" + // HashCountingApprox stores counts with approximation of around 5%. + HashCountingApprox HashCounting = "approx" + // HashCountingNone does not store counts. + HashCountingNone HashCounting = "none" +) + +// Index creates an indexed database of pwned passwords by reading hashes and +// their counts from a textual file where hashes are ordered by their values +// provided by https://haveibeenpwned.com/Passwords. +func Index(inputFilename, outputDir string, o *IndexOptions) error { + if o == nil { + o = new(IndexOptions) + } + if o.MinHashCount < 1 { + o.MinHashCount = 1 + } + if o.ShardCount == 0 { + o.ShardCount = defaultShardCount + } + if !isShardCountValid(o.ShardCount) { + return errors.New("invalid shard count") + } + if o.HashCounting == "" { + o.HashCounting = HashCountingExact + } + if o.LogFunc == nil { + o.LogFunc = func(format string, a ...interface{}) { + fmt.Printf(format+"\n", a...) + } + } + logFunc := o.LogFunc + + if _, err := os.Stat(outputDir); !os.IsNotExist(err) { + return fmt.Errorf("database directory %s already exists", outputDir) + } + + inputFile, err := os.Open(inputFilename) + if err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("input file %s does not exist", inputFilename) + } + return fmt.Errorf("open input file: %w", err) + } + defer inputFile.Close() + + logFunc("analyzing input file %s", inputFilename) + + progressTicker := time.NewTicker(10 * time.Second) + defer progressTicker.Stop() + + start := time.Now() + + stat, err := inputFile.Stat() + if err != nil { + return fmt.Errorf("input file stat: %w", err) + } + + inputFileSize := stat.Size() + + var i uint64 + var fileCursor uint64 + var maxHashCount uint64 + scanner := bufio.NewScanner(inputFile) + var prevLine string + for scanner.Scan() { + s := scanner.Text() + + fileCursor += uint64(len(s)) + 1 + + if s[:40] < prevLine { + return errors.New("input file is not sorted by hashes") + } + + c, err := strconv.ParseUint(s[41:], 10, 64) + if err != nil { + return fmt.Errorf("convert count to integer: line %v: %v", i, err) + } + + if c < o.MinHashCount { + continue + } + + i++ + + if c > maxHashCount { + maxHashCount = c + } + + select { + case <-progressTicker.C: + p := float64(fileCursor) / float64(inputFileSize) * 100 + d := time.Since(start) + logFunc("line: %v\tprogress: %.2f%%\teta: %v", i, p, time.Duration(float64(d)*100/p)-d) + default: + } + } + + count := i + + var countDecoder string + switch o.HashCounting { + case HashCountingExact: + countDecoder = "big32" + case HashCountingApprox: + countDecoder = "approx8" + case HashCountingNone: + countDecoder = "none" + default: + return fmt.Errorf("unsupported hash counter %s", o.HashCounting) + } + + dbSize, err := getDBSize(count, o.ShardCount, supportedHash, o.HashCounting) + if err != nil { + return fmt.Errorf("get db size: %w", err) + } + + logFunc("total hashes: %v", count) + logFunc("estimated db size: %v", formatBytes(dbSize)) + logFunc("max hash count: %v", maxHashCount) + + if err := os.MkdirAll(outputDir, 0777); err != nil { + return fmt.Errorf("create output dir %s: %w", outputDir, err) + } + + metaFile, err := os.Create(filepath.Join(outputDir, "db.json")) + if err != nil { + return fmt.Errorf("create db.json file: %w", err) + } + defer metaFile.Close() + + b, err := json.MarshalIndent(meta{ + Version: version, + Hash: supportedHash, + Count: count, + MaxHashCount: maxHashCount, + MinHashCount: o.MinHashCount, + ShardCount: o.ShardCount, + CountDecoder: countDecoder, + }, "", " ") + if err != nil { + return fmt.Errorf("encode db.json: %w", err) + } + + if err := ioutil.WriteFile(filepath.Join(outputDir, "db.json"), b, 0666); err != nil { + return fmt.Errorf("write db.json: %w", err) + } + + hashFileWriters := make(map[int]*bufio.Writer, o.ShardCount) + for i := 0; i < o.ShardCount; i++ { + f, err := os.Create(filepath.Join( + outputDir, + getShardFilename(i, o.ShardCount), + )) + if err != nil { + return fmt.Errorf("create hashes file %v: %w", i, err) + } + defer f.Close() + + w := bufio.NewWriterSize(f, 64*1024) + hashFileWriters[i] = w + defer w.Flush() + } + + logFunc("saving to: %v", outputDir) + + start = time.Now() + + if _, err := inputFile.Seek(0, io.SeekStart); err != nil { + return fmt.Errorf("seek to the beginning of input file: %w", err) + } + + var hashCountEncoder func(uint64) []byte + switch o.HashCounting { + case HashCountingExact: + b := make([]byte, 4) + hashCountEncoder = func(v uint64) []byte { + binary.BigEndian.PutUint32(b, uint32(v)) + return b + } + case HashCountingApprox: + e, err := approxcount.NewEncoder(uint64(maxHashCount)) + if err != nil { + return fmt.Errorf("new approxcount %v: %w", maxHashCount, err) + } + hashCountEncoder = func(v uint64) []byte { + return []byte{e.Encode(v)} + } + case HashCountingNone: + hashCountEncoder = func(v uint64) []byte { + return nil + } + default: + return fmt.Errorf("unsupported hash counter %s", o.HashCounting) + } + + indexFile, err := os.Create(filepath.Join(outputDir, "index.db")) + if err != nil { + return fmt.Errorf("create index file: %w", err) + } + defer indexFile.Close() + + indexFileWriter := bufio.NewWriterSize(indexFile, 64*1024) + defer indexFileWriter.Flush() + + i = 0 + fileCursor = 0 + var hashFileIndex uint32 + var partition, previousPartition uint64 + var previousShard int + scanner = bufio.NewScanner(inputFile) + if _, err := indexFileWriter.Write(make([]byte, 4)); err != nil { + return fmt.Errorf("write index zero entry: %w", err) + } + buf := make([]byte, 4) + for scanner.Scan() { + s := scanner.Text() + + fileCursor += uint64(len(s)) + 1 + + count, err := strconv.ParseUint(s[41:], 10, 64) + if err != nil { + return fmt.Errorf("convert count to integer: line %v: %w", i, err) + } + + prefix, err := hex.DecodeString(s[:6]) + if err != nil { + return fmt.Errorf("decode prefix: line %v: %w", i, err) + } + + partition = uint24(prefix) + if partition != previousPartition { + if partition < previousPartition { + return fmt.Errorf("partition %v not after partition %v", partition, previousPartition) + } + for i := previousPartition; i < partition; i++ { + binary.BigEndian.PutUint32(buf, hashFileIndex) + if _, err := indexFileWriter.Write(buf); err != nil { + return fmt.Errorf("write index cursor: line %v: %w", i, err) + } + } + previousPartition = partition + } + + shard := getShard(int(prefix[0]), o.ShardCount) + if shard != previousShard { + hashFileIndex = 0 + previousShard = shard + if _, err := indexFileWriter.Write(make([]byte, 4)); err != nil { + return fmt.Errorf("write index zero entry: %w", err) + } + } + + if count >= o.MinHashCount { + hash, err := hex.DecodeString(s[6:40]) + if err != nil { + return fmt.Errorf("decode hash: line %v: %w", i, err) + } + + if _, err := hashFileWriters[shard].Write(hash); err != nil { + return fmt.Errorf("write hash: line %v: %w", i, err) + } + if _, err := hashFileWriters[shard].Write(hashCountEncoder(uint64(count))); err != nil { + return fmt.Errorf("write hash count: line %v: %w", i, err) + } + + i++ + hashFileIndex++ + } + + select { + case <-progressTicker.C: + p := float64(fileCursor) / float64(inputFileSize) * 100 + d := time.Since(start) + logFunc("line: %v\tprogress: %.2f%%\teta: %v", i, p, time.Duration(float64(d)*100/p)-d) + default: + } + } + + if err := scanner.Err(); err != nil { + return fmt.Errorf("read input file: %w", err) + } + + binary.BigEndian.PutUint32(buf, uint32(hashFileIndex)) + for i := partition; i <= maxUint24; i++ { + if _, err := indexFileWriter.Write(buf); err != nil { + return fmt.Errorf("write index end %v: %w", i, err) + } + } + + logFunc("saved %v hashes", i) + return nil +} + +func getDBSize(count uint64, shardCount int, hash string, hashCounting HashCounting) (uint64, error) { + indexMaxSeek := (maxUint24 + shardCount) * indexLocationEncodedSize + + indexFileSize := indexMaxSeek + indexReadSize + + if hash != supportedHash { + return 0, errors.New("unsupported hashing algorithm") + } + + var countEncodedSize uint64 + switch hashCounting { + case HashCountingExact: + countEncodedSize = 4 + case HashCountingApprox: + countEncodedSize = 1 + case HashCountingNone: + countEncodedSize = 0 + default: + return 0, fmt.Errorf("unsupported hash counter %s", hashCounting) + } + + hashesFileSize := (hashRemainderSize + countEncodedSize) * count + + return uint64(indexFileSize) + hashesFileSize, nil +} + +func formatBytes(v uint64) string { + if v < 1024 { + return fmt.Sprintf("%d bytes", v) + } + var d uint64 = 1024 + if v < d*1024 { + return fmt.Sprintf("%.2f KiB", float64(v)/float64(d)) + } + d *= 1024 + if v < d*1024 { + return fmt.Sprintf("%.2f MiB", float64(v)/float64(d)) + } + d *= 1024 + return fmt.Sprintf("%.2f GiB", float64(v)/float64(d)) +} diff --git a/pkg/passwords/file/metrics.go b/pkg/passwords/file/metrics.go new file mode 100644 index 0000000..4a04aef --- /dev/null +++ b/pkg/passwords/file/metrics.go @@ -0,0 +1,43 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package file + +import ( + "github.com/prometheus/client_golang/prometheus" + m "resenje.org/compromised/pkg/metrics" +) + +type metrics struct { + // all metrics fields must be exported + // to be able to return them by Metrics() + // using reflection + CheckedCount prometheus.Counter + CompromisedCount prometheus.Counter +} + +func newMetrics() metrics { + subsystem := "passwords" + + return metrics{ + CheckedCount: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: m.Namespace, + Subsystem: subsystem, + Name: "checked_count", + Help: "Number of checked passwords.", + }), + CompromisedCount: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: m.Namespace, + Subsystem: subsystem, + Name: "compromised_count", + Help: "Number of detected compromised passwords.", + }), + } +} + +// Metrics provides prometheus metrics from this Service. +func (s *Service) Metrics() (cs []prometheus.Collector) { + return m.PrometheusCollectorsFromFields(s.metrics) +} diff --git a/pkg/passwords/file/service.go b/pkg/passwords/file/service.go new file mode 100644 index 0000000..3bc6104 --- /dev/null +++ b/pkg/passwords/file/service.go @@ -0,0 +1,176 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package file + +import ( + "bytes" + "context" + "encoding/binary" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + + "resenje.org/compromised/pkg/approxcount" + "resenje.org/compromised/pkg/passwords" +) + +var _ passwords.Service = (*Service)(nil) + +// Service implements passwords service by reading the passwords hash data +// directly from files stored on the filesystem. +type Service struct { + index *os.File + shards map[int]*os.File + shardCount int + countDecoder func([]byte) uint64 + countEncodedSize int64 + metrics metrics +} + +// New creates a new instance of Service by opening database files in a provided +// directory location on the filesystem. +func New(dir string) (*Service, error) { + b, err := ioutil.ReadFile(filepath.Join(dir, "db.json")) + if err != nil { + return nil, err + } + var m meta + if err := json.Unmarshal(b, &m); err != nil { + return nil, err + } + if m.Version > version { + return nil, errors.New("unsupported data version") + } + if m.Hash != supportedHash { + return nil, errors.New("unsupported hashing algorithm") + } + if !isShardCountValid(m.ShardCount) { + return nil, errors.New("invalid shard count") + } + + var countDecoder func([]byte) uint64 + var countEncodedSize int64 + switch m.CountDecoder { + case "big32": + countDecoder = func(b []byte) uint64 { + return uint64(binary.BigEndian.Uint32(b)) + } + countEncodedSize = 4 + case "approx8": + e, err := approxcount.NewEncoder(uint64(m.MaxHashCount)) + if err != nil { + return nil, err + } + countDecoder = func(b []byte) uint64 { + return e.Decode(b[0]) + } + countEncodedSize = 1 + case "none": + countDecoder = func(b []byte) uint64 { + return 1 + } + countEncodedSize = 0 + default: + return nil, errors.New("invalid count decoder") + } + + index, err := os.Open(filepath.Join(dir, "index.db")) + if err != nil { + return nil, err + } + shards := make(map[int]*os.File, m.ShardCount) + for i := 0; i < m.ShardCount; i++ { + shards[i], err = os.Open(filepath.Join( + dir, + getShardFilename(i, m.ShardCount), + )) + if err != nil { + return nil, fmt.Errorf("open hashes file %v: %w", i, err) + } + } + return &Service{ + index: index, + shards: shards, + shardCount: m.ShardCount, + countDecoder: countDecoder, + countEncodedSize: countEncodedSize, + metrics: newMetrics(), + }, nil +} + +// IsPasswordCompromised provides information if the password is compromised by +// reading the index and hashes files. +func (s *Service) IsPasswordCompromised(_ context.Context, sum [20]byte) (count uint64, err error) { + shard := getShard(int(sum[0]), s.shardCount) + + partition := uint24(sum[:partitionSize]) + + indexLocation := (int64(partition) + int64(shard)) * indexLocationEncodedSize // add the shard count as it starts with a zero value step + indexCursor, err := s.index.Seek(indexLocation, io.SeekStart) + if err != nil { + return 0, fmt.Errorf("index seek to %v: %w", indexLocation, err) + } + if indexLocation != indexCursor { + return 0, fmt.Errorf("index out of range: %v instead %v", indexCursor, indexLocation) + } + + buf := make([]byte, indexReadSize) + n, err := s.index.Read(buf) + if err != nil { + return 0, fmt.Errorf("index read %v at %v: %w", len(buf), indexCursor, err) + } + if n != len(buf) { + return 0, fmt.Errorf("index short read at %v: %v instead %v", indexCursor, n, len(buf)) + } + + hashRemainderStep := hashRemainderSize + s.countEncodedSize + + hashRemaindersStart := int64(binary.BigEndian.Uint32(buf[:indexLocationEncodedSize])) * hashRemainderStep + hashRemaindersEnd := int64(binary.BigEndian.Uint32(buf[indexLocationEncodedSize:indexLocationEncodedSize*2])) * hashRemainderStep + + hashFile := s.shards[shard] + + hashRemaindersCursor, err := hashFile.Seek(hashRemaindersStart, io.SeekStart) + if err != nil { + return 0, fmt.Errorf("hashes %v seek to %v: %w", shard, hashRemaindersStart, err) + } + if hashRemaindersStart != hashRemaindersCursor { + return 0, fmt.Errorf("hashes out of range: %v instead %v", hashRemaindersCursor, hashRemaindersStart) + } + + buf = make([]byte, hashRemainderStep) + passwordHashRemainder := sum[partitionSize:] + hashRemaindersCursor = hashRemaindersStart + for hashRemaindersCursor < hashRemaindersEnd { + n, err := hashFile.Read(buf) + if err != nil { + return 0, fmt.Errorf("hashes %v read %v at %v: %w", shard, len(buf), hashRemaindersCursor, err) + } + if n != len(buf) { + return 0, fmt.Errorf("hashes short read at cursor %v: %v instead %v", hashRemaindersCursor, n, len(buf)) + } + if bytes.Equal(passwordHashRemainder, buf[:hashRemainderSize]) { + return s.countDecoder(buf[hashRemainderSize:]), nil + } + hashRemaindersCursor += int64(n) + } + + return 0, nil +} + +// Close closes all open files. +func (s *Service) Close() error { + for v, f := range s.shards { + if err := f.Close(); err != nil { + return fmt.Errorf("close hashes file %v: %w", v, err) + } + } + return s.index.Close() +} diff --git a/pkg/passwords/file/service_test.go b/pkg/passwords/file/service_test.go new file mode 100644 index 0000000..6c368bb --- /dev/null +++ b/pkg/passwords/file/service_test.go @@ -0,0 +1,146 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package file_test + +import ( + "bufio" + "context" + "encoding/hex" + "fmt" + "math" + "os" + "path/filepath" + "strconv" + "testing" + + "resenje.org/compromised/pkg/passwords/file" +) + +func TestService(t *testing.T) { + t.Run("default index options", newServiceTest(nil)) + + t.Run("min hash count", newServiceTest(&file.IndexOptions{ + MinHashCount: 10, + })) + + for _, shardCount := range []int{1, 2, 4, 8, 16, 32, 64, 128, 256} { + t.Run(fmt.Sprintf("shard count %v", shardCount), newServiceTest(&file.IndexOptions{ + ShardCount: shardCount, + })) + } + + t.Run("approximate hash count", newServiceTest(&file.IndexOptions{ + HashCounting: file.HashCountingApprox, + })) + + t.Run("no hash count", newServiceTest(&file.IndexOptions{ + HashCounting: file.HashCountingNone, + })) + + t.Run("all custom index options", newServiceTest(&file.IndexOptions{ + MinHashCount: 5, + HashCounting: file.HashCountingApprox, + ShardCount: 8, + })) +} + +func newServiceTest(o *file.IndexOptions) func(t *testing.T) { + return func(t *testing.T) { + if o == nil { + o = new(file.IndexOptions) + } + + dir := t.TempDir() + + inputFilename := "testdata/pwned-passwords-sha1-ordered-by-hash.txt" + dbDir := filepath.Join(dir, "db") + + if err := file.Index( + inputFilename, + dbDir, + o, + ); err != nil { + t.Fatal(err) + } + + s, err := file.New(dbDir) + if err != nil { + t.Fatal(err) + } + defer s.Close() + + inputFile, err := os.Open(inputFilename) + if err != nil { + t.Fatal(err) + } + + scanner := bufio.NewScanner(inputFile) + + for scanner.Scan() { + line := scanner.Text() + + want, err := strconv.ParseUint(line[41:], 10, 64) + if err != nil { + t.Fatal(err) + } + + hash := line[:40] + got, err := s.IsPasswordCompromised(context.Background(), hexDecodeSHA1Sum(t, hash)) + if err != nil { + t.Fatal(err) + } + + if o.HashCounting == file.HashCountingNone { + want = 1 + } + + if want < o.MinHashCount { + want = 0 + } + + switch o.HashCounting { + case file.HashCountingExact, file.HashCountingNone: + if got != want { + t.Errorf("hash %s: got count %v, want %v", hash, got, want) + } + case file.HashCountingApprox: + tolerance := uint64(math.Round(float64(want) / 25)) + if got < want-tolerance || got > want+tolerance { + t.Errorf("hash %s: got count %v, want %v", hash, got, want) + } + } + } + + for _, hash := range []string{ + "0000000000000000000000000000000000000000", + "7890abcdef0123456789abcdef0123456789abcd", + "ffffffffffffffffffffffffffffffffffffffff", + } { + var want uint64 + + got, err := s.IsPasswordCompromised(context.Background(), hexDecodeSHA1Sum(t, hash)) + if err != nil { + t.Fatal(hash, err) + } + + if got != want { + t.Errorf("hash %s: got count %v, want %v", hash, got, want) + } + } + } +} + +func hexDecodeSHA1Sum(t *testing.T, s string) (sum [20]byte) { + t.Helper() + + b, err := hex.DecodeString(s) + if err != nil { + t.Fatal(err) + } + + copy(sum[:], b) + return sum +} diff --git a/pkg/passwords/file/testdata/pwned-passwords-sha1-ordered-by-hash.txt b/pkg/passwords/file/testdata/pwned-passwords-sha1-ordered-by-hash.txt new file mode 100644 index 0000000..67c13fc --- /dev/null +++ b/pkg/passwords/file/testdata/pwned-passwords-sha1-ordered-by-hash.txt @@ -0,0 +1,2862 @@ +002DCFC06E1B1B25F220203617B526794D10612A:7 +0046F504740C6234D66E9E6761A77BF368BA1A5A:2 +004D84B0BD17F5BB581E250E9AAA6EDC82B8089F:6 +008D79391C817885A59822F6153136C1CEFC2569:1 +008FB9607F766434554998725BFCF93601A4DBE3:1 +00A8CAD629EF1BCA9B46EB58ADFA6AE264391861:2 +00ADE2EB79C4668A5E6E42A36E92B3ACFC29198E:1 +00BE95CA0570076E09D07086E3F09BC9B614F8A6:1 +00DE582A85D49B0165BB527F2AB0E4ECA5585447:1 +011D4AB318B738D912907B5E23B3FAF792407E02:1 +012C6CF4AE284B05100B161AC0A644C1982F3572:1 +015855213FC0CD008EF76A56DA597DE15CA05BB8:3 +017BA905F605AF6F048B5DC12F8DB8DA0BE15C52:2 +018A0FD80FBAC19900985BE698510BCA9D48D945:2 +018E6B6A90A2BD849BD1C65CA16FB99D094664DD:1 +019D95CACA1B3AE840540C2691DCF5F2938AB049:1 +01A2699F5FE2235A3F671B96C141BC341D5EFC79:1 +01A372FAA33266FCF97CD99D24D4013CB41D4ACB:1 +01B0DAB69C87D16BF8ADCDAC44223EA0431F8152:2 +01BD172389F8C32824FEA8B2EF228853D225291B:15 +01D597715B97E10E900F5F7BEEF1F0BB8A440505:3 +01DE84BCD4217FAC90CAC69BD8D181FF141202B8:1 +01F922D075805FD7DC5FB867ABD6216D586C85FA:11 +01FEA4D7F9EB73AAB290BB7B33A90AA59C827198:4 +02120090BDA4FC256B3FB023733DC5539EF1CD58:2 +021385921B9351FCC99990DDE7148D285AB41E80:2 +022167FB0E2EBB589CDD3137D2127664FF700DE3:1 +024029A1BB1CF70A543C91DE65F68E501A59E836:1 +024A5B06538969DAA0B2C9ACB60C071AB47B9AE8:1 +024BA17085D5A6F354E1A07978852AB04FCC7D85:1 +025AA07D99C3632E2D996E811980E65C9F361B30:5 +0263BE61B9DAF1A1087385E31126BE473428056E:1 +02748BD25C64EE8766C5A5F28223EC0433CE0396:3 +027A545360CC8D6C12134B700FA0E078EA976D20:5 +02D47FCA9F0A7850445252AAE6C8AD356CE5B788:2 +02D81C2152F997CEB1627A90B6A1FBF5F13B6ED7:4 +02DF732A3DAA061520A2013D06C37819416CDDA0:1 +02FB4757A7D6D61786971BCCC07933E025144A97:1 +032F4DD893D97C4C8354CB3A1D77BACF37046604:1 +033A4C06EAA7A69D01B912963A84BFA78344CC39:1 +0342D942398E2A09ED31DC6333C2C312848F6FE6:1 +036BCAC2D7690ABB0DFD3BD6E1EF15A0EBD664EA:2 +0385B45FF589067B1A692AE7E99CEEECE564DEB6:3 +03961AFF3FF7215C7490B541666658FF17744C7B:2 +03A8DCAA33D23FAF5B9CE33A2974CB64A5225EA7:3 +03BCA868E93BE09D8EFD7728AA3EFD6D58515DED:21 +03E6463161E734697E52D7F60DC1AB77E1B286B6:1 +0408333D349EA84E3A2715576FF7F2A6AC1AB8D8:3 +04236FA8ECA5F76777BE0F31799C44CDFD8466CB:1 +042F4A5D194C1B61E7ABF51969F7C77D5B7AE327:14 +04314F77C2F882EC8B635874C52CBB03D79E0150:2 +043E4521EDEB856D344ACA32F6259609E52F7AA6:2 +04538FC74D5E15195AD156001AB932BD72FE958B:5 +047F39F2E9CD4255D862F93A787400586313A01B:8 +048866ACED5FD82B07C5988CF3A5B95DC5ABF064:1 +04A930EC872EBB7B0D0DA698FB32F34E4EC43907:1 +04D13E98DE514093B54070CB2BD8680DC6DFCEF7:2 +04D9220F36088602AA5BC7AF48FE59FB7DF6E324:8 +04E3C64903814D9BC554DDACD1E876FC93B270F9:2 +04FCFA06B178B2B724CAE092DA02F69C335E6B77:3 +0525F075AF895A0C605854ABD267B593C880E269:2 +053D46956E484FDE4935B316AFCDA703EFA6081F:1 +0555DAC13518629D6939339E504FE3594463E020:2 +05700BC89324F3AB2A411D70CCD2626F9339AFD7:3 +05778CD2BAA6761884C568B8670129175BA8AA48:1 +0584D9A5A2DDADF44789FE46EAC25E2C743258ED:1 +0585ED641EC07EC52B1D5E857F9728DF9D7C94F0:1 +058D2B983A801CB2F3ECD8A308B64F3B389E4887:1 +0593F56B334BE38CDD4E1A53726B1DB9E2EF5C70:5 +059BAA73ACD4BC014196114131DA2CF06CA7C182:3 +05CE9A7B2088A5638E1EA9DD35819D20391AABF5:1 +05E7129647B9065BD929E94F5A58526F19DD8071:3 +05F8C0D4AF6BE4702C903CC30E28FB75B6763679:2 +05FC49E4BDB698DE90B980B09F9734444B050C35:1 +0624E895909A0356BC472C6F99ACCE3D319B4185:1 +06509A57595A51465331B924F5625069B413A55B:2 +06517060D91C76DA2751E6CFC4A64B49F467CF9B:11 +065B5C505CAC80A036A8437E17B6B7648F4F5913:1 +066EAD9E53038335EADB54013697067886BD0998:2 +066F52D7E66B9C6D0F94D77F58CFECA099859799:4 +067821EAF23C419A902EB29011EF785536FF6AE5:3 +0681992D749C9A5958BA297C2873F3180220CD70:4 +068E69EBE0CD7FE9FA346A86994742F79E69E1B8:1 +0694847AC26FE4EFFD69496B992CA53D4D4FD963:1 +069A6406F44E2514D1A39A4E6191B862C6FDB4CA:1 +069F48D5603A8EB093CEF8143B5B260C80A74A70:2 +06A018717E18060E92CFEE547E1E89AEBBA674FD:2 +06E1757648EB21E54D617165DDA28209E4D05D17:2 +0706A51956832E378A7CA08800779E1557518903:26 +070DEF07423177E5C9A8B923AC0C763AAD1F700D:1 +071CFAC5714DF47284182AAD4ABF589F908A1252:1 +0735C6945A901457225B0D78D9286958AFB1E504:1 +073E7975C883B330434050487E61819D1CAD49DD:65 +0743B9E97419A9A907E35D62F8438F864F827285:2 +0791D2B36F23B2427F66652D9F08AD9A6288B4C3:4 +07C1B33997FD03ABD390F6F5755A68E0C772AA1B:3 +07DB0CF415B318BDFA399387F69987B53B9CBA18:1 +07E9D254F4323E1AFCB02A70F0909D8AC08363AA:2 +07F918F678171A699F403582F6417AB8F924783C:3 +08151E6957E47D7A875CA78FB19D59E87CB9606D:6 +082BCA77856B1B30D0E27A996AFB911D13B80494:15 +08310F265279075ACDD6A822A649715B1DE0A1AA:2 +086B55F6084CFDED655F38914433E77921CDA4BD:1 +08954D06B3A35CC178406F9EAA052C19729D2BC0:1 +08983B99798A8031C30D321FCF74456695F8A8A7:1 +08B8E129B27F244CDA3B30CAD79ABA75C6D7B128:2 +08B904724C7FB78FC4B3BD14DA15381A24398A20:3 +08BD7A9115BC983F9EDE6008806DC6505B28695F:1 +08DFAF43219645545E8DE462055B80F2BACCC4CC:1 +08E34CC39F87718A91057AF701F778CC7B7D0FB0:15 +0913FE22AEB76564FBD866FDBE1260BD5F73A985:1 +091DF131993F479095DC854A70062112C1531015:2 +093F8F25CFD1CB2A9EECF6BBC376E4AC6E2DB0D6:3 +095118C82535791C0D055E40546F8B7A84907DE3:12 +095739C69AC3A596D9268105515C0AFE583ADD99:1 +0990FDE2A3DAB9569D45C0EBBCB4D0600ED3DCFF:2 +0992526CE27E5A5CDD92AAB79AA10DC8F866532C:3 +0992A6435BCC298FC0148375C623B91198CBBF06:6 +09A55B785B189DC0643EF9148E044A937FBCBFAB:2 +09BDEFD966E4E7EDF5C7B1E385538FE72415595B:3 +0A03F87CC6EC6B435528E65F2F83E17BCA494CF3:2 +0A10BE8B09C492F9E6109EAC60B99EB6D2DEE913:4 +0A299AEABFB5EF19FB37F13CA1EAB64701649FD1:3 +0A3E1790F7E933FEEEC6F309A01525AC85CC1D20:2 +0A4A22C169CBDBF5297327EB75DCBD9342EB2FA3:4 +0A4B27A55B7A4D8A6CC9FBA464A4D014CA485C99:1 +0A4E872FC26BC846CD79ACF2A3413668ABF833E9:1 +0A546CBC82785ED2E11E48BF02A5386F33A902B1:4 +0A8059E4915086C6548966772868C584827500F2:1 +0A9A8474FB0F018C5AF26DC8E6123239ED378721:1 +0AF48888DF4DD17F4E42538FEF0DD39682D079BE:2 +0AF522D7A05D01BBE0F4938349815FE27C8535A8:18 +0B04CBEC23578F0EDF94233132C77ED75DA2873A:2 +0B14BBB8E0D2F900784E6055725B2F49BC5C5247:3 +0B3CDAA3BADD2CADBBF6F8CA259AAA5D045E5E85:1 +0B44C443CBC51D96BF8C7773C25EC3BB1535488F:1 +0B6615C2AF413BC8D5B3DBCE7A97AE74C6B28658:1 +0B6974F668A0322F54CB2DE0B5EFA4B9D46BFA7B:2 +0B7DF1911D90F3DB1539044FE85A23D1EE3541F4:19 +0B978B4BD2AE901E1338F27F089CBE6A6F3C1B14:1 +0B9EB5FBA025C645423966BB383A1BA1B39D3786:3 +0BA73EC96A93746B1606FB8E6DFD580B837A586F:2 +0BC30B19C44E4D221B2EEB481C1882A5220D9A65:1 +0BCEE387BD478CD2A96BBD9F152D1BB661EAE482:2 +0BDC69E21B399F55079A61FB36C267D2BA454663:6 +0BE24869692C994B88A7B76ADB4C24A3B0BA7424:2 +0BEC84978E5CD85071D3FEBEC1C292AFDA5E7F58:4 +0BEEF5A87A683C03B3DCDDFBE44D9C875913B036:4 +0BF02E06611B772CB49580A7CF6796106A28ACC2:1 +0C1F344DF293CD47F0A474CFFA7F9ECF4E7E17E6:1 +0C4696012799C4DD491EB16DB222554BEB03B0BA:2 +0C49C9D94DC2EA88324F18C4C4B4C0AE66525061:3 +0C4E811B5DB10E5BD934A562DF65B0878382B21C:1 +0C8B4141D26AE6604C42349157F175A034F4483E:1 +0C8CA3C608B37A017761A192E222E82286790B28:3 +0C9D81C3539DC88B61D316D6CD8540898FACEE15:12 +0CC07CFE434B33F0D6B9535A4B0F2E26C556AB75:2 +0CE491AA181E247F2F9AAC71AFFADD48E916783D:2 +0CFCF5509E3CCE9321432EDEA9C29E5F804DE943:2 +0D240C880E525D2900CA78B12DE6C09D2B632AA3:15 +0D35F19B66ACB15ABF20D61A2CF1369B9D990171:2 +0D3C5000D064C1C342A4930EF4DC210846950151:1 +0D4658F7A8BC1263BEE36431B0CEF3A8B63331B0:3 +0D4D1733AA584827964385D793A1F87A15A3E018:1 +0D5F73C49B28F297EA99BAE7633E36D931B934BE:2 +0D6003F2B00954DA961DDDE7B159944B2CCB1194:1 +0D79BE86055B6A51571ACDE9C7B34D7380AD5C70:1 +0D813F5E3E161C2520183750E42FB02E046AA412:1 +0D854A01F2D0148DFFF046CEAAF3D85CFBE748EC:2 +0DCF48DDEB92B06C9A9AFEF06044D8337468D48B:1 +0DFF48F39D8B3988BAA3D54696A54FD63218FA17:31510 +0DFF6A5A5F92B8B36BEFB8BF61D7BA5CBE6D4FC7:1 +0E17B74C66949F555D922CFDA8914A660A53A789:2 +0E24A564FA5C2011F99F6F83162B8519C05203BD:2 +0E48D12FA126CD2E4CF397DABBFE538646FF94E2:3 +0E57940CB6A9D077F7F4C92AA73514B40DB79F4D:2 +0E78044E351B723A4CAFAFDDB0C595F35F2A4D46:3 +0E80ADBCA60DE9B1EE2743EBD59B343C90AC6227:1 +0E831FFA4BB521893ED21181F621B4D2D7E500C0:2 +0EA514E8093DCD413941E6C25B0A3D1D5D65A00B:9 +0EC978455E5611BFAD2B4A390BBEC1A33CC52AF2:2 +0EDC0823D0185F0263D4113C7384A9DA74EAA9BD:3 +0EDC4E4D703589C53E068F20D4C1709FE03C852A:3 +0EEFA9A99E5469FF401BA6452B8D7E1B967C75EF:1 +0EF6AC0113E23CB13E8EB0245D7AC90207265536:2 +0EFC68C3AA6FAB07EC0D05A61AB9A2C7228A1674:1 +0F1F76751943BE03A71AB4D537DF947601E1655C:2 +0F2098053816CDFFBE6FE49CBE575DA469E68B3F:16 +0F261F547C6D515C8B3F5AE70D89877D98B5BCDB:2 +0F3217507B6C7FFC05F71F1EBF2BA326CE5F140D:1 +0F3637863A1B6EDD19B2201BA7B319369E9790DA:1 +0F40287A4599E6ECDC480B9057800A5ACAEEE1CF:41 +0F471FA751FC74933F5D3F346B9371A3E744B507:9 +0F9D4940D03168359E13C1D165DEAFF41AF74C79:3 +0F9E0078A4C9DC057F0C989096604667E51D4E88:24 +0F9F33C932AEA25C48FB2307874E21DBF4B0EA7C:4 +0FB9A5B966972B634DF6270DBD5D9334A4078D90:2 +0FCD37709EA2E30627555AF5E7459F12637F1402:1 +0FD46DAF1AB860202647AD947C96D596229E1BBE:1 +0FEA98983020F50854CAE5448E21F63659E806C3:2 +100C7E035423364A402B8CE7F908F6FEF9C3C14B:2 +101F412437C1B4F1DF83B4EDABD22B3F2BCF4425:2 +10472ADCA4236E8561086BE660F23B3B21B267D5:1 +1075F8930A41D47FDDE8D2E79A4028A33A8AE387:3 +10932589E9537B5CBE72F14367344956839C2330:2 +10BFAB4132F232277462C7A814B3C18E48D39D63:2 +10C59B5DD83848C68CF5953A937054EB015BE7F5:4 +10DFAFB19573480081A21DF108ACCFCECA1932D0:2 +10F7044623482A46C2062FDBDFB4C2BBBD421C44:5 +110415C0DC5E62DE7DC1DBCF095D9AE170FEC129:4 +110CE71CD9720DB76BAC1275AB452FFF7E362090:6 +1111F6AEDCFDD5C91A5FDF33B217D9F500FC36C9:4 +1114511CB167084DE51581ADE0AED646CAF82AC4:3 +1124280C9261F430E65A55CD8AF3038A92708D6A:8 +1125705FE79958302A58D3394841A41ED8886478:2 +11273D57B954F7B4A41CEE3F98C2F90BC80D2F59:169382 +1140FE380A0E08E7B4F94E7E7F8D993B5C58A612:1 +1165B4B86993D0EBBCA8BCAD5F873A08F2131AF2:2 +1180066DDB8147DDB27E3362EEF632DC7EE8862F:3 +11B5FB49045585D7BE7DA0D31D9E30F13E4A9BD9:1 +11BC6446944ABB9480009610A2514BE1057365EA:3 +11D28741FB2C24C2518B82CA44F2BA07337A23E4:2 +12106AA89BDD5C65EF5353476976F477030BFF70:1 +1217571DD53180B4962089B6B4EB6ED7C8950DE1:3 +1221DA6DE0D6FEAEA9ADD25693856339D53CD999:1 +12236C03BEE6CD7253ED3203ED53999B214A5131:5 +122742F445B3F32A22D2A4EBBDEBB6BDE126BC33:1 +126904D5373EA468DFF5929B41BA9FB77E413E93:1 +128DE030828B28492B9B4D14F03CEA1A3A1A210A:1 +129CBD25F38E95E6C2AD2DFD9E16ED480F7E9DDE:3 +12BE809934D9F2878D7C5686D2142BB484DAC239:3 +12D4D0E6463CBBBE7E4AA3D88CCD87B1622F0544:1 +12D51DE00130D022265924BA04599B376D9CCCBE:2 +12DB64C370B108D5EB35C87517CA388EFB91FAB5:12014 +12F22AF486070C3DA84589AF1EBDF9A9C9A2477C:1 +12F69039AD00CC537C6879A445D847F6DE7ABA6A:4 +13198B24A682CB3D0C87E9DEEE02F525034D9D0A:1 +134068B1FFAAB35B067EDEDE3ED55C89774B26BF:1 +13819BA39A0698B01EE20DC64B7D9896564AB1BE:2 +13A5710FA73A4C1DFD92B16CE2F44897DFDB3FFE:3 +13AF50EF2E6801D282D4BAC5A72AD29A007BCD67:2 +13D89FBC6DBF0EA6BCAD1A68297D2442043DF20E:28 +13E3F119BF19F577B41A1E850FC1884C5C88480A:1 +13E4B53156ED0C8263689B1B0B9517D8B0058843:3 +13E5CE9EFA3A676D64F41D5497D7DDF7495E8D92:1 +13F699D5052336ADCA85DFDD4F7E36E80CF05212:1 +13F8AF1E10228A5BD0BBDAC53E7A7C686E4B1C33:2 +1400D14CA5055DAACD2FA236765E9503F44655E3:1 +14252D6C0ED8B2CEBA191CAD570EA4F4B429CA45:3 +1440612F82A932628D961CB5A8F568DA45438F35:1 +14408D7F81D868C6FA5C34716651ECBABF6C9939:3 +1447914150E573011EB5068127C8B00C6E683C69:1 +14563540D1B35B1926E637D184BF79149785C485:2 +145A234C8A05B71323B12A6D5C47C4338A850B37:4 +14695AB5AA71D86380D06530F80E010B2592EC26:1 +14815972775E1F21D9FD8A2899FF7F14F680C849:15 +1495B9FA2CCF8504EDC391FDE65EC365426A2F81:1 +14A0F88DDE088DCD7B453FF1A89CDE30C51A31C6:1 +14AF2C78E6F111E08B4A199895CA9BC867B51796:2 +14D3A000E03036D912E587A285274AE7D94615F9:3 +150B3B0B72BA8C030402DB9314605B8BF41989C9:4 +15105904C31E030C2857816BB99F1B2303BE7C02:3 +1529FCC96E8B072C502B3AA8F6E26B21019987A6:1 +1537870E90226F0647D927C98D37917ADD18D8C1:1 +1540333CE09D42FC19E38346A9C0F8401035DF95:19 +155FAF6CDE0C4A540E9A722C0EA766AA949F2481:2 +156AD48E8510F4A39920BDA2FA97AA06A3C89EFE:1 +157A1BD4D0672A27C2AB55BED6D48358FF3E7FA1:1 +15908A4D66C0591100764E2C4A400AE878B4F541:2 +15BA017D2FDCA5ED1C6317207EC2CA1BC2FB1AB7:1 +15C005C54BC6DA0481FE9CD4EACCF0FD18B3D876:4 +15CEF01C6B9C9110492A15CFBF06CDB8DA9E9FD7:2 +15FF19C2AEA88C0F691B7B2D043119FC20447A72:3 +160609D33C64C3A913E014CA157C763A59E79F96:1 +1641C5A559E7A67F157C2A3C940AED1043F5532C:2 +1643076EB0066086D029979966E87960EF94A3B5:3 +1656B48309D5739A5E6AA8A29F6A56279EDAD0CC:1 +167D427A1D539AD14B46F557601ADA0FC7B9A190:1 +1684AB94529319333EF8B4231115A4AD32F32D3B:1 +169772B1AC1E4B1459435817A2AD782F88D509C6:2 +16AD3DC177AEB09FDDD318652F1A48A8BA4D6569:1 +16C6C247B2B59711D46AE05A7D38FD5B0FCBBE58:2 +171028114FF80996F2A85AE24FC29AEE885EF355:2 +17118390B7DD314BBF7A586F427AADBC02C81CD4:1 +1768225F8AFFD494E73B7A366CF3DC04FB806BCC:3 +17708151C8C51A3CC122308A2BA7A47DA601C2E9:1 +17716F244BEE52BB9926E0768D0FEF5BB573ABAA:1 +1781A78A80ED4CAD64090318ED6CF35F0CDC606C:2 +178590ED482807CD03EEB263E6D77146146609ED:2 +17DCBC91312C777DE7F54F6712DDBFB182E63E4D:1 +17FDDF303DD1A07E435D7CE132B7D0E071915181:3 +181E0566D639383E64F22251F7D7E83CF2F858AD:1 +18379D517F9A08C01350938B2D68E23719687416:6 +184B28BAAA230EE702D50E1EDAF5107AE14EF7DE:1 +185D44DEECA1C90E2DA1C0F07311DB3878216737:8 +185F8CEBDC127A6EB756AF0CB8540C78DC053BF0:1 +187A69314087268181EBA149FB022634F5FD59BF:13 +18BBB09DDBBDDF8F23E77EA3D9D8EF4FF3FB9FAF:1 +18D26FF90B09F9F475E52E0E4012875BB5B6554A:2 +18DB1DAAC081A0CF593E054BF09DD64346BC474E:2 +19224A6E7A495862F6D39A6EFDDB160D8C3A350E:3 +19307026D83DF6CA3A203779CE2F7E07A5105211:1 +1944F6F65D2CB8EF8F4B55563D103A350C6B69DB:2 +195019335F9236AA9F837692DA5D6713A791E40C:1 +1960883F63E0EEDCB2070ECE6AEF2CE397A31CD8:2 +196BE6B596EC9B484234E4EDB52674871F83F0BD:1 +19707FFCF32135C46C19E788CB749CE0A57AF860:2 +19806CC5B4883ABDD5E7B81FDABE98FD5C7EDEB7:1 +199662720ADFE35CD71A614203A169E20291420B:3 +19B5552E3673A0A883962CC9CB73EE2451D7D88B:8 +19CB538A1193077BBA993DAE3A44AA43024B0AAF:1 +19F416B933E3CEE4F8393084A9D09D30FB6AC068:4 +19F59730E6A9E938AEBF9C2F8CBA15549AEC4BDB:1 +1A00A6C888C6DFDDF0F4D362AA8FC142883DFCF8:1 +1A01D6301D843B2785476FF48FA181D67DA108A8:1 +1A0B61B80B319F782B745D5DEF4134B1DED17179:1 +1A0E8254B4E575C4EF52C02AC5025DA7095685AB:1 +1A161933C899301602F7706E802697AADDE4B672:2 +1A1831A458E04C97DC08ED40C3F93EBE0B4DFC3C:6 +1A1D4330D47966AA6CB2B65FB42391CB9A580E06:2 +1AC04E78502B334E28B201E5EA3C18336106F071:3 +1B0229CFE360358775EA82A69775D4DD8616AF93:1 +1B17233F9C025649DED703CD20829EA8B9EAE9AD:1 +1B1D5E3F0ACC9B800FD259A0A7A82143D131D89A:3 +1B434428E869E55EA186220C5F2BDA4F3729A681:2 +1B53B20828CBC14255E5440478035EDC3EDA239A:10 +1B83F1CED34367C298517CA4C3B59E87458209B8:16 +1BCF63FB94579ADD6434A68EB9C1F0ED600EC763:2 +1BDC71897EB7BA9621646BDD10026699AF08507F:3 +1BE6413F280AAC6CD69D2C26E2B3CEF2E5476913:1 +1BFE6D8E2BD0DEFCFFBD247FB9C0E7083054BA5B:3 +1C0A8EB778EF13663A266E6F17342B12870209FE:1 +1C32D8CE1F13CDAFB076CE3B2E61445080B7206C:3 +1C4BA3920B8034ACA67303E4CCEE1D7E03149F39:2 +1C56C9814CF0ADB9584E8DFB27F08A1E7F39EF89:1 +1C647D0807C1625268E13533BD0D334E79F17925:1 +1C666A92E9C3480E678D9357DDF51B35AD6A23E3:1 +1C9A90DEB57D49DD266EB76E53476A62FC48D120:1 +1CB3915973FBDFC94FF8A9B5595E6EDDBFAB4206:2 +1CBA0492E0CBF1A0BD6DA6593459F84044264AB1:1 +1CC8D976AD633243165730B044D21FC2F2BB13FE:4 +1CE8957F93B24B194B6BEB7FE8C643A3722CB567:37 +1D450D79D8CFFB8701ED5173767BBBFF69EB90C6:2 +1D7790EBD43582F6683B296783806D7FEDD0735D:1 +1D7C6D4F0ADE3C2CDD7A9CA027CFE89FF4D62B44:3 +1D9380038E92AFE053F8282CA40530456EFCF7E3:2 +1DACFF159F9E657BF0E167932390D666152C47DD:1 +1DB6A4A08231177C90065EA20C486329C906D69B:1 +1DD7F7605CA7A872D5073220086C217E2C3090DB:25 +1DE45A8930EC25436753FBDD55065E975B6F4F6A:1 +1DE74D7AF97DE0F286C1E9CDFAA0B4CA822DD19F:1 +1DEFFF2DB420FB5A3DAADE3699C7502C40C91A67:2 +1DF48C18CEB39337A7B3DC968B7F5AF1A00F7FE9:2 +1E0AF9FAB896C6DC8A469AE88062E48BDCA106EB:2 +1E0BA78A84D913556FEA843D029D70EEFCDC6ABC:1 +1E240A5343C64E18D22A08313DDE201FAF635B54:1 +1E569D59ED1A8AEACCA8EAE14F710E3A190F1822:12 +1E7C213789B843D32152AC2096484328B83C78E0:28 +1EB59D9A43521A01A6849D7168A7643367DCED3F:5 +1ED730831EA566693CCBD7BC65646DAA1562AB67:5 +1EFFDB8F37E148E861E637E7383859E851325562:4 +1F01E9F75C7E4513DD6F90038486B59AE37E91E9:1 +1F39D1AC3C6C3B578A179A089926F84334F61291:2 +1F71E0F4AC9B47CD93BF269E4017ABAAB9D3BD63:76583 +1F8C3A2EC5B6D9CF7CB5DC389562973F15B1EE62:7 +1FB1012C057D7DE267CED0966C8722BFE9C02CC1:2 +1FD2A480906FDDE4C76406C2177BB637FA8FBE55:2 +1FD971145A5C3890E075C4849008F9349F54729C:1 +1FED532589154CB80AF248263BA29551E8DA9144:3 +20054EB3CDB1C1F97D7FE6C3FB6D96C2A2ACBFF3:73 +2008921F7966B8794F84CDC14E52F48E14AD1021:1 +201602BF39643B0901B1378A3B320F03C62E7A1D:1 +202064E9DEBC8C3DDCBEE8CBB041FEC4A5EA2C54:1 +2036F5FCEDE6D4EE02B382542F6E20044C46E3AD:3 +208B645B3490126487C39DDDF6F9EA0798910890:6 +20B5A779AC52A55C934DB126326B7C262A8308A0:1 +20BA711DC680ED4C73017F8E16229436C6D3DA8A:3 +20C2FC9BC45A7E8800AA413F343CCB3ADE1ED5F2:1 +20C3013F341E749C001AD0838476EEBCB2DD4E01:2 +20C316C66956C964A34DDCF2E5BA2EC3F4EF22E7:2 +20D89425CF920A3408C7C6A088FEFE14D12DA545:1 +20DD549AD1AAC03A64B4492A71D1D3D418452E64:1 +20E3D75CC39816D56BED9128C5909CC4632C2200:1 +20F6C159CA1F764489B48F0499F8001AA436393E:3 +210BBEECE6FDE6594E4D981A7F6E29854765CE10:1 +212986B18AB3AF1DC239EBCD02DFEA56C5C5F62D:2 +212DF418AE0C5EA350F358D361945AB021DA664F:2 +21403B5CB4884B75FD73F0FD6BEA9FDED37674F9:1 +21A5C65AE8D2428D3A4199EB5A11344C76E560E2:1 +21B74215FDB273C9DF2396CF70AFA6FE2ED078BE:3 +21BA56C1EEB7EE625175A411249672D7E3A773E0:5 +21C04A5096C6A09FAB2FCE52751FAB8A197E59B8:28247 +21D1ABB1A94F646A54966461C286ECEB88B8680E:2 +21DD0BE37A816BA91C208F5D3962B0C01727B152:5 +21F586238A3A0D41CB8485A09C8C569796BE5D44:2 +220819AA59475F6AA0762F21B0C6E0D39B8D7FA8:1 +220A3F9046DF499B9419AFF87CF6188DD5431E06:4 +220D7A1A90DB7AB02B14A165540F413CF0639E73:13 +220F83ECABCE081378EDB81FD8C00C9A6A55075F:2 +2245F26664B69DF5D82892DA937310711CEB6480:2 +225A610C1EAA2715E85471DC05D84154BB243184:10 +225F8D83BABE278CBD2C2DADA7CA2420E9037B71:13 +2276FF48C9FD793240E1DDFC47383653088B8476:3 +22B00B7E19AE7DDC055260A09AE3E7F66D7727FE:2 +22DBD170C1F59D3107147BF827BA298ACF8BEF03:2 +22FF5BEA03ACEB3B94F6E57CEFEC9812DDF3709B:4 +2304DD1DC37D06DD2238CF3CAEA01E755502B52F:2 +2307F189546FCCBF16352D365C070ECD7E7D4A4F:10 +230FD138390A136BEA28031CF0484467DFFF5C87:9 +234663620C7DD2BBC7287DF60B02B1420EB1E5A7:2 +235C7AC42E410D78F379E3504E861A881D7A282A:6 +236BDA205A2A38454E6B7050DFF2B6F0DA321C8C:2 +2385B18EE9AE6CBFAD1A73258969B1A6C80ED639:1 +239B06E2DC18646D847B28DACB156C5BD49BD10B:13 +23B42715330AAF851E5D355870741F5872ABFCDD:2 +23C3C2ACA8C2129A3F5375600315E940677DAD90:11 +23D82F201B2CF22438E81105A4EAAD990CAE74CB:8 +23DEC54972FAD0D5FF3F394C7DC18E17AAE42202:1 +23EB87DDC7A7286EF8937E3AE6AAE42507B47ACA:2 +24100B1F38665193DCE1867F1A1D5242016BFBF5:1 +242A0BFC03D99FF2934D6E494D9FFD05814BF7D9:14 +2484C0DA567F0400E4431FAE1FFFF51881017A86:2 +248568CE0B943C8ABF4E1BB7154720924F30199E:3 +24A973D0D7C098090EAF7BB7FD578D71DF908BDD:1 +24BB787A30D6345556CC4BCE83192269E4151CA9:2 +24C007CB4AB891D53D340C279CC919AE3D2892A4:4 +24C7C6661E04DCE4B2E93CB7960854033111758F:1 +24D99659572EA78642786A7083C3ED303C4E464B:1 +24D9BF006F41AF471600DC7D2CC83443F6F7B393:2 +24E569BEDC969CE17E5A9472688CD7F1F24361C9:1 +24EC329108D245FF7EC07BEE803A3B37C4C375EB:5 +253D35CF577D824D4F34D46B4FEC2F8551662013:2 +25522175E3A747677015D8F79FA9856FD907B73F:1 +257CEAB2115581A3DBF574A5E251938618C62CCF:2 +25897A3C4FA6866974BC1A21B1CE5FA632E5E398:17 +258F5DCD7A3F162724F52373B3A684A931FECDFF:1 +25AD27CAB49ED1A999C51897116B26A891BB0E18:2 +25D2C38A6C3C61FC33471DAE80A202EE08F07E1F:4 +25E3BDC68F35F6E8DC9CFCFD028069E222F053C9:1 +25EC7A6DC6AA112D03CA80D0382DA8A59FF593B7:1 +260C09B570C4287CF9B55B9D8DFE599838931F81:1 +262F9D9C4D73405C555911CDD9C2BCA59069CFCB:2 +26394AF35AAF1A5A7A3EF59020C4D84E9B886B6D:2 +26766D1392497CEA0204A6BEC47333F69EA7566E:2 +26DAF48881933958BE543707505B555D123AB310:2 +26F4B3BA96671FD7E3DC636526E46E21F42BB0DA:3 +270D9CDF8A3DCFA4078F4BB2B6F78ECB2B62CFFC:1 +271575936FC374ED175B55CBA4AAA805FB8C7198:1 +272284E9AF376540A6F095EEA1A64E32D89D530F:1 +272DD80D9F024348CAC0E4FC0751396E82CC2521:1 +278750E32D75E975CC4EF7BEE8F800FE0363D0FE:1 +278F1322B2293F2801B360B6FB3727D359EDE10C:2 +27CFCEE1817B161D1E0C9E2C0805C45B859EA094:1 +27DFD583F9CE49C3DB2963BEE2F9D6D77B51C3BA:2 +27FE5FEAEAFE11B019CF4A159AD05ECB7360CFE9:1 +2816BD80197B03AF258E4C8A3AF63DE557E133DC:6 +281ABFAB91644E30B15B24C839C8D04D717616A9:2 +282A50762DDF36CC0DC9CAA487917375B92F6896:3 +285BA2998B2CF6C7B1AE810C7D235F080562F21D:4 +28767C5C238467D9A7A9199EEE2100F5ED75AE15:1 +289F09EBFE24E6B6C978D217DDCBE3F8961B1E02:2 +28AB56A6A28F5B759FBD8E6027293F25F8D215AC:3 +28B7E59F9FE02DDF19C1E936681F49DDFC560043:3 +28CAE51EDCFC7BB797D472993C348C1F6FF6261D:4 +28E7B27E93FDEA893219D45FAAC7CB00C57350E9:3 +28EBF601FF2DDA41BE59EAB413544610A2D25E7C:2 +28FF22658265AD3F74D903744D4B310B59E2F4E1:1 +29069FCE7D73BFC2EA5ED70827831A556AB9C406:2 +291384D63937101DEDE458CC38E0D31ED2E6C873:1 +29459A84F3F1C138F973F93EE1CCE41ED79F1F20:4 +29470B52B8940D11F90437207AFB4682DCB4A9C2:15 +294C04969C4F3958E549D768D5F36BE42B1BC424:1 +294FE31A5B651B0CB5009FEC005A6F8D5B4F289A:5 +296F349AB12E34E6D8852913A0FE1316E347D677:4 +298062B285B9DAAC0AC7AA7968312F645AFBDEF6:2 +298DA566744336BD92AAACAC8301E8F3F05813BE:2 +2990B1640015CF9B5219BDD64DA705D5EAD39321:6 +2995CD606B5571E31D5D439E835B7366C694B031:2 +29AA27F6D76AE3F5589E1E33C8CB96C783724994:7 +29B79F89B814FEF557FAC79EA059592A00C2471C:2 +29BA8EBD83407323B5C3C08D06105C141B3BF609:8 +29BE01906F0AB69E8083D3D2D46C36BC60F8183A:1 +29C78673248331C6D8EFDC3B1DC51872D42C1B1D:2 +29CBE6A26E1B980F54B2CA5D79AC4AD89E2D5512:2 +29F4DC99C64F5A644056AE96E94CF3E669D89A49:32 +29F78BB928E155C3533619A7B2407E0D6EB0C2D9:5 +2A12BAE1B2F92399F488F8D389A31DE14AEFCA28:1 +2A5AA957213CE65F52FE28FE54F5EE7CD5F22AB9:1 +2AE87A22CC2ED7D4DFAB17384B5F97CB4135E2A7:1 +2B3887690249B5CA059513553FE6B7137E7C2CD1:1 +2B4C5C17918C2F6F2681BCECE50E1BED541631CB:2 +2B70AF2D98E351CB2A518C6978A1DBE1809AE39F:5 +2B7143479F00345AB72A16997482BBB9FF6C6CE0:4 +2B8036B562AE35F62B0B13932BD490E16BFEAB75:1 +2B82E9A14F61450AC05A17154523621F701491F3:1 +2BA655E1AB34201DA9CA3B89BE536112FDF519DB:1 +2BCBD356B3B9A6796F837FD53A168391000025F2:2 +2BFA5E252BB7C718D0AB72C5B42351A4950E2F54:5 +2C166E740D16877A856823C9603BAD2EA21C5763:1 +2C2337B3EBB7EAB2B49B5DE928CBB44556B88309:6 +2C35748F26250435988EE9E16729375C5DD4B8CB:1 +2C3A468848E5C5090F5BDA01937259DDF207CDA9:1 +2C5A6F3F06455DC503262DEDFE5993728B494782:2 +2C7C6E52634262DCAA80F360A07D981735EAA45F:2 +2C8184A780FEE6692597DA665D8F58ED3E6C2F64:10 +2CAB92687E38CF5C272A51B43803AC14A80ECF87:1 +2CD148A1D3480153B16A9E06CC42488F915F8296:1 +2CD408F11813079172C97F646BE3AC0C641E09C0:2 +2CE4D80C72C3E39A9D02180162DC5F58F38F6469:1 +2CEE97F86CEBE038410A72C0901BE5C19079C1C1:10 +2CF1D365FDF49DFC7C548D7F684AE7026929E217:4 +2D14362BD839881B0C5C35B0AA53F0BDC849F634:5 +2D19067822F19855FE877690AC08D31D37CCBFF0:2 +2D40B9E1B1FD123487A9C19D787E04CF76FF32C3:3 +2D44492B61F7D04CC829D88E83C651EF396A4BCA:2 +2D5BA37640467289CD793615DCBA1D0F3736A01A:5 +2D5CE5A55BD29B55D08601B68376C6B723CF8AA5:1 +2D666114C24DAE2337324604B5AA3941402088C4:9 +2D7F5B3EBA25B92A202E84B3A1B600298EC936DD:2 +2D82C6925FE5C9BEDC2C450D35F3BF8DA5BA62AE:2 +2DA81E0F9746014B88E89344CC11EB0DBB7C98D5:4 +2DACD4CC67AFE975E3E778C8D583F080C1C42F23:2 +2DAE752B91D68F440D28BBAC2D42C9566325CBDF:1 +2DB7A2943833F51D43288FAAFD7C2049C98B9285:2 +2DC0643EE96E0654C3FAF7B7D6D4106C192E44A3:2 +2DCC29F369DED6A76E2B3CFA4B7AC11FA341101E:1 +2DDBE8B670748E16D7890AF2EA9F88679AE26BFC:3 +2DEDC5F65AC171CE6CD029E61DA26AADE10861C4:2 +2E0C7D96AE9ECE6EED8C1799C95EEAF4A5959352:2 +2E0CC9C21510EFB8934C2ADD3FD79A86DBCF5722:6 +2E0E67EF5C805607F345FAF9B45240414B51B817:1 +2E36B43A24622039D659BDA6C70F2715DC14889C:1 +2E3867D03FDB79A50DF7C722DDEE426BC9BB99A8:2 +2E3A2A1EF2B330FC5161488326F8942A971D8D44:3 +2E4ED3ED8325392DBCDBFE89E313D704E3929141:3 +2E77CDC340BF5FD38A857805917C70F95A4B9AAE:1 +2EBE79A5B8B78CBF3CEAF170E6374A3026A4ED73:7 +2EE3D66B9C4D84AAEB491AA863AFF907579F64CB:8 +2EE5FDE76FEE4FAEF983AA76090DEE12338A2048:1 +2F03440F7764821550A281DFCAC633B892D4D914:2 +2F154673916DF97901759470013CF729836AD6FC:5 +2F2C927BC9CD66086499298B05C5C705371EDFA6:1 +2F3810927E53D9B0570DAAC86418544A1C61342B:3 +2F57502E84B4A848F972599B145FFC96733A1242:1 +2F70C756F81471569C97925004235ECF52360329:2 +2F8781C9FC23F2AC2D8E82969CE74366D55680B6:3 +2F937BA04A6EB3F5608096CE47F24D153857B018:1 +2FCE32415B3B716A0DFAE226DB6B94AB591F2361:1 +2FD371D6240312C6278CDEABA7EB4ACBACC6FAC5:1 +2FE61D817718AF048897A55CDF206D1D77F18B40:4 +2FE9D2613DD9B2E14E11BF1EAB6BF10CB9627361:1 +2FED37409D4B8565624FF25399CE20F5CC269045:1 +2FF9DEB4F1FFAAE412B4C622A0B94FBA9DF49A96:2 +302A764DC52FCC1813E325B97D4284EB38080860:2 +30325B240B9D34C9867B21B3FB0C114DBE5806EE:2 +30473C6176515BDB39DFF59856BFEFE0CCC18ED0:2 +309CD5902695443DC2BFED00BC22B2EA4449AA56:1 +30ACBA6E83102A1718A5E3C232FD783B98EF3476:1 +30CBC728E562B872CFC53E8C8DEA570533E8F97E:2 +30E951A4777350D8CDF9C96A3970E16D5C80E24C:4 +30FE0FCB733B49CD714CACA485A6918918D30B9B:1 +31204F62AD521394A7926D5188E849D5E2EC117F:5 +312541C169AE79B10E83B7784EC750142A7905A4:1 +3128E80D9B69AE1E7DA745660A04E2EF7C74DD40:4 +312A8150A275BA63C08CA3EDE0F5FD64B934E3C1:6 +312C5BA344F2CF174E8CF53FE80C1667B299683C:9 +312F6C7CFF46A8B3BED31127EBBBE9C70432C898:16 +3156F0D884258B7577C996098F05268B4E0A2A49:2 +316B2C659C2DF6AE27F28CF6E7A72BCF1145B68C:1 +316E43AB7AD8801573864452D9ACCE956801C3FE:4 +318BA24E6428E402BCFC067CACF06E857A5689B8:1 +319A6DA7DF6FBD44FCFF7D2077C956FF07118425:1 +31C597381AF5EFDD6F92783D221DB150BD247BE9:1 +31DCE9CE306B97B2C120C265E84B0F1B7806B2B8:5 +31EF66C3D1F8324A07672578DCC0894B41D7AD2B:2 +320848D68C2F8D90D54A1036265260FEAAA2E8C9:3 +324A7089BA43211CA6B1FA32C58458407FC7290A:6 +3251F1A2F31196AF8E4AD084A880925A9C18CEC4:1 +3262A5D0DA78726AE79179E3A479E67930090FD7:2 +327334ACDFD23EAFB4A0C6CF8D43255F3C00F53F:2 +3289E6CFA374B4EBBD4DA13F707606B0576DE287:1 +32920A3DCBB2DE93C7C09C7982F5184653992483:1 +329EED909BE532DC1CF730229E617E4D276AAA5F:3 +32A73507A5F131F12EC52D3B18B7452AF7EC3FF4:1 +32B16DD3E04113DF7D594C2E2B89DC625A3B8003:2 +32B2FD3CE4EB6C5E2C012D84F543851C2842CE92:2 +32C31FDF69047B0C9B2474D904421453AD20F1AD:1 +32C75FFAC05F7A0C37BAF57AA8BE5838C21BF9E8:1 +32DEACE14EFCE6CE8B6C74D99A63E86E551CA57B:1 +32F27D6EE32D135A9E144D5EDDC8A5D39546BA49:1 +32FE3B71A60EF035DD5D9D85977355209195B151:2 +3325AD820C7C325980C13650D89E1AFB756F96FC:2 +3328863DBE907E617E9B0EADA102C6A04EF9338E:1 +334608F84F5D8C6D95DC702C5BD562DE584664CA:3 +3351B2A809DA86F694C1DDC5140AE1C3AA6ACF6D:2 +3362F0A8AB98D57E55D72CCC229E124CFCC86E60:2 +3363365B1E0FC7D5626EE9F290C13FBF71EDF993:1 +3364387ED7550B0976A62D1DDEDA49C1B9124826:2 +3367BE905585D2EFF09C4F824861F87F603F7130:3 +33781CC98112EF8ECBABF1683DB16ABD1BDCF5B1:2 +337BC07AB0720D10A777536B625B6C8412A438C8:1 +339C71F83DCD4932964CB9E18CDBC34E7E947AD2:2 +339D11F5BD594501D39858BF946B435FEB429BE8:10 +33BA8D54709EFE3192CD7B370F94D8FFC69BFDCA:2 +33BE95C03EAB5A2FBABD61BDE3853E03DEBC8E70:3 +33CD6B2DB340153C562467EFE891EFBAB43E8283:6 +33CFF235AE436D7F2E433BAA9E12402ABEAAD994:2 +33E01F47824597EF87536AFFC79CDACC48D94544:8 +33ECD29791A8C9A1EDE7469D73107AAB001DFAA1:1 +33EF0B125253E99FB87153E277BD2B15F72BA5D6:2 +3406BB2B64FCAB1B8D8651C4AB2239BF41C9DBB5:1 +340926BC6CF3802CBEAAE3484DA435DA7AAAD3B7:1 +340A905803CBAECF3B1367E4341355A6FE3D1C9E:1 +3413286153A83C214C22283145EC736C1A12BEA0:1 +343E5E1090873D9F67D99BF2A03BA5E485599CA1:3 +34517CE96D5A02D0189F9F770A4258206BDD9AE4:1 +347F5E0365853625F54A0E373C071391CFAD7D38:1 +349C78FC2E5344DB9D3C6394160F9AC7E269A6F8:3 +34A61E5DF83D89D70EBFB431D2EB4DAAC58F4923:5 +34B75D19405CEDD73B1956962C487AFCFF784123:3 +34F41BBE4FA3ADB12EB5267017ADC7926142046D:2 +3504CE09F833B2CA6475C9E14DA0EACF7EFFCBF1:2 +3507A347568987223F8AF2E18703504238C66A27:3 +355C9EF3900E4C25A56DFC50DB4D4833C00AC956:1 +356067AFA7568E1989AD8B7532199BAD346388CF:1 +3570E91F8470DE980973DB30A3799222B32128CF:1 +3597DAE47042AE58C080BF436827ADD83610FAA2:3 +35DF4A33104C57E7C7799BE1B94859AA0D49DFB5:3 +365958C712DE991231EA31A4608F95A6E2031C93:71 +36BF3E39163CF5A23744EA5B5C38D3979EE54D3F:10 +36C60D2CDA51E52083772B1C16E219D7D4504312:1 +36C857BB19F073EE8CED8BC9111F8B0A18FD66CE:2 +36FC0A4BBF74DFEDE01D8D707F224272CB170119:1 +3709AA2402CE8F4F40700F533E3C45F3A4F20808:1 +370B5315CF7318744EA8F5A45C1B0F6B2DB96579:1 +372673572EAB7E4E272F47CC56838BD7C56DE25D:1 +376134BFAEE8545059225F49D409BE0DBF042E87:1 +3764A9ADE0A32D92884445E70530F92494966BC8:3 +376EE7EE32FC55D460D7F186A8A44FC78A766A42:2 +37859DC50E7D7CC7168276CE67228A2091F8BE0B:1 +3787F73D734679C9555D0A190819319F6A232F3D:1 +3795975928E50B218B897CF750FE3D19C59E056E:3 +37F916B7EF31F3FB446A273206C6FCDCE357D7B3:10 +37FD064808EA08861977A33AC18EDC026D24CF8B:3 +3817C21FD8133F4AB9F9F0C388CF5A8998F0213D:2 +3817E2327961364F756C93E53B19411AFAB00579:1 +3828763D55E7F02656DBFA4FAA5AE5A171F494B2:2 +382FEBCA6171927CA3A4BBAA24F7381230B18C00:1 +384F7CC914887E841BE6156CB6DD2DFD15CA9440:3 +38748D48E459E5D60A6545D161258074166C7C1B:15 +3879C7472C876C7289094C54987EE3FCD1A1610E:4 +38B1BE469BD2B1557322FAF7B55AE529D8CCBB20:3 +38CC3462400DFAB1790790CB4C9BF1B53574AFD3:2 +38E05C984BA3B4D92DFF7E3AB33F1C32F7652ADE:1 +38FEE8B87B2E1F7A5A96BDEF9E5814E0EA62B95C:2 +39096D26833046D36BF453C189959F6BBF46D061:1 +390FB66C09B0110303678A1E809E8B7D9691E4B8:3 +391BAFEF73A2D69C7A8443FDF7F827FB4C77CE49:1 +391E1D802CCDDD16C26FD8247985D56494463252:1 +397C35FCBD11148DE9ADC1F1D36940841F760E59:5 +3984CACDD601E80379E174A803A5B084ADCAFCE6:14 +399181022A39650166E2DA8083A1C9A8509D749D:2 +39A390DCE71DBCB8EA559EDE808FAED14D24282B:1 +39C650C8FA052CE07C7D1BE66AAEEF54C9F048D3:1 +39F1FFEE460F808A6B9C845A0E8E56EB0F759B8E:4 +39F210EA9019D69A945E5BB595BD65AFB3BFE663:3 +3A09BADA48C5F651D5C84188FCB776FDD2906839:4 +3A2F07ACF9742663E77CE0CBD3111DEE694936F6:1 +3A3135CFF5FB05FA9B0D6D4E7677C8B3738A2785:2 +3A3E3EAF3F1FFFA9835FAFC7CCC358A006617969:2 +3A543D24C23DC0AFE60FA1CA7EFDC7921A400F7F:2 +3A5ED86FFF6D4029C08746222CF3A3A4F946D9A6:1 +3A8C3425601CCF2C3D70C6DAD9A5F7BE1492A96B:2 +3A966C0F89DE843934D7E7DA8AAECB13D8E4F061:1 +3A9A4CB6606A614FEFA8D2BC94FA70432CF3B04B:6 +3AA6C10A3B9C2A4A7F6FAAB3C45528D977088DFA:2 +3AC92C70A22F0D2F27279AC6064B1FE32A1FD9D4:2 +3ACC47FC9BE6AAAED118C95AA6CFA399BEDAB9F1:1 +3AD17F6BF2FF64BD1A0571A93186F0746F535B3F:2 +3AD1CBC1BBB37B0D1CC36DC90F14C545DEB3024D:1 +3AD632A396D56D2FFF9967C8FD431B8EF91FC96A:2 +3B11B63FC9717CE59D6A2BF0CBE8F40AD6141F39:5 +3B29BB736BABF6AE82D3E0661B03A36310D7D62A:2 +3B35185B0768254D7DCD9E43F81C7CF197D97A74:1 +3B3E4B8070A10FE32CEFA5A548A122D4881C38A6:4 +3B6D1B44B4C2B6DE69D6420CBE4DF454CA2F2A42:5 +3B6DF0F7B354A30771A9A9376651AD252DCDE79A:2 +3B96250155C80756D17AEB515AFC4EDB60EE42DB:5 +3BB26FB8B7F9D52FD286EB50E3BAE9BC054F2DFE:1 +3BC742C997A618ED1FA751494AC02E16305E66B0:2 +3BDC17E4A72D73EE0B9046A35D869D1A6EB9CEE4:2 +3BEB048A649FA3A25A88012E7C2448B2C6B7EECA:2 +3BF4A8070869CC6943F926A7E17E5889B38A2C20:1 +3C21ADC9304EBD3E2A2595E1B536AE1AEEDAB6D2:1 +3C259D68C9193731B12B531D06AEFAF4D1C2E4AF:7 +3C44C8AA2B0798C46BF0902355E4A8C371EFF35E:4 +3C6C3601440F7FC7C1D218E59B2FB82CFC4C5A65:2 +3CA002E0603F6F82A8C27BF8CA04540B899C5D1A:1 +3CAA4FEA5B6C227095005BAF1A394CA263F039BA:1 +3CE733F93D2740A6FE3DC73AEFCA0DFFBFFFBBAC:2 +3CF0B9E6C8F8A51BAF821795178FF7A0946EC92F:1 +3CFB93BBE47E7B55626DB3C0F67B4A6C4311B435:1 +3D11ABC40DDBC9692110E6D5F366F5506BC90EA8:5 +3D19BCA57D27771F6CF3D9D3B961E7036CE3CCD1:1 +3D26A531937429E9240042FB84CD9D7642F47BFC:2 +3D2D258AE6F6E3C1A167C4BCD45913346CB861E8:1 +3D33E2DDD87917FF3E430665979F52B62D2CE078:1 +3D376360A5E05488A2D6A6E131C47BDED81DD452:6 +3D459DBF85359833FEC8403F31CBFFBEBE03CD87:1 +3D52891C4533109D718F26A0D1EBB408BBD13560:2 +3D5A02FB11382713B1130DD303D7ECED67AC3206:1 +3D5FBFB2FF63081F248D2101611703110938211A:2 +3D69BF519CC29FE7B33DEEC2CB2483DB740C9FF1:1 +3D7C7E850992299F5E9DFC5158CDC369BF2E0DAB:3 +3D9679F4B2F59EE5AFEB4FBF41A8A6207E0C7C36:4 +3D96D5D81D89DBF79B698B9A37C373B1D729D6A6:3 +3DC08720B1A481A51508064F7295F88F08D19535:1 +3DE070843794CFFBDF1F6C1870F0F8AEA14F5A89:2 +3DE4A2C0F85D003F232AB4B180DAD18883794442:1 +3DE5B7A6DBA130F5133E91A2CBE22A8E58E92F69:3 +3DF5688592EF7D2C0375774DB4EE059F6F6BDABB:6 +3DF9D0A30BB0C58D62D7E0AEC9AF79D03560A54B:1 +3E2F55A8059E468219D4F265094845493B7B354B:2 +3E40C42823EFC13BD666929D0D1988E5C35EC362:1 +3E5895692A59D0610CA240557A2EC7A3E135F883:2 +3E60F818B4F8380EF208598454837909633D93B8:1 +3E6111F79039E1715E170BA00C04DE9A24611227:1 +3E7137617A2F5B5C6FCFE666BBC9406231A1DD6A:1 +3E782149EA39730069B5AFB6E2233012372E7BD9:1 +3ED6F6057185E0DD5887D67806AF6FC5EF9ABD04:9 +3EE8AB663D8437362CCD6B86236F47E37504E004:29 +3EEFD14E8D49DA7BF5D3CB1C1F14E47B68C1FD3F:1 +3EF7E8B233EAE54F15339E192D5CF9AE6E0B26BE:1 +3F201FE8C29BC4B6AF1440EC4F1E0D5512EA1605:1 +3F38E34624395FBC5E7FD995660C9733983721A8:7 +3F3F7CA6F138181BF00D0F1A0A775A5ABBB6640B:2 +3F422F50B0EF47128C7362B2F8B2938BDF12B422:1 +3F5DD152A6DCDE0061D3CB18D4D7F25F220AAC2C:2 +3FA106C09ED4247926CFAA93BB880AB4CA1BC80C:1 +3FA7BB2DA8836A908D66B4B63A57483525DEB856:4 +3FAA73E06E32EADA5364D9B5A550CF1E9E88F4D2:2 +3FBCF537770C81302E063B079335CED85A17B6DC:4 +3FF028623FE78E086FA208DCCC84E9B78FA7C62D:4 +3FFCB011418CC1CEA29ACBC90603C46099FD5F49:1 +400B794E14B92090065E32B018276663616FA648:2 +4016D4F4CC009A5D3987B6C80A6817746A2CAD7A:2 +403F033949288317AC4E52E7F40069D5DC26C374:10 +406E2FD336CB1C5D770861D00C48E961AAE9F41D:3 +40D5F34D7DFFAC08C5A85F635036CCD74AC4E234:3 +40E5B368CFBDA38D0B400F935F60F87003FB8F14:1 +40ED2AA7977F0099BE4E9B4EA041E3876BCBF481:1 +4140AD9C0E08596F19AD395E949A4E99DA16EF65:1 +4151484DEC0B75F4B80499F787763636ADFA6B06:3 +41A444D8A2312819647E86AC51D3FCEDA88A7D54:1 +41AC689E7F71DFB1AF2DC8012A834B7B711D406E:4 +41CBA8712013E6A9F403E320357D8D65E07E4188:1 +41D09B7B3056AF5D9D73DEF2ACE550FE6BF35032:1 +41D591728B9363A0D2D9C54E27397E2D90B7FEEA:1 +42199657ED02CF6A840F7CAFB6531A3FAD6BF257:2 +421ED5541214C213328EB129763E47CB3227BECF:2 +4230833A3EF8C7A37C99E26AD291CB16C4A97E1F:2 +423150D4D9168137A3488C1C78063DA5DFDD413D:1 +4241424EA31F239E2D335B4FB4F865C70C81C8D7:3 +424F8FD748B99A024AE99131628EFFB88E17EA6C:1 +4251A484B2D74B28EA511CAC2100BB4CA7F1F7CE:2 +426150B8067C25336843707382C2A7406EAF07EE:144 +4270951A6106571452CFD256EB4A7C59E49B4422:2 +428F9FDEDB04A47676C41C628760E0439F98CDFA:2 +42D1D769866510EE68940ACE34D7D54516C230FA:2 +431D0C4E39A7AEA5779590632E25D7C407625EE1:3 +43314F68F271D353F83113B3351B66EFF174B466:2 +434D3735B64415B21C7D3899A2A8F5065F38E039:2 +439555943498C209CE8C51A12CC45523FE4DC057:2 +43B6FBE6AE4F866E7E99DFD17F9F16B0A3D9FA83:3 +43BD856D0917BDD9D2E26DC6F8DF7523DBCF3EBE:8 +43D2054076312CE266D8FDC36BDD611D54483522:1 +43D891020934E7DA934F5AB737776C4FE99EC131:2 +4410D841356ECC4373F4B0F4B8AA2297CAA62C5F:3 +4434049D18B4ADA372913D86CC321D8167132678:1 +444AC709440E373410695AC8ECEF8526DD819674:2 +4465824DEF12595F696889D6DC2526F1E44EE0DA:1 +446AABEEB0B3C6AE6D99E90D37D989A5ABEA1C3A:3 +4470726D28FCDACEC7D4102BFD45F1CEFE5ED268:2 +44874B4E0F841775A2767F2EA31DCABC54FC5EA4:1 +44C64FB82168EA2F769E01AE81ED84762480185E:8 +44C8BBCDA124B90A210F0DFDB6F0CA253F9C7618:1 +44CF4C8024BF18A0FBCFA99FAB4127DF1BB5013E:3 +4555A9A0CAC2A3DC2AEBDF4EBA9CB424D9EB3231:1 +457601542E515D6924A6136AB28628901355809A:1 +457DF8E2F41C1C0B031F256234977CBE4EFD6C65:1 +458CC4A0B6536A16C6147DF027F8B926D821720F:3 +45E802DCA98E19E84883B30C8B8927C4BBAFF2C6:3 +46183B424B09489BD39F39B250F18338ECFB9409:1 +462051142559669CE17B0325A9327EEAA57BBB82:2 +46510D89FC0A7CC2F93220CC0799DDB8708C67D6:4 +465AB6E350700AC4F9002C9BE1C360B3415697F8:1 +46703869DA4645EF9519C1FA7E192A5BF81B5DEB:5 +467660F49B9B128A03CEACBCE0EF34C3922DA3F9:6 +4677F30498C240B1A81A5EB97C33BE239EF0E1DB:1 +4683B2C877DEFB8A641C64A6A2DA58E1F60B275A:2 +468BCEC7ACC712FAEC7E174BF4B4BF295A6ECDF3:4 +468CFA22DE3ABD7D4154A2D96F836F476509C50D:1 +469B4268ACD74D8F31A01F48A5F23CA713428BD7:3 +469B704E46F2DD984EC5127A4A844AE871D54E1A:2 +46A49708EAC2033ED35512BFE6438EA31D9CCDD8:1 +46A606430FCF804B58F7BACA000F011F725305C0:1 +46AE8A2605819D2069A009BCD3EB7421A3D39407:2 +46C3C908AF510E50E0F5620092F96F1359634818:2 +46C9E827E09D6F4B4FA04953DFFFE370A821A9BD:7 +46CDACA4C4AB431D08C29BB3B4DB307C25E40213:6 +46CE625359B4D1D37366E66CEB394EA2D65A16B6:1 +46D52F4626E02CA5E42FE11FCF2AAA076669D1F7:3 +46DDC8F332302638CEC88F5D502FABA766DA71B6:1 +46EFBCB476B4C2C3D9E1A2D3BCD1903C49164710:12 +46FBBD894E76B7D477F9B01E5FAF469085886AB6:1 +472BC05C25AFB0BB1A32F1B75146F73CD6F6630B:61 +473BAC06C5830CEA76C12AB3A189C7E4052C0F3E:2 +473D1A8A0C8E8106B0EC2502E6A3E936D97C71DB:6 +4750C1095CAFB2CEBF81E4A6C226890917BFC18A:3 +4798AE1268CB4F9E4BED5F7177C0C786C69498CD:4 +47BFDA20D07A38BA340008CF27D8B48549B5C994:1 +47F5912B4C06D19F79D01CCA44D16D66EC83677C:1 +4805DA125F125B6E767B2F0F2E8212CDAE0978BA:1 +480D67A21C0599EC067F3A87C8B2B4F33C549A9E:5 +48212B3153ABA03F0B9E279B2CD049A6DF9DF753:2 +482C67CD1E0C810AC5B0B79FBF72C322F497D82F:1 +483217F66AA098FE6A7F5E28216577641A3EE7DE:2 +48606DE0BE8CBCF9CB35CD13B5148DC6650A9E69:15 +48827E539352D2CAC2CF5F05F1043AA566B5BCBF:3 +48A7181D9C1CF11FD0B10D7B753663AD7D18C0B3:1 +48AC6E7C02E25A8822FC4710FDB75615D69D5C74:1 +48E45FE15C08A0A7C923F04DA820B0C70F09766B:4 +490F65DC1118549A85B1A507D9196448A5CF5652:1 +492D894C92F6E757210E574756FDCE0D2E070E69:1 +4932D4AA77FA42C53F8333B7AE124B84F50D4400:2 +49331E052CF1570CD9CBB67F56A8396988593971:1 +494104B92C47F1F62A20F44DB5F83C99E6BF5B6F:1 +49440904909480BAA0A3D84BE6746A7BD34F4781:2 +4954152C7266796888DC8C69C04FB40413F5756B:3 +495A185F4579E3FA743C8304E21B9784FF43EA9E:2 +495DC7FCD4134FD6DE1A1626CE414F128E9A8E31:3 +498AF4AC20FD31AC39B481BE70A43B9CFB7CB97A:2 +4991350CBC17B6F02B5D2B07D4A4FBA5E68D338F:2 +4994A1D19108C325DF890E6970FD2B7ABC6B2C84:3 +49F7806950C4817876299708121C57D1A818297F:1 +4A1D21D4C1FD47378AD3C88BFC2F4D0550D68EB5:1 +4A24FDA20729A0D4780D0055A28E93621FADF659:2 +4A260C5256AB0D5B5C1EB4A0604F6D7E8DFC9C65:1 +4A6A0F57FF3ACEF93C4AF24580D1DB3D20F3A755:3 +4A73E437B4CE29128D7CDBC494AEE4EFBF12C3F8:4 +4A805B17DD177C99162ABFC91C817FBDDE43C924:2 +4A99827694F3095EE11A707A62F1F403F28DE4C3:2 +4AAC2562E20B8274EBFB37F86642F38F7DC67E4C:3 +4AF02DB21E3AFAC3CD2A1FEB2774D906E8864B6F:3 +4B07015E85FF98BBF670400CAB06ECF756BAD418:1 +4B0B1C52D4D443CE46B12F0C15E8C963CB81878F:2 +4B2E2EB47687299E67E406C4BE1BF53FDAD65606:4 +4B5FA99CFCFAE25668EEA9F1B5DA8EF8E7E3BA52:2 +4B66FC71159C432E8A4B263A01048BBFDE516CD6:3 +4B72326F468C5EA50BCC5707553926927B5DEE37:1 +4B76FF4A4EA35B89E1614D247F48EF86E0F49DEE:3 +4BAC64404D8B411126BA6D3B141156AEB12CF921:1 +4BC7703AABC3B4AC230AE110473B53F7C247921D:104 +4BD13670369390D8AE8AF15779ED86FD32932D05:1 +4C00D4F58BD29536FC9860FBE327CE8454DF3A07:2 +4C0A065796254F0D1D55BD9B55CCF93A06112176:6 +4C0A9294C4AFA4238FE2A49D1B63029585EB1F75:1 +4C1075DEDF2D506BD77AA20B4DC197B68D6D2DE8:6 +4C1565B218D0ACF9CCD42D074488099C8F7E79B0:2 +4C25B6709C1A1A9CF6D3324A5890BBE0CBC0BBB2:2 +4C2B9FFD0302E74DCD63B4515701D86A24259033:2 +4C2C84A1EA110037F85A947811719B9A132EA247:6 +4C355ED709B2366AE9A11D7541A57A28BCD01445:1 +4C367A671BECDC6746FC82C600502902DDA53807:1 +4C42358BE1F94B06A4A19983CE844282B51B07B7:4 +4C75D0B946283C7885F5B74355B595DE1659518C:1 +4C9FBC445D6163AC80907E7CE1F0E5001C2F1DA5:2 +4CA6D9FDF75C7EDC571F40C9A310BAB133786A14:2 +4CB11874D98E2AD7470B4CCA1FCA48E7B0691B8B:6 +4CC6BE7D5AC82A5E093429D4B4C887FA0F71C5DB:3 +4CE479AEAEBD2CF9CBABFFB1F997D1E8BAE557AA:7 +4CE68C7A44AB489E1CA5C9BB09EB9656D5AC6DC7:1 +4D261208CF0E70D4DA99BFC3A807EBACCA3BB9BC:1 +4D2CA6BFA75F652D848F15E21D7620E33C20E603:2 +4D30816CF34CE6A0E993EFFD7E9668EEAAA57608:8 +4D3848532482D9D1C328D73A61B8B1046D69E9BA:2 +4D432859BC83FBEE993CD56697DA6337FDD93C35:2 +4D72E6B5EF44B9D52F01242BFBF4E8EBB4869319:2 +4D873E4AACA7099D6D4D515B408382FD3F5081F5:2 +4D99774E6FC8D5D46422F6BAF29E819DAD0E2BA5:1 +4D9B47119E13C4F6A1D3E0606F673AE4DFB5D112:4 +4DA4F3D26EA8B460357D2890140864895523A6EF:1 +4DAA98F60BF3EB093ADDADB3F3AE483F99409B6D:1 +4DCF89F6245814AE01EA3A1617E7935EDC688861:3 +4E2119FD3E80B1AA2C140A17746A52005B839C6F:3 +4E2BDCAB55AE0A4CEC52E8F37E8014988E581494:2 +4E3F39569BBF531CA53B7DDFAF4F693F88A82D63:2 +4E744B79CA360C12D0AC78B59E122E7F1BF88BDE:3 +4E77A1A55E9B98B2B7C8964C75A0ABF1842B8164:2 +4E81D0C00F7F458C6C94323B7CD2D41EBBAFA551:1 +4E8EFE0A76361261E883919A5EBEF8599E44101F:3 +4E91EDCFDA827C2FE3A508488E91EFAAE8C18995:2 +4E938730C6938742A45F2CF194E1C30D38589170:2 +4E951C1B56EEEA11C5552141F62501EAAC5805C9:4 +4E964D885A04494D84D028CFE40E8211B5E7B90B:2 +4EBD11F153F973A48A2AC1C368314134F8342A71:4 +4EEC74E184C75C24AC506A4FF38E61AC743A51BC:1 +4EF7D55359EFD1EA803AC99C040795387FDBF206:3 +4F09943DEC7073818E4C2A8A813F5D47849BDCE7:1 +4F10B9A26D5A1E03471C44F5AA2FD10E1F4D976A:4 +4F231823F894504E65E7776B7CC518A8DF52BED7:2 +4F26F491F5699AAE448EF465A999EB1CF119123A:8 +4F2FA5F78BD268DF800A0D4EAA040536E5D47841:3 +4F51A01120D9760E37AA3A8D138818DE8E6744FE:1 +4F56EAD3C8F3EB6B22C10173C3DCC94898A982AC:1 +4F7791D1C49CF00261CE2AB68E4B2704CA9C0E53:6 +4F7EDBB6292B9350F9CE1FFB0CFD8F02ACE3BC52:3 +4F8F4486CC90EE6E1EC754C8E6B1D12BFB95D404:1 +4FAA1E5CAEDDFE3E093535B35FA9452286F9F0C0:1 +4FABDFB2EBCBF2BAA1D6786343EFADF00A55E731:1 +4FCDA18BE00B38B469F50CF412986B64A3F5BB6F:6 +4FD6910073463F41B264958908FE17D6F65A2B2D:1 +4FE724E9B2B80494CD543AC87448FCA62EA9D893:5 +4FEA34A7572B66E4E48B02DD507FF2C4E5A69731:6 +4FEE69B709071A0F1E47A150BCAE6E31DB8D47F0:2 +4FF8842A428106ACB3D30995FCA0CBED8AA4233B:6 +4FFEECEAE25164705EC6DE3022BA3B6B20547FDD:1 +5013C1C14848838E638A302159511B565A61012B:1 +5036FFACAA42EFA01B9C4FC607A94B5F82BA403B:15 +50497EC42D5362D265E2490DE55B4966953ED547:3 +5054D7E0547D49AACE22142124F098BC90F7E134:4 +507EF4D265A0E91B62A4867FA5E38C32307AAC82:1 +50A4FD91738B5C0BCF7F85845CC2763A38C0734A:1 +50B9BF38CA6898D131C46A7450DF41A231E13AFA:5 +50C459B6B3E5DAB7CEFC7E34B103E9C36542492A:3 +50E89BAA0FBD2443DCF41278C429AAA5D9D90FE2:1 +50FCDC2FF3ED4189E77B9AD6AEA649AD756F90BE:2 +50FD0CF81331303F60E1F7B3E48C3AFA1121B915:2 +513F8F7685B40D9C61E12DA9BBF5C09D4559A4A2:1 +51666DBC110353473139A7995EE10CEB2C6777F9:2 +51736B9EDB66444D12E2544501A92574915D6FDC:1 +51898CFC537145D8E4C8936E04417F8734D0F164:1 +518A5D3974C272E6B10B02F7717C76B3294DF078:12 +518E40DDADCE54B106C21C821D47EAF745D39EDD:3 +518F35907187AF67A672D30CAE928304A9C629DD:1 +51AA4E1F163A6AEF951C5AF7F6DD9DFF0BA2607F:1 +51AF565FFAC7EBBB52CB618AE85EACEBD29932E3:1 +522E0D0DEDF93B556F10CE4E3DBD70A3DEC66AC9:3 +52377E0B5E994BF6FB17D184170FD4D75FDA59D5:1 +524B28236DDEFD0682CD894A325D22F9FB6A2EA2:1 +524B3CD7FE2929886A8776D80F2811ECA96ADEFF:1 +525571DB430FC1239A3B0720204B34AF681EFF85:3 +526A215D6346E54DB9D4C8A42EA780CC3A999F54:9 +5291F2E82C86580F80E743F5D96CE2F187EF46AF:2 +52C42E9A718A4699EDADA5F063CC11B82D182922:3 +52D6CC3AFF1FFF453164DE0C3AD6C9D7611EE8EB:4 +52FD70F9C269F211C6D0B631CCECAF9EB4B7F5C3:3 +530450DFAC0055AFC4B75CFBAFA4E2444432D6CE:7 +536457FAC241CAB40416770339602CE3548D6323:1 +536E222C9DA303FF76D587E4CA091EB25384AC3C:5 +539FCB557334E93DE6C3A78D14727F68B1A28E3F:1 +53D6EED6CD0E07DEA19621FCFF6297FA2C6CF6C9:22 +53D9D0A358C80BE89EAC41C9E372FDCB440BD176:12 +53DA0ADFB03548B587EB55C0BFB421A0ABE9E261:1 +53DAF04B423CECDAE086551F74B9DF5184082167:16 +53EB270A053A2758AF77F08335600D24B04F4DE4:1 +54146BA93419C04EDFCAED8D2CB19330A70C16A4:1 +5437E42BBF3FE435FDFD5F5FCD8DAEB5CA6BED5E:30 +54394807FE8A60F19DF4F3B25982135A8061AE1E:6 +54825C1E8C1431F0EE094AA683562D2713F170B7:18 +54A119C8636A82D458E8EE85EEF59B5803FF9CA2:1 +54BA9ECFC8F78CDB7DBE7A701F737085B2B9C312:1 +54DEA709BE356961980B26872FBD5B138BB6988B:15 +54DF2BA825510F1AA8372D44D5F573E64A8BE2C3:2 +553E6A896526280A387151C9632956FD64AC8B94:1 +55628FA692FFAEA7312F89E0BBF5BBA24808C579:2 +556C5C2BB3B84801D901870C57D2AC0CE39EF73C:2 +55C2FE3B40FD9E6CC932D2EA3793DC0464C6B8CA:4 +55C3217B96BF64D1A35EB7C97BB89B5DE8FCA952:4 +5604D8AEDD5B172DA9BA1856681DB1381B494DA6:4 +560F76DD456F20C93EE4E03513FAEC201E364A37:3 +561A3B5EB14814DAA6C94EF50BC7B126A56B9D8E:2 +5627F20061E1D4E9A3C4785C638C2AE549953466:27 +56374A040A44B21CF2F9DAAA249C2C0C78AC8F21:1 +5637540CD5308A298E814398D9351BA404AD890F:2 +56792D7BFFAC8D36FD981AEE20D68E78D9FFFD4D:1 +5688485AF8FAC7668324A05EC23CB76FB0F5256A:8 +5697518555A43AF492149832FCBDB830884FAB8C:2 +56A08DB3F15EB14ECED2A0166CE8DE34F9DBE4C3:1 +56A68BD7A88F710C5C28019EC5086202898CC76C:3 +570722696D9BA50376244A1666E0224077D82FC1:2 +5715C3526B90143A314F41F2DD633CF0EC2C8D8B:6 +571AAC838752F67F8B66B2CF080DCBB8DFFE8151:1 +571AADFB03EA08AFCE058818B544240DB20CEC64:1 +571D63282D8762B8E631936D82D8A0C316770D2F:1 +5728BEDA730674A495D2FDF2CF99721BF3F276BF:3 +5740554D417A81FF56C6DBBA35DF9FD31A13E93F:1 +5743A85A428A64B1780BB0532E64C8DD0EA9723B:6 +5752C955CB6D0A65BACA4096EF7BE2E347253242:1 +575D1E321D6370D5A6BA51E99833942594D9A26B:2 +576BD412E6F18325AE36FC689DED6E64B25E82DD:2 +5785EACF170C70D06F2B1F9823FB02C988B07043:2 +57C1E3F0D069CC36DCA22FCF102CA3320F92F454:3 +57C411E8AC49D00CFA1FEDC340D77ACF230D9A9A:1 +57CCF40A46858347850AEDFC3A013AC13D7A09E3:2 +57E181198CCC6ED154C46E9F6DFE6D02FB26934E:1 +5811CB7532E53D00EE3ED72E936502EB0841F83A:2 +5835BA5C2AFEF6469C468CFCA8083D6CDCEDDEC7:2 +588BBD44C6CE13B8FB43969FFF1C016413D4A7DC:6 +58DF741947C5B2995CC7153D7E033DB81AB60BBA:1 +58FB5681895C951840FDE60DDD5D5053CB16D55A:1 +5916BC578B627F88D2EBD9D99C997F4CCAD55FA8:2 +592292F61794CC4B2AF2E8E2602DE76D2C4FC68D:1 +593861180A7372F26B5810B4490C5BEC38161DD6:2 +594318A6F527339A0E160404E440F48B7C9792BF:6 +595054AC6F9673F78F68DDD5FC447CF39DD78D28:1 +5958FF137CE5F0D0F10FB30F52E0A4E1F1193CC8:2 +59B08AB4588C635D6F5F3B2F477FF3EC8C0F3D7D:1 +59B0E29E707102074095871D27FDA3CEE204EBE9:3 +5A134C2104D0AA89932BD060A9875AFBEBE5C33D:1 +5A153E887F167DA673E370A042DD361796F9288A:2 +5A250E6244057F55686D75B9B568C7B14E511A7F:2 +5A25E26501A4D85B31DD2F7C14CDA3B64EF2BD4F:2 +5A35CCAFE19074CC1E186FC3063AF911384989D0:2 +5A476048320785366F529AB051A1A291A967BB80:1 +5A5B9C075B361900CF5DEB62767574B713DBC08D:1 +5A8BE5364426DE0188977BAB6434B0E68EBFAF39:6 +5A93CF9935A01ADBF8688EDB62BFCE7820777A82:2 +5AA4FAEEE7B2E331DC50988455FBB1A157942A23:6 +5AA8E86D1A9B0B1B51A21E2B6F3AC7BA82CBF74D:2 +5AB5987588C8037A4C4B9EC5F3FE3452FB6C1C5C:1 +5AC4FFBA982B3C037E26DADE6EF6A5C3584B61CB:2 +5AF148B013DC27E032B3DADC36BCF84813DECAF9:4 +5AF68B239170F756D5346CE12A0D50A5A78BFB53:13 +5B02A0E9CBD42973923CBEBA5E9836E0CE108DA2:2 +5B278BD44A71F04B57619391A1756561EFDD9DF7:1 +5B2FBC87DDE06BC7A2AA839FE1087622EC5B21CB:3 +5B51F2614AF7F158A50A567D099C9169FF3B14C7:3 +5B5BAE0504690FB656A4E2C9E3388F447508E6BA:1 +5B9CD2236699DF43AD97C620BF06EC55BB728105:2 +5BC2DD5BB6F1423FD06EFD11BE2C1468DF9EC8B2:4 +5BDCB0D00982D6B390FF74E772FEE8B70D421D8D:2 +5BEFADCD614B2B6137C766629D205D0BD6EADA0B:4 +5BF116D17BF6B3460E7FD033AFC44CF4931FBD33:2 +5BF8E9F913EC773495E52FFEFC5DA75F35022F28:10 +5C09DECEA6EC5BC0F3FD8025E8F22E37B49984F0:1 +5C127C5D365EA14194C38FE5E38CC6F9823AF7DB:10 +5C317C72F6EECBE1710FDCC1325CA3AD2BF74AEB:3 +5C5F043C1D9C3DC00B2F7DA0368797854D4633D4:2 +5C8B5CBE2C2016A98BA6A2FD920C4E73AE349FF8:2 +5C8C8CBDF45BA2F207EEABF34ECC004CD3DC9642:1 +5C8F560DB59854C6615DDDBB630A926DF6F1E9DD:1 +5CA3BFCE7995A3D1091B7D8B30B4384142AA973D:2 +5CB0CF3E4F5B15B6A8BD46E2B056C1FE97881526:4 +5CC5B9406901549933875C6FBC26BBC90484450C:1 +5CC709D7B993D1E3389D315F5864A2403F505B55:2 +5CCCBF52830F832F1CCE6A58A3F51E8BD986A6CD:1 +5CE8F004B5E364C4AD9EC3C6B169758C892D710B:1 +5D0BEAF54BF90CA5CBA319F6CB963E5EBB881650:2 +5D0F5EA7979A16CCE5D790729A274BCEB29D4DDA:2 +5D0FD2787596893AF8DD551B9E2D662462661660:1 +5D19DBFC9E6FF08676E424D64C3F3091D61F41CE:1 +5D2FEEB05D6C5AF1C0D9C79D871B27802889574C:4 +5D9F22AAD8BD661F7B3A1F4877EEC861AA57C581:1 +5DAAF0A82DE99154491608CF2FD97A61ED193F18:1 +5DAF8501DB7CAFD93B7B03912E1CFB9DC6FA46DF:6 +5DCA71D0AF4A44356E6D91FDF783B647BF66EC6B:2 +5DCD4FDF02240DF77B5348E6276D044CCF6623B9:2 +5DCD51498EFB19610DB9208A34196D7CCB9FB29D:3 +5DDACA3681882E763F827FCB958C8B71375F4544:7 +5DDC2923007E1220859EBE8A64994BE1CCCFBEE7:2 +5DDCCE63AB63599DA494B5773B4A31AE68EBFFE5:14 +5DE64626C1C8DAE5BC3C3EFA2C3D673CB05B1157:1 +5DE9318437A368EF4FC5C4BE5C754D77A5B29D91:2 +5DF59657CF51B09E47985F09CDDAD5B486FC320D:1 +5E02DA7DC0E7420094EBDAE7A742D14C43817D32:1 +5E2D6E267610B01526C2ADC9E4076C01630519FB:1 +5E34A0E58AE9C8FB62C7EF9DBFD2AC68D3038F82:1 +5E458BD1032C8511F69C7A16CFEE3E3C1DDBB0A3:1 +5E9600EECDC5C889441B2D007A7CEA526284DDC2:4 +5EE69950C421FECB1146A53CFC2AC35287B3290C:1 +5EF8DAEF7C8508A3DA43B08B76F08CADE1F991FE:1 +5F13255503A32A85E84B53A3CEF1D2FAA6F3F5ED:3 +5F235DFC7F1C7D8B70EE752FE7F59F04A85BFC37:74959 +5F58DCB6CB41E391671527DFB272D29D4EA9A88C:5 +5F5E09CC7031FDA52B871C866C1EDBAC43353B5A:1 +5F8AA177D4576F6F106C6637366F5B95D1AF8275:1 +5F8CC4EAA6665932CC8084D5D8A3CD8E737023DC:2 +5FB38408A7846EB4E577CE73FFCCBE5BF81CBD17:1 +5FC88CE7A41B546BC5C1989AC0C0C1A137C624E4:3 +5FE64B32601C764CEB88F780DB57416AAC06A9C2:1 +5FEC0FBF0534BD93828372A763A44A5466356641:2 +5FFED8D11A84C42974A5D3CE8C807D043D5331FA:2 +60098EE75A58602B1B5FB0CC0B644F1B6BD6ED63:3 +60229EFA28C34E264C904C20081C894AA6B3071D:8 +6030556AB2A48C42B183CFD0D3778C48B78E401C:4 +60327F12F06D75B7138305F54DCFF665BCE9BC5D:1 +60358200FFB23BCBB585DFD72F99B4F20290109A:1 +603F23252F73CF5BAED7332F8283D453F87812AF:1 +608B60B681FF58149F42F61915F09B1913B31471:2 +60A86210FF84058BF34C9DEDFC1BD44411386D7C:1 +60A987B6BCE0CCCAE1C8C06955C52270D478EF77:2 +60F25EAF25ACEA5669E20D1734CDB2AA5449A8A2:2 +610EB0F71BEB680B3D2F8CF1228552753A2164D3:1 +6117F1ABEB319BD735C21CE67A584677B02F881B:1 +6134FE015367F22983233A40B8D22E0980009A43:1 +6136D4E177DC6ED0E2782C2716A722F96398FF49:27 +6151C2EF3CC431917E54D00929BF06783EF796C2:2 +616985F84126AB651D5F04F41889CFFD7352E798:5 +61796F162002248F42EF25D0B9AB253F841EAA00:1 +61816D5AB6E258C15577D78413098CF51F3FDCB2:1 +61A792DB5CC09DE36F863A2D46FBAA478A10EBD9:1 +61A8E18FDD29B35F8CBD788C1F2BE238BED023A2:2 +61DEFA79A4A0B6A1D1CE466B6BB749CB9A8A3266:2 +61F2FC723A941F2D79DBEAA66D488D519A2E5B0C:3 +61FE664EAEFE57DDEE9BD81BFD2129F82328ACCE:1 +622BA115BCCC8A75B71CA8F7B34236BD74CFAE4D:1 +6251907FCCAF1512F581A3D7DE6F36E27694E8AE:1 +6264D25037FFA4F42373051138EC692B7EAA593C:4 +62658D3615D44AAA3FC636CC85EAB46C355335CD:3 +629D594825E612350BBDA8A05C0C5EC7888DB137:1 +62A0E8235E88782EC0DB9C7CFAAB48C0B37FA261:1 +62A2092C60DA9530628E376843F905747D6844EB:2 +62A4BF40DCEBCF774789F38D4BFA38B6518FEDF0:1 +62A6ACF21A76369BC6BFB315ADA0068AB30A4011:1 +62A7266FBE0F63D9C43E72BF4885EE1E513508BB:1 +62B0EBCCA21C4A7D6AF6E48A1025FF55589784A2:5 +62BC98DDDB78AB98E985637280686BF87C367644:1 +630E8E0F2D22D9295F6B6210FC6216AB323CE9E8:3 +63343C47A771D91620F0157878E40BED407D6950:5 +634C49CA22DD014539485BDC3F7254CCA895449D:2 +63906EFDCAB0E3CD7494D896DEE3A48EF470D2C0:3 +63CD122C0A69D3E20BDBB7C0314CC63DB0EB7A2D:2 +63E59B54C47647324B126C0EFF281DAABC26600F:2 +63E94BCFBCA0C2A90DE0BF23CD3B3452227B0C90:2 +63EBBFD572FECC5179C4BB7E9D41DD29456D9941:2 +63EE2B09136A3497DBDA05B136C5FC04F79C92BD:1 +64036B59160FD490DF934377C45CB96289475FD0:3 +64248B591D1A66E2A0CE9C00D0A2F57B7BB1FBEE:1 +6449A88FEEE8AF8B4FE567FC85CB795A664DE349:2 +6450F389FD858184941520536E6716AB2FF7B2D7:3 +645D8B69950EB28A4EC4ADEABA129E052CF091D5:2 +645E05ED2506A44DF58ECEF8B9CFF6E72E76A149:2 +64CE9EE7DC33A9BBE06FEE17E21E96B8EB4C7FF2:3 +64DDE554220287AF58DA8671A014EBF13BE174E3:2 +64F165B1797A6326C404DBEDC2E90A2124E17967:19 +653C6CED40F4F7DDED8A408CB0F6F373B8ECD646:1 +653E1924EEA072D995809939B7F8E9A59470AB94:1 +655A07D3BFD31A85B439E4F3780878D2504AB44B:6 +65602009C5C9F90E8F01824F5972B7D22752540B:4 +65614502695AA09F8B8FA71FE2A60ED7B7E47FF5:1 +658891EDB5F3076868242D2B9ADC843848F7DBB9:1 +658B2175195A405199B0FFAB9B7257A84C5AE2B4:1 +658C3B7D33F8FFCE4AB3CD68500E926BF37C523F:1 +65ABE8B3368F8B845C3FF619FCD84F15160B2EA7:3 +65C4BC480E2F11993F21FB8228764163E77C693C:1 +65CCA6A0966605076B77298AC0555DE55E966F11:2 +65E1B071D006C4D995812DDCB30CBC5C6FB4899F:1 +65ECB3D60ABD21C3951CE7FA696939537723170B:1 +65FCD2D1C4D8AF261AE2DF26ACCC1CC84F39CEB7:3 +6605C96A207E97F510DD4A5E8403647FA06ABD00:1 +660C66E19694BF1564270F33C8C94C4A436A9D75:3 +663004EDE7B01E4E7099D8DE714F618A03B5BDA1:1 +663528C38FB9FA40D5C66794CC47FAE62677AF76:1 +6639AC855890E39FAEB4A014769A10DE58432987:1 +665CCB1514A8806B7BFB84ED173D4F41BC05D2AA:2 +66733016C99C30B5A0D1999F392BA9E0EC349BF5:1 +667DB258A714E819035B51ADEDC8601EAA22E894:2 +6681B4A9C344DE474518D9F51610629DB00EB1C4:1 +673E6FF7638A96A5284695C7F839CAD7F9DAA83E:4 +6740791E69854210AE89461DCBDA1F7A6BEF98BF:9 +675C0DE898E0F1E0075EED74B78D75DD941B62BA:2 +675C85FDF66196F192501E29C927049F905BAF25:1 +67629A7FF8EE7DCFFBA985B88A856582B1B781B4:1 +679CE606D1ABBAA330122543B27D868CE26FC96B:1 +67C9BBF020FF6E1F3839CA36E36F777A5834D69E:3 +67E5AF204558CEDDA96B16C5A3F4CBD1F863C822:4 +67E64130001F7C260C462025A7F90C070F309BD2:2 +6809D671C3A7EDC789920B90199A07F83463A05C:55 +6819AFEE2B6EA7357B5C107B3884335AD584163A:1 +681DB7FDEA410C924DD2CDCAE88CF4EA672F2E31:1 +683AB1B8CB01CEEE19298E9AA1E1FC14300D84C4:2 +685741F05D6E15370F2946FB040311D59F46A973:2 +6869750D09DE7B18BC2FABA401E89FC84D177A2F:5 +689138A2B1C630345A194B02095CAB3CAF999559:1 +689D551D422185CAA87A303A4CCDE9A96B36E3B8:2 +68A39169B111AE1E19DEAE5EBEB7C2F27D1A2572:4 +68B323846E42300EC74D51B93F63B798D738E2F7:1 +68CEA0243D036BB8BE148EDD2CE4BD1684C46326:2 +68D9FE5478251C63E92EFCF7D5856890B8E49CCB:2 +68DC03334973FB861E2A0ECBACBCFD4FAD5615E1:4 +690F46A8DEA34AE40EF8222E6FBCF98EE031D5B1:2 +691B59C24B489421872FF20D0B31E13678E73858:2 +692F3045586319B6147F26B9BA1F7CFF6BAD7700:24 +693591FE1D244ACEB2C84E7ABD8F6E1F80BC16E3:4 +693F0CE95C62C16BD1597E7FFAA87716C0641F14:4 +69410651561030A333D937E857E1D290E333819E:12 +6949756985A7D185DE9F950361D04C3399D767B1:1 +694ED53B27A2098127BEBDA9D2BD1D22F824F845:3 +6953ED4701E538D982AA3D34129F26BCDC85A302:2 +696D0C2FB4E5679B741C493BD28B14E8724E23B6:1 +699D040BE4C64E67CF6304A74CBAA4E5A29497C7:32 +69B31B4CC7D47D3AE00901876BE2D72F302FB8BF:2 +69D41A9B39167BB7C34579DCA5928D8AA46E268A:6 +69E1489CE0CF02624335D46E59F96CB6116CD908:4 +69ED28DAD6D4BF42BFBFFFDC8EDB5CB27D564229:3 +69F2E9DC8F3B9C529FA163961D18204AD0FD5517:2 +69F440E8D3A63C9410ADA3214C3C1D38FB8D65CE:4 +69FA914EB9D161F75EEE06EE940A992C45DFCEBC:8 +6A23DA657DFBE889FB8F9C9BEDB2AC210E3A9A4A:2 +6A5ADD0AEB18BA0C010BCEF1960CAC4955F112D6:1 +6A5C09E6D0B0B86F61690E1037CDA0C56628E05E:2 +6A74252AF747691EA70A0D66981720A88FDC0C37:1 +6A7CA12590F991B57D498EEFB72A8D5A8F933DC1:8 +6AA24F0D2E58AAEFFBD8D7EB1D4BE6C896659E83:1 +6AA79F0E29FB5C784368C5500F6015E421A5E211:2 +6ABFDA0F75D653B46B006D90345BA2388E241F37:4 +6AFB539BE2B6E941C22576CA1C3A7DAF72C794B9:2 +6B0613B82514F1C14383B2C1C23FE5DB1061ED4D:3 +6B2078E8678B2F64FBEE9EAA6F462170003E3657:2 +6B68FB6548D0A4B4FE9571857729283C43077C78:1 +6B72D143CAB4DBB12E6F2A7A0A38BD0CC633B08A:2 +6B8C02542FF7FB68D5BCA0CD91FA3ED02488B2D0:1 +6BD24C9CFB2A3AEEE68B65F8AC602DF5ADD3EFDB:9 +6C2324E2FEF186187398E8DC06B383E15E1E138F:2 +6C29088E3AA469B687C2AD91F06C60DAB54D1138:3 +6C30BEFC2DC9D4A504DC573AF0502CA50F63EE16:3 +6C30EAC81C5FDB91E3FC258D71DA0E600C04B94A:2 +6C4AB87185651BCDE309B0D49B8EE8C1779F38D9:2 +6C529BC7BBBD1E1F493AF73D0E7293F2504145B7:3 +6C65E3F16B10BF6D00DEA5DF9523450DFA55157E:2 +6C78D3C68B254F3FCB5FD0477910282999FFEC70:1 +6C898F1DA94C09697D05329B0A58922EE4E28348:2 +6CBEA9B2DFDBAF66DDC4B18681342B5C87BC113C:26 +6CC5A45034C8359853C1CF0FA0D91046CCF67BB1:2 +6D0BE65E2F4DA69A2E219D15EEB5853BA6FA31E0:2 +6D1876A9DC2C5475F9BE1942E7B24068DBF4AB69:2 +6D71753F41AC733E72C9A69C2A8312347B8AB719:2 +6D84206BBD67BCE8C06BB528873C41DE100F057B:4 +6D992FBFE23CFD0D668CA650238E60B9FB73F5AA:4 +6DB535FD708886A565266CB7941095159BA33825:1 +6DC9B1CF0E35B02C82F6168DCC54514AB1C573D4:2 +6DCBF0DB0051AF3DFC94A5D7106685744C645AD6:2 +6DD848ABA90F4719E32480597EE55924DF2FEE41:2 +6DD8C4D3BE4424FEB88B00B05D71B88049BC061A:1 +6DFC5F6293D67F374FBF66276C1B223AD74A9A8B:5 +6E0D0B1C17EA6C4855603605FA2B54E5566F9AE1:51 +6E199DA8CAF2B0ED866C2A30EA4EB72F3C678D86:1 +6E3954A38806CF98035936A2BE3CE5CB6A964483:1 +6E5F42729C891053A93CF5BDBDF37E041F71021A:1 +6E63B10C97386902EFBF437A362CE8C915B1F97B:2 +6E658101E217DE622247F29E7F7FD9AC41CD0632:2 +6E73AF2FD920989176B279EAEFCDE2E71CDA9D3D:1 +6E74577EFAB65439E2DBA38F3A95E2B8B583E667:15 +6EA298B9728C61C83D3D4358540977B0EFA4749C:3 +6EA29E3A3FE3115EF7C67ADF30205CBA0AC6B266:3 +6EA2E9881D2B65DAE88F72C0F8937AE728AA4F21:1 +6EA5DDB7D15532111AC09C2DB3CE5EF599757299:10 +6EDACEC7E7C912FC9931DB7CCEA4B605A6F242B4:3 +6EE2C9E7B0EEA0FA0988BEF249D083872157DA97:4 +6EE6D36B5655DD01AD5BBFBC5117C75EEA81FF3A:2 +6F1427613D577977F1C928F5270BFC7BE84733A8:2 +6F1CEC89915CB374ECA262A0505EF260B4C96DED:1 +6F482AF9068C2CE4DA320EE8A89E6811692246F6:2 +6F5683EAF140F3E425AC0CA5A82F095F22C84088:8 +6F6033A799EA0DEEDBB811A714E4322397BB5DF8:1 +6F8C089A5450F2DC62CEA68588C18212AB49ABD1:1 +6F9AB31F866CB186A4E18D389CE88CA4DA985070:1 +6FA1C6FF1FACCD8D14B2DE98B3DD0370A256DDC4:2 +6FB596F98E63634E24A644AA6BE03F3542902074:2 +6FB7F9ABFFA2FD9A125F91F08EAF73247A31E4F3:1 +6FF283F935AB266926AC2D870CDDB3A059E63C95:4 +6FF720D167CD9590ADC594A3CEA030F40A1DBEFD:1 +7004CE675FB63C5006915850CE45610A2432A9B7:10 +702BD352AFE13962C40C5EEE322A3B7F1859C452:2 +702D9C0E998501D1036BBB9EC5011C46AC1A5078:7 +70332091EC67729A0071785FABF032534BA635F3:1 +7035D6984D7CBE15009D57DE3E457D3456D6A17B:3 +70376A637FDDF1B5E659C8F22651E6E21D12D55A:4 +7042A46E80AFE0F4E3FBD257B99D4B265BFAC795:13 +704DA36FD3D2334BE6FA5B4D252F4EB60B36E161:5 +707A020C5BD9C9BC86DB2E99B162715BF8D1CFC3:3 +708B2ED439C7992F4923FCAD5FB3FBF08798CEA5:2 +709B5DD3347BA1082D9EFCD92BE8917A7B4C4F34:4 +70A2F0038CA104684CDFF819C41B2B6908F76FDD:1 +70AFF37F4E08212B09968D906D8E7008E54A6E3F:1 +70B66FEF216349615B3E8EC70E23F01450CBAAAB:1 +70BA78F71A0FDCCC9B61BE24A158EFFF6C8235D6:1 +70C213360C0EE51308DDBA2E9FA5C96230872399:5 +70D6002D51397E61EC113FE35C783FDA380A9946:1 +70E28F73C2B5EDB28303C168DBC3373ABF0A275E:2 +70FE478371C9FF4EF2689C962C4ED4D5C4206355:1 +7125DC60EAD8E4420890E07EC68483711B7CD868:1 +716784699821AA3EF2E086989ECA8E7A1B8053AA:4 +716B6A220E5D6144D9CB51AE00CE075181643DF4:1 +7174D4621252BADBF44292F0670BED5EED7A59C1:1 +71A6DB0ACD87F8CD1CD29443C85CD5FA98AFFF47:2 +71EE84E46E6D5069F03691FFD1733E7BC1857C2E:1 +72004DE0FDB33956BC5E8543DD273C684B8365B7:1 +7221CCAD3ED2787909655CCFAE443261A0CFD08B:1 +7240300BB32135846B4E97DD865E743B02DA725F:2 +727A4FC630CDC042E1049D25037E5DECEF6074D4:1 +729FEBA6D84872874F8F53371B7F4C8016CF27BC:2 +72A3FF1431651BEF6D2CE66043F0FC1787A21461:8 +72A8046E86DC3E38874DB558148F885198B4BB55:3 +72AE11D23FE8093172D655301F4ABF92461B2628:2 +72B17E3F11D8DD378F5DF51765A0E71104C495F2:2 +72C24A2BCE5D6FDDC70FB52845690168ED2B6AD3:5 +73270AD7B37059780A0A2CB784C83A2E70777588:78 +736765828135944998006BF4AE0A89D9106E0988:2 +7372472F6AF2CF39A6174249FB4B0DD9C70EAD10:2 +737C40491D3146BBD1831C08033CB68CA5439CC5:22 +7386E06FB8F46D2642E45DCF922DE2A34AFE3054:2 +739B61B378CBDB38458023A31A17ABD9CE25FC5B:6 +739D04AF4E7F9ADBFCEC852E150C2C49F9D0CF07:1 +73A4884D282746556EBF9B10E4BEC1D3F00DEC31:4 +73B3CD1D2A9DFFAB2FE9A1045875A21CA32C91E0:1 +73B8FD57C8CFA8192C5D19CA3B842E02D14316D3:4 +73BD6A0A23CC778904678C293BEB4607A37F9E11:2 +73CF7EACD3B601A9CA133999ABB774C8661DB66A:3 +73E6395ED49F0FBB87AAA627F73887B480538EE3:2 +74167BC768FCC9FF634E479B70781C3BEC3E5A7E:1 +741901D5F66A253A9566B9E3C34E1DB36ED11389:1 +741F6FB478BBEF57F4D734A6275A41DF329745EC:4 +7432ED4F2ECD194D309B5A5AEAEA78FFB16D687E:3 +74653B83723BEB46904028571BA69C060FF04AA7:1 +746EE3C56F21A5AF06D8BC7240BA62A144258ADD:1 +74841BC2A03A1A34E2BEC17BAB60AA05DA9CE59D:4 +7487821080449B71BC8853BBFD0B602149B68E8A:1 +748789988A6DA1DCF8AD0723CC7224A1C422B58B:2 +74AB357BE8EE0F9724100D170856F200BF74CDA3:1 +74BA8BBF540CEF0B867C1DE6392D783761901EF8:2 +74BC53398FC478CA61B5B36DBB3B10E3BF68FEBB:2 +74E59AC14A63F80745BC90B58FA197D3461B0FF8:4 +74EAD41713EC24424F2433298EFEF4D12D5DEFA4:3 +74F61E0D01947EECBE270D35194B9DEBCB4A0883:3 +750623740AAA0FADE018461684418AD03AA9DDA7:2 +751288DDAA59F96C2F4FC40C9D5559FB4F964856:1 +75200214410224CEF29EB1B612A2930359D9B223:2 +756CED485FC8767A67841FD4141DC057E3602227:1 +756DB5CBADA3665AFBCBBAC9D0802336308F0AF0:2 +75C846CF54F5EA7C82C1ED09BF40B34D4FBD73E2:3 +75D47A740AFBF89C1995E1B2FEF3B6CCDEC6C82C:1 +75F584F37CD3F3C9D7678A006D5206BE0DD32087:2 +76000C878CB5093C47B69DB595D3A181EF9C5DEC:1 +7605F2FCB6E0DA2DFC1FDE6795E7BA616A8459AF:1 +76099F85177767C08712BA756753EA40A532448B:2 +76226D9B655CA81452DCC0DCD6B761A85EA31EFC:2 +7636FE05746EAE68EB755F62F31F302A7A47E5C2:1 +765D717733798EAFBE986F9C487B6D8C7A8CD51D:2 +76615D80145539D5CCE3A4382B8363615DD661F3:4 +76A3D70DDB5B79C8E0F5AB36EB3A47315AFFC989:2 +76B54AC6FD946B558A6A3603B12DF75EA60DC73B:1 +76CE44BAB84742717778DBBCDF06B818C524B507:5 +76CF83B302991673D9B172CD194F254881B91DC3:2 +76F4A84F4E0139B8BDCC9921EA984A51868DD58E:3 +76F7E160C7C2F65770A81B0E61734D45788E718D:3 +7705B784C7A600B8EF802253F03C23320AB63993:5 +770FDF7EBF9F30BF39E1F1A0CBC51454F5B8E1BD:1 +773C5C246300DECC112050CB4CDA576303B910CD:1 +7750F01F9FADBE2140E6C56D01324188DEC5C7C5:1 +776441DF3BCD28FD6987575A2CA318EE937A069E:1 +777646707AE85489F98C83776A4F9E29A0933FA7:2 +779043CDE82F5D05AD12C4C65A51D8906871E581:5 +779D5F234C6381B73C4C8BE743B997D47EE35B62:1 +78120B0947BB97695B41B5B921ABB2D52177AF18:20 +7814AEA023A682E6D591D1E80E80041ABAAF5CA9:1 +782B1E6D2D752B60441B0281DD096EB8BF58F61F:2 +7849BACE6DBA95F2D7682EAA3DF0217279168DAC:3 +785375DEDDD17A214B6E459A8B09BC546CE0D38D:1 +7860B2046C31358E2620679534CFCB2012C400A9:1 +787DD928A3FD7B127663C7B62AAFEFED8BD85D30:1 +788866C4F630B424309E58C39F9FCEBB952F14D9:7 +78AD2427069A30ED653E2FEF5F7C74669C30B95A:17 +78E074B30BC6E5223974155D739F20605C0E6401:5 +78E63D8942C53F5B23A4C9F3EEFB12C239E93584:1 +78F4644336672909585D8E5A4CE4D4354B4955E7:2 +7931092D3498FA4400F1E235149A12A046230CB3:1 +79464919F21B63B225F444DD4D2779215D266515:12 +79512575412244D55AA7C62C0E1D641E5315409C:2 +7976C5734E57D89C34E33E0946E1B3A2FB251488:1 +79DD2E5257D6B1BB8A93CD49374C388606F05C21:2 +79FCAE6EE03FC3EE3E0D8040596C19AC8EDEBDA4:6 +7A00B0401E8A1BA15CDA80F533BF49605DAD00DD:2 +7A2330C2149B297B6B381D19579DAB2C55CEFA4C:2 +7A9CB62F4E75680852CDD35DAE82B3FF4BE37E71:2 +7AA81478B0E174DB66FC53BD6B983BB3592F56F2:1 +7ACAF3E4A0D469D140DD4788705735DE8BAF391D:2 +7AF01CDB28C51F63E4BF2069BCD84B3ECA6066C2:3 +7AF4FF93B0D577A0C2816577F2BB29F4A8829414:5 +7AFB9DB5A84BC104CB915FB31C6AEE8EF0090660:5 +7AFDD33E1CCAA751A5AE4B453257CB92960BB8CC:10472 +7AFE97FCFC9A6111D772AA3796623A330893C06C:1 +7B05B440C2B933DE2079B8354F2D5568FBB03492:40 +7B1765FE05AE4753F6CF53536FA2824CC2C0E37A:1 +7B18B52A36CEFD3534934FE6D5DDCD0852CB728D:4 +7B20EAFAAF918DF4E2C0C02A622873EBEC2B7736:1 +7B2365119FDAAC35BF53012F4FF28676CDAEE595:1 +7B6FADEA004992C9341B5F55CC9B02F9C4604E99:1 +7BB247AFF9F57D11E98F5BB13A6CFCC28CAEBC9C:2 +7BB934451AB2AF1303F176403474D8820BB996E7:3 +7BBE35138DA8918CFA2B07FC71AC64D55344D68D:1 +7BD7972F6954613013DB63A53D690B0473B55BDC:2 +7BD8A1FFD813856D1CD0F9114ED25EC61A317468:1 +7BE9DB0D507FCD215E132BB353604171E3FEC787:1 +7BEA956CCD19C083E58A4BAAC27F3BA31E10C71E:1 +7C2D4FEBAA9363ACFDF686B0C34C3677C97E7C02:4 +7C38F29D84DD5C8D769FEC87CE1CD9FB6AE1B565:1 +7C422398D2539E1E726963D24167586E7424DF3E:2 +7C47905B9A54F8F898F67CB1CBF20C7B65D107E6:3 +7C68A1C03247A460E218B99FBCEE89CAC8D0E09C:2 +7C6EEF5E1939796DF0944CEC7A3CD261DBD6C104:1 +7C832A33802A4846CA791E96D22218CB478E987B:4 +7C8CA97A4591340566D492C95322C504EAFC8E11:1 +7C8F6A68447FA174BDC2FEDB205D5D1D3ED8CB36:1 +7C9302037EA6A97C9334FD0DC9801496601024D2:2 +7C964B526D8A2B1183546D4BF67AF5CC7F10E18D:1 +7CA7FC461A843D5B8A85F9D9486A7A259F2BBF66:6 +7CBD615FA9ED6BF9E2491E22514D59B8C77B168A:2 +7CBFF0AF31DB9DF6DDE7AB96E3C42B4289F7332B:1 +7CEDE68E0E2EE92269A498DC2136D0ADAC9CD481:4 +7D07EE9FF2ABD2A3BC3FBC669BA9FA8EB8C00DAA:22 +7D16BCCDCF4047B6A25A4601F6F27B6C42DFB764:2 +7D2476AD92EFF461CC7A2EF86CD082A5F0C3F821:7 +7D3E13A98F79BB37FD6A52FAEA03309035B677D0:5 +7D8649488E9A57643957A1C63D1EBEC39AA66413:1 +7DB43AC2274A2082356516FA2FB894FBD64725DB:1 +7DC019BA1F7F131FD841A67692CC919842FE9DAA:6 +7DC20609A9686F00748AB2C3AB86251B11B5F9BE:2 +7DDFF01E604359EE12CD8261CECE403071BF0F1A:4 +7DE815AAA02AEA898869BA9097740741AFF166CE:2 +7DFC2331CBD8DCCCC374C87EB2D1F03031CC4696:1 +7E10E2B27C0363E05584C31050F3D236777E49B9:1 +7E52EEFBE5C889C19A530810CCD3B66768203623:2 +7E55D55E268F729587A9DC23BA0336946937D7DA:1 +7E65AE9E9CBF0423B3C99FB049B4D86462159E7F:5 +7E6F068E06D5A221DE50652D6C16008FCBE57606:2 +7E7467E6C63990CBE2D50088400CF8B8888F321F:2 +7EA764F5CA9345E097C511D44EC7228FBBF24A34:2 +7EB84D9BB51C350FEF735B1E19A3FB5D2805A12D:1 +7EF1B028EC2E8D87E1DB14B06B3E5D265CC1DE9B:2 +7EF5DC9E58FB7D62CBC3AA483E0676EAB3EBC580:3 +7F0A6EBBB69FB69D35EAB50B581E561C03835655:1 +7F23737F5481E0DCD59C369B61BA46E957EFD069:4 +7F261B80DB819F650B63F24112341DC461F894A4:5 +7F8FCB1C25F9CD7240035A5B1AE3C021BF2C91FB:1 +7FC9E890207F6FD32E49591FEE53A1FCE429877C:47 +7FCA1A7355EBAFE0AAB1839E1F1C794269698AB8:12 +7FE622A72DAE5619BFD3752289C6A985E9A4A3ED:1 +8000758106CBC694913AF0F52F01050B46C91AA5:2 +804643C34F2039EF17772ACDF1E37A73613335B1:2 +8053BD7EFF0FB4267D3A194BC2271D074005B933:1 +807A3991C25D091D5ED9BCCAA29373B5CC53F4FC:20 +80A2730CC6E48283A34BF78EAE3F44C8EBBA752A:1 +80DC9E99397ECAA8550265B8ED68026743E05914:1 +8121A7D973346235DCA19516E852F16E2A62D5F9:1 +81462770E8FD332528FA8F3B523D954B108318B9:1 +814AE1CD66F04DF34B6923EA38538DC74940FBF3:2 +815831D8C3266BC8F3BA06EA40D5CDC28112455E:2 +81949BFA73C7BD795B108E8430A349DD52D8696B:7 +819EE3F5710B97CA689BEAC458CAEFC9110832B2:3 +81BA5FE70CC471F0659787A23D0F16CA9E5F1F6D:62 +81C1164276236DC52BE6C4F40EF5553DB46C7E85:2 +81C9236E84ED243944790157A8A1910ACB0CC4E2:3 +81D3211C8EA8DC563EA2B057D721312494D325DF:1 +81F349A8EB696B638A2B9E9A9E85438FCDC39CD5:1 +820BBEF6A972769C8CD97DEDE003F985E3B06A11:5 +8221239A89FD3E9EAB597961CAC9AA110D5FDEAC:2 +825221C6A626EE2AACF9152B52386EC41B77A318:1 +825543AE88320DE04B39F472536BECA3CEA1860E:1 +8256A41BB1F83B4F681DFE99365A2906D2F3EDA5:2 +8272080B3CC31918223BD299EE0D2EBF79664297:1 +827F9035329F6F03E6CCB91F57D2891CA772A58F:3 +828EB0EBB9B955664880F96036550B47591B2FC4:2 +829990C0B85D1E3EB0AD1548A6D6291A32D00711:2 +82A9CABE571117881FC9CFD201B6C3490F29E64A:1 +82C8EE964994E8A324036895D687C139D7F891DD:2 +82F13EFC5A7D12FE61292EA359E256DCE6351166:1 +82F75595FB45264BEFF9CA4070067ACEC75CCC35:1 +832002911F1FB7A99D1B08159A246DCFC518E0D6:1 +834A74CAB34C3053775426F952453AF9F85723C7:3 +835D6448302CECA45F3C8DDB18FF6355351938A1:5 +8371F9524D2C4FEC8AE60F66FA00A7682231268C:1 +839CC97BD4FA80F9962EAB448FC0929FF6D5197C:1 +83A5263605C5EEDB1DA2638DA3EAA851F4C54B0E:3 +83D34699030A22C80472024996E534FA33BA7034:3 +83D58A45A39B29D20A2E1476859761BED9180155:3 +83E1871534DD0B37119856087A5142DC55DF170C:1 +83E2896293897795A53C7A6ABE1614A1EE93B612:4 +83EFB8FB99D3AE329F51E131368C60AECBBB1F63:1 +83F42DF47EFC859F50DB2D59C889AE2A939671E7:5 +84423750269599296F4F63C70397FDA5C3FE64C7:2 +84585C9D88E69D47FA6237E8ED2DE4921B973723:1 +846582F2360B145087D8F0FA627ABE312552D42C:1 +846F583BE4DD01802DB01CAB5A5466D252B293EE:3 +8479707D529FEB5C066E5F0E0E11B7BA84A8440D:2 +848BC8C33E97039B5C5F2E327BD9FB99E031D677:2 +849CA16C80A2C375A0A5519B88F9F518FCDFB027:6 +84A4F33539B0D1E4AB5115AD6052EC061EB55029:1 +84B34F1FAF6FDF26EF140217CAFCF75660F6CBFF:3 +84F210A20837DA94243BCED5086C538FED0A24B2:2 +85001A6DFD9422259847CB7BC609D8423CBD6A1D:2 +85074A8733A270F9075CF69FFAA06AB2C0969016:3 +85293BDEB39CB2AEF455A1B35321DD6419A1172E:1 +853DB420748A064D26A8CD1589783FBAF0B6526B:3 +85467B6573AA2F0BAA2B3DB8E49BC0E20C1CC8C7:2 +85597B212A5FA8946EB3520DC54F52B15B5288AA:2 +856AEE5BB80CF6B2195CA7DBC16043341C94A86C:2 +85905395CF10E35627507CA4F8E761F84FE419A7:3 +85C6B0EBA7CF65FD6C8764AA8E273B6617C3ED57:1 +85FFAC65979AACAFDB87D07E079F0EFCA1892108:1 +8612F8C47B8CF837F2FD828BF70D438C93782901:2 +8636AEFAA03B7485AFB441DCF426AA32EA2761F7:3 +8641ED7E43B4F873F117EF266D1BDF579C4E4D6B:1 +86671C30B743D226AA0B40209772051097EF71DB:12 +86701BE27D2446F71C579B4F258B80CC086FAD0B:1 +867737F87E06BA14BB8CE3549ACF0CE27850D43D:3 +868ABB632672A2BCB3F7EC6690E8EB422881C2A5:2 +868B9D8DA2520482D6B9EE483DC35549D27B3465:4 +868F83E76BC6626A36A9B379131AE9B5F4AB346C:6 +8691130A2839A354EA050C6154CC38D56429D0C5:1 +869AE267D322530C563A26F89792C6DF3C86B1C2:1 +86AB758906F588CD403E5817BF0C701752CD7EE2:5 +86C5057275EE2BC2DDFB4F25772FBB555C012E72:1 +86DFFCECC95A551381E9CBAD93F2D989B0CC740F:1 +86FE3814E5F3EFCA2307448508F11F852984AAA6:2 +8722F495F9D93D151E15334169C778BC7261C738:2 +8724E93BB34EB2F3B1AD8E7B96865D6709C7B858:2 +874F57B2C8469A4B7E7EC31637702A335D496331:2 +876947AA9A7D0BB9766263849DB60D54F891ECFD:1 +876ABE5227AEEAF76CC504DEE294BC27BBF17355:5 +876CA92088D52266A3634E14D3E184EEC579ED0A:2 +8779F2678CA154F30BF7D17DAAEB9AB1E03CE78B:5 +878BE8CC73728C26015A75C339FBC656DB2FE70A:1 +87AB3BC9C304F4ED5A98DBC2614E3C77024182EC:1 +87C7004269CF60808C073CE7DA95CDEAA2778584:3 +87C72D58287CC4AF2327E5A2F163CF093CF86ECF:6 +87CE6E32EA8D2C5DA99C6E31E7E6C495C38A43C8:1 +87CFC5F74FC29DECB873172A1DBFA3EAD9BCD94E:16 +87D1C1F2AB471086173335DA76D5E7F7956770C2:3 +87D9D21AD398802C84B8C4719D2B46BD0175648E:1 +87E7DDF9A98A7F86FF36CC8F1318177804213D59:1 +87EEC8D49160DC007110978507DDEAD645336482:2 +87F589457F9BCDD6940525CB5215204E42A876DC:8 +88295CFD98B18C90C444CA1560A0A0949D37368A:1 +8854E75CDD708CDC833BE2AF96BDD1703E829448:3 +8874E29D654AACBE0553A55137C53F1E68A42071:2 +8878805C31F6E40BA2D1A9E7C42A413C913AE8DD:2 +8879DC6E1422F41A9254F4B7C0305A9F0CC985B7:2 +88BA0D5BB3799B2BDE673EE2CC36879C1357191C:2 +88DD622CFD5C04F4F106870791569E2F499C1717:4 +88DDC70964868CCBB3CDE748F7589BF1C940D983:3 +88E9C58D0FEF8AA57C1220C0B93176A69945A563:2 +8926FCD4C4BCD06D3AB9AE3F6AE6BAEC7ED061F0:2 +8936138C8CF6194123AD7B5FFFE3E932558E8B17:2 +89409AF3289A9146D0BBD291ABD2B1CE3BB4FAED:2 +894A8A137F2421DD39231A60D7C7639C3D5D4723:12537 +8960D83C244A9F46504EF98DE9F4B475CEC94CDE:1 +8966D13D57AFDAB0F119243C6E9362959AC34F3B:1 +896973B3E4B0A003B5DB49ED87A6F50B8ACA3FA7:2 +8976B7C1C0682508CE5C8B67FD63DDCF2327834E:1 +899E09FDB9ADE1B2297174665C7B7BCF53BB4E95:1 +89AD2879E39DBFF78B2052A12BB607F2DEA1145D:2 +89DC3A5D4118283D0025AFB5885204F79FE115C3:1 +89DEF08AE51292DA1D8B743A06BDACB2D37ECFE4:2 +8A19202D3209F217F92064BD947EF882F3D77C31:2 +8A5B4B4AD5AD4EAE0349EE8FFC19F2DDFD204037:1 +8A847902429C88F7E0A851051E7FCF1BB7D4A983:2 +8A906F27DFD9A55EB11D980B18BDF2A344F83EB7:7 +8A94A98805EF4A1855F793B0DB9EFA234585AC96:1 +8AB53B0B2E8498FD97C934BECEF5DA83167CEB83:3 +8AD09392A80DD5239A18C8FC64FB980FC23C5BD3:16 +8AE6FAA37BB8C15E6D9744F97FF066BE05D175E1:1 +8AED01BFCCAA7630F770BFB691473D7536010F59:2 +8AF1B2987D75063AB9763441470BB03715F5CA5B:3 +8B060BABA8CFEF6F9B87E8163B1CDEE9BF80C4B6:6 +8B1B08A1455FF4B55BB9FB4D59776404E309433C:2 +8B27793B16C30768D0593988D57ABCCBFA6EBCA9:1 +8B2F5E3C10799926EC0522B59DE595A3AA97F6B4:1 +8B47E35600A4013318FF9178045A4C86B3AC2254:4 +8B627DD5863ED136076D65ADCE3EEF50F35FC95A:3 +8B64E35A97BA2E7A868F2134348F591BB0679DB3:2 +8B721DC5BE5B12D238E7BC11BF81D8E6E2E4FA31:1 +8B8B9EE7FD07FC0762230AB80F60A6D815A6B576:2 +8B910F97A050BF370C685DA68648D1F256DE2D1B:2 +8B925788612E2B17790365FE7BBA741831CE0C14:1 +8BC43143554EB81D498C5CAA7DE2415E8D44C999:1 +8C09E2383AEC2E17DF8350BBDF64EBA147B24E20:2 +8C38058DC5AEBC5293B8BFCACECCE384D883D356:3 +8C67BFD6591F246F3E5DC1FFA8EACAD0139B5D3C:1 +8CA932A2110ED0FEF6A0BDAC5F1B995C3303AD2A:1 +8CB51A58E3713507AF712B881CB380FB653CD1CD:1 +8D3DA479D14A5EF2783F00FE64F7470ABD2E44B7:1 +8D4A9E8E88D0657A48593C1E9B050972B2473E86:6 +8D65E4F91F8AB6002205EF7D5BB87B168ED5F7EA:1 +8D7B45B93B137F4F3B08617B3A5B32C38E7F1639:4 +8D7F70C363A1B70524BB836BE26936C565E1C749:1 +8D8D180411A3F30F595277DB29BCA00DAE97CC77:17 +8D95CB1EEF13B5983B11248E3EE6E61E47A619B1:3 +8DA44D467843C5ECCA26AE8C520A0A4427137A6D:20 +8DC216CB0419DCFB49014E0A125DBCD364BDBF1F:3 +8E06BBD0038FBB0B69EEE1E27CE16CD9A54E96A5:1 +8E1B8152F0985440761F242613F3AA0ABF044AC7:2 +8E3FE6AC1C20D53F7F2DD1C9739D7D273C3FD100:3 +8E4C9AA6FFBE7C1A0D6F6357977CE7354FFE9DE3:2 +8E6662870144ED9ECB369DD171F8974BB55633EC:3 +8E67ED14520B74AB3712A9502A3125BC0DA04E13:8 +8E71D08244F592AA1B252DC22EEA8C9BB55D62C0:1 +8E7F0DD4A88BB6942BE4519CA26CD0B0903CB6EB:1 +8E97022907F7849D54DD8DB212F647481E8A40D1:2 +8EB828F5575ABA209CB288FB753E6AB0704A5223:2 +8EBF00971B09E336E036955AC5FE36372A999FA9:1 +8EC12E21B0BC153752FC362D745F39582F749B1D:1 +8EC69678AE4BD114CE49AC7DFD8AC68F276A61C5:1 +8EDFEE50838EE6BD411CDA95CAD64C74B0E17070:1 +8EFF7EA66B308E52E67E41C320956988963DE9DF:1 +8F12E1B8E1D5387598A998EAC396EB0DAC97A2E6:3 +8F1543B824634D80EF21B0CA88D17A0BFE594FBF:11 +8F19FF1CC3863F91DE70732E304FE0632C808A4D:3 +8F483F75FC07485C5F975AA285E112E7DCF679F4:2 +8F7898CC223D067C316F2F0E19FE23DE5879C2A6:3 +8F8C0254E5381653E0ADF80BB8F5D08A21E4DB75:1 +8F9ADF83AB8CDFC61E666FEFBB613DDBEA7D2F16:1 +8FB1A39D5DC8627FB725F3226A01CB6DF192C10B:3 +8FB5DEB969C48A02555A2C297FF1904013D92564:7 +8FD0E716AE44D4EE1BB99FCD95127C29C0A22EFF:1 +8FE2ECA539F192A8D1CF35DB7F69AADF0000A88E:5 +8FF899F4D856DE80762AAAF92854F4C94B24DFCD:2 +9009B8321104A2CC4D598A3B292DD1653DDA628C:1 +900EE3FCC9C85ACF91FC9D269F38C23C0E720B8B:1 +9024FC8763D35109C4125C23C6DBCCC2512EA847:3 +902CC1F5E05EFED41E1233747379F509FBE48EF5:12 +9042B452F978D7CAAB93D5768396C9FBA66AD677:1 +905BBF7911724C81555AEA7F4A07B91FF787AB3E:1 +907AFE55807D7622CA913B00A51B81E403ECBEEB:3 +907C52487A3C9D480ED0D43DFE758BEA597062EB:2 +908D7AFAC448397D44963D8D9FAE7A87D4F8F30F:3 +90A90D8C8E2461A1B92249F648ECEFC7FDDEDA8A:3 +90AC6D6264FAC5F9FDC81519398BEFFEDCA83579:1 +90B46B631F6CF1CAD376D2A75A803AB82842A11E:1 +90B889613975074207D6EABF27DC345BD53CDFC2:3 +90C5A81FABD5B12B21D3588A50D237F10DB7D3E3:3 +90E9C4341BE8380B18A5812012CC088F09A31435:1 +90F138A5F71EBE08AA2025B80FF4175219ED571D:20 +9124CBE134ACD7C887165658B1487A8B0636B2BD:2 +91349F9BB247281183209C862C19C5D97A25B8DB:1 +9141839706A6D44CCD260B94A85C6BFEFC504358:5 +9146A5B20A0C109576EC71BAF823BE6C27C5EBE3:3 +914AEDD847E84A5444C43E5D23D3403BBC503D46:3 +915EEC0240F96E37E8D7B43EA2B0E67FEC1F72C5:2 +916598DC59CEF3E52984F9AF3AE990912C7A2C63:3 +916B0A194BD2D33E5D159B82021F5E23BC8B6CAE:4 +917C43C2BFD473D99C5E96838B5AABEF118DC090:5 +918F6EFE9E94393DD5D4FBC464FAF7DF9512F650:1 +9195B531E4EDDD0E10A328D9ED01970796EA88C0:3 +91AFA876CD82351C10FB18CC1DF90B317EAE6747:3 +91C12BCCADA4E75A422F148AB7FC9A494BAB222B:1 +91CBDCE05647A313F902E328682F464AC3EE1A1A:1 +91CFBCDD971C19B994028FBA9844166E851D4AB9:1 +91D39E3840060101366BC7F17DB5839CAC7BC456:4 +91EAC485F4E8F3C1DB5BBD03847276E24A3334EE:1 +91EBB7F864E04582A1BECE65F4EFC2BC9C850183:11 +91FCFA27E431A3504AB6A69E35B9B1B56E7EC068:1 +9200E60A8B828251919A5141B8835B51A4986579:3 +920E15E6DAA21D95096FEBD32662B0AE98DD392A:1 +92229A19833A83BC2CB78EEC9B0BA087D3E172AD:2 +922C54605B9CACE0EB120F085A5E88BD43DAEFF3:1 +927FCC883514D6DA259A55F737B15687CF6D1BB4:2 +92D384ED65B6FED956A2B98646287B0B2799E46A:1 +92E643D0AFA4B01936DA40283B08ACE0B7AE11DC:4 +92F4394F0A8BE24E49DDCE425AC3144A5439E8FB:1 +9323BE933DAB5FC4B0F10BD824C3782A445D0574:3 +932D8ABBADD80D871DDC3CEA9AEF2ED8DE6B237D:6 +932E3AD2EE35AE20937B515C8025BE45C0B7217F:1 +936EA8CF85621F122E2E55F457EF229F79DE221A:3 +93721DCF3C4292D394BBCA6179256AECFD5253B4:1 +93853B1FCB0C66D9A2BC11048E55F21510849A5A:1 +938FCB3939789031C1822B063487BC0A9E8E1D60:3 +939210D3A22B45AE2FA9B72B9BC798C7E5214B7F:1 +939EF2F3653876ACDAAF7FEA8243B3546F852177:4 +93C8F966DDDB484876548B35FFED28AE51922E88:1 +93EEA451511FDC5BEA41BE77E22CC9DCE9978CB5:1 +93FC71299C1850967A850BCEE8D97DF7D254CFE8:3 +941581376A560CA5CCFB1754C90B87342E8C5BF7:4 +942C63932EADBAD7E059411F7E1DBD8C8A28B589:5 +94516CC0DA58BC253145C23F0AC5C8161B2F334D:5 +9453A8B33A7BC25241F0526A665A919EADEAC459:1 +9461E1473D551C7C99F472C2D294D1A6C227B2D0:2 +9498B1AD6B102F8C65DC066E616CFAC560AF321F:1 +94B766F116580F6161C8D79E202F9CE8CBC863F5:3 +94DBFE008C73DC77C8FDFF0E9064535C8A125579:6 +94F0B01417799B5E53F39E61EB0FA4C7456C4B3D:5 +9502606CBA4A7AEAC8A629E9A710D2F982E131D8:3 +9516170FFFB154EFE4411A5E58816B00D9090042:3 +951A081D74166B55875CA9A3C2636C09E2E971A3:2 +952579478D4F517A670DC71D29B2E84B484C8440:2 +953CA98897426D554C5AD772D52D61DEF8236204:18 +955264A10DEA3C70C13A497E659967860FEB295D:5 +958DA48A3636CC2D53284191337A2656C4F1F540:2 +959208B4A035AB76339AF6405874FD923B5204ED:1 +95A0A9CBDDC6C20D074B5851E82405FCF5AB68F9:4 +95A3C11D69CB12A9E1BD28FCD588AB9700DC4BD1:4 +95A97690C6BB7B732E058C793200400C4BBC7698:3 +95D1EFADD8F377C14CB0BE9A0C24F789F1B37A32:3 +95E63B0D62AFE453DC5C8F1B1A7704C0CA89F58B:1 +962D107DA18AA980AA396E87D9D4E44B2DFD8CC0:5 +96317EE1070F1243319805D1F7C2F31E55785ED8:1 +964C2D89349E79A67D248FD5E08C63A42A4A5BEE:1 +964D9257C8AD13576E7FFBA160FD704EB0996A4A:29661 +9652515CE7AF5419A73E3A81EFCDDF2AFCA0EEAA:1 +96711270AC1068A0A03D7CD646B2857796AB3462:1 +967DF3E9587385743A9092B8C4B7A0F58FC74DB3:1 +96A00E522864E6D22FE387D24CCE54DD7578CE14:2 +96BBA17889012E9D7AC41E5FC64CBE2B3DB1B28C:4 +96C4AEFD118F79FCBEBFEA9EDFD8C59CC8907518:1 +9714A7AABD5B4BC8F29ED6E4F0DB3D3860224767:2 +9718257276A3F45215962A9520980A6586C2A854:1 +971D6FB532E121977A631CA339726441F7EE6678:1 +973E45ACDC78E32EA10D0929A90326C5FB9F90B2:1 +97CABA5077D965F9FA315615504D938F61971034:3 +97DE29ECA39700DAF38B7D983E55E0FCAE6D8A17:2 +9801E16D1561F779358A1FC2714DBCFCBEE16E47:1 +983DC9874B8EC1157DF6AB010A5AC8D95B953F13:2 +98B661975ABC68E7E00D8DCCF46193050B65F96F:4 +98D9BD4538815A9E83F4ECB508C49804832C1C1C:2 +992126F3422F00237092AA380F1C879C0258443C:11 +992BCA50C3C248A56DE1636F9CA0778993DB0253:2 +99643FBBE1BCC964714C56359BE2B078C7A5BB71:6 +99647DA9D8037100CC31C86FB941930EAF5C7255:2 +996E4A82A3D2766D0657987753300C4FC55F8D98:1 +99977B912931CC672F93E108BDCCD57104841B5F:9 +99A1BA32177D584E31BCD057B02A1C18A96E5868:1 +99B6F288C8A81EC620CAF7E642DA9C286E4E58D8:2 +99D6C556FDE014F862658AE835C0BDB849E1AA05:1 +99DDF2F80CDE1E479189770BE87307ECFD9E0AD6:2 +99FE9D8284BD1320F13CB8EFED60177F9F2FBBE3:41 +9A0B37B2257D5CE07BDFAD1B430EF85F148E8A88:1 +9A4D39DA9A96CB4B0A4BF37C25EFF06E5D4672E8:2 +9A682246D5A2623E0051C16A4B8587C6E8697462:2 +9A6FF1AEEB1AD14D0E61229DC3331D08CCBC6A06:6 +9A7A642EA0F023ADA3096F36187CD1499425DA53:1 +9A7FE89715F2ED6CEB3E5C58D3DD94876B3697C3:3 +9AA584EB84ADBEE043DB2F6EDB2D5E015E9B4F82:1 +9AB64D3105A0F3B0A35D64E74C47BBD788320371:62 +9AC8323E030EAD00B15F74AFE900FFE20F4C8FEB:10 +9AC9670D9139C21092136B773D421EB8FAA6ED3A:2 +9AC96E77CE46B6309DEEF318882DC0D4B047BBFA:2 +9AD0503E707B9B8F09F68A4F16CCF484BFAF6F27:3 +9AD8C8A305054BB223BDB96CB593ECDF16E6F291:1 +9AD96CF0F7814224A6C5E2C85B13B5F21A1EF013:2 +9B0CA8775266F384FE6AD29F1A6EF297F1B93EB9:1 +9B10420A337C2A7CD70B990BC8F66B584BE53C58:15 +9B1DAC4184DB4CF92D274056A136E4F63E46006A:3 +9B2C3AED3909930D7C9608E1BED19991BC0200F5:2 +9B5A6633FFE9CEA9F6F489C58A3569C4257BAFD3:1 +9B5C926B41B2FC2DF65CA0B6BD1B6A76B2B6CB6E:6 +9B612D60B1DA6C2E9276EDCA9BF23EDE43A8FD5A:3 +9B66FBF047E5B80FE3A19346474A4D70FB75AD7D:2 +9B74E45CEFE988138807DF51EB7AEE8D3FD1EFA8:4 +9B798E36FEDEEA6A24F745868EBA9606D6A90FA2:7 +9B7D6746865E3044C786DB2A51AD94B183F16C60:1 +9B96D8EBE422A8C6DE8746064093CB07EAEFA368:1 +9BC8676144E843D18EFBB22C34AB744CC40D9302:3 +9BCB5AAA9B406195EDAF5D6E1132CC9E7C3E3FE6:1 +9BE31BBAE5498E7C8C9323D69F35A21F20A66403:7 +9BE775CA98F6C4B4820C979F7407A7BEB88DF473:1 +9BEE43C5D60838F8E7C2741E839E8093724AB7A9:3 +9BFC101F5DEB18BD161A9C977CD9D1CBACE4027A:4 +9C20814185DCCC55AA0E2974DD165AC52CE65FC4:2 +9C21047F45B9420B78C2A99EE095808AEB699B90:1 +9C32685BC87671CD06AFC86ACC1C4EC2C759B63E:2 +9C456B1E4E9EF1B3C0B9A745A615F0BA8D3F4C59:1 +9C59C6978BC115C71DD1D1C93FE82D0D8A1938A9:1 +9C69087720D4AF1D1344BC7632275280A0EF871D:3 +9C7A849975A8C0C145EA2CDDC4C63520E2B1F249:1 +9C897606265480988D24F785342473B99159B0A8:1 +9C9FC86C953A40A61A26B8BB12ADAFFB23C45245:9 +9CA1412F43F31E8136352DEB1D50AF2DDA147B20:2 +9CE5CE068A57C8EBEBDBD2B4DE3AD9D535714E26:2 +9CEC73AEE10D32276EAE4D57E9AB0553613641B3:2 +9D08A7CB04FB8CEC13B172CFBC41B209245777ED:7 +9D116CB1344AE93AC1F9B8D67920996CDA2785CF:3 +9D359CFA71A683FF22B44998B365A8C4E4143E5A:2 +9D4C6D4CAB08F77FA5D36055D8D357191F8C8782:2 +9D7A3F95EA757687558F5503818D5E9243A1B99A:2 +9D9A64AEDAFB2A33865B99268C476C8DA2234FBE:1 +9DAA6EEBA3493C83B209F9C395CF192D4EA3FB91:8 +9DF47419DF71FA03D0ABC73E619F0C40ACD65DFF:2 +9DF8F0C6FE9C101E064E5476CFCEA1E7DA180DF4:2 +9E079F851F5F68AA13EF073516109C9CB28DED7C:1 +9E454DC62E39B665CE4ACF3AED5E6106786CFE45:1 +9E454F1DEC0E126AFAAAF63D0EB0543E30C229DC:1 +9E514FC3576C86CA2BE877C76726A3D9F6D00434:3 +9E5562BD2A79D834013851B8ADFA8552877D80FD:2 +9EA0B3E5EEB48CDE5C2E427BA7EDEDA6770FEB8F:1 +9EB2D22ABA7DF8F829A2DE2327DA6B98E5D709BF:5 +9EB2F1135A9D5559EAC3F847FD907AA8CF47FBBE:1 +9EBEA3C2919B6F246AD28AC6E1307D357DB0847F:1 +9ECB3791A57F1E902AD3A3DFEBF371FADB04332C:2 +9ED5F98B9FAADCEB2932BFFCB28BF738147585ED:2 +9F166A9BC892018C6DB623A5A1B165B57479C41C:1 +9F3C766CE1C66C998F4C5A8266980961E45C7C16:2 +9F435335978E1FA3625D7487EBC402D7ABA28C48:5 +9F48901BD67CA3F2F87070D579F116BDD6B4CCAC:5 +9F6DB1FB9F05EB4BEA9DAA7CCE82BF7DE5C25D95:1 +9F746A3245FA5B5C3EF4E076463251D34C4CB5B5:5 +9F996C6C3EBD4D572F31D6B34769EB098D12392D:1 +9F99F2363B687E2125C533F82AB1AA8B1EB13D36:3 +9FBB12F5BC2B89F781E11D09A6A711C5832D7345:1 +9FCECB1DC095DBC53C13EAC938362EE316DCA99B:1 +A002EBC0336681DCBBA4C515A5C5A3C0AF389EED:2 +A025410D2CD9CC4C8C329AFB66669798BDBD8B74:1 +A02F60FB3942654546116BCFD1980AFECFA91A41:2 +A04767E9695462629FFDC44B7F050C330D8DE142:1 +A04D5192698A5343CC028E620BF2E1561A3446B4:3 +A05E8EE7A60D53BECD0091DA1EDD44D1649D08F8:1 +A068809BB546947631BA8BFFF893BF43FA42A602:1 +A071CCA3ACADA8A9C98F5E5F13DCE5BD6F7E6DBF:5 +A07855794EA6616F94F37D0DA1F8E47BFD96ABA9:5 +A0A83CDA8784071DBD72E4B6BCF29DE88A9370BD:10 +A0AF665D6DDFA30A79C395C050AC4E61EC87E133:2 +A0F36BBF5AD28FA8501056DEAC8D6B21A08176BB:1 +A110D7373A36550A3DBE1FBEAF7BBCC79392FA30:3 +A11BB53BF5986D3BD8530B885BC26DD2D835CFEB:2 +A147A85866CECEFCCAE6A84A6A5C69DDBC9994B0:15 +A17381A6D227F96FD7738C5C410F59DFEEE81F07:3 +A1A7B126C76A7B4A10EB41412C380A23F47716BD:1 +A1AA1AE602DE37C411C9CA945CFD8BF235019BE6:1 +A1C157675D5E7B5D2E4924D592A2B6015024FE84:5 +A1CDA20622D2B50BD3091E3440B913C3942FF660:2 +A1E1D3785B06A16369A706B84C78E0A0CB37DBF0:3 +A1EC577D3E4784B184F8F2015CD7D24018D030AE:1 +A1ECBE96765E3DF9C225906574234F174C23C18D:2 +A1F257B1F5F3FDEDF3B314F2AB6BB9CDBD924666:15 +A21E1DD8491C57970F1D4264E891C41DD00DD93E:2 +A25B276F3EEC468D77B394EB494E8216FB45DA70:6 +A286C58011B6D5BA9CD6DF396C66D93F0AFD68FD:3 +A2BAA96B76840D64AE8EC4812D13A71A50F7DBEB:4 +A2C3E848CA6675E9B69274C7E34D18E88BC0C440:1 +A2C89DD6FF4C3DA91560BE45D8A6B73C90DA0140:1 +A2DA3A02569C6C95D5380F904698373503F3634B:1 +A2DBD29437B262758D1295B4987CF5E48FA524CA:4 +A2F90091963F90FC5116E97E4FA6C967D75CDE4F:1 +A325CF95C6269E9F26222C94A0FF7E1241E0501C:3 +A32FCC44D5F7987A5B0196DDA29420A9B9FED0F5:1 +A33D3A998E390778E8D37F4791416EC794C52DAC:1 +A349F32E5A4559976C492730D327AD94B0233DB4:2 +A35A788B2CE34E46D9694BF48BC3B6A035342BA5:1 +A366F8F1DB7BF7301A4D9F7975AC62A7D32788E9:1 +A37AC1083E17E3B5A8F92B354B9B50CCE3BB894E:1 +A37F93DACED6598244CC5919C657915AAF800CD0:3 +A38443840BB65591A8FE8B864F6618BA2A944E39:2 +A38C689FEBD750CF73B061656D202946C644B6C8:8 +A3AA0D82F074C895C923A601BB7EF33C2907925D:1 +A3B06CAA90738D36F5BA9967700CE2B0DDEE0A31:1 +A3BEB08383897EABE100B73534286B1B15A71CC3:4 +A3F20E62CF3010AE572C8156095AA9A375BDFC7F:1 +A3F8EB9BBE2165503A35532CB0AEE1441EA8FD62:5 +A40E192DE10CD405032EE39D0181E58296205298:1 +A412184FB2C66265FE1191B29F33B7F2B540FFE9:8 +A43072C563E92362E9C904142D49B813B7942106:4 +A443A4AFA0BA807FCD6B4A6BC55B43EC270EFC0B:1 +A48027C75322E19E4243C294DABB7E94A05044A8:4 +A48408419BC86E2D1A322E14C024F5494CD4EFDF:1 +A4E2DD3D252D0FBACABD1BF0819B51F1FFFF46CF:1 +A4E6937984A0EFE1AF3898856E6D77A8C348897A:1 +A4EE38EA50BFA5D754BE40A93A80C206F61AD7FF:1 +A4F16243C72E5448C107820FE1C5C2312E453823:1 +A5008B0FFF7000EF5E413438F275F3F2452892E4:4 +A53B3C6943F543B9A11358E6BF528C670582A25C:10 +A575EE3CC12BA3BDFFCEFB0D7BDD8ECB8054EAB1:3 +A58BD8E2466E9508EE5D367CBAB7CDBE5B27863C:5 +A594BFC02BDE3CA67D5F7293C96705E2E3B2DB31:1 +A59BD98AB724499743858452FAA4BD18CA3AB35C:2 +A5C15A75975DB9549E5F012C669D559A0AE7A99D:1 +A5FEA767AD0B9B53A85F97907ECA4DA778E6181D:2 +A603F13CA6D6B4A47A785CC18F5BE2583610FACF:1 +A62A850243E16C97DAD2F5C363BCF2441C4D728C:2 +A63AF7E20513C9F311ED96AE2A7073DF92CBFA0D:1 +A6676B08DE537F79F8BBFBF41CFD3BED4DF46051:1 +A66B1D8542B28218113BE7CA6F0A8468291017BA:1 +A6790A2B20C48500AC04A6ACD38161BBEAFF99F5:2 +A6BD6FAD633A1F5F729FE556290BC4F5BB8A7B9C:3 +A6C027B354B097EDD617784F4F2AC9B0FE34772F:2 +A6CBEC15910936579FA2DFA5F66F86B09801DD3E:2 +A6D8C06586B33A4E6E2A6CFF520DBDAD42931D73:37 +A6DDA7944D1ECE6F19ADBB84FFBB512B29E0F6A9:1 +A6E58AD87E3C3E5E676F23D0ABCBFB3A9C1789B5:2 +A71E7AFBE709D9C317B23CF6AA1C552D4C2286C4:9 +A74F21CB21FCA9F1B489F20E20771DFF6B070161:5 +A75805E0919B84901386B2862315A9071779DE20:3 +A76C57E84D9F9268F4DEA861C0C92DE1F48BCA79:2 +A789A079CA919294E064DAB7C6DA481742AADE37:2 +A7ABBF23CF83EBF556D517A6C307C49AA3C3F146:1 +A7B864ED9C16ABF9A36F655F89BFA20D123FCB6E:3 +A7BDB5D8ED3E6F8519542B475FAACB94C2EBAAB0:24 +A7E5B4606B446100A401FC76A754C54DD13FF589:2 +A7E82A2250323B80765621E24A4E8171C63B9E68:1 +A7F3D4AFD14E417098602B88A2B33EDDEF9DBEEA:1 +A80FD91C22642787B8E741FE2FBA76DB27B4CFEA:1 +A8164C9391D57C41644EF52DFD848CEA0AD8DA1C:2 +A816F0BD417530471CFDBB5951F79D3422CB2850:1 +A835A5B3B534F081429208978EAB7CA3F6185F51:2 +A83BAB0B0302564E51D31CC9DBC049F8C02D7D17:3 +A87305DAA76ADB6B62C02CCE2F2721E48CC32A3C:5 +A87D6826526B03C10134DB130803C5B6A9DD2837:2 +A89986B62506975BE1BD7487806946F3ACC3C0FA:1 +A89F9DE1DE72E7738E303D12C4014C101F909117:1 +A8A16E3C41B85B88E7A0DCC1D827BAE5825F248C:1 +A8D3FB6FC05F53031677AF6F148A0D02C9B66139:1 +A913BFD531D131F29BCAD8067DA15354896B6449:1 +A91E4EDAFFD492E9A1751803D514019BFBB29AD3:2 +A91FEDB543BEF73BA33EB9030A627D5BD436B706:21 +A9312C3CA1550A54B0643CA22B42B25940D1EFB5:2 +A9687959EB4792BF3CD06655FC2424AC5065AB3F:3 +A96C111DA67F473CF41D05ECDB2A3912F7D6E7EA:1 +A974C96918046BE4D73A4F5DCC3B0A5B6C3BB8D2:1 +A98DF09BD5B3CA38C9000F5EC2895A4650218DAD:2 +A9A22C1E599C7BFBBD39DA9AA06E3AFD46FEAB26:4 +A9AA5164A75046203B25BEB2C2615C8B57C002E3:3 +A9DE85DFC8BC162EC84BB08056E02918C79328F1:2 +A9DF65DD51119DCA6B77BB035FD2AECDB62E6EB8:13 +AA77782A25620D845FBDEF7F07A07C00C6603886:1 +AA9FB3B43755FE1E7406302EA10505404A65816A:2 +AAAF998BA70968D470FEC52152954D9A64C34C8C:1 +AAD00F1CE148B6B71655A1232D238185CE691FA0:4 +AADCA66ABAF79B497C87828897221FEC2FBEF693:2 +AAE6D718FBB98E334A59F6F37ABB4EADBDC24A3D:2 +AAF2D6471DC7A7E4031ADC7BBDA41E1665D97EEF:1 +AB3D580E09E6EE2E13144659166CF65BD210C935:22 +AB3F38553B1662AA8D68CA46B78973874DACC7ED:1 +AB3F514D782B317E48123B608E042550FE25A50A:1 +AB4139CA53D0160FA43890A792825A68C2114775:5 +AB4CA6C69DAB3DBCEAB0B852500E844BC6288C62:7 +AB5616DF29E95C8B25B16F33EA3934870BB2080B:2 +AB5FD209B858D1F80A8CF6748E0F68F51ACD0490:6 +AB616AC867CD225CFED802D32C9E989590245AAE:1 +AB6771AB258AD095E0CC65C669D77D1DAC4227AF:1 +AB72BCDCB8AFF085912158FF76C98F8EEB80EF89:1 +AB932E7A58399E78934F45D15A2014586F601BDF:5 +ABB81A3A7D61F8DEB4130DA05CCBC81568487C33:1 +ABCB54C537D1C2FCC1BC6088D29A09644F44E6AF:1 +ABF397CB1239E50ACAFD7A2E30687F35205B440A:5 +AC2E1B11B0E94ADEED65A99C14FCCA10FE7BAC87:1 +AC32617D2BFDC8475AD703CBB033B8D18F209A85:2 +AC32C26C0CC4BACF27883B564CAA46AADD70688D:1 +AC4E5B9D8332B937BBC18E6EDE8460BD7A725428:37 +AC60E3FCC9E19F06339DDFDA857E8A3F06B03D38:1 +AC77480F6E854122E7D6C93AC53860F85CBD915C:4 +AC8008C7F132F45C9DA0AF2D1117363787E96E95:2 +AC90BEEAC8CDD45149A330D7320C619734FE0F26:2 +AC9338DB96AE3A0C2B1962282D362873392A53F7:6 +ACB40405FB288A2AF7B338AB7EB8A3D9AEFAB90A:3 +ACDD22B7BD872486CF7A482F7D66944D54AA2867:3 +AD14A1BFEE0BE1A5406852A4440372F2710F5EAA:4 +AD2727FA432A31E7ED1025860A9160D0C188188A:2 +AD3C79A493698696A8F8E48EC966D85E1319D241:1 +AD3CD7B6C167E0774738AD4AE9BBFCAD57EF2CA4:3 +AD66606C84EAE5BF3B456045152EA15D84A588B6:3 +AD82D53BDB1202B8B10F9C9A024F9C521F43D4DE:1 +AD8750D78CFD8034A9B3847B6B6B12AB61BBA4FD:2 +AD8A7A14A8CE5F75F03F19746FFA7EB9865E1349:2 +AD90C377D025960DDF7BBB05744C275590D02618:2 +ADA73BCE50F78594039139F97E60BBEEFDF6D5F8:1 +ADB62ECBE8EBE5E9F1605B2C9D35E320C2267E22:3 +ADBDAA3917210427930190CD57A373E6CE932CDF:4 +ADC14D5018CD19095DEDD1F08B49A06DFCCE714F:1 +ADD944DD6677A05E54890B4F6D4ED574F3C0C341:1 +ADEC88E6C82D083B64429F08A4C28F41F40D4132:2 +AE307A49F22EFE30A9DF2D63A2AB3B8E1620B4FB:1 +AE42638C35CEFFDED32B1A123ECEE9BEB9F7EB22:1 +AE59BEE3D69A5F6F2C842272C2AEE4C6A002E15E:2 +AE5BAE09B85AC41C7FB31F6143BE3385B56FB7B1:4 +AE601F650452B73995A27109C3BE7DF94F526F33:1 +AECCA9841DF3745566FD84E156A62A62588DF405:5 +AED12343B96ACD74CC4F70D9B2B641F946A17E07:1 +AF06CC25649729B82DCDDDA2A3F695C05D984078:1 +AF1396D41A7500C60118BB0E560D3D27544756C7:2 +AF153AA381CA5BC34D275F0773AB311618BE76A0:7 +AF43565D00A9174CFAB5F0B9AC89A9AB5B95869E:1 +AF5E50C13B4402F1254DA34540EE279F22898AFA:3 +AF64E5F2524368162372C762DA5C37B829DE8781:2 +AFA41A6D7F50F760AFF27876F142B7A6DA6D64C6:1 +AFA706BB20A5BCDC805731F4AEC8F6325B03C4C3:1 +AFA7D2E1B03372A278C0A4EF58869B1F9FF1C96F:3 +AFC510C692EA1D443D3439C205A37BCC4D06D7BF:2 +AFC8FF7A840FA6E03B6B8BB7C504F6D8823F91BB:1 +AFD5127EAEF2185EB4D84E4914C8D621F1277BFA:1 +AFD95FEB584084F188CFA3A88B87B168065BFA7C:2 +AFF1CA7CE2EF65761613D2E3C7C6AD92CA699410:8 +AFF73CD83B1B663D40BC8C428936422AF1CF551C:3 +B01A64731F092D427EFF2662D8EA9E04CD3BEE01:1 +B02DFBCDED786B0DC6439084FF913F0ED1C818A1:2 +B03B8A2EA61D8480F9F9872D70B3E4906D61DAFA:3 +B03D1E4D2A226018CD1EA5013E1B296F95979ED0:1 +B04B8B0653A296D477359C72B707ACF0C7A3B125:11 +B0662AA65E84C22FA682F31CD99DE49EAD536DA4:2 +B07DDF476334A2744F7C4F043DC222368D60E4D3:2 +B07FF67BCC1CB9BED2F31AF398065294A82C0A45:14 +B08D8CBD64DC152E2929AABD462856824BD11E47:309 +B08E53A4C243730181FBB85DB8CADF799493492A:1 +B0B3C613B9A245E7CC072E2DB3F876A2605602E6:11 +B0BC3504D5CC4F1A44FFC4926ED9248BCE7DF6A0:1 +B0CCB5B0E93575799EF330959C4D7F067989F9A6:1 +B0DD70AAB6A463C5BDB4B105D99231D4B766E108:1 +B11CDADFDDAA5B1AC3B27C897BB0A14816E489B5:1 +B12BA3C9FD2DDDF19E08A2AB9AA0B576B6D2B2B7:2 +B12FBD8E67DA4F82C8D10617E66EAF640458678B:9 +B1492F2C0A7C1A13B6BF77CF112BA88F46F80BCB:5 +B14FE0653498C3FE67CFC9773CECE5A24C997DB9:107 +B172B231C81716DEB5E7BAA97CA76121392C55B1:2 +B18C3A24ECE2CCD6051072633EA98CD751E25292:2 +B1A3A2FFAAD5357D102346D06B43823251C234CA:2 +B1B1B553155442D51A31B3EC71A37BBC53DAA6FD:2 +B1BA8B40BFC7B2CC18D43F10851EB0BA5A26CB20:1 +B1C90C109D90A0F7D77696BB5371995A151D2742:2 +B1F8A1C419EECF1FCA05B188C0CA48CC6D207658:1 +B2438D253DDD9291A820155E408A5C9C12ACB2BA:2 +B2463C1E7F855C9C1FBD31C988238BBDA881BB96:5 +B25B9034168B07D0AD8D24A9E61AF4E21240F3D8:2 +B26DB076827B4D35938DDEAE375F1A994C59D95E:10 +B2B13030D126AF8B7967F4364369E971CCEF160A:1 +B2BA8E3255D3E9BC3A8D558C6E1FF4AA7CBDD70A:1 +B2BB537115189A601F46C1B56C73148EB9B478B2:2 +B2D9DBDEED2708C70BFD69627F2DC190C0C3FF23:3 +B2FB2D9B57099AD80AA6F6E2C794BEECDA914DA7:1 +B3061B7765962465C39C0CD94ED2D88D1966643B:32436 +B3266CED1F29D43207AB3681B740D1A0D292A9B4:1 +B3358FB44A9E07FD97F45A8C551033A83B5CBFC1:2 +B35CFB00FFD3E90D8A5ED1F8F9A9096802F87F6D:1 +B35E7DDC05E7531A301A4E9B786BA5878E42E89B:9 +B364E02377700E49FB0C216BDA7376097B87CD6F:1 +B365B148F7A02774ED74574C19F3F00307DB531A:1 +B381F966A52BA7EF9CA8EEEA6A805985BB5F26DB:3 +B389BD60223D7305C2CB13DFD5702A157B12B860:1 +B38E65D467AF5454A246EB7B65704B906D7A2197:2 +B39F91A54B335FD8E05F7C8334DEB6FF8F67CAC0:1 +B3B57EE4686005B8BE155C55E55B84C44B1010B8:1 +B3C800EF63464ACE63152B7BE7F81793684697C3:3 +B3D82BCEE8106F19B481D83FE741E05A330E2CC2:3 +B42BF253E6BAF851FF562A47C371ED755503C919:8 +B4409DAFCDFEC07E98705BBDC3442D69792F24E7:1 +B459FFFCB9F5F5223BBF1952F28D1FD61D6789F6:2 +B464C39FF792AEF5091EABF54029D3AFA2C51B62:5 +B471D07FBFBC9ADD4F50ED1692350FDE985E3B56:1 +B480C48A8626238390C80C80C689A310D12F6BF7:2 +B48FF46E9A6B547B28E7B1416103FD7E22646B97:1 +B4A35D3EF2FD40EB64B17EAE78D2616F903BCA8D:1 +B4BE932CAD7DF5717581866757027DE1103BE115:3 +B4C8404291589045986873D08EB648BC7F7C37FF:1 +B4CD4E12AD742012229FED3F8AC382E92B550150:1 +B501A78BE1416C724A30BBF37B97509219E69917:4 +B520AA1915DB1DB3D58794D68B624084120C5DE6:1 +B52916489AD0D8D3C363A3733E7C8E3828436DB7:19727 +B563A183CAB91CAF2396AFCE29ADACEFA3B660BD:1 +B588DC8C0E7DD7F2A50BC1EE6B06954A3B4DB545:6 +B5AFFE76C5429811E0C20942D4E4238005C8BA86:2 +B5F4555FBDBDE0241C5262889E3AA84379F738B7:6 +B60A9C50E382D7583ADF78E848AD4E007B46F02D:1 +B610048977E9E6E4AE8F237DF86F6EB92464566E:2 +B616531F68145F5515E82F9D2F74D3BEABA6D9C0:6 +B6344DEF631C74449468E95DE55994949FCE10B2:1 +B643BD2D1E770185D3B090AB32BE336F531D188F:1 +B64AE86D74411C97C9A9D467B9EF8E20C5EE612D:1 +B676AD25D5B2E621A7DCEDB7851D3EFB90757B1C:4 +B67D33C6D849055C9E42CE2C52B142ED0CAD287F:1 +B67E193A3F127B658F7B0254A708379362F8F3EE:1 +B6E0A7E1EA9A00446745119457B524B0992CE8EE:3 +B707D6C59F7FD98AEDFFFE30B821DEE250B68F52:1 +B71281D5215BEEFA8890DB9990541183C2B5C60D:2 +B7163F4324357BC7B7056D8887C31C2077655D48:2 +B72127CF007BF76765F070B9472C7E91BA432191:2 +B730C6F6940F42EB0ACED4258A99540985ABF03C:2 +B742AFD64D2A3BD76E5D4C74621AB1DA6AD7BADC:6 +B743920866A1FB9291F099BAE9A9160ADDDF6DFB:5 +B74F583B3FABCF58AB731F275DF6D8271BFDB87F:1 +B752C329E7585CA87A52C8E301B58546AF6BFF4E:7 +B75F96BF540D0D09BCCA339DF7E6BBDE183E9B30:1 +B7712791377DB70B9BEDBE727D48E663CAF7B28B:3 +B7A74564969D59E03C0B4AAE3EB9620C377971E5:2 +B7CC79DAAFC5BC7EEA5238607A8E2D2C5C0E06A5:1 +B7CF04D0C991816104A6D2947219A802C421A17A:1 +B7F337680DB18719330EE5A4587DBA1B6904E010:3 +B80A1447D4A295B2276C03338D1B9943D7428F32:1 +B8230960AFD3B4EE825B8E0314C645518DDD01AC:2 +B861534B2E33D84BAE2F4B399CB96375BFA08296:2 +B872614A1DFA1CD72F3DE871D88C9C3D974AFC97:45 +B88E18F8F5D65F32C4ACB8E2F414DA4BB4AB88EF:2 +B89832CE73525A93807B5010EBB1F1DEB8CCFCD5:1 +B8A31CC4D62259C8D2824013ED56C3FFCFAB5ABA:1 +B8EA0CA769DED542E1C5F784BB90113D3B5DC223:1 +B908EA3EE27E1ED369C8FA39B027E376C37B1C19:2 +B90982658A21BE7C04D96D1F6655D8FB534497A7:1 +B910FDCFEEA7779F5368A3CFA1464C6CAB8E7311:5 +B91413476E65ECEE1CF1C8F4DD43B086125CF937:2 +B92297824285DDFEE554490F0B2DC49AA02A4BB6:1 +B92796DA4F0048AB304FBFDF180213A62E435ADA:3 +B934B8EAA3BE97B4A2ED45C3A0F643D36F2B04AA:1 +B95170538F8D16C405D8F41691BEF43C6F241CA8:1 +B96645281CA4EB9027B03844B8C9C8F53B35E2C8:2 +B96D74FFC2F04DD14AB8666AE47992844457F1A8:3 +B9B2FC026D882B81EE10ABAA8004612D37BDF45A:6 +B9B7803B3A2115696A612346A62BAD5B080523B9:24 +B9D6633A60260061AFCDD1F8A4B82BED2BA8DCC3:2 +B9DE2587958DECA091F8A61C22BEA3E50F0B73A3:2 +B9E530872E97A29B1B2AE53AAA18FD3BC9B6AB65:1 +B9EB03287031615041D79AC07EE960165FF4F5E0:2 +BA285CB8901040520B70ABD1D9F3EA99CBEE8946:2 +BA3753D5FA5D989657A961686CAD92A84F2538A7:1 +BA6148225C7E1F17317165396B88BC71DD2D4DBD:1 +BA7AF3E4F39C076B59B1995A3434DF993C406086:6 +BAA6BB7D8B8960D4A10F504C90F560FD2CFE0849:1 +BAAFC9EF37FC7AC9C90B9AF5A8B8593ADBDE06A8:2 +BAF138818F33B36BC8B3A43DE04B7A337760BF6B:2 +BB128A41D045993D858FC5F7966FCDAB70209B86:1 +BB13B7292A1CA7A840651F125B8642F3CDAFCB22:3 +BB1D02E23D5147821563D37434AC649ADA4047E7:6 +BB3AAE53051D95183FE1CBCCB4CFC269E5612450:1 +BB6017D724E3EF93C9E532D86864C994376740AF:2 +BB6E04DA8E3C374ABE9F6BF55A78990EEF3B9B35:4 +BB951EFCB3095CAB83B33D4E999D1D2CF63C4EA4:3 +BB9C9B53E07C2F30985077DD10EE1C5EA600A09A:4 +BBAEA3A3D4DB6CD98EE11E2C1DFCF950AE83F52F:1 +BBB1BDDFF0FFCD9231B94C5BA7136CE97532F3AC:1 +BBB7941F6B172487A600FC32E7866E2298E6BD51:2 +BBEA1E171539F331E7CF046D3EB9CFC78AA01ED4:1 +BC21FC29833F4F2CF7DCB5D9563473AEED4DDA4B:2 +BC3455EEED9A85F4587A66E5B1FE1B410C8F0756:1 +BC5814515FB443DDA8E8D9AC965AEFE05F570537:2 +BC66B6D802B237393F954411391F972B88DA84FF:2 +BC68FBC1E717EFC48502357C3463E243D474826C:2 +BC7D8C3C76783195B14F094EDD9357EC938D2317:14 +BC7DB351DCFA2DF63A299C6EDDE5725D40061642:2 +BC963B58D1F9B0C633B3A98F316B24C64A6F91C7:1 +BCB6D0FABA021395A2621D7563FFD0C1FD5090D7:1 +BCCD4BACE82C9A2DDF8D4797BD52FD08D92300F6:3 +BCCFB5786CE81329C92C2A406A0FADF5961DA57C:1 +BCFAA1DD13607BA249E01DD25A70CB310A0CF2D0:2 +BD0B1C1F20DE3CD3E3C4E39C969E07D8C9461D8F:1 +BD0E5917AE33E6474C5E63E14A6EE156A5218E73:13 +BD233819FC261F163283D8AF5382B58CC8171536:4 +BD2AF4C8B67C5B8283FB1E4F7C6B4E2A112C2819:3 +BD3CE7D2514F74691A3C9CD8EB4B99A64624BBB8:1 +BD869062E7C78CF0A53B80561EECD5848B41BC84:1 +BDA03D252B20CA590B1100D162DCAE4148CC9E4E:2 +BDB7138221DBB0B723FE5589DACADFCC27776CA8:3 +BDBDD2008C51424DB94539F18A1E4147006F4A9F:1 +BDD96E9222C8E62ACACB8EB1EDA36108CF466FE4:2 +BDDF1642644EBC69E290AF9591B1E10A885B7E2E:2 +BDF66E4BA762B15BC38AE99C8F62DA4B921455EF:3 +BE0BF9FFD3FAAFCEA52F04B14BF8B18B90CFA324:1 +BE28276A6E46702EB66B7E388B7100C671B93B3F:2 +BE3CB75BB82F65BAFE7ED91B1644139433E9995B:11 +BE415310F2329E7C1A04E030EDA155E0EFAC6F45:1 +BE45204F4F5BAAD246D67327B8BFB8D5F7377300:1 +BE4D2941F9620C41A333BE9214F7E852B60EB187:4 +BE547CF8F106C3012B92595E90249C0121CBA75E:3 +BE61021D7D94A15CB9AADA335AC123E1729A923D:4 +BEAB123E494D575C3E3AE8CA444801B435B6C881:1 +BEAE81BB98C68E7B8224CB893CC6AE28018D00C9:1 +BEB74C1001F7BE89799988B12A38114B439BB09D:1 +BECE3AF6109353E3D3728E336F6E1C53B7AB7B9F:5 +BEE3C8864FE6005C09FF1C1D3D13D54DA9EB4C7F:6 +BF095560EF08565994307C4CEBDFB19B9558C063:2 +BF47734C60EF7A0E394F6178D63793127261E47B:1 +BF59A355EF8B62C72B64B42CBA1E2573C8346246:3 +BF5F198C4255555605B3E5E408FAC5883EA87650:2 +BF71B642F82696DA5C9F2F5CC9ADC5D19D8D7408:3 +BF7F9B8E7E5C403EFB70E58B5928C4B5F376F898:4 +BFB119C32CB89CC90DC88A358B8177B97FF9F8CB:12827 +BFB6DAA39B020D0F70E4B42254C093DA46CB8429:2 +BFB8EEA6C5D72F6021D3E856C1BA9518CFE91413:1 +BFBB983913E23BB9FDB08713AF9B5EE56B15F82D:2 +BFCB94E7FABB69AA46024B4AE6CB6F06EB045369:2 +BFD9EEE60A4C8E7B6CB06DAAB38227B9B3A677C8:2 +BFE971BA30AC7CB9C4FF1B84B858825332FBD2F5:1 +C00CD7B97757F3930D67C7238C895DA74AD7A758:1 +C042F67CBFC4F47D75C045EE2F8A58ED6EEDB45D:2 +C053F79700E90C9E7DA8B2103B09FA9B0D80EDE2:1 +C09A9E14E53D7A5BC1F56BC68833EFB733BE3296:3 +C09B7F7A92C2C35E2EB13ED3D9AF278700B002F3:2 +C09FEC3A67CACD8D31B6062A7B5D9938B10D0738:17 +C0A54E9A7A2F869177E37C44CFBE520365D4D4EE:1 +C10B3E5049974015171BB51588C0CC1CED551B27:1 +C13292CEB2C528FF48504040D65E4AB34921DCCE:1 +C171618D38AF040CB547803A8AB9CAB4B22B103D:3 +C181E1F2756A03938734298F3C828D0B53D46B99:5 +C19C5E3C4CBDB3BA1E048F7F741B773EA61E82BB:2 +C1A76A8F87F833B536AAA98C059A0C6A3CF8E233:1 +C1AB25370970E0A318DB211E4931B80BF51849B1:1 +C1AC933F24959FBD7E877C8CBF5F47A4E9990C6E:2 +C1BC5B0E45D1DF81FC38C89AA44EDE18ABA95174:4 +C1CC12050288A14574BA66E923081FF6D07DF841:1 +C1F801346E65360E06F7D36FA58CE23392E07461:2 +C2148B91F01A947FA55550A427E407BECBE9DEF4:1 +C229DE7033E9B7F97364280A8BDA39198F52DAD8:1 +C231BFA1E0C339D5EDC1FF55D3ED3EC5F8FC08F6:1 +C26079C68F088140F4A73241C0E005357AF00F8E:3 +C26B47D5DC8F3913501DB40673263789C2F87E0E:109 +C290C15309B7B0A964569C5038B5287E65C7629A:10 +C2B33C6CD3FE626E78FBB4996C3C7F9C86F156F8:3 +C2B58998EDFE8CB62A08FFF07DFFB73B458469C2:1 +C2D5735E919E3C1EC4D03976F4F4123DCCCBDC62:2 +C2ECA259DBD2225859990CEF432FF7F0E2A7495C:6 +C304F16BE9B4902BD9A48DB626B3F69068AA26A1:1 +C3169CFC7ECC8B16F6080AD80F70CA98EA1D9843:1 +C316CA9CDC80A051EC5974620EC049B123C47AFC:1 +C31782DE0FBA24C2CB17B7AAA8BCFBA70E976DC3:3 +C32AA1FD5DFCC2649FFEBFC1B491CA25FB13C130:1 +C32F870FDDD17E226227966FB105A5E87E5DC242:3 +C33F0A6C481213D1C0ADEBBD41FA9A675FEE1D38:1 +C3506C89751BB93C6CFCDF7AE5BAD8F44594377E:3 +C3891ED3C885FF8A7CD71E3E090A5F15101BA3DC:1 +C38D5315477DD1B66D66A69684E6E3ADE93B5ACA:2 +C3A178E18262C87091869D33E0D0305FB3306845:2 +C3A55E73D4F03ACC8EB4909B315C73A0149065EE:1 +C3A869A1E767EB30484A86801007C3CDE35A252B:15 +C3B6DE2E9A9321BBC874319E1CE07A57EEE4BC1E:2 +C3CF2A53F96A82FA98E736B94E2FF8FEE4450B69:1 +C3D90C7AEA09AD38F06AF34B2959B4529CDE24DA:4 +C3E40B5C8FA81EF4B1FBBF9DD21893ED31F02632:9 +C3F6CE8A14D30660F25397BA578E112525B7DD61:5 +C3FAAEAC128A4EFF998E9D4276FD52BC0DFD7472:1 +C400DD2423E5DCC57C849B48A153C7ADEFDBA69D:1 +C417FEB8AC6798E24A2353DE3A1FB3F9FC6350FE:1 +C4183B302906CBA1E2FAC4C66C71D8490EDF0F20:2 +C41A484EFBF9FAB9F2335169720835EC54FEBD37:1 +C428878B501765A8C9A3F859E641E52BC7CCCF93:1 +C42F5955BDD387F475ED603A2861492388F91FDA:1 +C44723181D2EE8C3F48981A5CDD4B595E2AC541A:1 +C46030C8976F6145FBA3DA04D9DC42BDB7C956C2:5 +C46F27CFA6121997066608E0B491A7C3D704226D:17 +C49A6B722787807F370B6C665DC12CB3DABA7A72:1 +C4A57BE8115DD039629041D61BE8F4BC0D8E93B4:1 +C4C3467AD2E5DF0A75BC97014918981E6E47CEED:1 +C4D4222AA3D75DEF75331185A975DCCE586B7E54:2 +C51485FE93ADDFC249E8015D4621E6E3BA67279B:2 +C51C201CBB66A48F27A80BCC6E9E059CF637A58B:2 +C5519B64C8DCFCA97B36B8C2C7B1DF33CDD8D261:6 +C55277A787E2A3BCCC177131A192F8D9C80D91AE:2 +C59760C6E8F5BD60422601DF4054AE57ACB3D9E5:1 +C5A6F585D187B5F906D5D442E1673CB2AF04316C:36783 +C5AA1581946EA9AD01DF53B3B0B5EC50CEE8506A:1 +C5AE810B69B3D197A88A0612D2EA1AEB995FFBF7:1 +C5F2B9D331AB483F1166B420507DACDBF9E55F5F:3 +C5F462F514506D54C1BABA96D5EA581612848FF4:5 +C60A260E310299F3CC6DA27310A379575044D2ED:1 +C6129F8B0855D2AB80E66C43C1FBB9E61A27D400:6 +C61BD9E46C13E465F5EBBAE9AAD8E89B8A056DB7:2 +C64178CC3783F4DB0814D5C9642A8AE326AD1B5E:8 +C64D40EE423617A02F904B7CDE7879C22FF2F2A5:1 +C6577E936DAA527E0E4E4775FDBC74BB1612151F:1 +C6704865B8280C1E86057766A710515F28B3A025:13 +C6741D7A2BA977BF3787405C7C5388B241E431B4:1 +C685D0278362526A3B5BF40087AB99E776D87158:1 +C6984834D3A1D4760A9FA54AF71F4F43BC4FC1AD:7 +C6A2972679D080E097B9D43C675A36247BB98ECA:1 +C6B95A77E02C45D14CC09846689EFB35F7A37B22:1 +C6BA1C69F6C7E03C649B2751748659CCC76AD025:2 +C6ED48C59FA15842CAB3AB3549FF5F012FED19BE:1 +C6F7880746A95B4EF9F88C80A195FD54DD64D7C5:1 +C6F84659F906ABA24C07B00840BBD164E080CE05:1 +C6FFDBC139DD60B8B8F1FF4342525FEB0E0B19B5:2 +C735D527292DB504303413F59F92C4C66A77402D:1 +C7384AB01FBF6ADE16744888919F3D86A223A561:1 +C76744AABDBEF08E149DB0EA83C7A45B46A499CA:2 +C76D3AD84A2F05BAE65B29A97090137CC41269BC:1 +C774C4164DD6446047D9069CEB1A472E569B43CB:2 +C77DB113BE1F79F1244A72AC117A49BCD30A0132:2 +C77FFC3D80BCC7D1B17886C3E6A48B0C13B40DDF:1 +C78A88E2B2A3DDD3E3807ED52F282C61A06FEB4D:3 +C79FE52F21A61454717AC9AFB4857BA09AED6827:1 +C7AB01BC2AF7EA367F52206089F8636199BE9B37:1 +C7ADA2CB35AD624F468A01658AD9EEC61F8FEED1:1 +C7B52869EE8CDC61A4CD736F271FD37764396FEF:2 +C7CF2F17B95D7A2235B86B8C3507B5EE665737F4:10 +C8194B0E5069F8F99124420E3FA906EDD9AA4444:1 +C823620BF8FB9B13E1C6E8439A3269942FDB8C2A:10 +C8250164A34A47BD16A8B4A4D41C3288C8B49EEF:2 +C8337F318A0696BC6955476E841AC0B3980D5769:1 +C838C868E10A941B625B154ECECDE82E2CD0C549:1 +C83B90AEE54360AD9FCE4A61D9869965F0DAE2C9:2 +C849D42D292E6827A96D6C82480693247F9CF861:1 +C851557B3D5A90FC55164AAAB2B064CD7DCE646D:1 +C85234516B41E437DC905C668CA90D310ABFCFCC:2 +C867A3193618A958D47C14C602BA20CDC3E85213:1 +C8A1E881B856C18B546B1BA53C7F6A7B59C9E361:3 +C8A7A5C455AB1A1D40E29988789F47B3E196EF0E:1 +C8D8F889C8AD712272E4BF94681A415613050AAD:1 +C8F9627F03CFC9EBF8C1556B4FC4736929111D0C:2 +C91E13CEEBD45F7696F0ACE61A40CE3C8C1B4371:1 +C9278B45A7B503F680DECC65EE584F620C94609C:3 +C95A8957C45506E6E5E22194DE773CD340B8318F:5 +C96BBA5205C78DE37A554D75069CDA94CC6CD655:4 +C96CC63BD0EF2E7916703057FB131A96F16338CB:2 +C972BEBDC6C6FBEAD44BA93AD49BBB53C216628A:1 +C980D681E2315315E5313230138B9A96A91CB8FE:9 +C997F26EB3EC23BB3405D917A825975DB4AAA065:1 +C99A75F01D21426B0A834CA32A0513ADA2E16280:3 +C99BC58B61FAA168205F2EC847917FAA495989AB:2 +C9A3496B8044FCC2124CE03EBB6ACB7C200ED130:3 +C9C671D7ABD2EBA5E22DDB025A4D02D19FCA00CA:2 +C9C81D3F505DFB65402C77F9A45FF0FF5CC997D8:6 +C9D20E141B65E6380E2EC07029B8DD608DB550D6:1 +C9E4C81FCE16A656F5C1A855A492FCB0385C1480:2 +C9E7F529EC338B7FA3F892556A3967173381998E:11 +C9F3F7C1D091092BE4190A0C34550DA8A6C73269:1 +C9FFC43DB0EB34CFE8FECCAD9AD6DC498F05C9C6:2 +CA064182E964E843AC671ACD65C7C6BA2DFD6DA1:21 +CA1D96651B045D8A6B94DC4F6BE1E23ACE1BC97D:1 +CA5321637964BE952422B895E94F56BB8BAECCDB:1 +CA566903ABC8A5E4D10EDC1AF83C20DF0C6FFC73:2 +CA5F4466C578B765F2506ACE5988F9B100906B60:1 +CA62AD0ED4439B9F5E678BC81110272CE5C89610:1 +CA78029EA9A7FD01F465BC52F9B90D9DA1AD4FDF:2 +CA91C1C7E5D62173BA9AC917EBC3FC0054AA7149:2 +CAC0829E25929B8D5DB5E7A2D033CE1A1A5257AD:30 +CADA840A8651985F219B60F2034F51110C5F6114:1 +CAE92EEEDCD75D9E3DC79AC3AD21B40E18850772:2 +CB0CD9FA7AF08CB9913A56024D1B11DA9E27B7F3:1 +CB312DF37CEC3962BF7481D62DF82EC21B9BDC5F:1 +CB3DC4A5BA9197A148426285E8EDBEC4DAF832A2:1 +CB55136725120C62B636F776EDB2D760BBC503E2:17 +CB559B2004DC1ABE37EA78A5B345735C5BBEC917:2 +CB635CEC3FDF94AF2800A88C78DDAD4DAF2A86F2:29 +CB74C7FD810CFA5E7A41BEACB175881B676FB441:2 +CB7D1DBE77520EBC7C7981DF637AC8E3129DC653:2 +CBC0E8A73259404C5B84BCBDCB38F0CF51C28AA5:16 +CBC65DC4DF264A41EC3216E81BBB3D4F9B60F472:3 +CBD8E0E00C0467565E64A1524B189753B65EE4FD:2 +CC0EEA6A07585C7300155582D30D394756679E93:1 +CC1D4836D364200E65073C30CA32E026548DFF5F:1 +CC2C6D74464D5EE90288538FD3009E7F5DA109A8:2 +CC38FA88914DC905132F5692CE712C6B20A12F30:1 +CC5E698064431CA2DC4F142494CB8C172AF15FF1:1 +CC64BC47AE1B4E01135695CFEB04ABB1C6407AD6:2 +CC6E0C56779A6C256BC9EB2F05B897B698C2A11D:1 +CCB1A9B7525D82C6BFA7B2B33FBE1A556E8641D9:8 +CCBD893CA2E862416DE9FEDE711DE45B4CB2AD11:1 +CD030086734E7040E32C9F10B1689757803FF848:2 +CD6CD11F10F533FED1F2A86FF9A1DE8FECC792B9:2 +CD964A71439E0091470EF9A04CA59B7000F6859A:1 +CDC6CF0A11FCBBACE918B181D5FA55BEAECF6E1F:2 +CDCE1ED3C5BA812C2976C5A9D7743BEADA9F9E1C:4 +CDD12FC6EB31E625EC8759D8A0A9DCE1A35B471F:1 +CDE8D4156F5876CE2546E23953E0494B8861B270:15 +CDEF20E683322038EEF9A4E989B46BAD5F69332C:2 +CE17825CD76AAEC5DC126F817E7A03A5234C2B2C:2 +CE2443029AB41D238D5C78EAD28534EA7FA2B85F:5 +CE630D756A6F466BF9ECF86514D8F86173B35174:4 +CE63A3A7A135E09248813AB6A3611A2C27E25F61:2 +CE722DB0F0388D07D1902A4374F709B84EA780EE:1 +CE934CD83BAFAB2408459E7029B1DA3DD9FC6AF5:4 +CE957BD87104C08F87699647788628010153E015:3 +CE9CAFDE993B9B74A7A8E7DF30293DFEF01A2DAC:3 +CEA21446E00FC33E2E67294C6B28EF14BFED1CDF:1 +CEDEAE84C3839EE51324A8F4FC54DCB794DB2202:2 +CEDEC3CAF5BB68BBA8A543395DC9C32F7DD77B2C:2 +CEEB47DA1B0A4033B271F3B74BAFD97DA86AD2E9:1 +CF043344BA53E2C3D0743873C36697FAAF217281:2 +CF1691AC781452C99FB70CF74A9A4FBED03A240F:1 +CF4161C8D31E1A81402D1A2D8F976835D600A5CE:4 +CF7A7B1609944B76078F9A10DF7B0A7E31650A1E:1 +CF80F39757F49140120B6E420AAA106AFAEC256B:1 +CF934A66B029A2057FA2A4DC3BBC687B13715EA6:1 +CFAFCEDAE2052BAE5E99E4A1CADE920968F08DD7:2 +D0193BC58D9FBA415230CEE6E23778AC33B42E6F:1 +D030367A0F223D88160E87B2E52F43D9058C183F:5 +D03150F376A0E211E3464EF15EF608C1D6BA5790:4 +D041C0B18A65D4196634C742B8EDD7E0F2E1ABD2:7 +D059EC8584F4B4A438114CC524F9BE9B8432C305:1 +D0706F12E64B6A48004E9633E651EE4A714A4A08:4 +D084E8CC33E72BF06A12063F47F0D34B543FAE6A:2 +D0964AF31133CB0CEFA6E5C7DBF7EC165060CDA5:3 +D09A2C7CA018ACF3FC5F918A753104CC97566E6C:6 +D0B987554D73A59A3732C2B9F515D6677AC065E4:1 +D0C517654E75987A8EC448705944C346D1B01B0F:5 +D0CD837427D107A91DF638790C28D12FE77E631A:1 +D0D224657FCB286E612244E54B8E263422266CAB:1 +D0D41A717830262512BB172D92E106D220645BA8:1 +D0D9B165EB90033A1A57A0AF1E72C6B42812BDE0:2 +D0DE13ADBADB56371BFE4D2F2F7592C05D425C65:1 +D0E60C34D7E0E43CEE6E053016BFE288A59FB2FF:1 +D106315280DD150A1C3396BA2CC6B96CD84941D0:2 +D118ECD7F886751D962BD732B61ED07F77469FFA:4 +D1292E76F388B7D9AD9A776EAF8A573CF6B931F8:1 +D1304360298C2A454F6E6890EA3A3C1E4AE140F6:1 +D1481D3A4E9BFE040CA5454A9CE203C46801DF0D:2 +D169C19C037D1E2EF6592C16875B11C5CA3B125A:1 +D1741414C1C677CC9149243142D59C46755B36E0:2 +D19C2F532EA11F4C1A9BBA932DBF0424C022E08E:2 +D1AC9E2BEC8E4E31C74D394903486ABB3D1C1F34:2 +D1D6AEADC67393F5BBD8DCF54D2143D841EEE27D:3 +D1E5615131779A434815249129A0913CAB531DF4:3 +D1F44F4BF690C516BACB97CD166335D9DFA24F03:2 +D1F7F7CD00A8B4349E85005975AE481C034BB9BA:2 +D1FE3D9E780998C69D7B22B4293C29C5B52CDCDD:1 +D202ECE2F70EEB905E42BFEB77176274178F2C88:5 +D217477351F26912BA83F90F5223E4F1980FE8C3:2 +D21B3D622B890194ABCE2EB7DAAE0A537EBA4FB2:6 +D24711469E8CB9AA7185EDEEF6F430B118B5752A:1 +D295C63519C8245B6D67646041E0EE077B8E43C9:2 +D2B56474FF42020EB2661114DF0EAE4AB0D77514:1 +D2D9B0CDACCF7232CC639C1FA9AF81C12DCAF597:1 +D30B8B85B06E529847584C05E8EEEDD96A5044F8:1 +D312C57985C566F74EE7BFCB57D28374DF076089:1 +D379D71B2DAB306468E8D22A9F079ED4F71DF1FC:2 +D37ED890BC1B5A7C94BA2894520D585D8E6BCA1F:1 +D380D4D7D0C99FEBC595AE4369DDD5EAE4A37179:1 +D3868E359C3C5D48E12117671292F9B9DCA3EAE4:2 +D3B07C2B5DE0FC692943F3AD008A2EC969BAEB18:2 +D3C061B85A65BD887E65ADD9F03FEB84E29DF382:1 +D3C355DEF9A1F6E7EB3CD4B6C2198B1E46E2A90C:5 +D3CF45EF82A746F0E72739F13282377227BB3879:2 +D3CF4C217AB297EC88447BA7A07AF43D7152D02B:6 +D3DBEE76DDAD156B59EA21A1F1013790E94DE9BD:2 +D3EDF153853267F7C1F42D8BA43A8A5D938C7599:1 +D3FA54F8D235B1D413023C096FCEA4822CC192F8:4 +D4421C2AF072DDFFA33DA5B660020C546B33E579:1 +D44507F58B5F2A39E65520402FA0BFC686083214:4 +D45960C2443064D5CC660C1F7CCE7A90D6CC8125:1 +D464847DEC550F29F34E9DBFC77F344354C051D5:4 +D47AEB492AA095511C2666E1E143F7135082290D:2 +D48AF5AA8BE2D80740950E98374888E2F70AC25B:4 +D48B78E2530AA4B38B474AE8B3F61546BC4113AC:10 +D4BCF92960EE524FD5CA634D378E26774BE8CFA8:4 +D4D5EA8B0D83D9D6E90165FC3028402EC953B085:8 +D4E136A8A8C37497C05C3CAD0EEF6823083956B2:2 +D4E50A222BC55BD7E5869FA5A7C352F96D73FCBE:3 +D4F105C31128F8AC4E45A4058E48FB4CF34DE307:20 +D55437BA670371BA275C8D713D4787D1C236C2C3:6 +D574868F60D99C4B7C55E514A813C3312A30811C:1 +D599B335B9E97E9E7806CC27D409B3D0BCF78096:1 +D59A51A8D1BA631172AC34E0411D2C61EC7D932A:1 +D5A7D4DF782C1B08CE183CF5A5E08C7D58DACBE3:3 +D5D7090F89045FF6527FB4AE27135668CEE2BF54:4 +D5EA56CAAB7B29E6DC29E4A9387E453C146779F7:2 +D607ECF09EA56EF3BE93CB5BACDFCF786930A6DE:6 +D6169C6B4D4B79C84977D423AB257DE61522C9F0:3 +D61A5A603D7153F4E011650CEC71D71A3D56F02A:12 +D61F3CF2C40B5FEC90B3E3B5276E7C809F432F4F:3 +D62741ABF2D986D264327C18D3DD279EC8191CDF:3 +D66E096FA6EF1F6EADA8EC0406360489C073E85B:1 +D6760E180613D6E9D8F8C4813EF548FC4B4CCEC3:2 +D6800599AD0E4E9C3957D4173790EEB48CC76106:1 +D6924E2CA57E6A61559C08B2A2588F58A8342493:3 +D69ACCBCF9A209FAD3A19B2F57CBA677C7A52BAE:2 +D6A4C093262467E23702845C9FF994574B064199:2 +D6BEE12BFBEC2AAE61D3561FBEDBDCB842D9B1A4:113 +D6CDAA8477EFD975237E63711FF257CE9F65A355:1 +D6D2F3CEFF61F47EDC82B6068D299C816B9E06B6:112 +D6F3B1B9EF26A64B889AD9A3669FEEF062315517:2 +D70E1CD3C6C7CC560F9CE75AA993B5DBA20585BD:3 +D720EA7F305A92F1A2237FE316B1563E60B8C5D2:2 +D7224B484F6A9950684BD7D86D560A48D6396038:2 +D72AAF3CDF2C3D6C0B36EAAB5EC5C04D8620E42A:16 +D78DD1F11A9441D15B0B12ABE199C05D1B8F7DFD:2 +D7AC4898F043E75410EF403583FFFCFF53FBB814:14 +D7BDFCBD5E974753C1A4D16E5672D4436B17C728:1 +D7D73FB6AB62F2DCF517EBC25117EE24B796177E:2 +D816229C2B118927ACA15849B7489EE0B9E48CCF:2 +D84E5BCF9A538A7BB36D1C2DAEACF05AC5F8A549:8 +D852BB0A778305C92F685AD61FE625BE23AF0501:1 +D8689C947BB6004F68BFD908FE092D02B58EFB58:2 +D8C25FD76346D01D902FCE68FDE192FA692DB002:1 +D90B4A8872255C3EAD54A79CE51FA983D9F7A7F3:12 +D9167681BC53FD608E312D3A7AB82B3DBFC79D4A:4 +D926B3252FCB5EAD0572CBE6133EFAD980161321:4 +D9283D2758C4460C4B143FD69F0C44D2A8219862:2 +D9456B828495ED5C5C68AFDC0A40B1B90C1C43AC:5 +D957EF540490EC82F9B931E24B021486F1A901C9:1 +D97F7E03709DE098B3203ABF78CDEE2754EECD00:2 +D984375474195DCD0DF8406820D226166A3B410F:3 +D9A67E95ACD24C79F6F2941C2FB85887EC282E79:3 +D9B74109D6D6B70D8DC0542E2096189CFBDDD722:3 +D9F609A82317CA813536538DFFAD4511485229AB:1 +DA0195762B75DDE1CC295A2456E41B3BF95A7D28:2 +DA1C76FD0172069771A2ED9B87669656F116FE30:4 +DA5ACC63E4027935FB879F98DB36852D7B0F486D:1 +DA8411B9251DC0F4922EF7E398C04414539F6724:3 +DA861A21F536486325D713645E24F21024C4086F:2 +DA91503B42ACEDC1ECE7C34A259DB193DFBB512D:1 +DA936B33D3846E95E641D7A99DC38EFE171A946E:3 +DA98E5B9A9AE385C7C88B1338FFF1CDD8E6EEF0B:2 +DAA914B11AE8F81A42F06868936F13A4AE43BECA:3 +DAB2FB12AE5F1BEB69AD68D9E9A65FD7A1E43838:5 +DACEECD2F9AB0708D00D4C0A5152ECA96EBE9EB7:2 +DAF151944D892AC1E0F224ECA2E80E729C06DE30:1 +DAF83A71D5C8D35436A201C4DACD7A1E16516C93:8 +DB1279528CE1DA249CF22CED1CB4D76FB7AE094A:1 +DB2ABC7973351868F7EFEBF6F14EB7F7C9E26720:3 +DB3253462AE72BE5566E33D03BFB42C4FECA1C78:1 +DB585EBDB1548114F7793E393007120208311B54:7 +DBB70B09495A0D8A9ED7C3A46B1FFCC2370FA4CC:1 +DBBF1E1332608F5E285B01CE78370681201B7507:13 +DC1511140D67F58769B5660E1FC679B40BB59EE3:1 +DC205E49F731894A31EAB08D7F7BE4080BC80FC9:1 +DC6B2D72CD283C3165D8131CBB5CDF600AD1214A:2 +DCA572BE7C2B0588B122DF918CB85FD9DADA66D1:1 +DCFEA24221D67E75E3830F20BC73A360D11B7F6D:1 +DD6EE5A2B68087FAEBECA11177EA2BFCCEB71ADD:1 +DD8191792EF43758F03282647F61F3604C882B6A:4 +DD987E8C00560E8F7AD9E9CF58A77352D060A1CE:2 +DE00014C0C57D5020E49C73E837255D77106B10A:2 +DE0ADE34DE076D181335F93BD0B9BEFB54B61A70:1 +DE13E90B286DADF802C5C5443BCDF50A1CD089B5:1 +DE36D24E0E15C60AD807A1BDD37CCFBC31F222F0:2 +DE6A39A40867B8DC6133E7518FA283B827DD6C16:3 +DE6A9A0D2A52C588EE121054C2751380F5566A07:2 +DE70E8555534C3310B2898312631673D1C3128A1:74 +DE77C9A26383B40892954520038C35CA1F440F94:2 +DEB912701FDC308793EC342EA9033E996F39B2A8:1 +DECA863427474F32050406486C7A10736EA32C8A:1 +DECAB33C4987752C34D956C7EBF5CE6BA2E8A1C8:8 +DEDA018FEF77D4CE6C99C22C129DA4AEB3A52786:2 +DEECE7CA3D9C457E7F6C45C7A42C01BF710A664F:3 +DF275214CA8A9FF9DED377CCFC6ADBC19E59BBA7:1 +DF3AEE2381FDE15CECDFCAE610369104EF372DDE:1 +DF71FEC4060BDEA10D1EB7710397A13470AD7A52:1 +DF7ACCF84B010D9272B061B37B0F365A1B7C7C31:1 +DFA25C79DC3DC8A5B5A21A589C45FC7610B9D895:2 +DFA5C87CF17CE87E0853D1EA68210F7037C35332:6 +DFA9E552467A04D9E587A8B7194BD89F358B05A7:3 +DFAB05F40286C94C15CEEF2E7C7EE11378822135:2 +DFC4C333C38B9492D11B102AE7FFCF9342AC1D63:1 +DFE5983E39671C80860C3E35BD75391F72E217B1:2 +E03A2F595F9CEBF59FF9447AAA7ED94DCCC3DFBF:1 +E04195439A82A1517B41108B9FDAE454E676FFE9:2 +E04E9AE063AAEADD74A71FAE68BF0EF72B96D355:2 +E08071167ABA718B174005F7956C8405E39001A6:3 +E0C8F5C9C7CD79589AC186155B3144895DEBF51A:1 +E0D39243CCC8FBB998B3F65489F27BC758D1C635:16 +E0D5ABC30EB30543F0068537EBE7F345BD7E75E5:29 +E0E62EBC1121F9C59FD5FBA6F921A76DF6026BAC:1 +E10D76EC383B8919245DD71BA106D6E527643EF4:1 +E1197CB2EF43F0844CA7E6AB0D8C55352A62C12C:1 +E12F633968826D6D556F4B75B1E37E813790B718:6 +E14630511F0B61529A16D43676B797B811197604:4 +E1566B60AC433969FE62CF7523E869FD1E2CC967:1 +E16A17D61EA725A031A34D5B4250267176FC6777:5 +E199771F8E068214A44B9C698923C3AFDD6E55A0:5 +E1A8D7D7BE908D7A6C358CB31567C71385409308:4 +E1AC872B301E226DC1E7AFBEF0C1284431E18404:3 +E1BBEDB61A594164D895B2268F67C17E1EDF77F2:1 +E1D2E86D10D26DF167C5856CDB7C5A27521A3BBD:3 +E2019C932CB1E24F6D784A5AA0A1E9C5435A7C3B:1 +E2024669B348E21B50C5E906320FF8648CF61CA0:2 +E211A563F23AD6D9808901590A85896AF13A89C5:1 +E22C2B81C8B717375D48B029DBCC5DDBE6EF60A4:1 +E230928C255DE6EA0EA85D21C02C819E038FBD74:2 +E23B1BA3B130DC6BD6C0E6BC2936B2269BAA1F6D:1 +E2421F9425F3E5E5EFFADA09C8DF2DEB294CA271:1 +E28A9A8046E3FB6B71491185220AD93236D71A3C:1 +E2B24230B4B755A5109F126A689C49EDC9A519EF:1 +E2DA2695054E2398C9A4C1BCD73B4D3248656D6F:3 +E2FAA9211EA10720802F171847F366D70D1FDCB5:19255 +E310B609EB90E6AEC3C8B57A04B0C2BB66D12A20:1 +E336F4811979268104DA3C8CD574C798D1CAB9F2:1 +E3469BB2E3105D58DA91A4235C999C8CBECCC460:2 +E35E9FA805EB332A9DA47318CD9669AAE9A8F2AA:13 +E368D6F177A2F9A233DDA86E5ED1989A822F4852:2 +E37BC9F86935B3F19BAB7B904A47A14C3A304EA6:1 +E38A2AA1C6D76B8E3B1A7E24C27F3A42490DFF1A:2 +E3D624D70C92022BDDCEF828AB5EC2B527E1CF65:1 +E3DAB585F0538893798A59CDF6C5930D6E5E82C1:3 +E3DF28E4B3FD09D62BB65D2AA797E3A9FF342C73:1 +E3E99371F3C9B24AAB153380DBC096EECA42C4D1:4 +E414B92C04BC2DF9765D3B7EAE38C7F9178ABA69:1 +E420A73C9B92AE5A4331D19D6BCB4429E72B12BF:3 +E421C0C486B875ED17207FF8A3DE43E28E239C74:1 +E43104171957769FE084C9945E6109726007C104:1 +E4347D0AE7A4A041B89DC2FE545028D440A51ABB:2 +E44DC323795364E1D253AA4DF70FF062D97500D5:2 +E45770E571E4186B8A4053F6B28527CCB768C310:9 +E46E0B1E227A037485649D9DA1419155EF5A0BAD:2 +E4741354BE4CFA1B5954783223311B0EA4A09E84:1 +E4761B381ADEDCC34FCF12883273A01CD44CCBAB:3 +E47864ACE5C8CA9248E001279D5E9CC1FEE23EF0:1 +E482F5CAEBBE5D0C39675BCAA231FE4A8DB4BB68:3 +E4ADE7D82FB5CC3B6C6CF9B3B669E38E5C6EE2A6:1 +E4B0F16797AA94AA7F0405DC6B3C504A391ED5D6:1 +E4E9A58EF49F8324422625884C0A033E3D14F80B:4 +E578CD91B3032ACEFE10979D0238EF0ACB0F38EB:1 +E57937E7AD8F2713633051BAC3848315180A2CF8:1 +E58427152271F0647D6DB31495826B7E0E370962:1 +E58EC1764C5297D4511A3EC1A8A9F136F9A03CDD:28 +E59ACB134B04F841B22512037A9C599BF3AE7C88:1 +E59F2D55F10984E794C9DCFA4070DC9A30624E87:4 +E5B4507EA8650EF76961298F2FC20A083D72F92C:4 +E5D2C80167FF268E776926D892E56689DBD1FFEC:2 +E609C86212A7984652A74C2A9ECD34BF07610B18:3 +E61000C91DAD7C6F2061015AEEC0C5A7B8D300BF:2 +E623E2A08517AFF57E81E8AC7A540E79CB895CA3:2 +E627B80843E577D857C4BFE7D3044527884CDB43:1 +E6324EFB70FCA95083DA5FF3E9ECC5514CB103DE:2 +E64FFE6ECC97358E4B64336FD3BE58B257F2F479:2 +E66B741C66EE3334E700827760C68FFECF4F40B0:2 +E66D25B34D01A4B3F4A1BA60B7B3953587CB1325:2 +E67DE8C179D629FE55E91FA91F5C23B64FF7035A:8 +E69CACE5D537558F2433BE4DB1DC031677207DB6:6 +E6BD39D5991175CEE9B49F57A58835599201CA35:15 +E6D53657A6A4F4109D0443C39ECAED147364966D:12 +E6DE8C39D223C83861BD2CDEE8FBC6D8FECA029A:5 +E6E17E066B9C249D834E4259D55C1F42E742F5F8:1 +E6F34AC3887BD66635BB072B6CD17AF4F4267AAC:2 +E6F977D279C72B99B51DF20BF21BCE6AD3E700A6:2 +E71A7C1C4DB9F34B1624EAAE04109835598B9FA3:130 +E76BB572157A0132FB7034190C79AB604A381AC3:3 +E7C0304335CC90404E8C15632662DD58D7C8BBF6:2 +E7D61A4524646582E131E40230F58E94644923DC:1 +E7DB56C99FF6499F64AF935B30C1520698F64147:5 +E82C1D525F9EB6BFB9F4B580315C932267C6073C:1 +E856AFF044D6B43DE1F2FCB0EB83BB24692384AB:1 +E8AF67213E58477CF2BC5B61CB112EA1A8BC9B0E:3 +E8B58AE49AD5E8DF6581CF557FC6CAFDC98A3D5E:1 +E903B8BC318592B1E31DD63CE007A41786225F2A:13 +E926FF3D819DE861B22C113D0B47BDFF43832AC3:1 +E95865E522649DAF7C964CF168CBB2D5C1A17980:2 +E9907CA1C34FACA703E9FBA1B9AAB36B5DE46E88:1 +EA2DC29D47757AA794A67496D463718C0E7DE6A2:1 +EA37943FFC7A34E7F0F83C15E7D80453225CE5BC:2 +EA38DEBA1006EA948B4D2E0558C439786D31B55E:2 +EA3A5586C9D2656FA250C7A43FD485D79DD1818D:1 +EA461EE873C79FC8CCF8AFB49B55593636AB4385:1 +EA6218A285F81573A77726FF2B13BFF20C4B226F:3 +EAA2ABC1601B11C1AB32818E00FF1C5E75A27B91:2 +EAA5CDF27F2972952A7256AE0B2B4F2006B0492D:1 +EAAE10084B173D104D0513E748570190DA82C050:1 +EABD111FBA1B01123CAA2F0AD26E9CF7A8E2926E:2 +EABF64DD70B17A92CEBCBE9BCC3443D6194662EC:4 +EAC178331DF6F983FBF77BC83C4E4CC400C8FB5B:2 +EAC58F5775D256C28E7327790931E35A6B182E5D:2 +EACDA93BAB3570BA702836D1A68EB35003113AE5:2 +EAD7224CAB91F33C517070EB8F1CC3B70B6C5525:1 +EB13588E8F9DEF7C8C6C239A06342A0BA6A6EC8D:1 +EB1767014CA2EBEC74A85DF5B786C3CA6DFD5FA1:2 +EB4578F0FF4BD8FD815DF55F277F6FEA8A21EA18:1 +EB492EC5901BEA1481C197A73DA8A49727C99B5F:2 +EB523F590DC54029E87345E330DF7C62BBB625D6:1 +EB5C041038835791C0691B50C2C4DAC85DB0BC08:4 +EB605DC1BFDC3B7B0B441104431411270E21EF41:1 +EB84C5C311854FFB5BA9A4D592893AD5443DC200:8 +EB98124B204EC2D449E51B089FAB8E19EAF33685:3 +EBA916235002BDAF2B941C50F6BD7E41AAFBD336:1 +EBD5DD2B00A5541A4662F71132860FC389CB986C:1 +EBEA764C25F2B4FC5391E162339E71072912CD7C:1 +EBED9A90E70E460B85043FED1CD56E26C1C66DA3:3 +EC05259C864C8FA09BABCBC6206653BCEA8D95F1:1 +EC0D58AB12DAADDC9867BC21D2F85B0BFB9F3ED2:6 +EC2DC9E360E7F264B12DA05472CA12ECC4895A94:1 +EC51F3F391D57634849F96176351D6FD2145FD8F:2 +ECC1F4545EB85D701B15BABD2FFCA97A7FD53205:3 +ECCF4B24C0F66EAC68BDF75AB88C930E54C17ED9:1 +ECF47C09FD37128C5887687DC045F7685B2735BC:1 +ECFB4A43C185C5C68E9FC0FA48E836DC30388E96:1 +ECFE5C81C52E40864CEEBD67C51F8BAEC565A565:9 +ED537E660C83DAC6E34AE0764DE5B97EBA2F6815:4 +ED74DFDF550BF3C87D426389B24AD9F01B419035:2 +ED8EC697322866B4E53B0E9F43BF2B8A360A432E:2 +ED979EC114BFC897E657D18A377D46A31A4F9B3A:10 +ED9D28523E2CE41F500D8843BD266243580B5275:1 +EDBA18FF9041A64C0DBEC3EF46DB915736986133:1 +EDBCB7E2A980404A1261D53C2BBA0C97F6982715:13 +EDDF8A9CEE9C267503CCE23F9AA3FCF2ACE951F2:3 +EDE03B5CB9F2A1FE3C9FBC61D29287968A877EA3:2 +EDEC35BA61DC5CB7D3F22118BD542416E33A1894:2 +EE2F6A38398B29E167A168E8B271D58D1C4FE54F:2 +EE3070300E453D0CF786A1A67A6069231FB0F717:1 +EE396BFD74EB0A419555CCB501C98CDD23F3F467:70 +EE47162C385E529AD0EDB07547F5E3C4EC79CD18:1 +EE8D43787CE2ACB59640318B70BD7042B8670D70:2 +EEC82F64072C769A374B1514FFEEF3E90377E74A:6 +EEE007442A07A4069EBCA3886CA49B36EEC0BBAE:13185 +EEE6E8AA0E9EE659AD8E5079809744E41FA38FCD:14 +EEF04CF64A8C76A5E4E1BDAA5C4AABB54FD5F4CE:1 +EEFFB464BCFE54C6A1FC1DA3985E83333BE62C6B:2 +EF07258F1717057E005070C346383CDBBD8B49DF:8 +EF1C1585AED5927055EE673BC44D9F1669FE1DE6:1 +EF38CD6EED6B25170D68D069E7443A94CE40517B:5 +EF4603EF46D6CE2847043E83F6489263F6445410:3 +EF4CC9C77AFFA12BA9E3E26E80B429D2DBE1F6B4:3 +EF4FCA23B4115B2793487FFE7A7DF979C5AC6DBF:4 +EF5FF20D4DDB4C670576DC0F5B4C65B6C31DEF8D:1 +EF6431E54BD4C3FAD85939257A1D32B2D22E1401:2 +EF785924741ABAF87C4222AA3F45B8A77444C89C:1 +EF82C3AB484112656E8F39E23BC4C658A94CDA9C:2 +EF88CC799AD255553DCC614CB0DE3799BCF3A432:8 +EF89A3A842B0384565A210F0122804F411FE51FB:53652 +EF9AC274D0076AFB027B8C3CF916672DD8BAED7C:2 +EFA8DB7E59CB70F38976FB47CAD444187E32C768:2 +EFD117B8158E372C760A58416EB0D46E462B2791:3 +EFDCE007A6169AE9A244AD25EBE70E5C9EE6C264:11 +F0162C22D48220159C652857CBEBF4A8E189C095:5 +F03512E70BB3A6CF55B38E22CE2AA8ED5966B71B:1 +F03599107E9DD9C0E6C3B2C8B6873146F0B41868:2 +F03D5F256C29028348C385C3557407E0B65780EE:33 +F04FB32EE97BA164F47293021A3977F995A8839D:1 +F05119545B1C186E5EA655D9C77580EA14F1678E:18 +F066FF5102A38A7362A36546461A8BA7DD743B91:2 +F069DE77CB4797C24A9FF209A701F9C8EC369522:1 +F07C43B4AFD579863144A78C87774288E9BFCF5F:1 +F0A1C553F641943F2815B1155D8D9B56F4022B58:78 +F10CBAB5C516795842635991B77F5785F319EE7B:2 +F11571A6246006838857D1A7587A8BCB64A9CAFB:2 +F118C5776212C454D6DEC9D85D45386578046EE0:15 +F12DB974C5212D928AE17523FF4D8345DCEFC781:2 +F13E93A51933029202DF28651A1923106521D08A:3 +F14092A6943342E9590A62424DB6230A0EC77B37:3 +F15967C50D76193621558D4C564B34709F0AFB9B:419 +F175435BB9797CA3EF9A3604B37F462F10B32571:1 +F18FA677AE9D44B2EBFB2B34ED687FE6E9A89E88:3 +F1C40B65DF58674EB123B7FB55E75D64CB761061:3 +F1FE6A83DE7A7F00752C78DC101967A4A8489782:1 +F2225BFEAEA3267ACD775FD3267EE9C2E4FD1F18:1 +F2435457B61FACC6CD2739166AD830C141914BAD:3 +F24AF92FBE9BFC45DEE0A9A697B94BE7A4F13E48:9 +F26148AE5E5E2C91947B7E7951A15C260FD021D2:3 +F26BCA648CD881C76FB2541E41FABCF59E027E73:4 +F2759EA0B56A880D1352A0CCC2785C2679FB32C0:1 +F27C6878ED5F7151682CC145FA72E6966E0CBF01:2 +F2B62F17F39509F105BC78910193EEE0DD13CCD1:1 +F2C1DB5266A0839989F31FAB4B5805FE0C02B301:4 +F3116B30241DE30EB0D27F4F788DAC580755EDAA:7 +F324898FA465F4FF9362341411EDF2D0799556ED:2 +F34CA594B8118CB42770260F7427C8CEB6CDCD01:3 +F38ADBF415C877EBAAC836B0346E312C5D8968CD:2 +F38D37407838487492D5C5EECD53E0B5946FB3BD:10 +F395E7C484D13D300E47FE75D5B8B1A8754D8EB2:2 +F39B1510D6E2520A1D4504BC8892339AF87305FC:8 +F3AAFBF96168846AA457F9CD7BE80A0749BB8C4E:2 +F3BE5AE72A4B3F851F032883878185B0AB5BEECF:1 +F3C1A1727CF6AF3B7135CEF82FBA021335E6A9AA:1 +F3C6626A1341A0EB003D5A6790EF4BCB511D9A87:7 +F3D8494F9F95B3271D2E8FDD1B5F65EB04D8DCFE:2 +F3E63B683A08BF967A6FB6AA6603952032326B35:1 +F40C2DACA3E8AD5916472D5B830FB756EAD558EA:3 +F418DE06C146A36814A1F8B9990A358B59EF79A3:1 +F41E298CC1EEE80551811F4CF4DC2D205DC278EB:4 +F43FA7707F38738F6C37601C533CA2B717D4CA92:2 +F444570EBC8FE7166589AA4BC382E27A46D57734:1 +F44BAD076714AB12A259BA612209D23606214821:1 +F49C4F393AC73029E444C86739C77C97EF3B44A3:2 +F4B029FD29088EE28D4F3988B8FE197F6311D28F:1 +F4F922F807417AB4EB11D67946E79D3F7E8629D9:9 +F51918B69D875B92E6AE3C857F40A884D6D645D6:2 +F5220F214C45467336203ABFCA98AA2F137EB6E7:2 +F525F86B27DA0AA9511C392976BE4D097C082ACD:1 +F551F89B4796DDB4D9EDC5AA76B2B8E172C26CB3:2 +F570F934C1A2BF76C5183F3DE0FFF60F21ED4D04:2 +F57D652EA5D231760D8A72B2241E8A44AA7882A4:4 +F582DB5E949DEF9019525F5DA0AC27E63C0BF180:2 +F59AE94B6A6B5A2651F850E0D0E9A3D27FAA1EC4:6 +F5A82842BE33D97C53146A313AD0EFDE38E5CB4D:5841 +F5F30017335C95F9EFA6B1E95D25820BA57B7C1B:3 +F6171E223D7866E7C617569C4F942B75D2377F2C:2 +F6280B9CE362A20D468A7C74FD3717237F08A39E:2 +F6410C234E9E797011D138AC3E11D29382771AEA:1 +F6446AAADE34C4D888A3A867938D79D34D68DC82:1 +F6808C5F590C740D3DE9C204EFA68E72E25EEBDD:80 +F68E6A3751AE5D14AA8700DEF0371F3A4E5AF756:2 +F68EDBC747619A21A9B016E39C532DCC72363A24:4 +F69070728D35ADCC56CFF2A6C052CAF291DBB062:15 +F696C61F9138D221FBC1E9E72DF3559B2E2FB356:1 +F6CEE44A6F43EE2B35DFA24A447DE8A399A02845:1 +F6CF1D1D893AFA5F23932048E4152986E60FF9D9:2 +F6DEE4CBCB573F9936FEE57E289040CAE9F3BA42:3 +F70391D54EA8FA848AD9665FC00E6997D021D20D:2 +F70B38B8F96E67FF3E253058E02CCFDC814E5A27:11 +F71E1F9FA905E98AAF521D9710BA4CE1ECEE1C97:1 +F73DECE809E19D7B6EA14DA806170BBEABB7C408:1 +F73F796BFF830A0992F7AC40144930F7FE7E7329:1 +F75368179BB426B5C205881BC3A5295F289EA5CE:1 +F753E9F905CC2D234BF2870F1F6378C56BBE67A5:3 +F772723488214D8D513615F112379227BD3EA4E8:3 +F787C2984A055DBE026218EB52650E1904EEB756:2 +F791C2C62FF5645C57839C2626F38291B2769A3B:5 +F79D98F1D7060E4457D9335204D21320F3C07BF4:3 +F7A7A3C8B1AC58A9FD077FB3642F023022B7C081:1 +F7AC5A4D5DE59916EE4AF96AAE70453BF1684595:2 +F7B13F37A30A23E55AC2213E25A9892CEC52EF48:3 +F7D9CDD86A87FF1D9D62C5057D40E34BB801F079:1 +F7F670B9A4F4DD5CE795434847B6AE9323A5659E:2 +F7F82BEF3881EF109822C1305D2BD872ECC54E26:3 +F8217D689A2C1EE459FB47B92C4226360B03EDA3:2 +F82DA00E850D26BE89D983FBDC1B589B0D374F5B:1 +F8348558869BE345D505FA678BB48B90ABBB17B8:1 +F84CC4A1CA69C60FB12E595D7AC14238EC11E552:1 +F88F93F42839E567A74037520157611F0B5289D8:2 +F8B8E665311C4496FE7730E6E7DF0B73EBB87152:4 +F8BA324FFA8DBFF03215C57C91A464C725AF13B5:2 +F905577361188ED2FE737937910D777AA2B30B93:6 +F9577D3F01D89C9A6E6386C9AC123377E7FC8133:2 +F962DAFDA6E6D3BDE9A8CDCF8E6031C048862980:2 +F968E4BA1C39E12EBD3E23D7FC181CA54FC44AE3:1 +F980E101EC957CA5B835138D56CA4601F4DABAE2:2 +F99CE021B3A10D9EBFAA5A80BAB59C0CD6626BEA:3 +F9BBAD958287CBBD2E3B9CE8D06428150FCD5F31:3 +F9C2A79B153BB690E1BE8EA16EEA110261C2DF2E:1 +F9DC5C57EF5E1B00FDED094BC288232239B56D0F:4 +F9ECA47712D51C0ABFFC642DFFADF74851900866:2 +FA35BE32170C0F0A551BE9D2A9653433518AA9D0:5 +FA37ECCA6ABEE6FBFA9D8FC69F229F6FDB51EFDE:1 +FA50B5AC284204589D2FB13C13FE96069F690E1C:1 +FA8581F6B8D2FB6D561DF4BBF3A30C8A13E57C60:3 +FAA7084533D66347AA2B84CF21F16B7049F3ED75:23 +FAA936A0B3B1902CA9EF635506D4D9E6343CB1BD:1 +FAB2624AC52338A109A0981D345D115F4A5D0427:1 +FAE0317CDCAD1CAF66CE2316A1C860504EB01917:3 +FAF3E7B9B92B3953ACBF330917EED49066163521:6 +FB16E4B0B049226A4D3B896094416685AC54B999:2 +FB1A09140A2D5B37886B7E202BCB7962BB1F60B9:39 +FB1D158E6CD78BA0E941F05D52BB2CBB89EAED67:3 +FB6B388124972788196D670CDD0A4A091B6B1903:4 +FB71432BDBCDAD103803FC9A7BF3D996A1A917F1:2 +FB891E5C7579F518FFC036FF6CA6B555EA1378AA:2 +FB8EC30ACE3ED27E8CC67E2276058CFB3EC6D23A:1 +FB97D24F63577401EDA8CC723389D59CA0D83209:1 +FBC0BB685C6CDA2FE38F322073794EDFB37CBB3D:1 +FBE790428E6646EBBAE2CA1E643610D878D4F652:3 +FBF918C2A6FAC4F1FAF3AB31B38CD8F10773AFB9:3 +FC1EEDC7050231AAEE3516C7DE98135CB1DA114B:1 +FC62C9AF750CF21317ADFDA623805EC42A5367E9:1 +FC685649B4654EB8160A0776D1865CC407DFE358:3 +FC6A8D0786D7955BC2FB3BB1875ED6C136CD3F24:2 +FC7662233811E7CF098EBB18CF1B5B86B5C45DE8:4 +FC8C460B359ED0D88C42230CD3CF3E6682D0B4F0:1 +FCADFF4DF86673763D2D37D609E2FF1DBAC2CA2B:1 +FCB0BCD9D87EFD73C33CD61CEDC4AA9E0CCD04C4:1 +FCB279BBFD535641CC8779A60DE5A0F33CC9479F:1 +FCC365C7EF4587EF745B47E4533F25090938A048:1 +FCC93A005436D746A414A88DC00FDE09D2840F6C:4 +FCF3F744DCAEF8B24FA8144FF67D4F4CE644DB30:2 +FD038AA0F82A166294D80A6B65065446A4AB5AC7:1 +FD07BC15D10E82ACCF49D05865D3E4E07F9C4C9F:12 +FD0DED1F17FE5D8B62429A1040BB64C22B06B76D:2 +FD17B72EF41F5992C8D049069F3BBFCC34BC65F2:1 +FD1AA3E836D3A97C90874E08E35B1CAF12E9502F:70 +FD2D707BED7E071D5B42D364DE2FB6E278449C3E:1 +FD44BA7B7F759D200DF1C016A367344D2BA43B9A:2 +FD7BD75164E47D297265D6848E6313DB6EE3F5A9:33 +FD8ABEF9D9CAEDB64AB2DC50B5E32F91D0CA853C:2 +FD9392460D3EAE8D206BC0E54F2E23ADFF29ABDA:1 +FD98B6C673E0FB2274AD21452AC8C84E00DCC03E:2 +FDA0710D390AFAC87E3FD5E32106D91CAEF79EBA:3 +FDB888503EE6B65D885483E291E39F0E738A6717:2 +FE14EDA9EAFEF64F40733174A8C07A1B771CEEC2:1 +FE54B3F75EA5A5741EEEA61B0E6D06DF4D5985F5:7 +FE59370B75DC5785ECB07F90056C949F0179C17C:2 +FE9A8ABD7BDA8CB625D63ADBD5ABA9A03A060E71:2 +FEAB749903F16ECAC9E7D3B66985166A5ACC442C:1 +FEAF8B10A718148EA6D28A41FB34EA37B7DC7B46:14 +FEB016897BF6B6727BF2D3FF5B1BB9A774E47CEB:2 +FECEC47787C71F22541C31A4EDBA33652376B918:1 +FEFF2CF292793B8A80481BD46AB108E1B63B5912:2 +FF60DEAE8A110F36615A2F397E47C2CB5DDA99ED:2 +FF6339754A55AA023B8E91EAF175E5A3CDD60DC8:5 +FF7750B3894C0253F4D1EA3F8D087084E947A737:8 +FFA494E44B80DB7872EA2BD908D92291A218547D:13962 +FFCB8D697190D618164C902E89CF9E4F50F323F3:1 +FFE08102E6CE7A6677F0BF5ECEBBA8E6A82CA879:5 +FFF6640210FFF7625CFB9AC0E3C15A1374B637C1:2 +FFFAC60D4233961B945A76AABDB38B727C240DAB:2 diff --git a/pkg/passwords/http/export_test.go b/pkg/passwords/http/export_test.go new file mode 100644 index 0000000..2d8a2a1 --- /dev/null +++ b/pkg/passwords/http/export_test.go @@ -0,0 +1,8 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package http + +type IsPasswordCompromisedResponse = isPasswordCompromisedResponse diff --git a/pkg/passwords/http/service.go b/pkg/passwords/http/service.go new file mode 100644 index 0000000..2730520 --- /dev/null +++ b/pkg/passwords/http/service.go @@ -0,0 +1,132 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package http + +import ( + "context" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" + + "resenje.org/compromised/pkg/passwords" +) + +var _ passwords.Service = (*Service)(nil) + +// Service implements passwords Service by communicating to the running +// 'compromised' API using HTTP client. +type Service struct { + httpClient *http.Client +} + +// New creates a new Service instance against the HTTP endpoint and with an +// optional custom HTTP client. +func New(endpoint string, httpClient *http.Client) (*Service, error) { + baseURL, err := url.Parse(endpoint) + if err != nil { + return nil, err + } + return &Service{ + httpClient: httpClientWithTransport(httpClient, baseURL), + }, nil +} + +type isPasswordCompromisedResponse struct { + Compromised bool `json:"compromised"` + Count uint64 `json:"count"` +} + +// IsPasswordCompromised provides the information if the password is compromised +// by making an HTTP request to the running 'compromised' API. +func (s *Service) IsPasswordCompromised(ctx context.Context, sha1Sum [20]byte) (count uint64, err error) { + var r isPasswordCompromisedResponse + if err := s.request(ctx, http.MethodGet, "v1/passwords/"+hex.EncodeToString(sha1Sum[:]), &r); err != nil { + return 0, err + } + + if r.Compromised { + return r.Count, nil + } + return 0, nil +} + +func (s *Service) request(ctx context.Context, method, path string, v interface{}) error { + req, err := http.NewRequest(method, path, nil) + if err != nil { + return err + } + req = req.WithContext(ctx) + + req.Header.Set("Accept", "application/json") + + r, err := s.httpClient.Do(req) + if err != nil { + return err + } + defer drain(r.Body) + + if r.StatusCode != 200 { + return fmt.Errorf("unexpected response status: %s", r.Status) + } + + if v != nil && strings.Contains(r.Header.Get("Content-Type"), "application/json") { + return json.NewDecoder(r.Body).Decode(&v) + } + return nil +} + +func httpClientWithTransport(c *http.Client, baseURL *url.URL) *http.Client { + if c == nil { + c = new(http.Client) + } + + transport := c.Transport + if transport == nil { + transport = http.DefaultTransport + } + + if !strings.HasSuffix(baseURL.Path, "/") { + baseURL.Path += "/" + } + + c.Transport = roundTripperFunc(func(r *http.Request) (resp *http.Response, err error) { + u, err := baseURL.Parse(r.URL.String()) + if err != nil { + return nil, err + } + r.URL = u + return transport.RoundTrip(r) + }) + return c +} + +// roundTripperFunc type is an adapter to allow the use of ordinary functions as +// http.RoundTripper interfaces. If f is a function with the appropriate +// signature, roundTripperFunc(f) is a http.RoundTripper that calls f. +type roundTripperFunc func(*http.Request) (*http.Response, error) + +// RoundTrip calls f(r). +func (f roundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) { + return f(r) +} + +func drain(r io.ReadCloser) { + go func() { + // Panicking here does not put data in + // an inconsistent state. + defer func() { + _ = recover() + }() + + _, _ = io.Copy(ioutil.Discard, r) + _ = r.Close() + }() +} diff --git a/pkg/passwords/http/service_test.go b/pkg/passwords/http/service_test.go new file mode 100644 index 0000000..70df43a --- /dev/null +++ b/pkg/passwords/http/service_test.go @@ -0,0 +1,104 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package http_test + +import ( + "context" + "encoding/hex" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + httppasswords "resenje.org/compromised/pkg/passwords/http" +) + +func TestIsPasswordCompromised_compromised(t *testing.T) { + client, mux := newClient(t) + + hash := "3d5896ffe806a482490b99f690650995b63c3513" + var want uint64 = 101 + + mux.HandleFunc("/v1/passwords/"+hash, func(w http.ResponseWriter, r *http.Request) { + b, err := json.Marshal(httppasswords.IsPasswordCompromisedResponse{ + Compromised: true, + Count: want, + }) + if err != nil { + t.Error(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", jsonContentType) + _, _ = w.Write(b) + }) + + got, err := client.IsPasswordCompromised(context.Background(), hexDecodeSHA1Sum(t, hash)) + if err != nil { + t.Fatal(err) + } + + if got != want { + t.Errorf("got count %v, want %v", got, want) + } +} + +func TestIsPasswordCompromised_notCompromised(t *testing.T) { + client, mux := newClient(t) + + hash := "3d5896ffe806a482490b99f690650995b63c3514" + var want uint64 + + mux.HandleFunc("/v1/passwords/"+hash, func(w http.ResponseWriter, r *http.Request) { + b, err := json.Marshal(httppasswords.IsPasswordCompromisedResponse{ + Compromised: false, + Count: want, + }) + if err != nil { + t.Error(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", jsonContentType) + _, _ = w.Write(b) + }) + + got, err := client.IsPasswordCompromised(context.Background(), hexDecodeSHA1Sum(t, hash)) + if err != nil { + t.Fatal(err) + } + + if got != want { + t.Errorf("got count %v, want %v", got, want) + } +} + +const jsonContentType = "application/json; charset=utf-8" + +func newClient(t testing.TB) (client *httppasswords.Service, mux *http.ServeMux) { + t.Helper() + + mux = http.NewServeMux() + server := httptest.NewServer(mux) + t.Cleanup(server.Close) + + client, err := httppasswords.New(server.URL, server.Client()) + if err != nil { + t.Fatal(err) + } + + return client, mux +} + +func hexDecodeSHA1Sum(t *testing.T, s string) (sum [20]byte) { + t.Helper() + b, err := hex.DecodeString(s) + if err != nil { + t.Fatal(err) + } + copy(sum[:], b) + return sum +} diff --git a/pkg/passwords/mock/service.go b/pkg/passwords/mock/service.go new file mode 100644 index 0000000..e5235c4 --- /dev/null +++ b/pkg/passwords/mock/service.go @@ -0,0 +1,34 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mock + +import ( + "context" + + "resenje.org/compromised/pkg/passwords" +) + +var _ passwords.Service = (*Service)(nil) + +// Service implements passwords service with injectable functionality mainly +// meant unit testing services that depend on passwords service. +type Service struct { + isPasswordCompromisedFunc func(ctx context.Context, sha1Sum [20]byte) (uint64, error) +} + +// New creates a new instance of Service by injecting the passed function as the +// service method. +func New(isPasswordCompromisedFunc func(ctx context.Context, sha1Sum [20]byte) (uint64, error)) *Service { + return &Service{ + isPasswordCompromisedFunc: isPasswordCompromisedFunc, + } +} + +// IsPasswordCompromised calls the function what is passed to the New +// constructor. +func (s *Service) IsPasswordCompromised(ctx context.Context, sha1Sum [20]byte) (uint64, error) { + return s.isPasswordCompromisedFunc(ctx, sha1Sum) +} diff --git a/pkg/passwords/passwords.go b/pkg/passwords/passwords.go new file mode 100644 index 0000000..d59131d --- /dev/null +++ b/pkg/passwords/passwords.go @@ -0,0 +1,13 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package passwords + +import "context" + +// Service specifies operations agains compromised passwords database. +type Service interface { + IsPasswordCompromised(ctx context.Context, sha1Sum [20]byte) (count uint64, err error) +} diff --git a/version.go b/version.go new file mode 100644 index 0000000..e821b14 --- /dev/null +++ b/version.go @@ -0,0 +1,18 @@ +// Copyright (c) 2020, Compromised AUTHORS. +// All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package compromised + +var ( + version = "0.1.0" // manually set semantic version number + commit string // automatically set git commit hash + + Version = func() string { + if commit != "" { + return version + "-" + commit + } + return version + "-dev" + }() +)