pax_global_header00006660000000000000000000000064151747154010014517gustar00rootroot0000000000000052 comment=f9b8ecaa37ae3135359345143e41cffc5a7e13b5 hex-2.4.2/000077500000000000000000000000001517471540100123105ustar00rootroot00000000000000hex-2.4.2/.credo.exs000066400000000000000000000076411517471540100142130ustar00rootroot00000000000000%{ configs: [ %{ name: "default", files: %{ included: ["lib/", "src/", "test/"], excluded: [~r"/_build/", ~r"/deps/"] }, requires: [], check_for_updates: false, # # You can customize the parameters of any check by adding a second element # to the tuple. # # To disable a check put `false` as second element: # # {Credo.Check.Design.DuplicatedCode, false} # checks: [ {Credo.Check.Consistency.ExceptionNames}, {Credo.Check.Consistency.LineEndings}, {Credo.Check.Consistency.MultiAliasImportRequireUse}, {Credo.Check.Consistency.SpaceAroundOperators}, {Credo.Check.Consistency.SpaceInParentheses}, {Credo.Check.Consistency.TabsOrSpaces}, # For some checks, like AliasUsage, you can only customize the priority # Priority values are: `low, normal, high, higher` or disable it (false). {Credo.Check.Design.AliasUsage, false}, # For others you can set parameters # If you don't want the `setup` and `test` macro calls in ExUnit tests # or the `schema` macro in Ecto schemas to trigger DuplicatedCode, just # set the `excluded_macros` parameter to `[:schema, :setup, :test]`. {Credo.Check.Design.DuplicatedCode, excluded_macros: []}, # Disabled for now as they are also checked by Code Climate {Credo.Check.Design.TagTODO, false}, {Credo.Check.Design.TagFIXME, false}, {Credo.Check.Readability.FunctionNames}, {Credo.Check.Readability.MaxLineLength, false}, {Credo.Check.Readability.ModuleAttributeNames}, {Credo.Check.Readability.ModuleDoc, false}, {Credo.Check.Readability.ModuleNames}, {Credo.Check.Readability.ParenthesesOnZeroArityDefs, false}, {Credo.Check.Readability.ParenthesesInCondition}, {Credo.Check.Readability.PredicateFunctionNames}, {Credo.Check.Readability.TrailingBlankLine}, {Credo.Check.Readability.TrailingWhiteSpace}, {Credo.Check.Readability.VariableNames}, {Credo.Check.Readability.RedundantBlankLines}, {Credo.Check.Readability.SinglePipe, false}, # This is the job of dialyzer {Credo.Check.Readability.Specs, false}, {Credo.Check.Readability.StringSigils}, {Credo.Check.Refactor.ABCSize, false}, {Credo.Check.Refactor.CondStatements}, # That's a feature! {Credo.Check.Refactor.DoubleBooleanNegation, false}, {Credo.Check.Refactor.FunctionArity, max_arity: 8}, {Credo.Check.Refactor.MatchInCondition}, {Credo.Check.Refactor.PipeChainStart, false}, {Credo.Check.Refactor.CyclomaticComplexity}, {Credo.Check.Refactor.NegatedConditionsInUnless}, {Credo.Check.Refactor.NegatedConditionsWithElse}, {Credo.Check.Refactor.Nesting, max_nesting: 3}, {Credo.Check.Refactor.UnlessWithElse}, # That's a feature! {Credo.Check.Refactor.VariableRebinding, false}, {Credo.Check.Warning.IExPry}, {Credo.Check.Warning.IoInspect, false}, # Those are warned by Elixir when it is ambiguous since Elixir v1.4 {Credo.Check.Warning.NameRedeclarationByAssignment, false}, {Credo.Check.Warning.NameRedeclarationByCase, false}, {Credo.Check.Warning.NameRedeclarationByDef, false}, {Credo.Check.Warning.NameRedeclarationByFn, false}, {Credo.Check.Warning.OperationOnSameValues}, {Credo.Check.Warning.BoolOperationOnSameValues}, {Credo.Check.Warning.UnusedEnumOperation}, {Credo.Check.Warning.UnusedKeywordOperation}, {Credo.Check.Warning.UnusedListOperation}, {Credo.Check.Warning.UnusedStringOperation}, {Credo.Check.Warning.UnusedTupleOperation}, {Credo.Check.Warning.OperationWithConstantResult} # Custom checks can be created using `mix credo.gen.check`. # ] } ] } hex-2.4.2/.formatter.exs000066400000000000000000000001411517471540100151060ustar00rootroot00000000000000[ inputs: [ "*.exs", "config/*.exs", "lib/**/*.ex", "test/**/*.{ex,exs}" ] ] hex-2.4.2/.github/000077500000000000000000000000001517471540100136505ustar00rootroot00000000000000hex-2.4.2/.github/dependabot.yml000066400000000000000000000002321517471540100164750ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" cooldown: default-days: 7 hex-2.4.2/.github/workflows/000077500000000000000000000000001517471540100157055ustar00rootroot00000000000000hex-2.4.2/.github/workflows/codeql.yml000066400000000000000000000026221517471540100177010ustar00rootroot00000000000000name: "CodeQL Advanced" on: push: branches: ["main"] pull_request: branches: ["main"] schedule: - cron: "29 8 * * 1" permissions: contents: read jobs: analyze: name: Analyze (${{ matrix.language }}) runs-on: "ubuntu-latest" permissions: security-events: write strategy: fail-fast: false matrix: include: - language: actions build-mode: none steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Initialize CodeQL uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 with: category: "/language:${{matrix.language}}" zizmor: name: Zizmor runs-on: ubuntu-latest permissions: security-events: write steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Run zizmor uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3 hex-2.4.2/.github/workflows/main.yml000066400000000000000000000073051517471540100173610ustar00rootroot00000000000000name: CI on: [push, pull_request] permissions: contents: read jobs: format: name: Format runs-on: ubuntu-22.04 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Install OTP and Elixir uses: erlef/setup-beam@fc68ffb90438ef2936bbb3251622353b3dcb2f93 # v1.24.0 with: otp-version: 28.1 elixir-version: 1.18.4 version-type: strict - name: Install dependencies run: mix deps.get - name: Check mix format run: mix format --check-formatted test: name: Test runs-on: ubuntu-22.04 services: postgres: image: postgres:14 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: postgres ports: - 5432:5432 options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 strategy: fail-fast: false matrix: pair: # - erlang: maint # elixir: main - erlang: 28.1 elixir: 1.18.4 - erlang: 27.2 elixir: 1.17.3 - erlang: 26.2 elixir: 1.16.1 - erlang: 25.3 elixir: 1.15.7 - erlang: 25.3 elixir: 1.14.5 - erlang: 25.3 elixir: 1.13.4 - erlang: 24.3 elixir: 1.12.3 env: HEXPM_OTP: OTP-28.1 HEXPM_ELIXIR: v1.18.4 HEXPM_BRANCH: main HEXPM_PATH: hexpm HEXPM_ELIXIR_PATH: hexpm_elixir HEXPM_OTP_PATH: hexpm_otp HEXPM_MIX_HOME: hexpm_mix HEXPM_MIX_ARCHIVES: hexpm_mix steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Install OTP and Elixir uses: erlef/setup-beam@fc68ffb90438ef2936bbb3251622353b3dcb2f93 # v1.24.0 with: otp-version: ${{matrix.pair.erlang}} elixir-version: ${{matrix.pair.elixir}} version-type: strict - name: Install OTP and Elixir for hexpm run: | wget https://repo.hex.pm/builds/otp/ubuntu-22.04/${HEXPM_OTP}.tar.gz mkdir -p ${HEXPM_OTP_PATH} tar -xf ${HEXPM_OTP}.tar.gz -C ${HEXPM_OTP_PATH} --strip-components=1 ${HEXPM_OTP_PATH}/Install -minimal $(pwd)/${HEXPM_OTP_PATH} wget https://repo.hex.pm/builds/elixir/${HEXPM_ELIXIR}.zip unzip -d ${HEXPM_ELIXIR_PATH} ${HEXPM_ELIXIR}.zip mkdir -p ${HEXPM_MIX_HOME} PATH=$(pwd)/${HEXPM_ELIXIR_PATH}/bin:$(pwd)/${HEXPM_OTP_PATH}/bin:${PATH} MIX_HOME=$(pwd)/${HEXPM_MIX_HOME} MIX_ARCHIVES=$(pwd)/${HEXPM_MIX_HOME} mix local.hex --force PATH=$(pwd)/${HEXPM_ELIXIR_PATH}/bin:$(pwd)/${HEXPM_OTP_PATH}/bin:${PATH} MIX_HOME=$(pwd)/${HEXPM_MIX_HOME} MIX_ARCHIVES=$(pwd)/${HEXPM_MIX_HOME} mix local.rebar --force - name: Set up hexpm run: | git clone -b ${HEXPM_BRANCH} https://github.com/hexpm/hexpm.git hexpm cd hexpm; PATH=$(pwd)/../${HEXPM_ELIXIR_PATH}/bin:$(pwd)/../${HEXPM_OTP_PATH}/bin:${PATH} MIX_HOME=$(pwd)/../${HEXPM_MIX_HOME} MIX_ARCHIVES=$(pwd)/../${HEXPM_MIX_HOME} MIX_ENV=hex ../${HEXPM_ELIXIR_PATH}/bin/mix deps.get; cd .. cd hexpm; PATH=$(pwd)/../${HEXPM_ELIXIR_PATH}/bin:$(pwd)/../${HEXPM_OTP_PATH}/bin:${PATH} MIX_HOME=$(pwd)/../${HEXPM_MIX_HOME} MIX_ARCHIVES=$(pwd)/../${HEXPM_MIX_HOME} MIX_ENV=hex ../${HEXPM_ELIXIR_PATH}/bin/mix compile; cd .. - name: Install dependencies run: | sudo rm -rf /usr/local/bin/rebar3 mix deps.get mix deps.compile - name: Run tests run: mix test hex-2.4.2/.gitignore000066400000000000000000000001461517471540100143010ustar00rootroot00000000000000/_build /deps /tmp /src/mix_safe_erl_term.erl erl_crash.dump hex-1.x.csv hex-1.x.csv.signed *.ez /doc hex-2.4.2/CHANGELOG.md000066400000000000000000001131061517471540100141230ustar00rootroot00000000000000# CHANGELOG ## v2.4.2 (2026-04-30) ### Enhancements * Add download stats and release dates to `mix hex.info` * Show `mix hex.package diff` commands in `mix hex.outdated` output * Update docs published message to indicate docs may not be immediately available ### Bug fixes * Clean up paths in `mix hex.package diff` output * Fix organization auth to create user key instead of organization key * Fix registry cache purging * Fix `mix deps.get` timing out when fetching private packages in parallel * Skip auth prompts in `mix deps.get` when only public packages are needed * Restrict user OAuth fallback to trusted repositories * Only exchange API keys for OAuth tokens on repositories that opted in ### Security fixes * Raise on `mix.lock` checksum mismatches instead of silently rewriting them (CVE-2026-32148) ## v2.4.1 (2026-03-24) ### Bug fixes * Fix OAuth exchange attempted for repos configured before v2.4.0 * Propagate OAuth exchange configuration to organization repos * Deduplicate OAuth token exchange during parallel package fetching * Restore Hex code paths after compilation pruning ## v2.4.0 (2026-03-14) ### Enhancements * Replace password based authentication with OAuth device flow * Add 2FA support for API write operations * Use OAuth tokens for hexpm and custom repositories * Add `--print-url` flag to `mix hex.search` * Include stdlib packages by default in `mix hex.search` * Change private docs URLs from *.hexdocs.pm to *.hexorgs.pm * Improve authentication error handling for package fetching ### Bug fixes * Fix `--within-requirements` option in `mix hex.outdated` ### Security fixes * Use safe deserialization for parsing package manifests ## v2.3.2 (2026-02-27) ### Security fixes * Fix unsafe deserialization of Erlang terms in API responses (CVE-2026-21619) ## v2.3.1 (2025-10-26) ### Bug fixes * Fix repository name verification for organizations and custom repositories ## v2.3.0 (2025-10-26) ### Enhancements * Update `mix hex.search` task with full-text documentation search across all your dependencies * Add `:only` column and filtering to `mix hex.outdated` * Improve registry verification error messages * Improve caching of packages across multiple repos * Add HTTP request debug logging with `MIX_DEBUG=1` * Prevent publishing packages with `:in_umbrella` dependencies ### Bug fixes * Handle truthy boolean for `CI` env variable * Fix `warn_if_outdated` if package name and app name are different * Allow nested maps for `extra` package metadata ## v2.2.2 (2025-06-27) ### Bug fixes * `mix hex.publish`: Pass `--no-listeners` to `deps.loadpaths` ## v2.2.1 (2025-05-09) ### Bug fixes * Precompile Hex for Elixir 1.18 using Elixir 1.18.0 ## v2.2.0 (2025-05-09) ### Enhancements * Add verifications of the registry cache ets table (`~/.cache/hex/cache.ets`) * Add help when docs download times out * Support `:warn_if_outdated` in deps Let's say you have the following in `mix.exs`: ```elixir {:ex_doc, ">= 0.0.0", warn_if_outdated: true, only: :dev} ``` and ExDoc is at version v0.37.2 in `mix.lock`, and v0.37.3 was released. You will now see: ```sh $ mix deps.get Resolving Hex dependencies... Resolution completed in 0.054s Unchanged: ex_doc 0.37.2 warning: the following deps are outdated and set "warn_if_outdated: true": * ex_doc 0.37.3 is available ``` ## v2.1.1 (2024-05-22) ### Bug fixes * Fix overriding Hex packages from non Hex dependencies ## v2.1.0 (2024-05-21) ### Enhancements * Improve solver error message when collecting conflicting requirements from multiple places. Fixes the "empty" versions error. This is done by including path and git parents of hex packages in the solver. * Add "(CI)" to `user-agent` HTTP header if environment variable `CI` is set * Improve message for authentication errors * Set exit code 1 on `mix hex.organization auth` errors * Add `--sort` flag `mix hex.outdated` * Improve error message when trying to publish existing package with permissions * Consider ex_doc `:output` option when publishing documentation * Warn on unknown dependency options ### Bug fixes * Do not close registry server in post_converge. Fixes the "the table identifier does not refer to an existing ETS table" error. * Revert Mix changes on Hex application stop * Fix passing requests from umbrella apps to solver * Handle empty package name in `mix hex.info` ## v2.0.6 (2023-02-06) ### Bug fixes * Fix application startup when there are configured organizations ## v2.0.5 (2023-02-06) ### Bug fixes * Fix HTTP authentication for repositories added before v2.0.3 ## v2.0.4 (2023-02-02) ### Bug fixes * Fix trust lookup for organizations ## v2.0.3 (2023-02-01) ### Enhancements * Remove dependency on `ssh` application * Add `trusted_mirror_url` config and `HEX_TRUSTED_MIRROR_URL` environment variable. When setting either of these the repository authentication key will be included in requests. `mirror_url` config and `HEX_MIRROR_URL` have changed to no longer include the authentication key in requests. This is to ensure secrets are not sent to repository mirrors that are not trusted. ## v2.0.2 (2023-01-30) ### Enhancements * Remove `hex.install` task ### Bug fixes * Fixes for upcoming Elixir 1.15.0 release ## v2.0.1 (2023-01-09) ### Enhancements * Raise when parsing intersected ranges * Skip unselected optionals during solving ### Bug fixes * Fix converging of requirements from different sources * Fix organization owner prompt during publish * Do not override locked deps ## v2.0.0 (2022-10-30) ### Enhancements * New version solver with higher performance and better error messages * Switch to IPv6 if requests fail due to connection errors ### Bug fixes * Fix browser opening on Windows ## v1.0.1 (2021-12-14) ### Bug fixes * Fix compatibility with some Elixir and OTP combinations ## v1.0.0 (2021-12-13) ### Enhancements * Set exit code to 1 when `mix hex.publish` fails * Validate OSS licenses * Read authorization credentials from `~/.netrc` * Error if building package with an `app: false` dependency ### Bug fixes * Do not error if the organization authorization key could not be verified, this improves handling of API server issues * Improvements to version solver to prevent scenarios where it takes a long time to find a solution * Improve error when update checker times out ## v0.21.3 (2021-09-18) ### Enhancements * Add config `no_short_urls` and env var `HEX_NO_SHORT_URLS` to disable short URL generation * Mention `mix hex.sponsor` when fetching packages that accept sponsorship * Add `--key` option to `mix hex.repo show NAME` to print repository key * Improve output when update check fails * Print hint if version resolution is slow ### Bug fixes * Improve version backtracking to fix slow version resolutions and downgrading of dependencies ## v0.21.2 (2021-04-14) ### Enhancements * Add support for `mix hex.package fetch PACKAGE` (without version) ### Bug fixes * Gracefully handle missing hex metadata in sponsor task * Fix building hex registry * Update ssl opts for host validation on redirect * Store correct password after confirmation failure ## v0.21.1 (2021-01-15) ### Enhancements * Warn when using ssl-10.2 * Disable API write operations when using ssl-10.2 ## v0.21.0 (2021-01-14) ### Enhancements * Add `--epub` option to `mix hex.docs offline` * Add `--replace` option to `mix hex.publish` * Add locked version to `mix hex.info ` * Clarify publish message around ownership * Remove reliance on colors for hex.outdated * Follow XDG Base Directory Specification * Add link to diffs page in footer of `mix hex.outdated` * Introduce `latest` branch to install Hex using `mix archive.install git ...` * Add `--repo` flag to `mix hex.package` task * Make `mix hex.package diff` more CLI-friendly * Customize hostname check to allow also wildcard certificates * Use API for dependency config in mix hex.info * Do not pass --canonical to docs task * Always add `*.DS_Store` to `:exclude_patterns` * Add note about updatable packages to `mix hex.outdated` task * Use tarball outer checksum to check cache freshness * Add `--within-requirements` flag to `mix hex.outdated` * Add `--fetch-public-key FINGERPRINT` to `mix hex.repo add` * Return non-zero exit when package or release are not found in `mix hex.info` * Add `no_proxy` configuration * Add `mix hex.package diff APP VERSION` * Add `mix hex.sponsor` for listing all dependencies ask for sponsors or support * Add `mix hex.registry build` for building registries locally ### Bug fixes * Fix order of organizations displayed on `mix hex.publish` * Fix stacktrace warning * Hide `mix hex.install` private task * Fix `mix hex.repo remove` command doc * Fix backtracking on single parent * Do not unpack the tarball on `mix hex.package fetch` unless `--unpack` is passed * Re-fetch stale cached package if registry checksum changed * Fix compatibility with OTP 24 ## v0.20.6 (2020-10-20) ### Bug fixes * Fix compatibility with OTP 24 ## v0.20.5 (2020-02-05) ### Enhancements * Add timestamps to entries in registry cache for easier debugging * Bump registry cache version to invalidate old caches * Warn if fetching registry without outer checksum ### Bug fixes * Do not require that the registry supports outer checksums * Missing outer checksum is not a mismatch, this will fix "out of date" errors when the manifest is newer than the lockfile ## v0.20.4 (2020-02-04) ### Bug fixes * Fix tarball file extraction through symlinks ## v0.20.3 (2020-02-03) ### Enhancements * Fetch the latest non-prerelease version of a package in `mix hex.docs ` ### Bug fixes * Correctly handle old manifest files without crashing ## v0.20.2 (2020-02-03) ### Enhancements * Add `--output` option to `mix hex.package fetch` task * Add `cacerts_path` configuration for custom CA certificate files * Improve output in `mix hex.publish` to make it more clear to what repository you are publishing * Explain red colors in hex.outdated ### Bug fixes * Fix HTTP timeout config * Do not allow creating empty packages ### Security fixes * Fix for directory traversal vulnerability for symlinks in tarballs * Update package checksum to include the entire tarball instead of specific files inside it ## v0.20.1 (2019-06-10) ### Bug fixes * Do not print transfer message when not transferring ## v0.20.0 (2019-06-09) ### Enhancements * Add per-project Hex configuration. Configure Hex under the `:hex` key inside your project configuration in `mix.exs` * Show location of package after running `mix hex.build` * List all available Hex tasks when running `mix hex` * List subtasks when running `mix hex` * Remove tarball if it is invalid to avoid it being as cache in the future * Show umbrella children `mix.exs` location in `mix hex.outdated` * Add `mix hex.owner transfer` task * Show improved error message on invalid configs * Add `mix hex.package fetch` task * Add `mix hex.package diff` task ### Bug fixes * Fix `mirror_url` config * Fix `api_url` config * Do no try to remove docs after reverting package – docs are already automatically removed ## v0.19.0 (2019-01-15) ### Enhancements * Improve output of `mix hex.config` * Print publisher in `mix hex.info PACKAGE VERSION` * Add organization flag to dependency config in `mix hex.info PACKAGE` ### Bug fixes * Don't follow symlinks when adding files to tarballs * Error with a descriptive msg when building a package with git dependencies * Improve listing of incompatible package versions when displaying backtrack error message * Improve resolver performance when it needs to do a lot of backtracking ### Security fixes * Verify authenticity of registry records. This fixes a vulnerability that would allow a malicious mirror to serve modified versions of Hex packages. A new check has been introduced that requires the latest registry record version, if you are using a repository or mirror that has not been updated yet you can disable this check by setting the environment variable `HEX_NO_VERIFY_REPO_ORIGIN=1`. Further clarification of this issue will come at a later stage. ## v0.18.2 (2018-11-08) ### Enhancements * Add checks before publishing docs * Update generated protobuf files for Registry with OTP 21 compatibility * No longer list tasks in `mix hex` task * Use hexdocs organization URLs * Adds `--dry-run` option to publish tasks * Do not print "Unchanged" dependencies on mix deps.get in green * Validate hex config keys * Add `c_src/` and `Makefile` to default package files * Publish Mix task docs on * Add recommendation when retiring and require `--message` flag ### Bug fixes * Use rebar3, not rebar, when guessing build tool * Fix issue saving write key when resetting local password ## v0.18.1 (2018-07-06) ### Bug fixes * Fix normalization of repo paths when authenticating organization ## v0.18.0 (2018-07-05) ### API keys When authenticating with `mix hex.user auth` two API keys are generated instead of single one. One key is unencrypted with read access and the other is encrypted with your local password and has full read/write access to the API. Now commands that don't make any changes will not require a password. Additionally, we generate a single key that gives access to all your organization repositories, instead of one key for each repository. It also has the added benefit that you don't have to reauthenticate if you are added to a new organization. We have also added support for keys owned directly by an organization instead of a specific user, these keys can be accessed through `mix hex.organization`. This is useful when generating keys for a CI environment, previously when personal keys were used, a person leaving an organization or revoking the key could negatively affect CI workflow. ### Improvements to continuous integration workflows The `HEX_API_KEY` environment variable has been introduced to be able run commands that require an authentication without having to authenticate manually with `mix hex.user auth` which has user input prompts. The key set with `HEX_API_KEY` can be generated with `mix hex.user key generate` or `mix hex.organization key ORGANIZATION generate`. It also makes it possible to run commands such as `mix hex.publish` without being prompted for a password. By passing the `--yes` flag to `mix hex.publish` you can publish your package (together with `HEX_API_KEY`) without any confirmation prompts. This allows you to publish your package as part of your CI build process. ### Ignoring `:maintainers` field In previous Hex versions we required `:maintainers` key to be present when publishing package. At the same time, on hex.pm we are also showing package owners (controlled by the `mix hex.owner` task). It was confusing to show both maintainers and owners and figure out which really control the package, so we've dropped showing maintainers on hex.pm and the field will no longer be added to package's metadata. If maintainers field was used to give credit to current and/or past contributors we encourage to mention that in project's README instead. ### Enhancements * Add `--yes` flag to `hex.publish` for publishing without any confirmation prompts * Add `HEX_API_KEY` environment variable for setting and overriding the key used when authenticating against the API * Generate a single key for all organization repositories when authenticating a new user * Return a non-zero exit code from `hex.outdated` when dependencies are outdated * Generate two API keys when authenticating, one encrypted with write access, and one unencrypted with only read access * Add ownership levels to `hex.owner` task * When resolving, try all possible backtrack branches and select the best solution * Improve formatting of multi-line validation errors * Do not use `:maintainers` package configuration field * Change `hex.organization` to generate keys owned by organization instead of the user generating them * Add options to `hex.organization key` for revoking and listing keys owned by organization * Improve interface for `hex.user key` and `hex.organization key`, the following commands have changed: * `hex.user key --generate` => `hex.user key generate` * `hex.user key --list` => `hex.user key list` * `hex.user key --revoke KEY_NAME` => `hex.user key revoke KEY_NAME` * `hex.user key --revoke-all` => `hex.user key revoke --all` * `hex.organization key ORGANIZATION` => `hex.organization key ORGANIZATION generate` ## v0.17.8 (2018-07-01) ### Bug fixes * Fix private packages on Windows ## v0.17.7 (2018-04-20) ### Bug fixes * Fix crash when unpacking tarballs with broken symlinks * Correct the type of build tools package metadata ## v0.17.6 (2018-04-18) ### Bug fixes * Fix crash when printing resolver output when having lock entries from other SCMs ## v0.17.5 (2018-04-18) ### Bug fixes * Fix crash when printing resolver output for old lock files ## v0.17.4 (2018-04-18) ### Enhancements * Tarball and registry code has been extracted to the `hex_erl` package * Hide retired versions when showing latest release in `hex.info` task * Add `hex.docs offline` and `hex.docs` online tasks * Add `--key-name` flag to key generation tasks * Add `:exclude_patterns` to package config for excluding files from package * Resolver now backtracks children before parents to improve versions selected when backtracking * Change some errors to warnings when building private packages * Group resolved dependency output into unchanged, updated, and downgraded when running `deps.get` and `deps.update` tasks * Add authentication to `hex.docs` task for showing private package documentation * Improve error message when package fetch times out * General improvements to tasks when accessing organizations ### Bug fixes * Fix wrong publish message when using `--organization` flag in `hex.publish` task * Set file times inside tarballs to 2000-01-01 to fix tars on FAT file systems * Fix `hex.docs open` task on Windows ## v0.17.3 (2018-01-17) ### Bug fixes * Handle missing package descriptions in `hex.search` task * Fix printing of package checksum after publishing ## v0.17.2 (2018-01-16) ### Enhancements * Increase `hex.publish` timeouts and make it configurable with `:http_timeout` config and `HEX_HTTP_TIMEOUT` variable * Test key before adding it with `hex.organization auth NAME --key KEY` * Remove pre-release publish restriction for private packages * Add package descriptions to `hex.search` task * Improve error message when there are no versions matching requirement * Add latest stable version to `hex.search` task * Add `metadata.config` file to checked out dependency directory * Warn if we detect a lock entry from a newer Hex version * Add `hex.build --output` and `hex.build --unpack` tasks * Preserve symlinks and empty directories in tar * Simplify Hex output on deps.get * General improvements to tarball creation and unpacking * List umbrella children's top level dependencies in `hex.outdated` * Include `.formatter.exs` file in default package builds * Prompt user when authentication is required * Automatically auth all organizations when authing user with `hex.user auth` * Highlight if a package release has been retired in `hex.info` * Display package website links in `mix hex.owner packages` ### Bug fixes * Do not crash if failing to write tarball * Disable HTTP pipelining to avoid bugs in HTTP client * Also purge registry etags when repository source changed * Retry HTTP requests on `:socket_closed_remotely` errors * Fix package tarballs being reproducible * Authenticate HTTP requests for `hex.search` * Populate managers when initially getting dependencies * Check dependencies on `hex.audit` and `hex.publish` * Fix fetching of private packages that overrides public packages * Fix HTTP redirect handling * Don't display internal configs in `hex.config` ## v0.17.1 (2017-08-29) ### Enhancements * Improve error message when package does not exist * Improve error message when no versions exist for given requirement * Add `--key` flag to `hex.organization auth` to authorize by giving a key directly without supplying a password * Add `hex.organization key` to generate a key for accessing the organization's repository ## v0.17.0 (2017-08-28) ### Private packages and organizations Hex.pm is adding support for private packages with organizations. See https://hex.pm/docs/private for more details. To authorize an organization on your machine run `mix hex.organization auth acme`, this will store the organization's repository details in Hex so that you can fetch packages from the repository. As soon as you are added as a member to an organization you can administer and publish packages, if you have the appropriate role, with the `--organization` flag or by setting the `:organization` option on the package configuration. Different from the last release packages will always be pulled from the default `hexpm` repository and you have to override it with the `:organization` or `:repo` options on the dependency configuration. ### Enhancements * Add `hex.organization` task * Rename `hex.user key` flag `--remove*` to `--revoke*` to clarify what it does * Add `--organization` flag to tasks working on packages * Add `:organization` option to package configuration * Add support for publishing to organizations * Improve error message when docs task is missing * Add `--confirm` flag to `hex.publish` task ### Bug fixes * Fix version validation exceptions * Reintroduce `HEX_MIRROR` environment variable * Preserve file modes when building tarball * Disallow `:app` option for dependencies ## v0.16.1 (2017-06-22) ### Enhancements * Add `mix hex.repo show` task for showing repo configuration * Improve error message if there are no releases for given requirement in the registry * Add `mix hex.audit` task for checking for retired packages ### Bug fixes * Do not try to publish docs if package publish failed * Do not update lock entry if only metadata changed * Do not show authentication details when printing URLs * Fix password reset * Fix race condition where some entries may not be cached if they were added just before application closed * Support PAX tarballs, created on OTP 20, when using older OTP versions. Additionally, make it less likely PAX tarballs are created ## v0.16.0 (2017-04-18) ### Multiple repository support This version adds support for using packages from multiple repositories. With the `hex.repo` task additional repositories can be added to Hex. With it you can add additional repositories or replace the default "hexpm" repository by running `mix hex.repo add hexpm ...`, check the docs for more information. To use a dependency from another repository add `repo: :my_other_repo` to the dependency definition in `mix.exs` and make sure you have added `my_other_repo` with `mix hex.repo add my_other_repo`. Dependencies of a package will be automatically pulled from the same repository as the parent package unless otherwise stated with the `:repo` option on the dependency definition. ### Enhancements * Add `hex.repo` task * Move `hex.key` tasks to `hex.user keys` * Warn or error if publishing a package with pre-release dependencies ### Bug fixes * Do not check for updates when running in offline mode * Fix an issue where dependency resolution could take a very long time * Do not publish docs if publishing the package failed * Fix an issue where HTTP timeouts could cause the application to freeze * Ensure managers always exist in the lock ## v0.15.0 (2016-12-24) ### Package retirement With this new release you can mark versions of your packages as retired when you no longer recommend its use. This can be because the release has a serious security flaw, something went wrong with the release so that it's unusable or because the package has been renamed or deprecated. A retired version is still usable and fetchable but it will show as retired on hex.pm and when resolved Hex will show a warning to the user with the retirement message. ### Enhancements * Add --module flag to `hex.docs` task * Changed `hex.outdated` task to show if a dependency can be updated * Add `hex.retire` task for package retirement * Warn when resolving retired packages * Restrict number of default SSL ciphers ### Bug fixes * Do not make conditional HTTP request if file is missing * Ensure cache file is saved when Hex exits ## v0.14.1 (2016-11-24) ### Enhancements * Add environment variable `HEX_HTTP_CONCURRENCY` for limiting number of concurrent HTTP requests ### Bug fixes * Fix compatibilities with older Elixir version (<= 1.1) * Ensure build tools are unique in mix.lock and when publishing * Fix `hex.docs open` opening websites on Unix systems * Do not crash on diverged dependencies with conflicting SCMs * Fix some duplicate HTTP requests on slow networks * Limit concurrent registry HTTP requests ## v0.14.0 (2016-10-28) ### New registry format Hex has switched to a new registry format that is more efficient and will scale better as the registry grows. The new registry format is encoded with protocol buffers and is split into multiple files (one file per package) to avoid fetching one big file with data you will not need. The resolver will make more HTTP requests but will in total fetch much less data. The specification for the new format can be found here: https://github.com/hexpm/specifications/pull/10. The old ETS based registry format is no longer supported in the client but will continue to be available from the registry for the foreseeable future. ### Enhancements * `hex.docs open` will by default open the online hexdocs for the given package * An `--offline` option has been added to `hex.docs open` for opening docs stored on your local filesystem and it will automatically fetch the docs if they are not available locally * Only support secure SSL ciphers and safe SSL versions (support for SSLv3 has been dropped) * Improvements to the language in the resolver error messages ### Bug fixes * Fix an issue where duplicate build tool names could be added to the package metadata ## v0.13.2 (2016-09-19) ### Bug fixes * Only error on non-Hex dependencies when building ## v0.13.1 (2016-09-19) ### Enhancements * Most warnings on `hex.publish` are now errors ### Bug fixes * Fix bug where the old config format was not readable * Convert old config format to new format on every read * Fix `HEX_UNSAFE_REGISTRY` negation ## v0.13.0 (2016-07-30) ### Enhancements * Inform about new Hex version in `hex.info` * Support `extra` metadata field * Print package checksum when building and publishing * Warn if using registry from cache * Show creation time of API keys in `hex.keys list` * Improve the error message if OTP has broken SNI in `:ssl` application * Verify dependencies from registry against lock * Hex will now automatically encrypt your local API key, use `hex.user passphrase` to change the encryption passphrase * Improve resolver error message to mention behavior of pre-releases and overrides * Improve error message if a dependency has configured the OTP application name incorrectly for another dependency * `hex.publish` now also publishes docs by default, use `hex.publish package` and `hex.publish docs` to respectively publish package and docs independently * `hex.docs` will now open or fetch documentation tarballs * `hex.key remove` will now also de-auth the user if the local API key was removed * Add status messages when publishing and reverting ### Bug fixes * Fix bug where the client was fetching packages even when lock is OK * Fix resolver sometimes not producing any backtrack output * Verify certificate against correct hostname after redirect ## v0.12.1 (2016-05-31) ### Enhancements * Only show proxy settings when MIX_DEBUG=1 * Add retries to idempotent requests ### Bug fixes * Fix crash when you get multiple backtrack messages ## v0.12.0 (2016-05-15) ### Enhancements * Add package checksums to lock, ensuring a locked package can not change its content * Add managers and deps to lock, allowing Hex to run without loading the registry * Align deps fetching output from scm * Update hex.pm repo URL to https://repo.hex.pm * Link to policies when registering account * Update CoC links * Improve conflict messages * Improve error messages when ex_doc is missing when publishing docs * Show app name of dependency in `hex.info` * Warn about long package descriptions ### Bug fixes * Fix `HEX_UNSAFE_HTTPS` environment variable and `unsafe_https` config ## v0.11.5 (2016-04-07) ### Enhancements * Add more registry metrics to `hex.info` ### Bug fixes * Fix a bug where Hex was about a bit too enthusiastic when informing the user of new versions * Fix some missing future-proofing of lock ## v0.11.4 (2016-04-06) ### Enhancements * Use HTTPS to Hex.pm repository * Make lock backwards compatible by treating it as a list and only matching on the front ### Bug fixes * Correctly show update notification * Remove duplicate parents from backtrack messages * Fix invalid message in `hex.outdated` if locked version is a pre-release ## v0.11.3 (2016-03-14) ### Bug fixes * Do not crash if registry fails to fetch * Remove force update of registry if it is more than a week old ## v0.11.2 (2016-03-11) ### Enhancements * Verify registry signature against public key * Improve missing registry error message * Deprecate `HEX_CDN` in favor of `HEX_REPO` and `HEX_MIRROR`. See the `hex` task for more information * Deprecate `:cdn_url` config in favor of `:repo_url` and `mirror_url`. See the `hex.config` task for more information * Improve performance of parallel package fetching * Use fastly instead of S3 for the Hex.pm repository * Add `--delete` option to `hex.config` task ### Bug fixes * Show local time in hex.info * Correctly unlock all dependencies on `deps.update` * Always fetch registry if it's missing or known to be old ## v0.11.1 (2016-03-03) ### Bug fixes * Fix incorrect build version check * Fix parsing of requirements without spaces ## v0.11.0 (2016-03-03) ### Enhancements * Append the OTP version to the user_agent function * Improve output of http request timeout errors * Warn if `:manager` or `:compile` is set on dependencies when publishing * Add `--pre` flag to `hex.outdated` * Use erlang binary term encoding for API instead of elixir encoding * Pull package name from correct source when publish docs * Pass canonical url to ex_doc task * Change hexdocs links to use https * Add `hex.outdated APP` to list all requirements on given dependency * Do not allow pre-releases for dependencies unless the requirement uses a pre-release version * Optimize version cache memory usage ### Bug fixes * Fix incorrect build version check for dev versions of Elixir * Fix loop when backtracking in resolver * Fix timeout errors on slow systems ## v0.10.4 (2016-01-26) ### Enhancements * Make the experimental resolver the default ### Bug fixes * Ensure registry can be opened/closed multiple times * Ensure `hex.search` task handles empty results * Fix experimental resolvers only backtracking on parents that had requirements that failed * Fix merging of overlapping parent and package versions in backtrack messages ## v0.10.3 (2016-01-23) ### Bug fixes * Fix bug when umbrella child has dependency with `:only` ## v0.10.2 (2016-01-22) ### Enhancements * General optimizations in dependency resolver * Add experimental faster backtracker that does more aggressive backtracking, set environment variable `HEX_EXPERIMENTAL_RESOLVER=1` to use it * Merge backtrack messages that have similar parents * Merge multiple versions into version ranges when possible for more succinct backtrack messages ### Bug fixes * Reduce memory usage when resolver produces many backtrack messages ## v0.10.1 (2016-01-15) ### Bug fixes * Fix a crash when a dependency is missing its version requirement ## v0.10.0 (2016-01-14) ### Enhancements * Add support for authentication when using HTTP proxies * Add more build information to `hex.info` task to ease debugging * Greatly improve backtracking error messages * Prevent packages for being published without a description * Improve error printing when S3 return errors * Improve output from `hex.outdated` task * Warn if a package dependency is missing its requirement * Improve error message from `hex.docs` task when `ex_doc` dependency is missing * Remove useless output when fetching dependencies * Improve package output in `hex.info` task ### Bug fixes * Fix a rare bug that could cause the resolver to go into an infinite loop * UTF8 encode package metadata * Only list missing files if `:files` is set * Fix bug when umbrella child has dependency with `:only` ## v0.9.0 (2015-09-25) ### Enhancements * Pass build tool information to Mix (supported in Elixir 1.1.0) * Make Hex a proper OTP application * Update CA store * Warn if files are missing when building package * Improve error message when resolution fails because of a locked dependency * Add `hex.registry` task for loading and dumping registry * Add `HEX_OFFLINE` for running in offline mode which skips fetching registry and packages * Add `hex.build` task for building package without publishing * Reduce noise when users gets lots of resolution errors and generally improve their output * Add Server Name Indication support for HTTPS requests * Add `HEX_UNSAFE_HTTPS` for disabling certificate checking * Rename `:contributors` metadata to `:maintainers` to better reflect purpose of field ### Bug fixes * `HEX_API` no longer automatically adds `api/` to URL * Fix crash when user doesn't explicitly override Hex package when needed * Fix bug where metadata in package tarball was not properly UTF8 encoded * Fix error message when registry file is missing * Support `hex.outdated` task for umbrella projects * Do not raise on bad data in a users old lock ## v0.8.3 (2015-07-17) ### Security fixes * Fix a bug that would trust any certificate in the certificate chain signed by a trusted CA, this could allow the certificate, that is not a CA, to issue and sign new certificates for any host ## v0.8.2 (2015-07-13) ### Enhancements * Sort dependency resolver results ### Bug fixes * Fix build_tools metadata being sent incorrectly ## v0.8.1 (2015-07-12) ### Enhancements * Warn if registry file is missing when loading deps ### Bug fixes * Consider new optional requirements for already activated dependency * Add multiple build tools to metadata ## v0.8.0 (2015-05-19) ### Enhancements * Warn if using insecure SSL because of old OTP version * Use yellow test for warning text * Include build_tools in release metadata * Print more metadata when publishing ### Bug fixes * Fix an error when printing an http status codes * Always fetch new registry if it's older than 7 days ## v0.7.5 (2015-04-12) ### Enhancements * Add task `hex.user test` for testing user authentication. * Add task `hex.outdated` for listing outdated packages compared to the registry. * Update CA store as of April 3. * Inform user if authentication failed because they did not confirm email. * Improve error message for unsupported tarball version. ### Bug fixes * Fix a bug where overriding a Hex dependency with a non-Hex dependency was ignored when the overriding at least two levels deep in the dependency tree ## v0.7.4 (2015-03-16) ### Bug fixes * Include all conflicting requirements in backtrack message * Fix a bug where backtrack message failed on optional requests ## v0.7.3 (2015-03-04) ### Bug fixes * Fix an error when merging locked and optional dependencies ## v0.7.2 (2015-03-04) ### Enhancements * Print messages on backtracks if dependency resolution failed, this is intended to help users resolve conflicts ### Bug fixes * Fix a bug where a dependency converged in mix did not consider all its requirements * Fix a bug where dependencies in the lock was considered even if they weren't requested ## v0.7.1 (2015-02-15) ### Bug fixes * Fix updating the registry ## v0.7.0 (2015-02-15) ### Enhancements * Print proxy options on startup * Add `mix hex.user password reset` and remove `mix hex.user update` * Create version 3 tarballs with erlang term encoded metadata ### Bug fixes * Verify peer certificate against CA certificate public key in `partial_chain` * Fix a bug where overriding a Hex dependency with a non-Hex dependency was ignored when the overriding happened in a sub-dependency * Create hex directory before writing registry ## v0.6.2 (2015-01-02) ### Enhancements * Add PKIX hostname verification according to RFC6125 * Improve error messages from HTTP error codes * Improve HTTP performance * Add config options `api_url`, `cdn_url`, `http_proxy` and `https_proxy` * Support both doc/ and docs/ as documentation directory ## v0.6.1 (2014-12-11) ### Enhancements * Convert config file to erlang term file ## v0.6.0 (2014-10-12) ### Enhancements * Add support for packages with a different OTP application name than the package name * Add task `mix hex.docs` for uploading project documentation * Add email confirmation ### Bug fixes * Allow you to change your password with `mix hex.user update` * Correctly display dependencies in `mix hex.info PACKAGE VERSION` * Verify peer certificates when fetching tarball ## v0.5.0 (2014-09-19) ### Enhancements * Verify peer certificate for SSL (only available in OTP 17.3) * Reduce archive size with compiler option `debug_info: false` * Add support for config as an erlang term file * Warn if Hex was built against a different major.minor Elixir version ## v0.4.3 (2014-09-06) ## v0.4.2 (2014-08-31) ### Enhancements * Add task `hex.user whoami` that prints the locally authorized user * Add task `hex.user deauth` to deauthorize the local user * Rename environment variable `HEX_URL` to `HEX_API` to not confuse it with `HEX_CDN` ### Bug fixes * Print newline after progress bar ## v0.4.1 (2014-08-12) ### Enhancements * Add progress bar for uploading the tarball when publishing * Compare tarball checksum against checksum in registry * Bump tarball support to version 3 * Rename task for authenticating on the local machine from `hex.key new` to `hex.user auth` * Remove the ability to pass password as a CLI parameter ### Bug fixes * Support lower-case proxy environment variables * Remove any timeouts when fetching package tarballs hex-2.4.2/README.md000066400000000000000000000040371517471540100135730ustar00rootroot00000000000000# Hex [![CI](https://github.com/hexpm/hex/workflows/CI/badge.svg)](https://github.com/hexpm/hex/actions) Hex is a package manager for the Erlang ecosystem. This project currently provides tasks that integrate with Mix, [Elixir](https://github.com/elixir-lang/elixir)'s build tool. See [hex.pm](https://hex.pm) for installation instructions and other documentation. ## Contributing Install Hex locally for development with: `mix install`. ### Bundled CA certs Hex bundles a list of root CA certificates used for certificate validation in HTTPS. The certificates are fetched from [Mozilla's source tree](http://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt) with curl's [mk-ca-bundle.pl](https://github.com/bagder/curl/blob/master/lib/mk-ca-bundle.pl) script. The bundle created from the Perl script is stored in `lib/hex/http/ca-bundle.crt` and is included in source control, the file should be updated when new releases are made by Mozilla. When Hex is compiled the certificates are parsed and included with the compiled artifacts. The task `mix certdata` automates this process. ### hexpm Integration tests run against the API server [hexpm](https://github.com/hexpm/hexpm). It needs to be cloned into `../hexpm` or `HEXPM_PATH` needs to be set and point its location. hexpm also requires postgresql with username `postgres` and password `postgres`. Exclude integration tests with `mix test --exclude integration`. ## License Copyright 2015 Six Colors AB Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. hex-2.4.2/RELEASE.md000066400000000000000000000056051517471540100137200ustar00rootroot00000000000000# Release process This document simply outlines the release process: 1. Run `mix certdata` to update the CA bundle 2. Remove all `-dev` extension from versions (see below for all files) 3. Ensure CHANGELOG is updated and add current date 4. Update the local `scripts/release_hex.sh` with the Elixir and OTP versions Hex should be built against. 5. Commit changes above with title "Release vVERSION" and generate new tag 6. Set branch latest to the new tag: `git switch latest && git merge --ff-only vVERSION && git switch -` 7. Push main branch, latest branch and the new tag: `git push origin main latest --tags` 8. Create GitHub release 9. Run the `scripts/release_hex.sh` script and set the path to the private key for Elixir `ELIXIR_PEM=path/to/elixir.pem ./scripts/release_hex.sh VERSION` where `VERSION` is the Hex version being released without a `v` prefix 10. Purge the `installs` surrogate key on the "Hex Builds Compute prod" Fastly service: ``` fastly purge --service-id GbeDoh1ZO7MEM3zut4K2fR --key installs sleep 5 fastly purge --service-id GbeDoh1ZO7MEM3zut4K2fR --key installs ``` 11. Increment version and add `-dev` extension to versions (see below for all files) 12. Commit changes above with title "Bump to vVERSION-dev" 13. Push main ## All builds Hex needs to built for every Elixir supported vMAJOR.MINOR version. Currently that is every minor version released after 1.0.0. Always build on the latest patch version and make sure tests pass before building the archive. ## Places where version is mentioned * mix.exs `@version` attribute * CHANGELOG.md ## S3 paths * s3.hex.pm/installs/hex.ez (latest Hex built against oldest supported Elixir) * s3.hex.pm/installs/[ELIXIR]/hex.ez * s3.hex.pm/installs/[ELIXIR]/hex-[HEX].ez * s3.hex.pm/installs/hex-1.x.csv * s3.hex.pm/installs/hex-1.x.csv.signed ## S3 upload commands Only for oldest elixir and OTP version: ``` aws s3 cp hex.ez s3://s3.hex.pm/installs/hex.ez --acl public-read ``` ``` aws s3 cp hex.ez s3://s3.hex.pm/installs/[ELIXIR]/hex.ez --acl public-read && aws s3 cp hex-[HEX].ez s3://s3.hex.pm/installs/[ELIXIR]/hex-[HEX].ez --acl public-read && aws s3 cp hex-1.x.csv s3://s3.hex.pm/installs/hex-1.x.csv --acl public-read && aws s3 cp hex-1.x.csv.signed s3://s3.hex.pm/installs/hex-1.x.csv.signed --acl public-read ``` ## Hex release CSV ### CSV format ``` hex_version,sha512(hex-[HEX].ez),elixir_version ``` Example: ``` 0.9.0,4f6eae32124500117691740358df2a078d014f4d396a56a73b3e553e0b112b3f0ac9e0f7e0763feb85c889bac20571c6e028e5f4c252ac158cbb3c586efe992f,1.0.0 0.9.0,21fd3dbff18b2d2d51b41e147ac8bd13188a9840dae8f4ced6c150e227df64c3c6c5a472c3fd74e170f14fcf7cbeb7d85e12a520438bf0731c1ac55d2f6a4a8a,1.1.0 ``` ### Generate sha ``` shasum -a 512 hex-[HEX].ez ``` ### Sign CSV ``` openssl dgst -sha512 -sign elixir.pem hex-1.x.csv | openssl base64 > hex-1.x.csv.signed ``` hex-2.4.2/config/000077500000000000000000000000001517471540100135555ustar00rootroot00000000000000hex-2.4.2/config/config.exs000066400000000000000000000002271517471540100155440ustar00rootroot00000000000000import Config if config_env() == :test do config :logger, level: :warning config :logger, :console, format: "$date $time [$level] $message\n" end hex-2.4.2/lib/000077500000000000000000000000001517471540100130565ustar00rootroot00000000000000hex-2.4.2/lib/hex.ex000066400000000000000000000010521517471540100141760ustar00rootroot00000000000000defmodule Hex do @moduledoc false def start() do {:ok, _} = Application.ensure_all_started(:hex) end def stop() do case Application.stop(:hex) do :ok -> :ok {:error, {:not_started, :hex}} -> :ok end end # For compatibility during development def start(start_type, start_args) do Hex.Application.start(start_type, start_args) end def version(), do: unquote(Mix.Project.config()[:version]) def elixir_version(), do: unquote(System.version()) def otp_version(), do: unquote(Hex.Utils.otp_version()) end hex-2.4.2/lib/hex/000077500000000000000000000000001517471540100136425ustar00rootroot00000000000000hex-2.4.2/lib/hex/api/000077500000000000000000000000001517471540100144135ustar00rootroot00000000000000hex-2.4.2/lib/hex/api/auth.ex000066400000000000000000000004361517471540100157150ustar00rootroot00000000000000defmodule Hex.API.Auth do @moduledoc false alias Hex.API.Client def get(domain, resource, auth) do config = Client.config(auth) params = %{ domain: to_string(domain), resource: to_string(resource) } :mix_hex_api_auth.test(config, params) end end hex-2.4.2/lib/hex/api/client.ex000066400000000000000000000035311517471540100162310ustar00rootroot00000000000000defmodule Hex.API.Client do @moduledoc false def config(opts \\ []) do config = %{ :mix_hex_core.default_config() | http_adapter: {Hex.HTTP, %{}}, api_url: Hex.State.fetch!(:api_url), http_user_agent_fragment: user_agent_fragment() } config |> maybe_put_api_key(opts) |> maybe_put_otp(opts) |> maybe_put_organization(opts) |> maybe_put_repository(opts) end defp maybe_put_api_key(config, opts) do cond do opts[:key] -> # Add Bearer prefix only for OAuth tokens token = if opts[:oauth], do: "Bearer #{opts[:key]}", else: opts[:key] Map.put(config, :api_key, token) opts[:user] && opts[:pass] -> # For basic auth, add it as an HTTP header base64 = Base.encode64("#{opts[:user]}:#{opts[:pass]}") headers = Map.get(config, :http_headers, %{}) headers = Map.put(headers, "authorization", "Basic #{base64}") Map.put(config, :http_headers, headers) true -> config end end defp maybe_put_otp(config, opts) do if otp = opts[:otp] do Map.put(config, :api_otp, otp) else config end end defp maybe_put_organization(config, opts) do if org = opts[:organization] || opts[:api_organization] do Map.put(config, :api_organization, to_string(org)) else config end end defp maybe_put_repository(config, opts) do if repo = opts[:repository] do Map.put(config, :api_repository, repo) else config end end def build_config(repo, auth) do opts = if repo, do: [repository: to_string(repo)], else: [] opts = if auth, do: Keyword.merge(opts, auth), else: opts config(opts) end def user_agent_fragment do ci = if Hex.State.fetch!(:ci), do: " (CI)", else: "" "hex/#{Hex.version()} (elixir/#{System.version()})#{ci}" end end hex-2.4.2/lib/hex/api/key.ex000066400000000000000000000044761517471540100155540ustar00rootroot00000000000000defmodule Hex.API.Key do @moduledoc false alias Hex.API.Client def new(name, permissions, auth) do Mix.Tasks.Hex.with_otp_retry(auth, fn auth_with_otp -> config = Client.config(auth_with_otp) # Convert permissions to binary map format expected by hex_core permissions = Enum.map(permissions, fn perm -> Map.new(perm, fn {k, v} -> {to_string(k), to_string(v)} end) end) :mix_hex_api_key.add(config, to_string(name), permissions) end) end def get(auth) do config = Client.config(auth) :mix_hex_api_key.list(config) end def delete(name, auth) do Mix.Tasks.Hex.with_otp_retry(auth, fn auth_with_otp -> config = Client.config(auth_with_otp) :mix_hex_api_key.delete(config, to_string(name)) end) end def delete_all(auth) do Mix.Tasks.Hex.with_otp_retry(auth, fn auth_with_otp -> config = Client.config(auth_with_otp) :mix_hex_api_key.delete_all(config) end) end defmodule Organization do @moduledoc false alias Hex.API.Client def new(organization, name, permissions, auth) do Mix.Tasks.Hex.with_otp_retry(auth, fn auth_with_otp -> config = Client.config(Keyword.put(auth_with_otp, :api_organization, to_string(organization))) # Convert permissions to binary map format expected by hex_core permissions = Enum.map(permissions, fn perm -> Map.new(perm, fn {k, v} -> {to_string(k), to_string(v)} end) end) :mix_hex_api_key.add(config, to_string(name), permissions) end) end def get(organization, auth) do config = Client.config(Keyword.put(auth, :api_organization, to_string(organization))) :mix_hex_api_key.list(config) end def delete(organization, name, auth) do Mix.Tasks.Hex.with_otp_retry(auth, fn auth_with_otp -> config = Client.config(Keyword.put(auth_with_otp, :api_organization, to_string(organization))) :mix_hex_api_key.delete(config, to_string(name)) end) end def delete_all(organization, auth) do Mix.Tasks.Hex.with_otp_retry(auth, fn auth_with_otp -> config = Client.config(Keyword.put(auth_with_otp, :api_organization, to_string(organization))) :mix_hex_api_key.delete_all(config) end) end end end hex-2.4.2/lib/hex/api/oauth.ex000066400000000000000000000061171517471540100160760ustar00rootroot00000000000000defmodule Hex.API.OAuth do @moduledoc false alias Hex.API.Client @client_id "78ea6566-89fd-481e-a1d6-7d9d78eacca8" @doc """ Initiates the OAuth device authorization flow. Returns device code, user code, and verification URIs for user authentication. Optionally accepts a name parameter to identify the token. ## Examples iex> Hex.API.OAuth.device_authorization("api") {:ok, {200, _headers, %{ "device_code" => "...", "user_code" => "ABCD-1234", "verification_uri" => "https://hex.pm/oauth/device", "verification_uri_complete" => "https://hex.pm/oauth/device?user_code=ABCD-1234", "expires_in" => 600, "interval" => 5 }}} """ def device_authorization(scopes, name \\ nil) do config = Client.config() opts = if name, do: [name: name], else: [] :mix_hex_api_oauth.device_authorization(config, @client_id, scopes, opts) end @doc """ Polls the OAuth token endpoint for device authorization completion. ## Examples iex> Hex.API.OAuth.poll_device_token(device_code) {:ok, {200, _headers, %{ "access_token" => "...", "refresh_token" => "...", "token_type" => "Bearer", "expires_in" => 3600 }}} """ def poll_device_token(device_code) do config = Client.config() :mix_hex_api_oauth.poll_device_token(config, @client_id, device_code) end @doc """ Refreshes an access token using a refresh token. ## Examples iex> Hex.API.OAuth.refresh_token(refresh_token) {:ok, {200, _headers, %{ "access_token" => "...", "refresh_token" => "...", "token_type" => "Bearer", "expires_in" => 3600 }}} """ def refresh_token(refresh_token) do config = Client.config() :mix_hex_api_oauth.refresh_token(config, @client_id, refresh_token) end @doc """ Exchanges an API key for a short-lived OAuth access token using the client credentials grant. Optionally accepts a custom API URL for the OAuth exchange endpoint. ## Examples iex> Hex.API.OAuth.exchange_api_key(api_key, "api") {:ok, {200, _headers, %{ "access_token" => "...", "token_type" => "bearer", "expires_in" => 1800, "scope" => "api" }}} iex> Hex.API.OAuth.exchange_api_key(api_key, "api", nil, "https://custom.hex.pm") {:ok, {200, _headers, %{...}}} """ def exchange_api_key(api_key, scopes, name \\ nil, api_url \\ nil) do config = Client.config() config = if api_url do Map.put(config, :api_url, api_url) else config end scope_string = if is_list(scopes), do: Enum.join(scopes, " "), else: scopes opts = if name, do: [name: name], else: [] :mix_hex_api_oauth.client_credentials_token(config, @client_id, api_key, scope_string, opts) end @doc """ Revokes an OAuth token (access or refresh token). ## Examples iex> Hex.API.OAuth.revoke_token(token) {:ok, {200, _headers, nil}} """ def revoke_token(token) do config = Client.config() :mix_hex_api_oauth.revoke_token(config, @client_id, token) end end hex-2.4.2/lib/hex/api/package.ex000066400000000000000000000026201517471540100163440ustar00rootroot00000000000000defmodule Hex.API.Package do @moduledoc false alias Hex.API.Client def get(repo, name, auth \\ []) when name != "" do config = Client.build_config(repo, auth) :mix_hex_api_package.get(config, to_string(name)) end def search(repo, search, auth \\ []) do config = Client.build_config(repo, auth) search_params = [{:sort, "downloads"}] :mix_hex_api_package.search(config, to_string(search), search_params) end defmodule Owner do @moduledoc false alias Hex.API.Client def add(repo, package, owner, level, transfer, auth) when package != "" do Mix.Tasks.Hex.with_otp_retry(auth, fn auth_with_otp -> config = Client.build_config(repo, auth_with_otp) :mix_hex_api_package_owner.add( config, to_string(package), to_string(owner), to_string(level), transfer ) end) end def delete(repo, package, owner, auth) when package != "" do Mix.Tasks.Hex.with_otp_retry(auth, fn auth_with_otp -> config = Client.build_config(repo, auth_with_otp) :mix_hex_api_package_owner.delete( config, to_string(package), to_string(owner) ) end) end def get(repo, package, auth) when package != "" do config = Client.build_config(repo, auth) :mix_hex_api_package_owner.list(config, to_string(package)) end end end hex-2.4.2/lib/hex/api/release.ex000066400000000000000000000032371517471540100163760ustar00rootroot00000000000000defmodule Hex.API.Release do @moduledoc false alias Hex.API.Client def get(repo, name, version, auth \\ []) do config = Client.build_config(repo, auth) :mix_hex_api_release.get(config, to_string(name), to_string(version)) end def publish(repo, tar, auth, progress \\ fn _ -> nil end, replace \\ false) def publish(repo, tar, auth, progress, replace?) do Mix.Tasks.Hex.with_otp_retry(auth, fn auth_with_otp -> config = Client.build_config(repo, auth_with_otp) # Pass progress callback through adapter config adapter_config = %{progress_callback: progress} config = Map.put(config, :http_adapter, {Hex.HTTP, adapter_config}) params = [{:replace, replace?}] :mix_hex_api_release.publish(config, tar, params) end) end def delete(repo, name, version, auth) do Mix.Tasks.Hex.with_otp_retry(auth, fn auth_with_otp -> config = Client.build_config(repo, auth_with_otp) :mix_hex_api_release.delete(config, to_string(name), to_string(version)) end) end def retire(repo, name, version, body, auth) do Mix.Tasks.Hex.with_otp_retry(auth, fn auth_with_otp -> config = Client.build_config(repo, auth_with_otp) # Convert body to binary map for hex_core params = Map.new(body, fn {k, v} -> {to_string(k), to_string(v)} end) :mix_hex_api_release.retire(config, to_string(name), to_string(version), params) end) end def unretire(repo, name, version, auth) do Mix.Tasks.Hex.with_otp_retry(auth, fn auth_with_otp -> config = Client.build_config(repo, auth_with_otp) :mix_hex_api_release.unretire(config, to_string(name), to_string(version)) end) end end hex-2.4.2/lib/hex/api/release_docs.ex000066400000000000000000000027001517471540100174000ustar00rootroot00000000000000defmodule Hex.API.ReleaseDocs do @moduledoc false alias Hex.API.Client def get(repo, name, version) do config = Client.build_config(repo, []) path = :mix_hex_api.build_repository_path(config, [ "packages", to_string(name), "releases", to_string(version), "docs" ]) :mix_hex_api.get(config, path) end def publish(repo, name, version, tar, auth, progress \\ fn _ -> nil end) do Mix.Tasks.Hex.with_otp_retry(auth, fn auth_with_otp -> config = Client.build_config(repo, auth_with_otp) # Pass progress callback through adapter config adapter_config = %{progress_callback: progress} config = Map.put(config, :http_adapter, {Hex.HTTP, adapter_config}) path = :mix_hex_api.build_repository_path(config, [ "packages", to_string(name), "releases", to_string(version), "docs" ]) body = {"application/octet-stream", tar} :mix_hex_api.post(config, path, body) end) end def delete(repo, name, version, auth) do Mix.Tasks.Hex.with_otp_retry(auth, fn auth_with_otp -> config = Client.build_config(repo, auth_with_otp) path = :mix_hex_api.build_repository_path(config, [ "packages", to_string(name), "releases", to_string(version), "docs" ]) :mix_hex_api.delete(config, path) end) end end hex-2.4.2/lib/hex/api/short_url.ex000066400000000000000000000005011517471540100167660ustar00rootroot00000000000000defmodule Hex.API.ShortURL do @moduledoc false alias Hex.API.Client def create(url) do config = Client.config() case :mix_hex_api_short_url.create(config, to_string(url)) do {:ok, {201, _headers, %{"url" => short_url}}} -> {:ok, short_url} _error -> :error end end end hex-2.4.2/lib/hex/api/user.ex000066400000000000000000000010021517471540100157200ustar00rootroot00000000000000defmodule Hex.API.User do @moduledoc false alias Hex.API.Client def me(auth) do config = Client.config(auth) :mix_hex_api_user.me(config) end def get(username) do config = Client.config() :mix_hex_api_user.get(config, to_string(username)) end # NOTE: Only used for testing def new(username, email, password) do config = Client.config() :mix_hex_api_user.create( config, to_string(username), to_string(password), to_string(email) ) end end hex-2.4.2/lib/hex/application.ex000066400000000000000000000024661517471540100165130ustar00rootroot00000000000000defmodule Hex.Application do @moduledoc false use Application def start(_, _) do dev_setup() Mix.SCM.append(Hex.SCM) Mix.RemoteConverger.register(Hex.RemoteConverger) start_httpc() opts = [strategy: :one_for_one, name: Hex.Supervisor] Supervisor.start_link(children(), opts) end def stop(_state) do Mix.RemoteConverger.register(nil) if function_exported?(Mix.SCM, :delete, 1) do apply(Mix.SCM, :delete, [Hex.SCM]) end :ok end if Mix.env() in [:dev, :test] do defp dev_setup do :erlang.system_flag(:backtrace_depth, 20) end else defp dev_setup, do: :ok end defp start_httpc() do :inets.start(:httpc, profile: :hex) opts = [ max_sessions: 8, max_keep_alive_length: 4, keep_alive_timeout: 120_000 ] :httpc.set_options(opts, :hex) end if Mix.env() == :test do defp children do [ Hex.Netrc.Cache, Hex.OAuth, Hex.Repo, Hex.State, Hex.Server, {Hex.Parallel, [:hex_fetcher]} ] end else defp children do [ Hex.Netrc.Cache, Hex.OAuth, Hex.Repo, Hex.State, Hex.Server, {Hex.Parallel, [:hex_fetcher]}, Hex.Registry.Server, Hex.UpdateChecker ] end end end hex-2.4.2/lib/hex/config.ex000066400000000000000000000047661517471540100154620ustar00rootroot00000000000000defmodule Hex.Config do @moduledoc false def read() do case File.read(config_path()) do {:ok, binary} -> case decode_term(binary) do {:ok, term} -> term {:error, _} -> config = decode_elixir(binary) write(config) config end {:error, _} -> [] end end def update(config) do read() |> Keyword.merge(config) |> write() end def remove(keys) do read() |> Keyword.drop(keys) |> write() end def write(config) do config = Enum.reject(config, fn {_key, value} -> is_nil(value) end) string = encode_term(config) path = config_path() File.mkdir_p!(Path.dirname(path)) File.write!(path, string) config end defp config_path() do Path.join(hex_home(), "hex.config") end defp hex_home() do state_pid = Process.whereis(Hex.State) if state_pid && state_pid != self() do Hex.State.fetch!(:config_home) else {_, config_home} = find_config_home(:user_config) config_home end end def find_config_home(setting) do cond do dir = System.get_env("HEX_HOME") -> {{:env, "HEX_HOME"}, dir} System.get_env("MIX_XDG") in ["1", "true"] -> {{:env, "MIX_XDG"}, :filename.basedir(setting, "hex", %{os: :linux})} true -> {:ok, Path.expand("~/.hex")} end end defp encode_term(list) do list |> Enum.map(&[:io_lib.print(&1) | ".\n"]) |> IO.iodata_to_binary() end defp decode_term(string) do {:ok, pid} = StringIO.open(string) try do consult(pid, [], string) after StringIO.close(pid) end end defp consult(pid, acc, string) when is_pid(pid) do case :io.read(pid, ~c"") do {:ok, term} -> consult(pid, [term | acc], string) {:error, reason} -> {:error, reason} :eof -> {:ok, Enum.reverse(acc)} end end defp decode_elixir(string) do {term, _binding} = Code.eval_string(string) term end def read_repos(config) do hexpm = Hex.Repo.default_hexpm_repo() (config[:"$repos"] || %{}) |> Hex.Repo.merge_hexpm(hexpm) |> Hex.Repo.update_organizations() end def update_repos(repos) do config_repos = repos |> Hex.Repo.clean_organizations() |> Hex.Repo.clean_hexpm() state_repos = repos |> Hex.Repo.merge_hexpm() |> Hex.Repo.update_organizations() Hex.Config.update([{:"$repos", config_repos}]) Hex.State.put(:repos, state_repos) end end hex-2.4.2/lib/hex/dev.ex000066400000000000000000000042771517471540100147700ustar00rootroot00000000000000if Mix.env() == :dev do defmodule Hex.Dev do @moduledoc false @ets_name __MODULE__ @registry_filename "cache.ets" @repo "hexpm" @ets_version 3 def extract_registry(packages, new_path) do {:ok, original_ets} = :ets.file2tab(String.to_charlist(ets_path())) new_ets = :ets.new(@ets_name, []) try do :ets.insert(new_ets, {:version, @ets_version}) copy(original_ets, new_ets, :last_update) Enum.each(packages, fn package -> copy_package(original_ets, new_ets, package) end) :ok = :ets.tab2file(new_ets, String.to_charlist(new_path)) after :ets.delete(original_ets) :ets.delete(new_ets) end end defp copy_package(original_ets, new_ets, package) do unless :ets.member(new_ets, {:versions, @repo, package}) do copy(original_ets, new_ets, {:versions, @repo, package}) copy(original_ets, new_ets, {:registry_etag, @repo, package}) copy(original_ets, new_ets, {:timestamp, @repo, package}) case :ets.lookup(original_ets, {:versions, @repo, package}) do [{_, versions}] -> IO.puts("COPYING #{package}") Enum.each(versions, fn version -> copy(original_ets, new_ets, {:deps, @repo, package, version}) copy(original_ets, new_ets, {:inner_checksum, @repo, package, version}) copy(original_ets, new_ets, {:outer_checksum, @repo, package, version}) copy(original_ets, new_ets, {:retired, @repo, package, version}) copy(original_ets, new_ets, {:timestamp, @repo, package, version}) [{_, dep_tuples}] = :ets.lookup(original_ets, {:deps, @repo, package, version}) Enum.each(dep_tuples, fn {@repo, dependency, _app, _requirement, _optional} -> copy_package(original_ets, new_ets, dependency) end) end) [] -> :ok end end end defp copy(original_ets, new_ets, key) do tuples = :ets.lookup(original_ets, key) :ets.insert(new_ets, tuples) end defp ets_path() do Path.join(Hex.State.fetch!(:cache_home), @registry_filename) end end end hex-2.4.2/lib/hex/http.ex000066400000000000000000000335201517471540100151620ustar00rootroot00000000000000defmodule Hex.HTTP do @moduledoc false @behaviour :mix_hex_http @request_timeout 15_000 @request_redirects 3 @request_retries 2 @chunk_size 10_000 @spec config() :: :mix_hex_core.config() def config do %{ :mix_hex_core.default_config() | http_adapter: {__MODULE__, %{}} } end # Keep old signatures for backward compatibility def request(method, url, headers, body) do request(method, url, headers, body, %{}) end def request(method, url, headers, body, opts) when is_list(opts) do adapter_config = Keyword.get(opts, :adapter_config, %{}) request(method, url, headers, body, adapter_config) end @impl :mix_hex_http def request(method, url, headers, body, adapter_config) when is_map(adapter_config) do {method, url, request, http_opts, timeout, profile} = prepare_request(method, url, headers, body, adapter_config) Hex.Shell.debug("Hex.HTTP.request(#{inspect(method)}, #{inspect(url)})") result = retry(method, request, http_opts, @request_retries, profile, fn request, http_opts -> redirect(request, http_opts, @request_redirects, fn request, http_opts -> timeout(request, http_opts, timeout, fn request, http_opts -> :httpc.request(method, request, http_opts, [body_format: :binary], profile) |> handle_response(method, url) end) end) end) # Convert to hex_core expected format case result do {:ok, status, headers, body} -> # Convert headers to map with binary keys/values for hex_core headers = Map.new(headers, fn {k, v} -> {to_string(k), to_string(v)} end) {:ok, {status, headers, body}} {:error, reason} -> {:error, reason} end end @impl :mix_hex_http def request_to_file(method, url, headers, body, filename, adapter_config) when is_map(adapter_config) do {method, url, request, http_opts, timeout, profile} = prepare_request(method, url, headers, body, adapter_config) Hex.Shell.debug("Hex.HTTP.request_to_file(#{inspect(method)}, #{inspect(url)})") filename_charlist = String.to_charlist(filename) result = retry(method, request, http_opts, @request_retries, profile, fn request, http_opts -> redirect(request, http_opts, @request_redirects, fn request, http_opts -> timeout(request, http_opts, timeout, fn request, http_opts -> :httpc.request(method, request, http_opts, [{:stream, filename_charlist}], profile) |> handle_response_to_file(method, url) end) end) end) # Convert to hex_core expected format case result do {:ok, status, headers} -> # Convert headers to map with binary keys/values for hex_core headers = Map.new(headers, fn {k, v} -> {to_string(k), to_string(v)} end) {:ok, {status, headers}} {:error, reason} -> {:error, reason} end end defp prepare_request(method, url, headers, body, adapter_config) do # Convert method to atom if it's not already method = if is_binary(method), do: String.to_atom(method), else: method # Convert URL to string if it's binary url = if is_binary(url), do: url, else: to_string(url) # Convert headers from map to our format headers = if is_map(headers), do: headers, else: Map.new(headers) headers = add_basic_auth_via_netrc(headers, url) timeout = adapter_config[:timeout] || Hex.State.fetch!(:http_timeout, fn val -> if is_integer(val), do: val * 1000 end) || @request_timeout # Handle progress callback for uploads progress_callback = Map.get(adapter_config, :progress_callback) {body, extra_headers} = wrap_body_with_progress(body, progress_callback) headers = Map.merge(headers, extra_headers) # Work around httpc bug: disable connection reuse when using Expect: 100-continue # httpc doesn't properly handle connection state when receiving final status (401) # instead of 100 Continue response headers = if headers["expect"] == "100-continue" do Map.put(headers, "connection", "close") else headers end http_opts = build_http_opts(url, timeout) request = build_request(url, headers, body) profile = Hex.State.fetch!(:httpc_profile) {method, url, request, http_opts, timeout, profile} end defp handle_response_to_file({:ok, :saved_to_file}, method, url) do Hex.Shell.debug("Hex.HTTP.request_to_file(#{inspect(method)}, #{inspect(url)}) => 200") {:ok, 200, %{}} end defp handle_response_to_file({:ok, {{_version, code, _reason}, headers, _body}}, method, url) do Hex.Shell.debug("Hex.HTTP.request_to_file(#{inspect(method)}, #{inspect(url)}) => #{code}") headers = Map.new(headers, &decode_header/1) handle_hex_message(headers["x-hex-message"]) {:ok, code, headers} end defp handle_response_to_file({:error, term}, method, url) do Hex.Shell.debug( "Hex.HTTP.request_to_file(#{inspect(method)}, #{inspect(url)}) => #{inspect(term, limit: :infinity, pretty: true)}" ) {:error, term} end defp fallback(:inet), do: :inet6 defp fallback(:inet6), do: :inet defp build_http_opts(url, timeout) do [ relaxed: true, timeout: timeout, ssl: Hex.HTTP.SSL.ssl_opts(url), autoredirect: false ] ++ proxy_config(url) end defp build_request(url, headers, body) do url = String.to_charlist(url) headers = Enum.map(headers, &encode_header/1) case body do {content_type, body} -> # content_type might already be a charlist from hex_core content_type = if is_binary(content_type) do String.to_charlist(content_type) else content_type end {url, headers, content_type, body} nil -> {url, headers} :undefined -> {url, headers} end end defp encode_header({name, value}) do {String.to_charlist(name), String.to_charlist(value)} end defp retry(:get, request, http_opts, times, profile, fun) do result = case fun.(request, http_opts) do {:http_error, _, _} = error -> {:retry, error} {:error, :socket_closed_remotely} = error -> {:retry, error} {:error, {:failed_connect, [{:to_address, to_addr}, {inet, inet_l, reason}]}} when inet in [:inet, :inet6] and reason in [:ehostunreach, :enetunreach, :eprotonosupport, :nxdomain] -> :httpc.set_options([ipfamily: fallback(inet)], profile) {:retry, {:error, {:failed_connect, [{:to_address, to_addr}, {inet, inet_l, reason}]}}} other -> {:noretry, other} end case result do {:retry, _} when times > 0 -> retry(:get, request, http_opts, times - 1, profile, fun) {_other, result} -> result end end defp retry(_method, request, http_opts, _times, _profile, fun), do: fun.(request, http_opts) defp redirect(request, http_opts, times, fun) do case fun.(request, http_opts) do {:ok, code, headers, body} -> case handle_redirect(code, headers) do {:ok, location} when times > 0 -> do_redirect(request, http_opts, location, times, fun) {:ok, _location} -> Mix.raise("Too many redirects") :error -> {:ok, code, headers, body} end {:ok, code, headers} -> case handle_redirect(code, headers) do {:ok, location} when times > 0 -> do_redirect(request, http_opts, location, times, fun) {:ok, _location} -> Mix.raise("Too many redirects") :error -> {:ok, code, headers} end {:error, reason} -> {:error, reason} end end defp do_redirect(request, http_opts, location, times, fun) do ssl_opts = Hex.HTTP.SSL.ssl_opts(to_string(location)) http_opts = Keyword.put(http_opts, :ssl, ssl_opts) request |> update_request(location) |> redirect(http_opts, times - 1, fun) end defp handle_redirect(code, headers) when code in [301, 302, 303, 307, 308] do if location = headers["location"] do {:ok, location} else :error end end defp handle_redirect(_, _) do :error end defp update_request({_url, headers, content_type, body}, new_url) do {new_url, headers, content_type, body} end defp update_request({_url, headers}, new_url) do {new_url, headers} end defp timeout(request, http_opts, timeout, fun) do Task.async(fn -> fun.(request, http_opts) end) |> task_await(:timeout, timeout) end defp task_await(%Task{ref: ref, pid: pid} = task, reason, timeout) do receive do {^ref, result} -> Process.demonitor(ref, [:flush]) result {:DOWN, ^ref, _, _, _reason} -> {:error, :timeout} after timeout -> Process.unlink(pid) Process.exit(pid, reason) task_await(task, :kill, timeout) end end defp handle_response({:ok, {{_version, code, _reason}, headers, body}}, method, url) do Hex.Shell.debug("Hex.HTTP.request(#{inspect(method)}, #{inspect(url)}) => #{code}") headers = Map.new(headers, &decode_header/1) handle_hex_message(headers["x-hex-message"]) {:ok, code, headers, unzip(body, headers)} end defp handle_response({:error, term}, method, url) do Hex.Shell.debug( "Hex.HTTP.request(#{inspect(method)}, #{inspect(url)}) => #{inspect(term, limit: :infinity, pretty: true)}" ) {:error, term} end defp decode_header({name, value}) do {List.to_string(name), List.to_string(value)} end defp unzip(body, headers) do content_encoding = headers["content-encoding"] || "" if String.contains?(content_encoding, "gzip") do :zlib.gunzip(body) else body end end def proxy_config(url) do {http_proxy, https_proxy} = proxy_setup() proxy_auth(URI.parse(url), http_proxy, https_proxy) end defp proxy_setup do no_proxy = no_proxy() http_proxy = (proxy = Hex.State.fetch!(:http_proxy)) && proxy(:http, proxy, no_proxy) https_proxy = (proxy = Hex.State.fetch!(:https_proxy)) && proxy(:https, proxy, no_proxy) {http_proxy, https_proxy} end defp proxy(scheme, proxy, no_proxy) do uri = URI.parse(proxy) if uri.host && uri.port do host = String.to_charlist(uri.host) :httpc.set_options([{proxy_scheme(scheme), {{host, uri.port}, no_proxy}}], :hex) end uri end defp proxy_scheme(scheme) do case scheme do :http -> :proxy :https -> :https_proxy end end defp proxy_auth(%URI{scheme: "http"}, http_proxy, _https_proxy) do proxy_auth(http_proxy) end defp proxy_auth(%URI{scheme: "https"}, _http_proxy, https_proxy) do proxy_auth(https_proxy) end defp proxy_auth(nil) do [] end defp proxy_auth(%URI{userinfo: nil}) do [] end defp proxy_auth(%URI{userinfo: auth}) do destructure [user, pass], String.split(auth, ":", parts: 2) user = String.to_charlist(user) pass = String.to_charlist(pass || "") [proxy_auth: {user, pass}] end defp no_proxy() do (Hex.State.fetch!(:no_proxy) || "") |> String.split(",", trim: true) |> Enum.map(&String.trim/1) |> Enum.map(&normalize_no_proxy_domain_desc/1) |> Enum.map(&String.to_charlist/1) end defp normalize_no_proxy_domain_desc(<<".", rest::binary>>) do "*." <> rest end defp normalize_no_proxy_domain_desc(host) do host end def handle_hex_message(nil) do :ok end def handle_hex_message(header) do {message, level} = :binary.list_to_bin(header) |> parse_hex_message case level do "warn" -> Hex.Shell.info("API warning: " <> message) "fatal" -> Hex.Shell.error("API error: " <> message) _ -> :ok end end @space [?\s, ?\t] defp parse_hex_message(message) do {message, rest} = skip_ws(message) |> quoted level = skip_ws(rest) |> opt_level {message, level} end defp skip_ws(<>) when char in @space, do: skip_ws(rest) defp skip_ws(rest), do: rest defp skip_trail_ws(<>, str, ws) when char in @space do skip_trail_ws(rest, str, <>) end defp skip_trail_ws(<>, str, ws) do skip_trail_ws(rest, <>, "") end defp skip_trail_ws("", str, _ws) do str end defp quoted("\"" <> rest), do: do_quoted(rest, "") defp do_quoted("\"" <> rest, acc), do: {acc, rest} defp do_quoted(<>, acc), do: do_quoted(rest, <>) defp opt_level(";" <> rest), do: do_level(rest) defp opt_level(_), do: nil defp do_level(rest) do "level" <> rest = skip_ws(rest) "=" <> rest = skip_ws(rest) rest |> skip_ws() |> skip_trail_ws("", "") end defp add_basic_auth_via_netrc(%{"authorization" => _} = headers, _url), do: headers defp add_basic_auth_via_netrc(%{} = headers, url) do url = URI.parse(url) case Hex.Netrc.lookup(url.host) do {:ok, %{username: username, password: password}} -> base64 = :base64.encode_to_string("#{username}:#{password}") Map.put(headers, "authorization", "Basic #{base64}") _ -> headers end end defp wrap_body_with_progress(body, progress_callback) do case body do {content_type, binary_body} when is_binary(binary_body) and is_function(progress_callback, 1) -> total_size = byte_size(binary_body) body_fn = fn size when size < total_size -> new_size = min(size + @chunk_size, total_size) chunk = new_size - size progress_callback.(new_size) {:ok, :binary.part(binary_body, size, chunk), new_size} _size -> :eof end body_result = {content_type, {body_fn, 0}} headers = %{"content-length" => Integer.to_string(total_size)} {body_result, headers} _other -> {body, %{}} end end end hex-2.4.2/lib/hex/http/000077500000000000000000000000001517471540100146215ustar00rootroot00000000000000hex-2.4.2/lib/hex/http/ca-bundle.crt000066400000000000000000006715561517471540100172110ustar00rootroot00000000000000## ## Bundle of CA Root Certificates ## ## Certificate data from Mozilla as of: Thu Apr 30 17:57:20 2026 GMT ## ## Find updated versions here: https://curl.se/docs/caextract.html ## ## This is a bundle of X.509 certificates of public Certificate Authorities ## (CA). These were automatically extracted from Mozilla's root certificates ## file (certdata.txt). This file can be found in the mozilla source tree: ## https://raw.githubusercontent.com/mozilla-firefox/firefox/refs/heads/release/security/nss/lib/ckfw/builtins/certdata.txt ## ## It contains the certificates in PEM format and therefore ## can be directly used with curl / libcurl / php_curl, or with ## an Apache+mod_ssl webserver for SSL client authentication. ## Just configure this file as the SSLCACertificateFile. ## ## Conversion done with mk-ca-bundle.pl version 1.33. ## SHA256: 3b98d4e3ff57a326d9587c33633039c8c3a9cf0b55f7ca581d7598ff329eb1f3 ## Entrust Root Certification Authority ==================================== -----BEGIN CERTIFICATE----- MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 -----END CERTIFICATE----- QuoVadis Root CA 2 ================== -----BEGIN CERTIFICATE----- MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6 XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt 66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3 UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK +JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1 WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II 4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8 VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u -----END CERTIFICATE----- QuoVadis Root CA 3 ================== -----BEGIN CERTIFICATE----- MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8 nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4 ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2 Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp 8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto= -----END CERTIFICATE----- DigiCert Assured ID Root CA =========================== -----BEGIN CERTIFICATE----- MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO 9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW /lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF 66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i 8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe +o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== -----END CERTIFICATE----- DigiCert Global Root CA ======================= -----BEGIN CERTIFICATE----- MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H 4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y 7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm 8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= -----END CERTIFICATE----- DigiCert High Assurance EV Root CA ================================== -----BEGIN CERTIFICATE----- MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K -----END CERTIFICATE----- SwissSign Gold CA - G2 ====================== -----BEGIN CERTIFICATE----- MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR 7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64 OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm 5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr 44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ -----END CERTIFICATE----- SecureTrust CA ============== -----BEGIN CERTIFICATE----- MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b 01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR 3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= -----END CERTIFICATE----- Secure Global CA ================ -----BEGIN CERTIFICATE----- MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g 8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi 0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW -----END CERTIFICATE----- COMODO Certification Authority ============================== -----BEGIN CERTIFICATE----- MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH +7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV 4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA 1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN +8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== -----END CERTIFICATE----- COMODO ECC Certification Authority ================================== -----BEGIN CERTIFICATE----- MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X 4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= -----END CERTIFICATE----- Certigna ======== -----BEGIN CERTIFICATE----- MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY 1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== -----END CERTIFICATE----- ePKI Root Certification Authority ================================= -----BEGIN CERTIFICATE----- MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX 12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= -----END CERTIFICATE----- certSIGN ROOT CA ================ -----BEGIN CERTIFICATE----- MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD 0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD -----END CERTIFICATE----- NetLock Arany (Class Gold) Főtanúsítvány ======================================== -----BEGIN CERTIFICATE----- MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu 0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw /HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= -----END CERTIFICATE----- Microsec e-Szigno Root CA 2009 ============================== -----BEGIN CERTIFICATE----- MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG 0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm 1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi LXpUq3DDfSJlgnCW -----END CERTIFICATE----- GlobalSign Root CA - R3 ======================= -----BEGIN CERTIFICATE----- MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ 0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r kpeDMdmztcpHWD9f -----END CERTIFICATE----- Izenpe.com ========== -----BEGIN CERTIFICATE----- MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ 03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU +zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK 0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+ 0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+ SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5 aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5 nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== -----END CERTIFICATE----- Go Daddy Root Certificate Authority - G2 ======================================== -----BEGIN CERTIFICATE----- MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq 9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD +qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r 5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 -----END CERTIFICATE----- Starfield Root Certificate Authority - G2 ========================================= -----BEGIN CERTIFICATE----- MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx 4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 -----END CERTIFICATE----- Starfield Services Root Certificate Authority - G2 ================================================== -----BEGIN CERTIFICATE----- MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 -----END CERTIFICATE----- AffirmTrust Commercial ====================== -----BEGIN CERTIFICATE----- MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv 0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= -----END CERTIFICATE----- AffirmTrust Networking ====================== -----BEGIN CERTIFICATE----- MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 /PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 /ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= -----END CERTIFICATE----- AffirmTrust Premium =================== -----BEGIN CERTIFICATE----- MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV 5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs +7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 /bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo +Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB /wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC 6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK +4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== -----END CERTIFICATE----- AffirmTrust Premium ECC ======================= -----BEGIN CERTIFICATE----- MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X 57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM eQ== -----END CERTIFICATE----- Certum Trusted Network CA ========================= -----BEGIN CERTIFICATE----- MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI 03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= -----END CERTIFICATE----- TWCA Root Certification Authority ================================= -----BEGIN CERTIFICATE----- MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP 4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG 9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== -----END CERTIFICATE----- Security Communication RootCA2 ============================== -----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ +T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R 3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk 3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 -----END CERTIFICATE----- Actalis Authentication Root CA ============================== -----BEGIN CERTIFICATE----- MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6 zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2 oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7 hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8 EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5 jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0 JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+ Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC 4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo 2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9 vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== -----END CERTIFICATE----- Buypass Class 2 Root CA ======================= -----BEGIN CERTIFICATE----- MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1 g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn 9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b /+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0 oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN rJgWVqA= -----END CERTIFICATE----- Buypass Class 3 Root CA ======================= -----BEGIN CERTIFICATE----- MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR 5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh 7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH 2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV /afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8 ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2 KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz 6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi Cp/HuZc= -----END CERTIFICATE----- T-TeleSec GlobalRoot Class 3 ============================ -----BEGIN CERTIFICATE----- MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3 DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK 9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W 0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== -----END CERTIFICATE----- D-TRUST Root Class 3 CA 2 2009 ============================== -----BEGIN CERTIFICATE----- MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ 4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm 2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I= -----END CERTIFICATE----- D-TRUST Root Class 3 CA 2 EV 2009 ================================= -----BEGIN CERTIFICATE----- MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T 7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60 sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35 11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+ PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv w9y4AyHqnxbxLFS1 -----END CERTIFICATE----- CA Disig Root R2 ================ -----BEGIN CERTIFICATE----- MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7 A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa 5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8 1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01 utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0 sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV 7+ZtsH8tZ/3zbBt1RqPlShfppNcL -----END CERTIFICATE----- ACCVRAIZ1 ========= -----BEGIN CERTIFICATE----- MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1 MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0 RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ 0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7 8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR 5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J 9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0 dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2 MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49 nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3 sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd 3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p EfbRD0tVNEYqi4Y7 -----END CERTIFICATE----- TWCA Global Root CA =================== -----BEGIN CERTIFICATE----- MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99 sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M 8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg /eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8 EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3 zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0= -----END CERTIFICATE----- TeliaSonera Root CA v1 ====================== -----BEGIN CERTIFICATE----- MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4 MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+ 6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA 3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3 F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7 gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx 0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2 qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6 Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= -----END CERTIFICATE----- T-TeleSec GlobalRoot Class 2 ============================ -----BEGIN CERTIFICATE----- MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3 DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970 2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4 r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR 3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN 9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg== -----END CERTIFICATE----- Atos TrustedRoot 2011 ===================== -----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4 MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr 54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+ DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320 HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9 61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G 3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed -----END CERTIFICATE----- QuoVadis Root CA 1 G3 ===================== -----BEGIN CERTIFICATE----- MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQELBQAwSDELMAkG A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv b3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJN MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEg RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakE PBtVwedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWerNrwU8lm PNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF34168Xfuw6cwI2H44g4hWf6 Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh4Pw5qlPafX7PGglTvF0FBM+hSo+LdoIN ofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXpUhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/l g6AnhF4EwfWQvTA9xO+oabw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV 7qJZjqlc3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/GKubX 9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSthfbZxbGL0eUQMk1f iyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KOTk0k+17kBL5yG6YnLUlamXrXXAkg t3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOtzCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZI hvcNAQELBQADggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2cDMT/uFPpiN3 GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUNqXsCHKnQO18LwIE6PWThv6ct Tr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP +V04ikkwj+3x6xn0dxoxGE1nVGwvb2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh 3jRJjehZrJ3ydlo28hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fa wx/kNSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNjZgKAvQU6 O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhpq1467HxpvMc7hU6eFbm0 FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFtnh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOV hMJKzRwuJIczYOXD -----END CERTIFICATE----- QuoVadis Root CA 2 G3 ===================== -----BEGIN CERTIFICATE----- MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDELMAkG A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv b3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJN MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIg RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFh ZiFfqq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMWn4rjyduY NM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ymc5GQYaYDFCDy54ejiK2t oIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+o MiwMzAkd056OXbxMmO7FGmh77FOm6RQ1o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+l V0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZo L1NesNKqIcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQ sSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43ehvNURG3YBZwjgQQvD 6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxh lRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALGcC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTAD AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZI hvcNAQELBQADggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RCroijQ1h5fq7K pVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9 x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4nlv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgz dWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6X U/IyAgkwo1jwDQHVcsaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+Nw mNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNgKCLjsZWD zYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeMHVOyToV7BjjHLPj4sHKN JeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4WSr2Rz0ZiC3oheGe7IUIarFsNMkd7Egr O3jtZsSOeWmD3n+M -----END CERTIFICATE----- QuoVadis Root CA 3 G3 ===================== -----BEGIN CERTIFICATE----- MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQELBQAwSDELMAkG A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv b3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJN MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMg RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286 IxSR/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNuFoM7pmRL Mon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXRU7Ox7sWTaYI+FrUoRqHe 6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+cra1AdHkrAj80//ogaX3T7mH1urPnMNA3 I4ZyYUUpSFlob3emLoG+B01vr87ERRORFHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3U VDmrJqMz6nWB2i3ND0/kA9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f7 5li59wzweyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634RylsSqi Md5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBpVzgeAVuNVejH38DM dyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0QA4XN8f+MFrXBsj6IbGB/kE+V9/Yt rQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZI hvcNAQELBQADggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnIFUBhynLWcKzS t/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5WvvoxXqA/4Ti2Tk08HS6IT7SdEQ TXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFgu/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9Du DcpmvJRPpq3t/O5jrFc/ZSXPsoaP0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGib Ih6BJpsQBJFxwAYf3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmD hPbl8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+DhcI00iX 0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HNPlopNLk9hM6xZdRZkZFW dSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ywaZWWDYWGWVjUTR939+J399roD1B0y2 PpxxVJkES/1Y+Zj0 -----END CERTIFICATE----- DigiCert Assured ID Root G2 =========================== -----BEGIN CERTIFICATE----- MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQG EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgw MTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIw ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSAn61UQbVH 35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4HteccbiJVMWWXvdMX0h5i89vq bFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9HpEgjAALAcKxHad3A2m67OeYfcgnDmCXRw VWmvo2ifv922ebPynXApVfSr/5Vh88lAbx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OP YLfykqGxvYmJHzDNw6YuYjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+Rn lTGNAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTO w0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPIQW5pJ6d1Ee88hjZv 0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I0jJmwYrA8y8678Dj1JGG0VDjA9tz d29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4GnilmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAW hsI6yLETcDbYz+70CjTVW0z9B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0M jomZmWzwPDCvON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo IhNzbM8m9Yop5w== -----END CERTIFICATE----- DigiCert Assured ID Root G3 =========================== -----BEGIN CERTIFICATE----- MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQ BgcqhkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJfZn4f5dwb RXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17QRSAPWXYQ1qAk8C3eNvJs KTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgF UaFNN6KDec6NHSrkhDAKBggqhkjOPQQDAwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5Fy YZ5eEJJZVrmDxxDnOOlYJjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy 1vUhZscv6pZjamVFkpUBtA== -----END CERTIFICATE----- DigiCert Global Root G2 ======================= -----BEGIN CERTIFICATE----- MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQG EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUx MjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkq hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI2/Ou8jqJ kTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx1x7e/dfgy5SDN67sH0NO 3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQq2EGnI/yuum06ZIya7XzV+hdG82MHauV BJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyM UNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQAB o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu 5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsr F9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0U WTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBH QRFXGU7Aj64GxJUTFy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/ iyK5S9kJRaTepLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl MrY= -----END CERTIFICATE----- DigiCert Global Root G3 ======================= -----BEGIN CERTIFICATE----- MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQGEwJV UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYD VQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAw MDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k aWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0C AQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FGfp4tn+6O YwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPOZ9wj/wMco+I+o0IwQDAP BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNp Yim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y 3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34 VOKa5Vt8sycX -----END CERTIFICATE----- DigiCert Trusted Root G4 ======================== -----BEGIN CERTIFICATE----- MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQG EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEw HwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 MTIwMDAwWjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0G CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEp pz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9o k3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7Fsa vOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6 MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtm mnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7 f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFH dL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8 oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud DwEB/wQEAwIBhjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2SV1EY+CtnJYY ZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd+SeuMIW59mdNOj6PWTkiU0Tr yF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWcfFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy 7zBZLq7gcfJW5GqXb5JQbZaNaHqasjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iah ixTXTBmyUEFxPT9NcCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN 5r5N0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie4u1Ki7wb /UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mIr/OSmbaz5mEP0oUA51Aa 5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tK G48BtieVU+i2iW1bvGjUI+iLUaJW+fCmgKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP 82Z+ -----END CERTIFICATE----- COMODO RSA Certification Authority ================================== -----BEGIN CERTIFICATE----- MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UE BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlv biBBdXRob3JpdHkwHhcNMTAwMTE5MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMC R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBB dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR6FSS0gpWsawNJN3Fz0Rn dJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8Xpz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZ FGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+ 5eNu/Nio5JIk2kNrYrhV/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pG x8cgoLEfZd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z+pUX 2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7wqP/0uK3pN/u6uPQL OvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZahSL0896+1DSJMwBGB7FY79tOi4lu3 sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVICu9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+C GCe01a60y1Dma/RMhnEw6abfFobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5 WdYgGq/yapiqcrxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w DQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvlwFTPoCWOAvn9sKIN9SCYPBMt rFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+ nq6PK7o9mfjYcwlYRm6mnPTXJ9OV2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSg tZx8jb8uk2IntznaFxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwW sRqZCuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiKboHGhfKp pC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmckejkk9u+UJueBPSZI9FoJA zMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yLS0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHq ZJx64SIDqZxubw5lT2yHh17zbqD5daWbQOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk52 7RH89elWsn2/x20Kk4yl0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7I LaZRfyHBNVOFBkpdn627G190 -----END CERTIFICATE----- USERTrust RSA Certification Authority ===================================== -----BEGIN CERTIFICATE----- MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCBiDELMAkGA1UE BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh dGlvbiBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UE BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCAEmUXNg7D2wiz 0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2j Y0K2dvKpOyuR+OJv0OwWIJAJPuLodMkYtJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFn RghRy4YUVD+8M/5+bJz/Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O +T23LLb2VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT79uq /nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6c0Plfg6lZrEpfDKE Y1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmTYo61Zs8liM2EuLE/pDkP2QKe6xJM lXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97lc6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8 yexDJtC/QV9AqURE9JnnV4eeUB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+ eLf8ZxXhyVeEHg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF MAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPFUp/L+M+ZBn8b2kMVn54CVVeW FPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KOVWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ 7l8wXEskEVX/JJpuXior7gtNn3/3ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQ Eg9zKC7F4iRO/Fjs8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM 8WcRiQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYzeSf7dNXGi FSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZXHlKYC6SQK5MNyosycdi yA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9c J2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRBVXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGw sAvgnEzDHNb842m1R0aBL6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gx Q+6IHdfGjjxDah2nGN59PRbxYvnKkKj9 -----END CERTIFICATE----- USERTrust ECC Certification Authority ===================================== -----BEGIN CERTIFICATE----- MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UEBhMC VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv biBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMC VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqfloI+d61SRvU8Za2EurxtW2 0eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinngo4N+LZfQYcTxmdwlkWOrfzCjtHDix6Ez nPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNV HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBB HU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu 9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= -----END CERTIFICATE----- GlobalSign ECC Root CA - R5 =========================== -----BEGIN CERTIFICATE----- MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEkMCIGA1UECxMb R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD EwpHbG9iYWxTaWduMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6 SFkc8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8kehOvRnkmS h5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd BgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYIKoZIzj0EAwMDaAAwZQIxAOVpEslu28Yx uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7 yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3 -----END CERTIFICATE----- IdenTrust Commercial Root CA 1 ============================== -----BEGIN CERTIFICATE----- MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQG EwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBS b290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQwMTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzES MBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENB IDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ld hNlT3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU+ehcCuz/ mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gpS0l4PJNgiCL8mdo2yMKi 1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1bVoE/c40yiTcdCMbXTMTEl3EASX2MN0C XZ/g1Ue9tOsbobtJSdifWwLziuQkkORiT0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl 3ZBWzvurpWCdxJ35UrCLvYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzy NeVJSQjKVsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZKdHzV WYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHTc+XvvqDtMwt0viAg xGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hvl7yTmvmcEpB4eoCHFddydJxVdHix uuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5NiGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZI hvcNAQELBQADggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH 6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwtLRvM7Kqas6pg ghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93nAbowacYXVKV7cndJZ5t+qnt ozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmV YjzlVYA211QC//G5Xc7UI2/YRYRKW2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUX feu+h1sXIFRRk0pTAwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/ro kTLql1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG4iZZRHUe 2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZmUlO+KWA2yUPHGNiiskz Z2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7R cGzM7vRX+Bi6hG6H -----END CERTIFICATE----- IdenTrust Public Sector Root CA 1 ================================= -----BEGIN CERTIFICATE----- MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQG EwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3Rv ciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcNMzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJV UzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBS b290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTy P4o7ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGyRBb06tD6 Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlSbdsHyo+1W/CD80/HLaXI rcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF/YTLNiCBWS2ab21ISGHKTN9T0a9SvESf qy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoS mJxZZoY+rfGwyj4GD3vwEUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFn ol57plzy9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9VGxyh LrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ2fjXctscvG29ZV/v iDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsVWaFHVCkugyhfHMKiq3IXAAaOReyL 4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gDW/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8B Af8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMw DQYJKoZIhvcNAQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHVDRDtfULAj+7A mgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9TaDKQGXSc3z1i9kKlT/YPyNt GtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8GlwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFt m6/n6J91eEyrRjuazr8FGF1NFTwWmhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMx NRF4eKLg6TCMf4DfWN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4 Mhn5+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJtshquDDI ajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhAGaQdp/lLQzfcaFpPz+vC ZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ 3Wl9af0AVqW3rLatt8o+Ae+c -----END CERTIFICATE----- Entrust Root Certification Authority - G2 ========================================= -----BEGIN CERTIFICATE----- MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAUBgNV BAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVy bXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ug b25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIw HhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoT DUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMx OTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25s eTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwggEi MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP /vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXz HHfV1IWNcCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKU s/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4y TGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRx AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ6 0B7vfec7aVHUbI2fkBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5Z iXMRrEPR9RP/jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDgi nWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+ vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xO e4pIb4tF9g== -----END CERTIFICATE----- Entrust Root Certification Authority - EC1 ========================================== -----BEGIN CERTIFICATE----- MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMCVVMx FjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn YWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXpl ZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 IC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYw FAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2Fs LXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQg dXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt IEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHy AsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef 9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE FLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3h vxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8 kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G -----END CERTIFICATE----- CFCA EV ROOT ============ -----BEGIN CERTIFICATE----- MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJDTjEwMC4GA1UE CgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNB IEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkxMjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEw MC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQD DAxDRkNBIEVWIFJPT1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnV BU03sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpLTIpTUnrD 7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5/ZOkVIBMUtRSqy5J35DN uF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp7hZZLDRJGqgG16iI0gNyejLi6mhNbiyW ZXvKWfry4t3uMCz7zEasxGPrb382KzRzEpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7 xzbh72fROdOXW3NiGUgthxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9f py25IGvPa931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqotaK8K gWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNgTnYGmE69g60dWIol hdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfVPKPtl8MeNPo4+QgO48BdK4PRVmrJ tqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hvcWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAf BgNVHSMEGDAWgBTj/i39KNALtbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB /wQEAwIBBjAdBgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObTej/tUxPQ4i9q ecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdLjOztUmCypAbqTuv0axn96/Ua 4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBSESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sG E5uPhnEFtC+NiWYzKXZUmhH4J/qyP5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfX BDrDMlI1Dlb4pd19xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjn aH9dCi77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN5mydLIhy PDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe/v5WOaHIz16eGWRGENoX kbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+ZAAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3C ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su -----END CERTIFICATE----- OISTE WISeKey Global Root GB CA =============================== -----BEGIN CERTIFICATE----- MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBtMQswCQYDVQQG EwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAw MzJaFw0zOTEyMDExNTEwMzFaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYD VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEds b2JhbCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3HEokKtaX scriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGxWuR51jIjK+FTzJlFXHtP rby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk 9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNku7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4o Qnc/nSMbsrY9gBQHTC5P99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvg GUpuuy9rM2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB /zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI hvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrghcViXfa43FK8+5/ea4n32cZiZBKpD dHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0 VQreUGdNZtGn//3ZwLWoo4rOZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEui HZeeevJuQHHfaPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= -----END CERTIFICATE----- SZAFIR ROOT CA2 =============== -----BEGIN CERTIFICATE----- MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTELMAkG A1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4xGDAWBgNV BAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJ BgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYD VQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5Q qEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT3PSQ1hNK DJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw3gAeqDRHu5rr/gsUvTaE 2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr63fE9biCloBK0TXC5ztdyO4mTp4CEHCdJ ckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwi ieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P AQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOC AQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5 O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67 oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul 4+vJhaAlIDf7js4MNIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6 +/NNIxuZMzSgLvWpCz/UXeHPhJ/iGcJfitYgHuNztw== -----END CERTIFICATE----- Certum Trusted Network CA 2 =========================== -----BEGIN CERTIFICATE----- MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCBgDELMAkGA1UE BhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1 bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y ayBDQSAyMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQ TDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENB IDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWADGSdhhuWZGc/IjoedQF9 7/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+o CgCXhVqqndwpyeI1B+twTUrWwbNWuKFBOJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40b Rr5HMNUuctHFY9rnY3lEfktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2p uTRZCr+ESv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1mo130 GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02isx7QBlrd9pPPV3WZ 9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOWOZV7bIBaTxNyxtd9KXpEulKkKtVB Rgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgezTv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pye hizKV/Ma5ciSixqClnrDvFASadgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vM BhBgu4M1t15n3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZI hvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQF/xlhMcQSZDe28cmk4gmb3DW Al45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTfCVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuA L55MYIR4PSFk1vtBHxgP58l1cb29XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMo clm2q8KMZiYcdywmdjWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tM pkT/WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jbAoJnwTnb w3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksqP/ujmv5zMnHCnsZy4Ypo J/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Kob7a6bINDd82Kkhehnlt4Fj1F4jNy3eFm ypnTycUm/Q1oBEauttmbjL4ZvrHG8hnjXALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLX is7VmFxWlgPF7ncGNf/P5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7 zAYspsbiDrW5viSP -----END CERTIFICATE----- Hellenic Academic and Research Institutions RootCA 2015 ======================================================= -----BEGIN CERTIFICATE----- MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcT BkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0 aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl YXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAx MTIxWjCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMg QWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNV BAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIw MTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDC+Kk/G4n8PDwEXT2QNrCROnk8Zlrv bTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+eh iGsxr/CL0BgzuNtFajT0AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+ 6PAQZe104S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06CojXd FPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV9Cz82XBST3i4vTwr i5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrDgfgXy5I2XdGj2HUb4Ysn6npIQf1F GQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2 fu/Z8VFRfS0myGlZYeCsargqNhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9mu iNX6hME6wGkoLfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVdctA4GGqd83EkVAswDQYJKoZI hvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0IXtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+ D1hYc2Ryx+hFjtyp8iY/xnmMsVMIM4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrM d/K4kPFox/la/vot9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+y d+2VZ5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/eaj8GsGsVn 82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnhX9izjFk0WaSrT2y7Hxjb davYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQl033DlZdwJVqwjbDG2jJ9SrcR5q+ss7F Jej6A7na+RZukYT1HCjI/CbM1xyQVqdfbzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVt J94Cj8rDtSvK6evIIVM4pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGa JI7ZjnHKe7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0vm9q p/UsQu0yrbYhnr68 -----END CERTIFICATE----- Hellenic Academic and Research Institutions ECC RootCA 2015 =========================================================== -----BEGIN CERTIFICATE----- MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0 aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u cyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj aCBJbnN0aXR1dGlvbnMgRUNDIFJvb3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEw MzcxMlowgaoxCzAJBgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmlj IEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUQwQgYD VQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIEVDQyBSb290 Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKgQehLgoRc4vgxEZmGZE4JJS+dQS8KrjVP dJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJajq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoK Vlp8aQuqgAkkbH7BRqNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O BBYEFLQiC4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaeplSTA GiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjn dc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR -----END CERTIFICATE----- ISRG Root X1 ============ -----BEGIN CERTIFICATE----- MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE BhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD EwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT DElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r Vygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1 3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K b4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN Aymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ 4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf 1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu hjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH usEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r OPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY 9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV 0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt hDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw TdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx e5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA JzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ m+kXQ99b21/+jh5Xos1AnX5iItreGCc= -----END CERTIFICATE----- AC RAIZ FNMT-RCM ================ -----BEGIN CERTIFICATE----- MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNVBAYT AkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTAeFw0wODEw MjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJD TTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC ggIBALpxgHpMhm5/yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcf qQgfBBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAzWHFctPVr btQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxFtBDXaEAUwED653cXeuYL j2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z374jNUUeAlz+taibmSXaXvMiwzn15Cou 08YfxGyqxRxqAQVKL9LFwag0Jl1mpdICIfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mw WsXmo8RZZUc1g16p6DULmbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnT tOmlcYF7wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peSMKGJ 47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2ZSysV4999AeU14EC ll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMetUqIJ5G+GR4of6ygnXYMgrwTJbFaa i0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE FPd9xf3E6Jobd2Sn9R2gzL+HYJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1o dHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1RXxlDPiyN8+s D8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYMLVN0V2Ue1bLdI4E7pWYjJ2cJ j+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrT Qfv6MooqtyuGC2mDOL7Nii4LcK2NJpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW +YJF1DngoABd15jmfZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7 Ixjp6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp1txyM/1d 8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B9kiABdcPUXmsEKvU7ANm 5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wokRqEIr9baRRmW1FMdW4R58MD3R++Lj8UG rp1MYp3/RgT408m2ECVAdf4WqslKYIYvuu8wd+RU4riEmViAqhOLUTpPSPaLtrM= -----END CERTIFICATE----- Amazon Root CA 1 ================ -----BEGIN CERTIFICATE----- MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYD VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1 MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgH FzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQ gLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0t dHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcce VOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB /zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3 DQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PM CCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy 8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa 2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2 xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5 -----END CERTIFICATE----- Amazon Root CA 2 ================ -----BEGIN CERTIFICATE----- MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwFADA5MQswCQYD VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAyMB4XDTE1 MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC ggIBAK2Wny2cSkxKgXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4 kHbZW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg1dKmSYXp N+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K8nu+NQWpEjTj82R0Yiw9 AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvd fLC6HM783k81ds8P+HgfajZRRidhW+mez/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAEx kv8LV/SasrlX6avvDXbR8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSS btqDT6ZjmUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz7Mt0 Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6+XUyo05f7O0oYtlN c/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI0u1ufm8/0i2BWSlmy5A5lREedCf+ 3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSw DPBMMPQFWAJI/TPlUq9LhONmUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oA A7CXDpO8Wqj2LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY +gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kSk5Nrp+gvU5LE YFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl7uxMMne0nxrpS10gxdr9HIcW xkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygmbtmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQ gj9sAq+uEjonljYE1x2igGOpm/HlurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbW aQbLU8uz/mtBzUF+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoV Yh63n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE76KlXIx3 KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H9jVlpNMKVv/1F2Rs76gi JUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT4PsJYGw= -----END CERTIFICATE----- Amazon Root CA 3 ================ -----BEGIN CERTIFICATE----- MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5MQswCQYDVQQG EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAzMB4XDTE1MDUy NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZB f8ANm+gBG1bG8lKlui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjr Zt6jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSrttvXBp43 rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkrBqWTrBqYaGFy+uGh0Psc eGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteMYyRIHN8wfdVoOw== -----END CERTIFICATE----- Amazon Root CA 4 ================ -----BEGIN CERTIFICATE----- MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5MQswCQYDVQQG EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSA0MB4XDTE1MDUy NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN /sGKe0uoe0ZLY7Bi9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri 83BkM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV HQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WBMAoGCCqGSM49BAMDA2gA MGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlwCkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1 AE47xDqUEpHJWEadIRNyp4iciuRMStuW1KyLa2tJElMzrdfkviT8tQp21KW8EA== -----END CERTIFICATE----- TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 ============================================= -----BEGIN CERTIFICATE----- MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNVBAcT D0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtub2xvamlr IEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24g TWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRp ZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYD VQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXllIEJpbGlt c2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklUQUsxLTArBgNVBAsTJEth bXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBTTTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11 IFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y8 6Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wc wv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh0 3+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9 WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQU ZT/HiobGPN08VFw1+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ KoZIhvcNAQELBQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yc lNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R e37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0j q5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= -----END CERTIFICATE----- GDCA TrustAUTH R5 ROOT ====================== -----BEGIN CERTIFICATE----- MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCQ04xMjAw BgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8wHQYDVQQD DBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVow YjELMAkGA1UEBhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0B AQEFAAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJjDp6L3TQs AlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBjTnnEt1u9ol2x8kECK62p OqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+uKU49tm7srsHwJ5uu4/Ts765/94Y9cnrr pftZTqfrlYwiOXnhLQiPzLyRuEH3FMEjqcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ 9Cy5WmYqsBebnh52nUpmMUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQ xXABZG12ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloPzgsM R6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3GkL30SgLdTMEZeS1SZ D2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeCjGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4 oR24qoAATILnsn8JuLwwoC8N9VKejveSswoAHQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx 9hoh49pwBiFYFIeFd3mqgnkCAwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlR MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZmDRd9FBUb1Ov9 H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5COmSdI31R9KrO9b7eGZONn35 6ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ryL3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd +PwyvzeG5LuOmCd+uh8W4XAR8gPfJWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQ HtZa37dG/OaG+svgIHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBD F8Io2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV09tL7ECQ 8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQXR4EzzffHqhmsYzmIGrv /EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrqT8p+ck0LcIymSLumoRT2+1hEmRSuqguT aaApJUqlyyvdimYHFngVV3Eb7PVHhPOeMTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== -----END CERTIFICATE----- SSL.com Root Certification Authority RSA ======================================== -----BEGIN CERTIFICATE----- MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxDjAM BgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24x MTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYw MjEyMTczOTM5WhcNNDEwMjEyMTczOTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NM LmNvbSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcNAQEBBQAD ggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2RxFdHaxh3a3by/ZPkPQ/C Fp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aXqhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8 P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcCC52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/ge oeOy3ZExqysdBP+lSgQ36YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkp k8zruFvh/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrFYD3Z fBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93EJNyAKoFBbZQ+yODJ gUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVcUS4cK38acijnALXRdMbX5J+tB5O2 UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi8 1xtZPCvM8hnIk2snYxnP/Okm+Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4s bE6x/c+cCbqiM+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4GA1UdDwEB/wQE AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGVcpNxJK1ok1iOMq8bs3AD/CUr dIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBcHadm47GUBwwyOabqG7B52B2ccETjit3E+ZUf ijhDPwGFpUenPUayvOUiaPd7nNgsPgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAsl u1OJD7OAUN5F7kR/q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjq erQ0cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jra6x+3uxj MxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90IH37hVZkLId6Tngr75qNJ vTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/YK9f1JmzJBjSWFupwWRoyeXkLtoh/D1JI Pb9s2KJELtFOt3JY04kTlf5Eq/jXixtunLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406y wKBjYZC6VWg3dGq2ktufoYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NI WuuA8ShYIc2wBlX7Jz9TkHCpBB5XJ7k= -----END CERTIFICATE----- SSL.com Root Certification Authority ECC ======================================== -----BEGIN CERTIFICATE----- MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMCVVMxDjAMBgNV BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xMTAv BgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEy MTgxNDAzWhcNNDEwMjEyMTgxNDAzWjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAO BgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuBBAAiA2IA BEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI7Z4INcgn64mMU1jrYor+ 8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPgCemB+vNH06NjMGEwHQYDVR0OBBYEFILR hXMw5zUE044CkvvlpNHEIejNMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTT jgKS++Wk0cQh6M0wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCW e+0F+S8Tkdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+gA0z 5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl -----END CERTIFICATE----- SSL.com EV Root Certification Authority RSA R2 ============================================== -----BEGIN CERTIFICATE----- MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNVBAYTAlVTMQ4w DAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9u MTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy MB4XDTE3MDUzMTE4MTQzN1oXDTQyMDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQI DAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYD VQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMIICIjAN BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvqM0fNTPl9fb69LT3w23jh hqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssufOePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7w cXHswxzpY6IXFJ3vG2fThVUCAtZJycxa4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTO Zw+oz12WGQvE43LrrdF9HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+ B6KjBSYRaZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcAb9Zh CBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQGp8hLH94t2S42Oim 9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQVPWKchjgGAGYS5Fl2WlPAApiiECto RHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMOpgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+Slm JuwgUHfbSguPvuUCYHBBXtSuUDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48 +qvWBkofZ6aYMBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa49QaAJadz20Zp qJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBWs47LCp1Jjr+kxJG7ZhcFUZh1 ++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nx Y/hoLVUE0fKNsKTPvDxeH3jnpaAgcLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2G guDKBAdRUNf/ktUM79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDz OFSz/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXtll9ldDz7 CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEmKf7GUmG6sXP/wwyc5Wxq lD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKKQbNmC1r7fSOl8hqw/96bg5Qu0T/fkreR rwU7ZcegbLHNYhLDkBvjJc40vG93drEQw/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1 hlMYegouCRw2n5H9gooiS9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX 9hwJ1C07mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== -----END CERTIFICATE----- SSL.com EV Root Certification Authority ECC =========================================== -----BEGIN CERTIFICATE----- MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMCVVMxDjAMBgNV BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xNDAy BgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYw MjEyMTgxNTIzWhcNNDEwMjEyMTgxNTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NM LmNvbSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB BAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMAVIbc/R/fALhBYlzccBYy 3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1KthkuWnBaBu2+8KGwytAJKaNjMGEwHQYDVR0O BBYEFFvKXuXe0oGqzagtZFG22XKbl+ZPMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe 5d7SgarNqC1kUbbZcpuX5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJ N+vp1RPZytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZgh5Mm m7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== -----END CERTIFICATE----- GlobalSign Root CA - R6 ======================= -----BEGIN CERTIFICATE----- MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEgMB4GA1UECxMX R2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds b2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQxMjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9i YWxTaWduIFJvb3QgQ0EgLSBSNjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFs U2lnbjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQss grRIxutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1kZguSgMpE 3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxDaNc9PIrFsmbVkJq3MQbF vuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJwLnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqM PKq0pPbzlUoSB239jLKJz9CgYXfIWHSw1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+ azayOeSsJDa38O+2HBNXk7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05O WgtH8wY2SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/hbguy CLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4nWUx2OVvq+aWh2IMP 0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpYrZxCRXluDocZXFSxZba/jJvcE+kN b7gu3GduyYsRtYQUigAZcIN5kZeR1BonvzceMgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQE AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNV HSMEGDAWgBSubAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGtIxg93eFyRJa0 lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr6155wsTLxDKZmOMNOsIeDjHfrY BzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLjvUYAGm0CuiVdjaExUd1URhxN25mW7xocBFym Fe944Hn+Xds+qkxV/ZoVqW/hpvvfcDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr 3TsTjxKM4kEaSHpzoHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB1 0jZpnOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfspA9MRf/T uTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+vJJUEeKgDu+6B5dpffItK oZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+t JDfLRVpOoERIyNiwmcUVhAn21klJwGW45hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= -----END CERTIFICATE----- OISTE WISeKey Global Root GC CA =============================== -----BEGIN CERTIFICATE----- MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQswCQYDVQQGEwJD SDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEo MCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRa Fw00MjA1MDkwOTU4MzNaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQL ExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh bCBSb290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4nieUqjFqdr VCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4Wp2OQ0jnUsYd4XxiWD1Ab NTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd BgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7TrYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0E AwMDaAAwZQIwJsdpW9zV57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtk AjEA2zQgMgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 -----END CERTIFICATE----- UCA Global G2 Root ================== -----BEGIN CERTIFICATE----- MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQG EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBHbG9iYWwgRzIgUm9vdDAeFw0x NjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0xCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlU cnVzdDEbMBkGA1UEAwwSVUNBIEdsb2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A MIICCgKCAgEAxeYrb3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmT oni9kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzmVHqUwCoV 8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/RVogvGjqNO7uCEeBHANBS h6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDcC/Vkw85DvG1xudLeJ1uK6NjGruFZfc8o LTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIjtm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/ R+zvWr9LesGtOxdQXGLYD0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBe KW4bHAyvj5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6DlNaBa 4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6iIis7nCs+dwp4wwc OxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznPO6Q0ibd5Ei9Hxeepl2n8pndntd97 8XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O BBYEFIHEjMz15DD/pQwIX4wVZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo 5sOASD0Ee/ojL3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl1qnN3e92mI0A Ds0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oUb3n09tDh05S60FdRvScFDcH9 yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LVPtateJLbXDzz2K36uGt/xDYotgIVilQsnLAX c47QN6MUPJiVAAwpBVueSUmxX8fjy88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHo jhJi6IjMtX9Gl8CbEGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZk bxqgDMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI+Vg7RE+x ygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGyYiGqhkCyLmTTX8jjfhFn RR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bXUB+K+wb1whnw0A== -----END CERTIFICATE----- UCA Extended Validation Root ============================ -----BEGIN CERTIFICATE----- MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQG EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9u IFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMxMDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8G A1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIi MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrs iWogD4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvSsPGP2KxF Rv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aopO2z6+I9tTcg1367r3CTu eUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dksHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR 59mzLC52LqGj3n5qiAno8geK+LLNEOfic0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH 0mK1lTnj8/FtDw5lhIpjVMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KR el7sFsLzKuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/TuDv B0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41Gsx2VYVdWf6/wFlth WG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs1+lvK9JKBZP8nm9rZ/+I8U6laUpS NwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQDfwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS 3H5aBZ8eNJr34RQwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEL BQADggIBADaNl8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQVBcZEhrxH9cM aVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5c6sq1WnIeJEmMX3ixzDx/BR4 dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb +7lsq+KePRXBOy5nAliRn+/4Qh8st2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOW F3sGPjLtx7dCvHaj2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwi GpWOvpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2CxR9GUeOc GMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmxcmtpzyKEC2IPrNkZAJSi djzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbMfjKaiJUINlK73nZfdklJrX+9ZSCyycEr dhh2n1ax -----END CERTIFICATE----- Certigna Root CA ================ -----BEGIN CERTIFICATE----- MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UE BhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAwMiA0ODE0NjMwODEwMDAzNjEZ MBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0xMzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjda MFoxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYz MDgxMDAwMzYxGTAXBgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4IC DwAwggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sOty3tRQgX stmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9MCiBtnyN6tMbaLOQdLNyz KNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPuI9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8 JXrJhFwLrN1CTivngqIkicuQstDuI7pmTLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16 XdG+RCYyKfHx9WzMfgIhC59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq 4NYKpkDfePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3YzIoej wpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWTCo/1VTp2lc5ZmIoJ lXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1kJWumIWmbat10TWuXekG9qxf5kBdI jzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp/ /TBt2dzhauH8XwIDAQABo4IBGjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw HQYDVR0OBBYEFBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of 1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczovL3d3d3cuY2Vy dGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilodHRwOi8vY3JsLmNlcnRpZ25h LmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYraHR0cDovL2NybC5kaGlteW90aXMuY29tL2Nl cnRpZ25hcm9vdGNhLmNybDANBgkqhkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOIt OoldaDgvUSILSo3L6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxP TGRGHVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH60BGM+RFq 7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncBlA2c5uk5jR+mUYyZDDl3 4bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdio2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd 8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS 6Cvu5zHbugRqh5jnxV/vfaci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaY tlu3zM63Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayhjWZS aX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw3kAP+HwV96LOPNde E4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= -----END CERTIFICATE----- emSign Root CA - G1 =================== -----BEGIN CERTIFICATE----- MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJJTjET MBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRl ZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBHMTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgx ODMwMDBaMGcxCzAJBgNVBAYTAklOMRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVk aHJhIFRlY2hub2xvZ2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIB IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQzf2N4aLTN LnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO8oG0x5ZOrRkVUkr+PHB1 cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aqd7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHW DV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhMtTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ 6DqS0hdW5TUaQBw+jSztOd9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrH hQIDAQABo0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQDAgEG MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31xPaOfG1vR2vjTnGs2 vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjMwiI/aTvFthUvozXGaCocV685743Q NcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6dGNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q +Mri/Tm3R7nrft8EI6/6nAYH6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeih U80Bv2noWgbyRQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx iN66zB+Afko= -----END CERTIFICATE----- emSign ECC Root CA - G3 ======================= -----BEGIN CERTIFICATE----- MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQGEwJJTjETMBEG A1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEg MB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4 MTgzMDAwWjBrMQswCQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11 ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g RzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0WXTsuwYc 58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xySfvalY8L1X44uT6EYGQIr MgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuBzhccLikenEhjQjAOBgNVHQ8BAf8EBAMC AQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+D CBeQyh+KTOgNG3qxrdWBCUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7 jHvrZQnD+JbNR6iC8hZVdyR+EhCVBCyj -----END CERTIFICATE----- emSign Root CA - C1 =================== -----BEGIN CERTIFICATE----- MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkGA1UEBhMCVVMx EzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNp Z24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UE BhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQD ExNlbVNpZ24gUm9vdCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+up ufGZBczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZHdPIWoU/ Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH3DspVpNqs8FqOp099cGX OFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvHGPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4V I5b2P/AgNBbeCsbEBEV5f6f9vtKppa+cxSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleooms lMuoaJuvimUnzYnu3Yy1aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+ XJGFehiqTbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQAD ggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87/kOXSTKZEhVb3xEp /6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4kqNPEjE2NuLe/gDEo2APJ62gsIq1 NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrGYQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9 wC68AivTxEDkigcxHpvOJpkT+xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQ BmIMMMAVSKeoWXzhriKi4gp6D/piq1JM4fHfyr6DDUI= -----END CERTIFICATE----- emSign ECC Root CA - C3 ======================= -----BEGIN CERTIFICATE----- MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQGEwJVUzETMBEG A1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMxIDAeBgNVBAMTF2VtU2lnbiBF Q0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UE BhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQD ExdlbVNpZ24gRUNDIFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd 6bciMK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4OjavtisIGJAnB9 SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0OBBYEFPtaSNCAIEDyqOkA B2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMDA2gA MGUCMQC02C8Cif22TGK6Q04ThHK1rt0c3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwU ZOR8loMRnLDRWmFLpg9J0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ== -----END CERTIFICATE----- Hongkong Post Root CA 3 ======================= -----BEGIN CERTIFICATE----- MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQELBQAwbzELMAkG A1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJSG9uZyBLb25nMRYwFAYDVQQK Ew1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25na29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2 MDMwMjI5NDZaFw00MjA2MDMwMjI5NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtv bmcxEjAQBgNVBAcTCUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMX SG9uZ2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz iNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFOdem1p+/l6TWZ5Mwc50tf jTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mIVoBc+L0sPOFMV4i707mV78vH9toxdCim 5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOe sL4jpNrcyCse2m5FHomY2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj 0mRiikKYvLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+TtbNe/ JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZbx39ri1UbSsUgYT2u y1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+l2oBlKN8W4UdKjk60FSh0Tlxnf0h +bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YKTE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsG xVd7GYYKecsAyVKvQv83j+GjHno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwID AQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEwDQYJKoZIhvcN AQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG7BJ8dNVI0lkUmcDrudHr9Egw W62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCkMpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWld y8joRTnU+kLBEUx3XZL7av9YROXrgZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov +BS5gLNdTaqX4fnkGMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDc eqFS3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJmOzj/2ZQw 9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+l6mc1X5VTMbeRRAc6uk7 nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6cJfTzPV4e0hz5sy229zdcxsshTrD3mUcY hcErulWuBurQB7Lcq9CClnXO0lD+mefPL5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB 60PZ2Pierc+xYw5F9KBaLJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fq dBb9HxEGmpv0 -----END CERTIFICATE----- Microsoft ECC Root Certificate Authority 2017 ============================================= -----BEGIN CERTIFICATE----- MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQgRUND IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4 MjMxNjA0WjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw NAYDVQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQ BgcqhkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZRogPZnZH6 thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYbhGBKia/teQ87zvH2RPUB eMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTIy5lycFIM +Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlf Xu5gKcs68tvWMoQZP3zVL8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaR eNtUjGUBiudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= -----END CERTIFICATE----- Microsoft RSA Root Certificate Authority 2017 ============================================= -----BEGIN CERTIFICATE----- MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBlMQswCQYDVQQG EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQg UlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIw NzE4MjMwMDIzWjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u MTYwNAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcw ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZNt9GkMml 7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0ZdDMbRnMlfl7rEqUrQ7e S0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw7 1VdyvD/IybLeS2v4I2wDwAW9lcfNcztmgGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+ dkC0zVJhUXAoP8XFWvLJjEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49F yGcohJUcaDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaGYaRS MLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6W6IYZVcSn2i51BVr lMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4KUGsTuqwPN1q3ErWQgR5WrlcihtnJ 0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH+FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJ ClTUFLkqqNfs+avNJVgyeY+QW5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYw DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZCLgLNFgVZJ8og 6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OCgMNPOsduET/m4xaRhPtthH80 dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk +ONVFT24bcMKpBLBaYVu32TxU5nhSnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex /2kskZGT4d9Mozd2TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDy AmH3pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGRxpl/j8nW ZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiAppGWSZI1b7rCoucL5mxAyE 7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKT c0QWbej09+CVgI+WXTik9KveCjCHk9hNAHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D 5KbvtwEwXlGjefVwaaZBRA+GsCyRxj3qrg+E -----END CERTIFICATE----- e-Szigno Root CA 2017 ===================== -----BEGIN CERTIFICATE----- MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNVBAYTAkhVMREw DwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwOVkFUSFUt MjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJvb3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZa Fw00MjA4MjIxMjA3MDZaMHExCzAJBgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UE CgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3pp Z25vIFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtvxie+RJCx s1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+HWyx7xf58etqjYzBhMA8G A1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSHERUI0arBeAyxr87GyZDv vzAEwDAfBgNVHSMEGDAWgBSHERUI0arBeAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEA tVfd14pVCzbhhkT61NlojbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxO svxyqltZ+efcMQ== -----END CERTIFICATE----- certSIGN Root CA G2 =================== -----BEGIN CERTIFICATE----- MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNVBAYTAlJPMRQw EgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjAeFw0xNzAy MDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJBgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lH TiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIP ADCCAgoCggIBAMDFdRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05 N0IwvlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZuIt4Imfk abBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhpn+Sc8CnTXPnGFiWeI8Mg wT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKscpc/I1mbySKEwQdPzH/iV8oScLumZfNp dWO9lfsbl83kqK/20U6o2YpxJM02PbyWxPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91Qqh ngLjYl/rNUssuHLoPj1PrCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732 jcZZroiFDsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fxDTvf 95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgyLcsUDFDYg2WD7rlc z8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6CeWRgKRM+o/1Pcmqr4tTluCRVLERL iohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1Ud DgQWBBSCIS1mxteg4BXrzkwJd8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOB ywaK8SJJ6ejqkX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQlqiCA2ClV9+BB /AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0OJD7uNGzcgbJceaBxXntC6Z5 8hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+cNywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5 BiKDUyUM/FHE5r7iOZULJK2v0ZXkltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklW atKcsWMy5WHgUyIOpwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tU Sxfj03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZkPuXaTH4M NMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE1LlSVHJ7liXMvGnjSG4N 0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MXQRBdJ3NghVdJIgc= -----END CERTIFICATE----- Trustwave Global Certification Authority ======================================== -----BEGIN CERTIFICATE----- MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJV UzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2 ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9u IEF1dGhvcml0eTAeFw0xNzA4MjMxOTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJV UzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2 ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9u IEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALldUShLPDeS0YLOvR29 zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0XznswuvCAAJWX/NKSqIk4cXGIDtiLK0thAf LdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4Bq stTnoApTAbqOl5F2brz81Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9o WN0EACyW80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotPJqX+ OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1lRtzuzWniTY+HKE40 Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfwhI0Vcnyh78zyiGG69Gm7DIwLdVcE uE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm +9jaJXLE9gCxInm943xZYkqcBW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqj ifLJS3tBEW1ntwiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1UdDwEB/wQEAwIB BjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W0OhUKDtkLSGm+J1WE2pIPU/H PinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfeuyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0H ZJDmHvUqoai7PF35owgLEQzxPy0QlG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla 4gt5kNdXElE1GYhBaCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5R vbbEsLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPTMaCm/zjd zyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qequ5AvzSxnI9O4fKSTx+O 856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxhVicGaeVyQYHTtgGJoC86cnn+OjC/QezH Yj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu 3R3y4G5OBVixwJAWKqQ9EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP 29FpHOTKyeC2nOnOcXHebD8WpHk= -----END CERTIFICATE----- Trustwave Global ECC P256 Certification Authority ================================================= -----BEGIN CERTIFICATE----- MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYDVQQGEwJVUzER MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBI b2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZp Y2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYD VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRy dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1 NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABH77bOYj 43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoNFWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqm P62jQzBBMA8GA1UdEwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt 0UrrdaVKEJmzsaGLSvcwCgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjz RM4q3wghDDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7 -----END CERTIFICATE----- Trustwave Global ECC P384 Certification Authority ================================================= -----BEGIN CERTIFICATE----- MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYDVQQGEwJVUzER MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBI b2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZp Y2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYD VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRy dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4 NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuBBAAiA2IABGvaDXU1CDFH Ba5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJj9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr /TklZvFe/oyujUF5nQlgziip04pt89ZF1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNV HQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNn ADBkAjA3AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsCMGcl CrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVuSw== -----END CERTIFICATE----- NAVER Global Root Certification Authority ========================================= -----BEGIN CERTIFICATE----- MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEMBQAwaTELMAkG A1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRGT1JNIENvcnAuMTIwMAYDVQQD DClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4 NDJaFw0zNzA4MTgyMzU5NTlaMGkxCzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVT UyBQTEFURk9STSBDb3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlv biBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVAiQqrDZBb UGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH38dq6SZeWYp34+hInDEW +j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lEHoSTGEq0n+USZGnQJoViAbbJAh2+g1G7 XNr4rRVqmfeSVPc0W+m/6imBEtRTkZazkVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2 aacp+yPOiNgSnABIqKYPszuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4 Yb8ObtoqvC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHfnZ3z VHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaGYQ5fG8Ir4ozVu53B A0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo0es+nPxdGoMuK8u180SdOqcXYZai cdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3aCJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejy YhbLgGvtPe31HzClrkvJE+2KAQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNV HQ4EFgQU0p+I36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoNqo0hV4/GPnrK 21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatjcu3cvuzHV+YwIHHW1xDBE1UB jCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm+LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bx hYTeodoS76TiEJd6eN4MUZeoIUCLhr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTg E34h5prCy8VCZLQelHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTH D8z7p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8piKCk5XQ A76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLRLBT/DShycpWbXgnbiUSY qqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oG I/hGoiLtk/bdmuYqh7GYVPEi92tF4+KOdh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmg kpzNNIaRkPpkUZ3+/uul9XXeifdy -----END CERTIFICATE----- AC RAIZ FNMT-RCM SERVIDORES SEGUROS =================================== -----BEGIN CERTIFICATE----- MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQswCQYDVQQGEwJF UzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgwFgYDVQRhDA9WQVRFUy1RMjgy NjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1SQ00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4 MTIyMDA5MzczM1oXDTQzMTIyMDA5MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQt UkNNMQ4wDAYDVQQLDAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNB QyBSQUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuBBAAiA2IA BPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LHsbI6GA60XYyzZl2hNPk2 LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oKUm8BA06Oi6NCMEAwDwYDVR0TAQH/BAUw AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqG SM49BAMDA2kAMGYCMQCuSuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoD zBOQn5ICMQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJyv+c= -----END CERTIFICATE----- GlobalSign Root R46 =================== -----BEGIN CERTIFICATE----- MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUAMEYxCzAJBgNV BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQDExNHbG9iYWxTaWduIFJv b3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAX BgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIi MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08Es CVeJOaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQGvGIFAha/ r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud316HCkD7rRlr+/fKYIje 2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo0q3v84RLHIf8E6M6cqJaESvWJ3En7YEt bWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSEy132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvj K8Cd+RTyG/FWaha/LIWFzXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD4 12lPFzYE+cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCNI/on ccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzsx2sZy/N78CsHpdls eVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqaByFrgY/bxFn63iLABJzjqls2k+g9 vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYD VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEM BQADggIBAHx47PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti2kM3S+LGteWy gxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIkpnnpHs6i58FZFZ8d4kuaPp92 CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRFFRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZm OUdkLG5NrmJ7v2B0GbhWrJKsFjLtrWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qq JZ4d16GLuc1CLgSkZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwye qiv5u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP4vkYxboz nxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6N3ec592kD3ZDZopD8p/7 DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3vouXsXgxT7PntgMTzlSdriVZzH81Xwj3 QEUxeCp6 -----END CERTIFICATE----- GlobalSign Root E46 =================== -----BEGIN CERTIFICATE----- MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYxCzAJBgNVBAYT AkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQDExNHbG9iYWxTaWduIFJvb3Qg RTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNV BAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcq hkjOPQIBBgUrgQQAIgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkB jtjqR+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGddyXqBPCCj QjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQxCpCPtsad0kRL gLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZk vLtoURMMA/cVi4RguYv/Uo7njLwcAjA8+RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+ CAezNIm8BZ/3Hobui3A= -----END CERTIFICATE----- GLOBALTRUST 2020 ================ -----BEGIN CERTIFICATE----- MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkGA1UEBhMCQVQx IzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVT VCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYxMDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAh BgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAy MDIwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWi D59bRatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9ZYybNpyrO VPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3QWPKzv9pj2gOlTblzLmM CcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPwyJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCm fecqQjuCgGOlYx8ZzHyyZqjC0203b+J+BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKA A1GqtH6qRNdDYfOiaxaJSaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9OR JitHHmkHr96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj04KlG DfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9MedKZssCz3AwyIDMvU clOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIwq7ejMZdnrY8XD2zHc+0klGvIg5rQ mjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUw AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1Ud IwQYMBaAFNwuH9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJCXtzoRlgHNQIw 4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd6IwPS3BD0IL/qMy/pJTAvoe9 iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf+I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS 8cE54+X1+NZK3TTN+2/BT+MAi1bikvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2 HcqtbepBEX4tdJP7wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxS vTOBTI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6CMUO+1918 oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn4rnvyOL2NSl6dPrFf4IF YqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+IaFvowdlxfv1k7/9nR4hYJS8+hge9+6jl gqispdNpQ80xiEmEU5LAsTkbOYMBMMTyqfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg== -----END CERTIFICATE----- ANF Secure Server Root CA ========================= -----BEGIN CERTIFICATE----- MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNVBAUTCUc2MzI4 NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lv bjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNVBAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3Qg Q0EwHhcNMTkwOTA0MTAwMDM4WhcNMzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEw MQswCQYDVQQGEwJFUzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQw EgYDVQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9vdCBDQTCC AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCjcqQZAZ2cC4Ffc0m6p6zz BE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9qyGFOtibBTI3/TO80sh9l2Ll49a2pcbnv T1gdpd50IJeh7WhM3pIXS7yr/2WanvtH2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcv B2VSAKduyK9o7PQUlrZXH1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXse zx76W0OLzc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyRp1RM VwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQzW7i1o0TJrH93PB0j 7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/SiOL9V8BY9KHcyi1Swr1+KuCLH5z JTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJnLNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe 8TZBAQIvfXOn3kLMTOmJDVb3n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVO Hj1tyRRM4y5Bu8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAOBgNVHQ8BAf8E BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEATh65isagmD9uw2nAalxJ UqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzx j6ptBZNscsdW699QIyjlRRA96Gejrw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDt dD+4E5UGUcjohybKpFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM 5gf0vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjqOknkJjCb 5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ/zo1PqVUSlJZS2Db7v54 EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ92zg/LFis6ELhDtjTO0wugumDLmsx2d1H hk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI+PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGy g77FGr8H6lnco4g175x2MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3 r5+qPeoott7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw= -----END CERTIFICATE----- Certum EC-384 CA ================ -----BEGIN CERTIFICATE----- MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQswCQYDVQQGEwJQ TDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2Vy dGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2 MDcyNDU0WhcNNDMwMzI2MDcyNDU0WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERh dGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx GTAXBgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATEKI6rGFtq vm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7TmFy8as10CW4kjPMIRBSqn iBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68KjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD VR0OBBYEFI0GZnQkdjrzife81r1HfS+8EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNo ADBlAjADVS2m5hjEfO/JUG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0 QoSZ/6vnnvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k= -----END CERTIFICATE----- Certum Trusted Root CA ====================== -----BEGIN CERTIFICATE----- MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6MQswCQYDVQQG EwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0g Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0Ew HhcNMTgwMzE2MTIxMDEzWhcNNDMwMzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMY QXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBB dXRob3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEB AQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZn0EGze2jusDbCSzBfN8p fktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/qp1x4EaTByIVcJdPTsuclzxFUl6s1wB52 HO8AU5853BSlLCIls3Jy/I2z5T4IHhQqNwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2 fJmItdUDmj0VDT06qKhF8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGt g/BKEiJ3HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGamqi4 NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi7VdNIuJGmj8PkTQk fVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSFytKAQd8FqKPVhJBPC/PgP5sZ0jeJ P/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0PqafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSY njYJdmZm/Bo/6khUHL4wvYBQv3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHK HRzQ+8S1h9E6Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1 vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQADggIBAEii1QAL LtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4WxmB82M+w85bj/UvXgF2Ez8s ALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvozMrnadyHncI013nR03e4qllY/p0m+jiGPp2K h2RX5Rc64vmNueMzeMGQ2Ljdt4NR5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8 CYyqOhNf6DR5UMEQGfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA 4kZf5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq0Uc9Nneo WWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7DP78v3DSk+yshzWePS/Tj 6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTMqJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmT OPQD8rv7gmsHINFSH5pkAnuYZttcTVoP0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZck bxJF0WddCajJFdr60qZfE2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb -----END CERTIFICATE----- TunTrust Root CA ================ -----BEGIN CERTIFICATE----- MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQELBQAwYTELMAkG A1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUgQ2VydGlmaWNhdGlvbiBFbGVj dHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJvb3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQw NDI2MDg1NzU2WjBhMQswCQYDVQQGEwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBD ZXJ0aWZpY2F0aW9uIEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIw DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZn56eY+hz 2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd2JQDoOw05TDENX37Jk0b bjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgFVwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7 NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZGoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAd gjH8KcwAWJeRTIAAHDOFli/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViW VSHbhlnUr8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2eY8f Tpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIbMlEsPvLfe/ZdeikZ juXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISgjwBUFfyRbVinljvrS5YnzWuioYas DXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwS VXAkPcvCFDVDXSdOvsC9qnyW5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI 04Y+oXNZtPdEITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0 90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+zxiD2BkewhpMl 0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYuQEkHDVneixCwSQXi/5E/S7fd Ao74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRY YdZ2vyJ/0Adqp2RT8JeNnYA/u8EH22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJp adbGNjHh/PqAulxPxOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65x xBzndFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5Xc0yGYuP jCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7bnV2UqL1g52KAdoGDDIzM MEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQCvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9z ZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZHu/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3r AZ3r2OvEhJn7wAzMMujjd9qDRIueVSjAi1jTkD5OGwDxFa2DK5o= -----END CERTIFICATE----- HARICA TLS RSA Root CA 2021 =========================== -----BEGIN CERTIFICATE----- MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQG EwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u cyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0EgUm9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUz OFoXDTQ1MDIxMzEwNTUzN1owbDELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRl bWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNB IFJvb3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569lmwVnlskN JLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE4VGC/6zStGndLuwRo0Xu a2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uva9of08WRiFukiZLRgeaMOVig1mlDqa2Y Ulhu2wr7a89o+uOkXjpFc5gH6l8Cct4MpbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K 5FrZx40d/JiZ+yykgmvwKh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEv dmn8kN3bLW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcYAuUR 0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqBAGMUuTNe3QvboEUH GjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYqE613TBoYm5EPWNgGVMWX+Ko/IIqm haZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHrW2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQ CPxrvrNQKlr9qEgYRtaQQJKQCoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8G A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAUX15QvWiWkKQU EapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3f5Z2EMVGpdAgS1D0NTsY9FVq QRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxajaH6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxD QpSbIPDRzbLrLFPCU3hKTwSUQZqPJzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcR j88YxeMn/ibvBZ3PzzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5 vZStjBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0/L5H9MG0 qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pTBGIBnfHAT+7hOtSLIBD6 Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79aPib8qXPMThcFarmlwDB31qlpzmq6YR/ PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YWxw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnn kf3/W9b3raYvAwtt41dU63ZTGI0RmLo= -----END CERTIFICATE----- HARICA TLS ECC Root CA 2021 =========================== -----BEGIN CERTIFICATE----- MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQswCQYDVQQGEwJH UjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBD QTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9vdCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoX DTQ1MDIxMzExMDEwOVowbDELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWlj IGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJv b3QgQ0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7KKrxcm1l AEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9YSTHMmE5gEYd103KUkE+b ECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW 0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAi rcJRQO9gcS3ujwLEXQNwSaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/Qw CZ61IygNnxS2PFOiTAZpffpskcYqSUXm7LcT4Tps -----END CERTIFICATE----- Autoridad de Certificacion Firmaprofesional CIF A62634068 ========================================================= -----BEGIN CERTIFICATE----- MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCRVMxQjBA BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIw QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY 7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1Ud DgQWBBRlzeurNR4APn7VdMActHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4w gZswgZgGBFUdIAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABCAG8AbgBhAG4A bwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAwADEANzAOBgNVHQ8BAf8EBAMC AQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9miWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL 4QjbEwj4KKE1soCzC1HA01aajTNFSa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDb LIpgD7dvlAceHabJhfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1il I45PVf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZEEAEeiGaP cjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV1aUsIC+nmCjuRfzxuIgA LI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2tCsvMo2ebKHTEm9caPARYpoKdrcd7b/+A lun4jWq9GJAd/0kakFI3ky88Al2CdgtR5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH 9IBk9W6VULgRfhVwOEqwf9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpf NIbnYrX9ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNKGbqE ZycPvEJdvSRUDewdcAZfpLz6IHxV -----END CERTIFICATE----- vTrus ECC Root CA ================= -----BEGIN CERTIFICATE----- MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMwRzELMAkGA1UE BhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBS b290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDczMTA3MjY0NFowRzELMAkGA1UEBhMCQ04xHDAa BgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBSb290IENBMHYw EAYHKoZIzj0CAQYFK4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9JpRCux3NCNtzslt188+c ToL0v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A2MMrMudwpremIFUde4BdS49n TPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHbH88wDwYDVR0TAQH/BAUwAwEB/zAO BgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIwV53dVvHH4+m4SVBrm2nDb+zDfSXkV5UT QJtS0zvzQBm8JsctBp61ezaf9SXUY2sAAjEA6dPGnlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQL YgmRWAD5Tfs0aNoJrSEGGJTO -----END CERTIFICATE----- vTrus Root CA ============= -----BEGIN CERTIFICATE----- MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQELBQAwQzELMAkG A1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xFjAUBgNVBAMTDXZUcnVzIFJv b3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMxMDcyNDA1WjBDMQswCQYDVQQGEwJDTjEcMBoG A1UEChMTaVRydXNDaGluYSBDby4sTHRkLjEWMBQGA1UEAxMNdlRydXMgUm9vdCBDQTCCAiIwDQYJ KoZIhvcNAQEBBQADggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB6ksDXhA/kFocizuwZots SKYcIrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoRuBLpDLvPbmyAhykUAyyNJJrI ZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6WTEN+VRS+GrPSbcKvdmaVayqwlHeF XgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z98Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KA YPxMvDVTAWqXcoKv8R1w6Jz1717CbMdHflqUhSZNO7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70 kLJrxLT5ZOrpGgrIDajtJ8nU57O5q4IikCc9Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2 AXPKBlim0zvc+gRGE1WKyURHuFE5Gi7oNOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZNpGvu /9ROutW04o5IWgAZCfEF2c6Rsffr6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQUqqzApVg+QxMaPnu 1RcN+HFXtSXkKe5lXa/R7jwXC1pDxaWG6iSe4gUH3DRCEpHWOXSuTEGC2/KmSNGzm/MzqvOmwMVO 9fSddmPmAsYiS8GVP1BkLFTltvA8Kc9XAgMBAAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYg scasGrz2iTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOC AgEAKbqSSaet8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWd nxEt/Hlk3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1jbhd47F18iMjr jld22VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvMKar5CKXiNxTKsbhm7xqC5PD4 8acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIivTDUHKgLKeBRtRytAVunLKmChZwOgzoy8sHJn xDHO2zTlJQNgJXtxmOTAGytfdELSS8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVTsMo554Wg icEFOwE30z9J4nfrI8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7IdMakLXogqzu4 sEb9b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9mK5S4fNBUvupLnKW nyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1PTi07NEPhmg4NpGaXutIc SkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929vensBxXVsFy6K2ir40zSbofitzmdHxghm+H l3s= -----END CERTIFICATE----- ISRG Root X2 ============ -----BEGIN CERTIFICATE----- MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQswCQYDVQQGEwJV UzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElT UkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVT MSkwJwYDVQQKEyBJbnRlcm5ldCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNS RyBSb290IFgyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0H ttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9ItgKbppb d9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV HQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZIzj0EAwMDaAAwZQIwe3lORlCEwkSHRhtF cP9Ymd70/aTSVaYgLXTWNLxBo1BfASdWtL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5 U6VR5CmD1/iQMVtCnwr1/q4AaOeMSQ+2b1tbFfLn -----END CERTIFICATE----- HiPKI Root CA - G1 ================== -----BEGIN CERTIFICATE----- MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBPMQswCQYDVQQG EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xGzAZBgNVBAMMEkhpUEtJ IFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRaFw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYT AlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kg Um9vdCBDQSAtIEcxMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0 o9QwqNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twvVcg3Px+k wJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6lZgRZq2XNdZ1AYDgr/SE YYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnzQs7ZngyzsHeXZJzA9KMuH5UHsBffMNsA GJZMoYFL3QRtU6M9/Aes1MU3guvklQgZKILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfd hSi8MEyr48KxRURHH+CKFgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj 1jOXTyFjHluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDry+K4 9a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ/W3c1pzAtH2lsN0/ Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgMa/aOEmem8rJY5AIJEzypuxC00jBF 8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYD VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQD AgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi 7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqcSE5XCV0vrPSl tJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6FzaZsT0pPBWGTMpWmWSBUdGSquE wx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9TcXzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07Q JNBAsNB1CI69aO4I1258EHBGG3zgiLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv 5wiZqAxeJoBF1PhoL5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+Gpz jLrFNe85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wrkkVbbiVg hUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+vhV4nYWBSipX3tUZQ9rb yltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQUYDksswBVLuT1sw5XxJFBAJw/6KXf6vb/ yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ== -----END CERTIFICATE----- GlobalSign ECC Root CA - R4 =========================== -----BEGIN CERTIFICATE----- MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYDVQQLExtHbG9i YWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds b2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgwMTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9i YWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds b2JhbFNpZ24wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkW ymOxuYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNVHQ8BAf8E BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/+wpu+74zyTyjhNUwCgYI KoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147bmF0774BxL4YSFlhgjICICadVGNA3jdg UM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm -----END CERTIFICATE----- GTS Root R1 =========== -----BEGIN CERTIFICATE----- MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQGEwJV UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg UjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0G CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7wCl7raKb0 xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjwTcLCeoiKu7rPWRnWr4+w B7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0PfyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXW nOunVmSPlk9orj2XwoSPwLxAwAtcvfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk 9+aCEI3oncKKiPo4Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zq kUspzBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92wO1A K/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70paDPvOmbsB4om3xPX V2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDW cfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0T AQH/BAUwAwEB/zAdBgNVHQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQAD ggIBAJ+qQibbC5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuyh6f88/qBVRRi ClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM47HLwEXWdyzRSjeZ2axfG34ar J45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8JZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYci NuaCp+0KueIHoI17eko8cdLiA6EfMgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5me LMFrUKTX5hgUvYU/Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJF fbdT6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ0E6yove+ 7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm2tIMPNuzjsmhDYAPexZ3 FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bbbP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3 gm3c -----END CERTIFICATE----- GTS Root R2 =========== -----BEGIN CERTIFICATE----- MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQGEwJV UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg UjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0G CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3Lv CvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY6Dlo7JUl e3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAuMC6C/Pq8tBcKSOWIm8Wb a96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7kRXuJVfeKH2JShBKzwkCX44ofR5GmdFrS +LFjKBC4swm4VndAoiaYecb+3yXuPuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7M kogwTZq9TwtImoS1mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJG r61K8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RWIr9q S34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKaG73VululycslaVNV J1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCqgc7dGtxRcw1PcOnlthYhGXmy5okL dWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0T AQH/BAUwAwEB/zAdBgNVHQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQAD ggIBAB/Kzt3HvqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8 0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyCB19m3H0Q/gxh swWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2uNmSRXbBoGOqKYcl3qJfEycel /FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMgyALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVn jWQye+mew4K6Ki3pHrTgSAai/GevHyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y5 9PYjJbigapordwj6xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M 7YNRTOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924SgJPFI/2R8 0L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV7LXTWtiBmelDGDfrs7vR WGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjW HYbL -----END CERTIFICATE----- GTS Root R3 =========== -----BEGIN CERTIFICATE----- MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJVUzEi MCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMw HhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZ R29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjO PQIBBgUrgQQAIgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout 736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL24CejQjBA MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTB8Sa6oC2uhYHP0/Eq Er24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azT L818+FsuVbu/3ZL3pAzcMeGiAjEA/JdmZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV 11RZt+cRLInUue4X -----END CERTIFICATE----- GTS Root R4 =========== -----BEGIN CERTIFICATE----- MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJVUzEi MCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQw HhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZ R29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjO PQIBBgUrgQQAIgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzu hXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvRHYqjQjBA MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSATNbrdP9JNqPV2Py1 PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/C r8deVl5c1RxYIigL9zC2L7F8AjEA8GE8p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh 4rsUecrNIdSUtUlD -----END CERTIFICATE----- Telia Root CA v2 ================ -----BEGIN CERTIFICATE----- MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQxCzAJBgNVBAYT AkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2 MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1NTRaMEQxCzAJBgNVBAYTAkZJMRowGAYDVQQK DBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2MjCCAiIwDQYJKoZI hvcNAQEBBQADggIPADCCAgoCggIBALLQPwe84nvQa5n44ndp586dpAO8gm2h/oFlH0wnrI4AuhZ7 6zBqAMCzdGh+sq/H1WKzej9Qyow2RCRj0jbpDIX2Q3bVTKFgcmfiKDOlyzG4OiIjNLh9vVYiQJ3q 9HsDrWj8soFPmNB06o3lfc1jw6P23pLCWBnglrvFxKk9pXSW/q/5iaq9lRdU2HhE8Qx3FZLgmEKn pNaqIJLNwaCzlrI6hEKNfdWV5Nbb6WLEWLN5xYzTNTODn3WhUidhOPFZPY5Q4L15POdslv5e2QJl tI5c0BE0312/UqeBAMN/mUWZFdUXyApT7GPzmX3MaRKGwhfwAZ6/hLzRUssbkmbOpFPlob/E2wnW 5olWK8jjfN7j/4nlNW4o6GwLI1GpJQXrSPjdscr6bAhR77cYbETKJuFzxokGgeWKrLDiKca5JLNr RBH0pUPCTEPlcDaMtjNXepUugqD0XBCzYYP2AgWGLnwtbNwDRm41k9V6lS/eINhbfpSQBGq6WT0E BXWdN6IOLj3rwaRSg/7Qa9RmjtzG6RJOHSpXqhC8fF6CfaamyfItufUXJ63RDolUK5X6wK0dmBR4 M0KGCqlztft0DbcbMBnEWg4cJ7faGND/isgFuvGqHKI3t+ZIpEYslOqodmJHixBTB0hXbOKSTbau BcvcwUpej6w9GU7C7WB1K9vBykLVAgMBAAGjYzBhMB8GA1UdIwQYMBaAFHKs5DN5qkWH9v2sHZ7W xy+G2CQ5MB0GA1UdDgQWBBRyrOQzeapFh/b9rB2e1scvhtgkOTAOBgNVHQ8BAf8EBAMCAQYwDwYD VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAoDtZpwmUPjaE0n4vOaWWl/oRrfxn83EJ 8rKJhGdEr7nv7ZbsnGTbMjBvZ5qsfl+yqwE2foH65IRe0qw24GtixX1LDoJt0nZi0f6X+J8wfBj5 tFJ3gh1229MdqfDBmgC9bXXYfef6xzijnHDoRnkDry5023X4blMMA8iZGok1GTzTyVR8qPAs5m4H eW9q4ebqkYJpCh3DflminmtGFZhb069GHWLIzoBSSRE/yQQSwxN8PzuKlts8oB4KtItUsiRnDe+C y748fdHif64W1lZYudogsYMVoe+KTTJvQS8TUoKU1xrBeKJR3Stwbbca+few4GeXVtt8YVMJAygC QMez2P2ccGrGKMOF6eLtGpOg3kuYooQ+BXcBlj37tCAPnHICehIv1aO6UXivKitEZU61/Qrowc15 h2Er3oBXRb9n8ZuRXqWk7FlIEA04x7D6w0RtBPV4UBySllva9bguulvP5fBqnUsvWHMtTy3EHD70 sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pTVmBds9hCG1xLEooc6+t9 xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAWysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQ raVplI/owd8k+BsHMYeB2F326CjYSlKArBPuUBQemMc= -----END CERTIFICATE----- D-TRUST BR Root CA 1 2020 ========================= -----BEGIN CERTIFICATE----- MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQswCQYDVQQGEwJE RTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEJSIFJvb3QgQ0EgMSAy MDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5NDQ1OVowSDELMAkGA1UEBhMCREUxFTATBgNV BAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDEgMjAyMDB2MBAG ByqGSM49AgEGBSuBBAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE1HaTJddZO0Flax7mNCq7 dPYSzuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJGT11NIXe7WB9xwy0QVK5buXu QqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHOREKv/VbNafAkl1bK6CKBrqx9t MA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6gPKA6hjhodHRwOi8vY3JsLmQtdHJ1c3Qu bmV0L2NybC9kLXRydXN0X2JyX3Jvb3RfY2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVj dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxP PUQtVHJ1c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjOPQQD AwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFWwKrY7RjEsK70Pvom AjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHVdWNbFJWcHwHP2NVypw87 -----END CERTIFICATE----- D-TRUST EV Root CA 1 2020 ========================= -----BEGIN CERTIFICATE----- MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQswCQYDVQQGEwJE RTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEVWIFJvb3QgQ0EgMSAy MDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5NTk1OVowSDELMAkGA1UEBhMCREUxFTATBgNV BAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBFViBSb290IENBIDEgMjAyMDB2MBAG ByqGSM49AgEGBSuBBAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL46bSj8WeeHsxiamJrSc8 ZRCC/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3VlSSowZ/Rk99Yad9rDwpdhQntJ raOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH8QARY3OqQo5FD4pPfsazK2/umL MA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6gPKA6hjhodHRwOi8vY3JsLmQtdHJ1c3Qu bmV0L2NybC9kLXRydXN0X2V2X3Jvb3RfY2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVj dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxP PUQtVHJ1c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjOPQQD AwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CAy/m0sRtW9XLS/BnR AjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJbgfM0agPnIjhQW+0ZT0MW -----END CERTIFICATE----- DigiCert TLS ECC P384 Root G5 ============================= -----BEGIN CERTIFICATE----- MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQswCQYDVQQGEwJV UzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURpZ2lDZXJ0IFRMUyBFQ0MgUDM4 NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMx FzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQg Um9vdCBHNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1Tzvd lHJS7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp0zVozptj n4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICISB4CIfBFqMA4GA1UdDwEB /wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQCJao1H5+z8blUD2Wds Jk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQLgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIx AJSdYsiJvRmEFOml+wG4DXZDjC5Ty3zfDBeWUA== -----END CERTIFICATE----- DigiCert TLS RSA4096 Root G5 ============================ -----BEGIN CERTIFICATE----- MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBNMQswCQYDVQQG EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0 MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcNNDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJV UzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2 IFJvb3QgRzUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS8 7IE+ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG02C+JFvuU AT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgpwgscONyfMXdcvyej/Ces tyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZMpG2T6T867jp8nVid9E6P/DsjyG244gXa zOvswzH016cpVIDPRFtMbzCe88zdH5RDnU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnV DdXifBBiqmvwPXbzP6PosMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9q TXeXAaDxZre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cdLvvy z6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvXKyY//SovcfXWJL5/ MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNeXoVPzthwiHvOAbWWl9fNff2C+MIk wcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPLtgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4E FgQUUTMc7TZArxfTJc1paPKvTiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8w DQYJKoZIhvcNAQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7HPNtQOa27PShN lnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLFO4uJ+DQtpBflF+aZfTCIITfN MBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQREtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/ u4cnYiWB39yhL/btp/96j1EuMPikAdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9G OUrYU9DzLjtxpdRv/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh 47a+p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilwMUc/dNAU FvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WFqUITVuwhd4GTWgzqltlJ yqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCKovfepEWFJqgejF0pW8hL2JpqA15w8oVP bEtoL8pU9ozaMv7Da4M/OMZ+ -----END CERTIFICATE----- Certainly Root R1 ================= -----BEGIN CERTIFICATE----- MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAwPTELMAkGA1UE BhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2VydGFpbmx5IFJvb3QgUjEwHhcN MjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2Vy dGFpbmx5MRowGAYDVQQDExFDZXJ0YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIP ADCCAgoCggIBANA21B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O 5MQTvqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbedaFySpvXl 8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b01C7jcvk2xusVtyWMOvwl DbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGI XsXwClTNSaa/ApzSRKft43jvRl5tcdF5cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkN KPl6I7ENPT2a/Z2B7yyQwHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQ AjeZjOVJ6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA2Cnb rlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyHWyf5QBGenDPBt+U1 VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMReiFPCyEQtkA6qyI6BJyLm4SGcprS p6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud DgQWBBTgqj8ljZ9EXME66C6ud0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAsz HQNTVfSVcOQrPbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d 8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi1wrykXprOQ4v MMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrdrRT90+7iIgXr0PK3aBLXWopB GsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9ditaY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+ gjwN/KUD+nsa2UUeYNrEjvn8K8l7lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgH JBu6haEaBQmAupVjyTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7 fpYnKx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLyyCwzk5Iw x06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5nwXARPbv0+Em34yaXOp/S X3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6OV+KmalBWQewLK8= -----END CERTIFICATE----- Certainly Root E1 ================= -----BEGIN CERTIFICATE----- MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQswCQYDVQQGEwJV UzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlubHkgUm9vdCBFMTAeFw0yMTA0 MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJBgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlu bHkxGjAYBgNVBAMTEUNlcnRhaW5seSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4 fxzf7flHh4axpMCK+IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9 YBk2QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8EBAMCAQYw DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4hevIIgcwCgYIKoZIzj0E AwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozmut6Dacpps6kFtZaSF4fC0urQe87YQVt8 rgIwRt7qy12a7DLCZRawTDBcMPPaTnOGBtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR -----END CERTIFICATE----- Security Communication ECC RootCA1 ================================== -----BEGIN CERTIFICATE----- MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYTAkpQMSUwIwYD VQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYDVQQDEyJTZWN1cml0eSBDb21t dW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYxNjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTEL MAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNV BAMTIlNlY3VyaXR5IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQA IgNiAASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+CnnfdldB9sELLo 5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpKULGjQjBAMB0GA1UdDgQW BBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAK BggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3L snNdo4gIxwwCMQDAqy0Obe0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70e N9k= -----END CERTIFICATE----- BJCA Global Root CA1 ==================== -----BEGIN CERTIFICATE----- MIIFdDCCA1ygAwIBAgIQVW9l47TZkGobCdFsPsBsIDANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQG EwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJVFkxHTAbBgNVBAMMFEJK Q0EgR2xvYmFsIFJvb3QgQ0ExMB4XDTE5MTIxOTAzMTYxN1oXDTQ0MTIxMjAzMTYxN1owVDELMAkG A1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQD DBRCSkNBIEdsb2JhbCBSb290IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPFm CL3ZxRVhy4QEQaVpN3cdwbB7+sN3SJATcmTRuHyQNZ0YeYjjlwE8R4HyDqKYDZ4/N+AZspDyRhyS sTphzvq3Rp4Dhtczbu33RYx2N95ulpH3134rhxfVizXuhJFyV9xgw8O558dnJCNPYwpj9mZ9S1Wn P3hkSWkSl+BMDdMJoDIwOvqfwPKcxRIqLhy1BDPapDgRat7GGPZHOiJBhyL8xIkoVNiMpTAK+BcW yqw3/XmnkRd4OJmtWO2y3syJfQOcs4ll5+M7sSKGjwZteAf9kRJ/sGsciQ35uMt0WwfCyPQ10WRj eulumijWML3mG90Vr4TqnMfK9Q7q8l0ph49pczm+LiRvRSGsxdRpJQaDrXpIhRMsDQa4bHlW/KNn MoH1V6XKV0Jp6VwkYe/iMBhORJhVb3rCk9gZtt58R4oRTklH2yiUAguUSiz5EtBP6DF+bHq/pj+b OT0CFqMYs2esWz8sgytnOYFcuX6U1WTdno9uruh8W7TXakdI136z1C2OVnZOz2nxbkRs1CTqjSSh GL+9V/6pmTW12xB3uD1IutbB5/EjPtffhZ0nPNRAvQoMvfXnjSXWgXSHRtQpdaJCbPdzied9v3pK H9MiyRVVz99vfFXQpIsHETdfg6YmV6YBW37+WGgHqel62bno/1Afq8K0wM7o6v0PvY1NuLxxAgMB AAGjQjBAMB0GA1UdDgQWBBTF7+3M2I0hxkjk49cULqcWk+WYATAPBgNVHRMBAf8EBTADAQH/MA4G A1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAUoKsITQfI/Ki2Pm4rzc2IInRNwPWaZ+4 YRC6ojGYWUfo0Q0lHhVBDOAqVdVXUsv45Mdpox1NcQJeXyFFYEhcCY5JEMEE3KliawLwQ8hOnThJ dMkycFRtwUf8jrQ2ntScvd0g1lPJGKm1Vrl2i5VnZu69mP6u775u+2D2/VnGKhs/I0qUJDAnyIm8 60Qkmss9vk/Ves6OF8tiwdneHg56/0OGNFK8YT88X7vZdrRTvJez/opMEi4r89fO4aL/3Xtw+zuh TaRjAv04l5U/BXCga99igUOLtFkNSoxUnMW7gZ/NfaXvCyUeOiDbHPwfmGcCCtRzRBPbUYQaVQNW 4AB+dAb/OMRyHdOoP2gxXdMJxy6MW2Pg6Nwe0uxhHvLe5e/2mXZgLR6UcnHGCyoyx5JO1UbXHfmp GQrI+pXObSOYqgs4rZpWDW+N8TEAiMEXnM0ZNjX+VVOg4DwzX5Ze4jLp3zO7Bkqp2IRzznfSxqxx 4VyjHQy7Ct9f4qNx2No3WqB4K/TUfet27fJhcKVlmtOJNBir+3I+17Q9eVzYH6Eze9mCUAyTF6ps 3MKCuwJXNq+YJyo5UOGwifUll35HaBC07HPKs5fRJNz2YqAo07WjuGS3iGJCz51TzZm+ZGiPTx4S SPfSKcOYKMryMguTjClPPGAyzQWWYezyr/6zcCwupvI= -----END CERTIFICATE----- BJCA Global Root CA2 ==================== -----BEGIN CERTIFICATE----- MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQswCQYDVQQGEwJD TjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJVFkxHTAbBgNVBAMMFEJKQ0Eg R2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgyMVoXDTQ0MTIxMjAzMTgyMVowVDELMAkGA1UE BhMCQ04xJjAkBgNVBAoMHUJFSUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRC SkNBIEdsb2JhbCBSb290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ3LgJGNU2e1uVCxA/jl SR9BIgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK++kpRuDCK /eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0OBBYEFNJKsVF/BvDRgh9Obl+rg/xI 1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8 W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8g UXOQwKhbYdDFUDn9hf7B43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w== -----END CERTIFICATE----- Sectigo Public Server Authentication Root E46 ============================================= -----BEGIN CERTIFICATE----- MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQswCQYDVQQGEwJH QjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBTZXJ2 ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5 WjBfMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0 aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUr gQQAIgNiAAR2+pmpbiDt+dd34wc7qNs9Xzjoq1WmVk/WSOrsfy2qw7LFeeyZYX8QeccCWvkEN/U0 NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+6xnOQ6OjQjBAMB0GA1Ud DgQWBBTRItpMWfFLXyY4qp3W7usNw/upYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB /zAKBggqhkjOPQQDAwNnADBkAjAn7qRaqCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RH lAFWovgzJQxC36oCMB3q4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21U SAGKcw== -----END CERTIFICATE----- Sectigo Public Server Authentication Root R46 ============================================= -----BEGIN CERTIFICATE----- MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBfMQswCQYDVQQG EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBT ZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwHhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1 OTU5WjBfMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3 DQEBAQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDaef0rty2k 1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnzSDBh+oF8HqcIStw+Kxwf GExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xfiOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMP FF1bFOdLvt30yNoDN9HWOaEhUTCDsG3XME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vu ZDCQOc2TZYEhMbUjUDM3IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5Qaz Yw6A3OASVYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgESJ/A wSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu+Zd4KKTIRJLpfSYF plhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt8uaZFURww3y8nDnAtOFr94MlI1fZ EoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+LHaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW 6aWWrL3DkJiy4Pmi1KZHQ3xtzwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWI IUkwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQYKlJfp/imTYp E0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52gDY9hAaLMyZlbcp+nv4fjFg4 exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZAFv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M 0ejf5lG5Nkc/kLnHvALcWxxPDkjBJYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI 84HxZmduTILA7rpXDhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9m pFuiTdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5dHn5Hrwd Vw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65LvKRRFHQV80MNNVIIb/b E/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmm J1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAYQqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL -----END CERTIFICATE----- SSL.com TLS RSA Root CA 2022 ============================ -----BEGIN CERTIFICATE----- MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQG EwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxTU0wuY29tIFRMUyBSU0Eg Um9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloXDTQ2MDgxOTE2MzQyMVowTjELMAkGA1UEBhMC VVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgUlNBIFJv b3QgQ0EgMjAyMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANCkCXJPQIgSYT41I57u 9nTPL3tYPc48DRAokC+X94xI2KDYJbFMsBFMF3NQ0CJKY7uB0ylu1bUJPiYYf7ISf5OYt6/wNr/y 7hienDtSxUcZXXTzZGbVXcdotL8bHAajvI9AI7YexoS9UcQbOcGV0insS657Lb85/bRi3pZ7Qcac oOAGcvvwB5cJOYF0r/c0WRFXCsJbwST0MXMwgsadugL3PnxEX4MN8/HdIGkWCVDi1FW24IBydm5M R7d1VVm0U3TZlMZBrViKMWYPHqIbKUBOL9975hYsLfy/7PO0+r4Y9ptJ1O4Fbtk085zx7AGL0SDG D6C1vBdOSHtRwvzpXGk3R2azaPgVKPC506QVzFpPulJwoxJF3ca6TvvC0PeoUidtbnm1jPx7jMEW TO6Af77wdr5BUxIzrlo4QqvXDz5BjXYHMtWrifZOZ9mxQnUjbvPNQrL8VfVThxc7wDNY8VLS+YCk 8OjwO4s4zKTGkH8PnP2L0aPP2oOnaclQNtVcBdIKQXTbYxE3waWglksejBYSd66UNHsef8JmAOSq g+qKkK3ONkRN0VHpvB/zagX9wHQfJRlAUW7qglFA35u5CCoGAtUjHBPW6dvbxrB6y3snm/vg1UYk 7RBLY0ulBY+6uB0rpvqR4pJSvezrZ5dtmi2fgTIFZzL7SAg/2SW4BCUvAgMBAAGjYzBhMA8GA1Ud EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU+y437uOEeicuzRk1sTN8/9REQrkwHQYDVR0OBBYEFPsu N+7jhHonLs0ZNbEzfP/UREK5MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAjYlt hEUY8U+zoO9opMAdrDC8Z2awms22qyIZZtM7QbUQnRC6cm4pJCAcAZli05bg4vsMQtfhWsSWTVTN j8pDU/0quOr4ZcoBwq1gaAafORpR2eCNJvkLTqVTJXojpBzOCBvfR4iyrT7gJ4eLSYwfqUdYe5by iB0YrrPRpgqU+tvT5TgKa3kSM/tKWTcWQA673vWJDPFs0/dRa1419dvAJuoSc06pkZCmF8NsLzjU o3KUQyxi4U5cMj29TH0ZR6LDSeeWP4+a0zvkEdiLA9z2tmBVGKaBUfPhqBVq6+AL8BQx1rmMRTqo ENjwuSfr98t67wVylrXEj5ZzxOhWc5y8aVFjvO9nHEMaX3cZHxj4HCUp+UmZKbaSPaKDN7Egkaib MOlqbLQjk2UEqxHzDh1TJElTHaE/nUiSEeJ9DU/1172iWD54nR4fK/4huxoTtrEoZP2wAgDHbICi vRZQIA9ygV/MlP+7mea6kMvq+cYMwq7FGc4zoWtcu358NFcXrfA/rs3qr5nsLFR+jM4uElZI7xc7 P0peYNLcdDa8pUNjyw9bowJWCZ4kLOGGgYz+qxcs+sjiMho6/4UIyYOf8kpIEFR3N+2ivEC+5BB0 9+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA= -----END CERTIFICATE----- SSL.com TLS ECC Root CA 2022 ============================ -----BEGIN CERTIFICATE----- MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQswCQYDVQQGEwJV UzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxTU0wuY29tIFRMUyBFQ0MgUm9v dCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2MDgxOTE2MzM0N1owTjELMAkGA1UEBhMCVVMx GDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgRUNDIFJvb3Qg Q0EgMjAyMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABEUpNXP6wrgjzhR9qLFNoFs27iosU8NgCTWy JGYmacCzldZdkkAZDsalE3D07xJRKF3nzL35PIXBz5SQySvOkkJYWWf9lCcQZIxPBLFNSeR7T5v1 5wj4A4j3p8OSSxlUgaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSJjy+j6CugFFR7 81a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NWuCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGG MAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w 7deedWo1dlJF4AIxAMeNb0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5 Zn6g6g== -----END CERTIFICATE----- Atos TrustedRoot Root CA ECC TLS 2021 ===================================== -----BEGIN CERTIFICATE----- MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4wLAYDVQQDDCVB dG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0wCwYDVQQKDARBdG9zMQswCQYD VQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0MTcwOTI2MjJaMEwxLjAsBgNVBAMMJUF0b3Mg VHJ1c3RlZFJvb3QgUm9vdCBDQSBFQ0MgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYT AkRFMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEloZYKDcKZ9Cg3iQZGeHkBQcfl+3oZIK59sRxUM6K DP/XtXa7oWyTbIOiaG6l2b4siJVBzV3dscqDY4PMwL502eCdpO5KTlbgmClBk1IQ1SQ4AjJn8ZQS b+/Xxd4u/RmAo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR2KCXWfeBmmnoJsmo7jjPX NtNPojAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMDaAAwZQIwW5kp85wxtolrbNa9d+F851F+ uDrNozZffPc8dz7kUK2o59JZDCaOMDtuCCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGY a3cpetskz2VAv9LcjBHo9H1/IISpQuQo -----END CERTIFICATE----- Atos TrustedRoot Root CA RSA TLS 2021 ===================================== -----BEGIN CERTIFICATE----- MIIFZDCCA0ygAwIBAgIQU9XP5hmTC/srBRLYwiqipDANBgkqhkiG9w0BAQwFADBMMS4wLAYDVQQD DCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgUlNBIFRMUyAyMDIxMQ0wCwYDVQQKDARBdG9zMQsw CQYDVQQGEwJERTAeFw0yMTA0MjIwOTIxMTBaFw00MTA0MTcwOTIxMDlaMEwxLjAsBgNVBAMMJUF0 b3MgVHJ1c3RlZFJvb3QgUm9vdCBDQSBSU0EgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNV BAYTAkRFMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtoAOxHm9BYx9sKOdTSJNy/BB l01Z4NH+VoyX8te9j2y3I49f1cTYQcvyAh5x5en2XssIKl4w8i1mx4QbZFc4nXUtVsYvYe+W/CBG vevUez8/fEc4BKkbqlLfEzfTFRVOvV98r61jx3ncCHvVoOX3W3WsgFWZkmGbzSoXfduP9LVq6hdK ZChmFSlsAvFr1bqjM9xaZ6cF4r9lthawEO3NUDPJcFDsGY6wx/J0W2tExn2WuZgIWWbeKQGb9Cpt 0xU6kGpn8bRrZtkh68rZYnxGEFzedUlnnkL5/nWpo63/dgpnQOPF943HhZpZnmKaau1Fh5hnstVK PNe0OwANwI8f4UDErmwh3El+fsqyjW22v5MvoVw+j8rtgI5Y4dtXz4U2OLJxpAmMkokIiEjxQGMY sluMWuPD0xeqqxmjLBvk1cbiZnrXghmmOxYsL3GHX0WelXOTwkKBIROW1527k2gV+p2kHYzygeBY Br3JtuP2iV2J+axEoctr+hbxx1A9JNr3w+SH1VbxT5Aw+kUJWdo0zuATHAR8ANSbhqRAvNncTFd+ rrcztl524WWLZt+NyteYr842mIycg5kDcPOvdO3GDjbnvezBc6eUWsuSZIKmAMFwoW4sKeFYV+xa fJlrJaSQOoD0IJ2azsct+bJLKZWD6TWNp0lIpw9MGZHQ9b8Q4HECAwEAAaNCMEAwDwYDVR0TAQH/ BAUwAwEB/zAdBgNVHQ4EFgQUdEmZ0f+0emhFdcN+tNzMzjkz2ggwDgYDVR0PAQH/BAQDAgGGMA0G CSqGSIb3DQEBDAUAA4ICAQAjQ1MkYlxt/T7Cz1UAbMVWiLkO3TriJQ2VSpfKgInuKs1l+NsW4AmS 4BjHeJi78+xCUvuppILXTdiK/ORO/auQxDh1MoSf/7OwKwIzNsAQkG8dnK/haZPso0UvFJ/1TCpl Q3IM98P4lYsU84UgYt1UU90s3BiVaU+DR3BAM1h3Egyi61IxHkzJqM7F78PRreBrAwA0JrRUITWX AdxfG/F851X6LWh3e9NpzNMOa7pNdkTWwhWaJuywxfW70Xp0wmzNxbVe9kzmWy2B27O3Opee7c9G slA9hGCZcbUztVdF5kJHdWoOsAgMrr3e97sPWD2PAzHoPYJQyi9eDF20l74gNAf0xBLh7tew2Vkt afcxBPTy+av5EzH4AXcOPUIjJsyacmdRIXrMPIWo6iFqO9taPKU0nprALN+AnCng33eU0aKAQv9q TFsR0PXNor6uzFFcw9VUewyu1rkGd4Di7wcaaMxZUa1+XGdrudviB0JbuAEFWDlN5LuYo7Ey7Nmj 1m+UI/87tyll5gfp77YZ6ufCOB0yiJA8EytuzO+rdwY0d4RPcuSBhPm5dDTedk+SKlOxJTnbPP/l PqYO5Wue/9vsL3SD3460s6neFE3/MaNFcyT6lSnMEpcEoji2jbDwN/zIIX8/syQbPYtuzE2wFg2W HYMfRsCbvUOZ58SWLs5fyQ== -----END CERTIFICATE----- TrustAsia Global Root CA G3 =========================== -----BEGIN CERTIFICATE----- MIIFpTCCA42gAwIBAgIUZPYOZXdhaqs7tOqFhLuxibhxkw8wDQYJKoZIhvcNAQEMBQAwWjELMAkG A1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dpZXMsIEluYy4xJDAiBgNVBAMM G1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHMzAeFw0yMTA1MjAwMjEwMTlaFw00NjA1MTkwMjEw MTlaMFoxCzAJBgNVBAYTAkNOMSUwIwYDVQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMu MSQwIgYDVQQDDBtUcnVzdEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzMwggIiMA0GCSqGSIb3DQEBAQUA A4ICDwAwggIKAoICAQDAMYJhkuSUGwoqZdC+BqmHO1ES6nBBruL7dOoKjbmzTNyPtxNST1QY4Sxz lZHFZjtqz6xjbYdT8PfxObegQ2OwxANdV6nnRM7EoYNl9lA+sX4WuDqKAtCWHwDNBSHvBm3dIZwZ Q0WhxeiAysKtQGIXBsaqvPPW5vxQfmZCHzyLpnl5hkA1nyDvP+uLRx+PjsXUjrYsyUQE49RDdT/V P68czH5GX6zfZBCK70bwkPAPLfSIC7Epqq+FqklYqL9joDiR5rPmd2jE+SoZhLsO4fWvieylL1Ag dB4SQXMeJNnKziyhWTXAyB1GJ2Faj/lN03J5Zh6fFZAhLf3ti1ZwA0pJPn9pMRJpxx5cynoTi+jm 9WAPzJMshH/x/Gr8m0ed262IPfN2dTPXS6TIi/n1Q1hPy8gDVI+lhXgEGvNz8teHHUGf59gXzhqc D0r83ERoVGjiQTz+LISGNzzNPy+i2+f3VANfWdP3kXjHi3dqFuVJhZBFcnAvkV34PmVACxmZySYg WmjBNb9Pp1Hx2BErW+Canig7CjoKH8GB5S7wprlppYiU5msTf9FkPz2ccEblooV7WIQn3MSAPmea mseaMQ4w7OYXQJXZRe0Blqq/DPNL0WP3E1jAuPP6Z92bfW1K/zJMtSU7/xxnD4UiWQWRkUF3gdCF TIcQcf+eQxuulXUtgQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEDk5PIj 7zjKsK5Xf/IhMBY027ySMB0GA1UdDgQWBBRA5OTyI+84yrCuV3/yITAWNNu8kjAOBgNVHQ8BAf8E BAMCAQYwDQYJKoZIhvcNAQEMBQADggIBACY7UeFNOPMyGLS0XuFlXsSUT9SnYaP4wM8zAQLpw6o1 D/GUE3d3NZ4tVlFEbuHGLige/9rsR82XRBf34EzC4Xx8MnpmyFq2XFNFV1pF1AWZLy4jVe5jaN/T G3inEpQGAHUNcoTpLrxaatXeL1nHo+zSh2bbt1S1JKv0Q3jbSwTEb93mPmY+KfJLaHEih6D4sTNj duMNhXJEIlU/HHzp/LgV6FL6qj6jITk1dImmasI5+njPtqzn59ZW/yOSLlALqbUHM/Q4X6RJpstl cHboCoWASzY9M/eVVHUl2qzEc4Jl6VL1XP04lQJqaTDFHApXB64ipCz5xUG3uOyfT0gA+QEEVcys +TIxxHWVBqB/0Y0n3bOppHKH/lmLmnp0Ft0WpWIp6zqW3IunaFnT63eROfjXy9mPX1onAX1daBli 2MjN9LdyR75bl87yraKZk62Uy5P2EgmVtqvXO9A/EcswFi55gORngS1d7XB4tmBZrOFdRWOPyN9y aFvqHbgB8X7754qz41SgOAngPN5C8sLtLpvzHzW2NtjjgKGLzZlkD8Kqq7HK9W+eQ42EVJmzbsAS ZthwEPEGNTNDqJwuuhQxzhB/HIbjj9LV+Hfsm6vxL2PZQl/gZ4FkkfGXL/xuJvYz+NO1+MRiqzFR JQJ6+N1rZdVtTTDIZbpoFGWsJwt0ivKH -----END CERTIFICATE----- TrustAsia Global Root CA G4 =========================== -----BEGIN CERTIFICATE----- MIICVTCCAdygAwIBAgIUTyNkuI6XY57GU4HBdk7LKnQV1tcwCgYIKoZIzj0EAwMwWjELMAkGA1UE BhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dpZXMsIEluYy4xJDAiBgNVBAMMG1Ry dXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHNDAeFw0yMTA1MjAwMjEwMjJaFw00NjA1MTkwMjEwMjJa MFoxCzAJBgNVBAYTAkNOMSUwIwYDVQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQw IgYDVQQDDBtUcnVzdEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi AATxs8045CVD5d4ZCbuBeaIVXxVjAd7Cq92zphtnS4CDr5nLrBfbK5bKfFJV4hrhPVbwLxYI+hW8 m7tH5j/uqOFMjPXTNvk4XatwmkcN4oFBButJ+bAp3TPsUKV/eSm4IJijYzBhMA8GA1UdEwEB/wQF MAMBAf8wHwYDVR0jBBgwFoAUpbtKl86zK3+kMd6Xg1mDpm9xy94wHQYDVR0OBBYEFKW7SpfOsyt/ pDHel4NZg6ZvccveMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjBe8usGzEkxn0AA bbd+NvBNEU/zy4k6LHiRUKNbwMp1JvK/kF0LgoxgKJ/GcJpo5PECMFxYDlZ2z1jD1xCMuo6u47xk dUfFVZDj/bpV6wfEU6s3qe4hsiFbYI89MvHVI5TWWA== -----END CERTIFICATE----- Telekom Security TLS ECC Root 2020 ================================== -----BEGIN CERTIFICATE----- MIICQjCCAcmgAwIBAgIQNjqWjMlcsljN0AFdxeVXADAKBggqhkjOPQQDAzBjMQswCQYDVQQGEwJE RTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBHbWJIMSswKQYDVQQDDCJUZWxl a29tIFNlY3VyaXR5IFRMUyBFQ0MgUm9vdCAyMDIwMB4XDTIwMDgyNTA3NDgyMFoXDTQ1MDgyNTIz NTk1OVowYzELMAkGA1UEBhMCREUxJzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkg R21iSDErMCkGA1UEAwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgRUNDIFJvb3QgMjAyMDB2MBAGByqG SM49AgEGBSuBBAAiA2IABM6//leov9Wq9xCazbzREaK9Z0LMkOsVGJDZos0MKiXrPk/OtdKPD/M1 2kOLAoC+b1EkHQ9rK8qfwm9QMuU3ILYg/4gND21Ju9sGpIeQkpT0CdDPf8iAC8GXs7s1J8nCG6NC MEAwHQYDVR0OBBYEFONyzG6VmUex5rNhTNHLq+O6zd6fMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P AQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMHVSi7ekEE+uShCLsoRbQuHmKjYC2qBuGT8lv9pZ Mo7k+5Dck2TOrbRBR2Diz6fLHgIwN0GMZt9Ba9aDAEH9L1r3ULRn0SyocddDypwnJJGDSA3PzfdU ga/sf+Rn27iQ7t0l -----END CERTIFICATE----- Telekom Security TLS RSA Root 2023 ================================== -----BEGIN CERTIFICATE----- MIIFszCCA5ugAwIBAgIQIZxULej27HF3+k7ow3BXlzANBgkqhkiG9w0BAQwFADBjMQswCQYDVQQG EwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBHbWJIMSswKQYDVQQDDCJU ZWxla29tIFNlY3VyaXR5IFRMUyBSU0EgUm9vdCAyMDIzMB4XDTIzMDMyODEyMTY0NVoXDTQ4MDMy NzIzNTk1OVowYzELMAkGA1UEBhMCREUxJzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJp dHkgR21iSDErMCkGA1UEAwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgUlNBIFJvb3QgMjAyMzCCAiIw DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAO01oYGA88tKaVvC+1GDrib94W7zgRJ9cUD/h3VC KSHtgVIs3xLBGYSJwb3FKNXVS2xE1kzbB5ZKVXrKNoIENqil/Cf2SfHVcp6R+SPWcHu79ZvB7JPP GeplfohwoHP89v+1VmLhc2o0mD6CuKyVU/QBoCcHcqMAU6DksquDOFczJZSfvkgdmOGjup5czQRx UX11eKvzWarE4GC+j4NSuHUaQTXtvPM6Y+mpFEXX5lLRbtLevOP1Czvm4MS9Q2QTps70mDdsipWo l8hHD/BeEIvnHRz+sTugBTNoBUGCwQMrAcjnj02r6LX2zWtEtefdi+zqJbQAIldNsLGyMcEWzv/9 FIS3R/qy8XDe24tsNlikfLMR0cN3f1+2JeANxdKz+bi4d9s3cXFH42AYTyS2dTd4uaNir73Jco4v zLuu2+QVUhkHM/tqty1LkCiCc/4YizWN26cEar7qwU02OxY2kTLvtkCJkUPg8qKrBC7m8kwOFjQg rIfBLX7JZkcXFBGk8/ehJImr2BrIoVyxo/eMbcgByU/J7MT8rFEz0ciD0cmfHdRHNCk+y7AO+oML KFjlKdw/fKifybYKu6boRhYPluV75Gp6SG12mAWl3G0eQh5C2hrgUve1g8Aae3g1LDj1H/1Joy7S WWO/gLCMk3PLNaaZlSJhZQNg+y+TS/qanIA7AgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAdBgNV HQ4EFgQUtqeXgj10hZv3PJ+TmpV5dVKMbUcwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS2 p5eCPXSFm/c8n5OalXl1UoxtRzANBgkqhkiG9w0BAQwFAAOCAgEAqMxhpr51nhVQpGv7qHBFfLp+ sVr8WyP6Cnf4mHGCDG3gXkaqk/QeoMPhk9tLrbKmXauw1GLLXrtm9S3ul0A8Yute1hTWjOKWi0Fp kzXmuZlrYrShF2Y0pmtjxrlO8iLpWA1WQdH6DErwM807u20hOq6OcrXDSvvpfeWxm4bu4uB9tPcy /SKE8YXJN3nptT+/XOR0so8RYgDdGGah2XsjX/GO1WfoVNpbOms2b/mBsTNHM3dA+VKq3dSDz4V4 mZqTuXNnQkYRIer+CqkbGmVps4+uFrb2S1ayLfmlyOw7YqPta9BO1UAJpB+Y1zqlklkg5LB9zVtz aL1txKITDmcZuI1CfmwMmm6gJC3VRRvcxAIU/oVbZZfKTpBQCHpCNfnqwmbU+AGuHrS+w6jv/naa oqYfRvaE7fzbzsQCzndILIyy7MMAo+wsVRjBfhnu4S/yrYObnqsZ38aKL4x35bcF7DvB7L6Gs4a8 wPfc5+pbrrLMtTWGS9DiP7bY+A4A7l3j941Y/8+LN+ljX273CXE2whJdV/LItM3z7gLfEdxquVeE HVlNjM7IDiPCtyaaEBRx/pOyiriA8A4QntOoUAw3gi/q4Iqd4Sw5/7W0cwDk90imc6y/st53BIe0 o82bNSQ3+pCTE4FCxpgmdTdmQRCsu/WU48IxK63nI1bMNSWSs1A= -----END CERTIFICATE----- FIRMAPROFESIONAL CA ROOT-A WEB ============================== -----BEGIN CERTIFICATE----- MIICejCCAgCgAwIBAgIQMZch7a+JQn81QYehZ1ZMbTAKBggqhkjOPQQDAzBuMQswCQYDVQQGEwJF UzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25hbCBTQTEYMBYGA1UEYQwPVkFURVMtQTYyNjM0MDY4 MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFMIENBIFJPT1QtQSBXRUIwHhcNMjIwNDA2MDkwMTM2 WhcNNDcwMzMxMDkwMTM2WjBuMQswCQYDVQQGEwJFUzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25h bCBTQTEYMBYGA1UEYQwPVkFURVMtQTYyNjM0MDY4MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFM IENBIFJPT1QtQSBXRUIwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARHU+osEaR3xyrq89Zfe9MEkVz6 iMYiuYMQYneEMy3pA4jU4DP37XcsSmDq5G+tbbT4TIqk5B/K6k84Si6CcyvHZpsKjECcfIr28jlg st7L7Ljkb+qbXbdTkBgyVcUgt5SjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUk+FD Y1w8ndYn81LsF7Kpryz3dvgwHQYDVR0OBBYEFJPhQ2NcPJ3WJ/NS7Beyqa8s93b4MA4GA1UdDwEB /wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjAdfKR7w4l1M+E7qUW/Runpod3JIha3RxEL2Jq68cgL cFBTApFwhVmpHqTm6iMxoAACMQD94vizrxa5HnPEluPBMBnYfubDl94cT7iJLzPrSA8Z94dGXSaQ pYXFuXqUPoeovQA= -----END CERTIFICATE----- TWCA CYBER Root CA ================== -----BEGIN CERTIFICATE----- MIIFjTCCA3WgAwIBAgIQQAE0jMIAAAAAAAAAATzyxjANBgkqhkiG9w0BAQwFADBQMQswCQYDVQQG EwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJUV0NB IENZQkVSIFJvb3QgQ0EwHhcNMjIxMTIyMDY1NDI5WhcNNDcxMTIyMTU1OTU5WjBQMQswCQYDVQQG EwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJUV0NB IENZQkVSIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDG+Moe2Qkgfh1s Ts6P40czRJzHyWmqOlt47nDSkvgEs1JSHWdyKKHfi12VCv7qze33Kc7wb3+szT3vsxxFavcokPFh V8UMxKNQXd7UtcsZyoC5dc4pztKFIuwCY8xEMCDa6pFbVuYdHNWdZsc/34bKS1PE2Y2yHer43CdT o0fhYcx9tbD47nORxc5zb87uEB8aBs/pJ2DFTxnk684iJkXXYJndzk834H/nY62wuFm40AZoNWDT Nq5xQwTxaWV4fPMf88oon1oglWa0zbfuj3ikRRjpJi+NmykosaS3Om251Bw4ckVYsV7r8Cibt4LK /c/WMw+f+5eesRycnupfXtuq3VTpMCEobY5583WSjCb+3MX2w7DfRFlDo7YDKPYIMKoNM+HvnKkH IuNZW0CP2oi3aQiotyMuRAlZN1vH4xfyIutuOVLF3lSnmMlLIJXcRolftBL5hSmO68gnFSDAS9TM fAxsNAwmmyYxpjyn9tnQS6Jk/zuZQXLB4HCX8SS7K8R0IrGsayIyJNN4KsDAoS/xUgXJP+92ZuJF 2A09rZXIx4kmyA+upwMu+8Ff+iDhcK2wZSA3M2Cw1a/XDBzCkHDXShi8fgGwsOsVHkQGzaRP6AzR wyAQ4VRlnrZR0Bp2a0JaWHY06rc3Ga4udfmW5cFZ95RXKSWNOkyrTZpB0F8mAwIDAQABo2MwYTAO BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSdhWEUfMFib5do5E83 QOGt4A1WNzAdBgNVHQ4EFgQUnYVhFHzBYm+XaORPN0DhreANVjcwDQYJKoZIhvcNAQEMBQADggIB AGSPesRiDrWIzLjHhg6hShbNcAu3p4ULs3a2D6f/CIsLJc+o1IN1KriWiLb73y0ttGlTITVX1olN c79pj3CjYcya2x6a4CD4bLubIp1dhDGaLIrdaqHXKGnK/nZVekZn68xDiBaiA9a5F/gZbG0jAn/x X9AKKSM70aoK7akXJlQKTcKlTfjF/biBzysseKNnTKkHmvPfXvt89YnNdJdhEGoHK4Fa0o635yDR IG4kqIQnoVesqlVYL9zZyvpoBJ7tRCT5dEA7IzOrg1oYJkK2bVS1FmAwbLGg+LhBoF1JSdJlBTrq /p1hvIbZv97Tujqxf36SNI7JAG7cmL3c7IAFrQI932XtCwP39xaEBDG6k5TY8hL4iuO/Qq+n1M0R FxbIQh0UqEL20kCGoE8jypZFVmAGzbdVAaYBlGX+bgUJurSkquLvWL69J1bY73NxW0Qz8ppy6rBe Pm6pUlvscG21h483XjyMnM7k8M4MZ0HMzvaAq07MTFb1wWFZk7Q+ptq4NxKfKjLji7gh7MMrZQzv It6IKTtM1/r+t+FHvpw+PoP7UV31aPcuIYXcv/Fa4nzXxeSDwWrruoBa3lwtcHb4yOWHh8qgnaHl IhInD0Q9HWzq1MKLL295q39QpsQZp6F6t5b5wR9iWqJDB0BeJsas7a5wFsWqynKKTbDPAYsDP27X -----END CERTIFICATE----- SecureSign Root CA12 ==================== -----BEGIN CERTIFICATE----- MIIDcjCCAlqgAwIBAgIUZvnHwa/swlG07VOX5uaCwysckBYwDQYJKoZIhvcNAQELBQAwUTELMAkG A1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMR0wGwYDVQQDExRT ZWN1cmVTaWduIFJvb3QgQ0ExMjAeFw0yMDA0MDgwNTM2NDZaFw00MDA0MDgwNTM2NDZaMFExCzAJ BgNVBAYTAkpQMSMwIQYDVQQKExpDeWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMU U2VjdXJlU2lnbiBSb290IENBMTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6OcE3 emhFKxS06+QT61d1I02PJC0W6K6OyX2kVzsqdiUzg2zqMoqUm048luT9Ub+ZyZN+v/mtp7JIKwcc J/VMvHASd6SFVLX9kHrko+RRWAPNEHl57muTH2SOa2SroxPjcf59q5zdJ1M3s6oYwlkm7Fsf0uZl fO+TvdhYXAvA42VvPMfKWeP+bl+sg779XSVOKik71gurFzJ4pOE+lEa+Ym6b3kaosRbnhW70CEBF EaCeVESE99g2zvVQR9wsMJvuwPWW0v4JhscGWa5Pro4RmHvzC1KqYiaqId+OJTN5lxZJjfU+1Uef NzFJM3IFTQy2VYzxV4+Kh9GtxRESOaCtAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P AQH/BAQDAgEGMB0GA1UdDgQWBBRXNPN0zwRL1SXm8UC2LEzZLemgrTANBgkqhkiG9w0BAQsFAAOC AQEAPrvbFxbS8hQBICw4g0utvsqFepq2m2um4fylOqyttCg6r9cBg0krY6LdmmQOmFxv3Y67ilQi LUoT865AQ9tPkbeGGuwAtEGBpE/6aouIs3YIcipJQMPTw4WJmBClnW8Zt7vPemVV2zfrPIpyMpce mik+rY3moxtt9XUa5rBouVui7mlHJzWhhpmA8zNL4WukJsPvdFlseqJkth5Ew1DgDzk9qTPxpfPS vWKErI4cqc1avTc7bgoitPQV55FYxTpE05Uo2cBl6XLK0A+9H7MV2anjpEcJnuDLN/v9vZfVvhga aaI5gdka9at/yOPiZwud9AzqVN/Ssq+xIvEg37xEHA== -----END CERTIFICATE----- SecureSign Root CA14 ==================== -----BEGIN CERTIFICATE----- MIIFcjCCA1qgAwIBAgIUZNtaDCBO6Ncpd8hQJ6JaJ90t8sswDQYJKoZIhvcNAQEMBQAwUTELMAkG A1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMR0wGwYDVQQDExRT ZWN1cmVTaWduIFJvb3QgQ0ExNDAeFw0yMDA0MDgwNzA2MTlaFw00NTA0MDgwNzA2MTlaMFExCzAJ BgNVBAYTAkpQMSMwIQYDVQQKExpDeWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMU U2VjdXJlU2lnbiBSb290IENBMTQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDF0nqh 1oq/FjHQmNE6lPxauG4iwWL3pwon71D2LrGeaBLwbCRjOfHw3xDG3rdSINVSW0KZnvOgvlIfX8xn bacuUKLBl422+JX1sLrcneC+y9/3OPJH9aaakpUqYllQC6KxNedlsmGy6pJxaeQp8E+BgQQ8sqVb 1MWoWWd7VRxJq3qdwudzTe/NCcLEVxLbAQ4jeQkHO6Lo/IrPj8BGJJw4J+CDnRugv3gVEOuGTgpa /d/aLIJ+7sr2KeH6caH3iGicnPCNvg9JkdjqOvn90Ghx2+m1K06Ckm9mH+Dw3EzsytHqunQG+bOE kJTRX45zGRBdAuVwpcAQ0BB8b8VYSbSwbprafZX1zNoCr7gsfXmPvkPx+SgojQlD+Ajda8iLLCSx jVIHvXiby8posqTdDEx5YMaZ0ZPxMBoH064iwurO8YQJzOAUbn8/ftKChazcqRZOhaBgy/ac18iz ju3Gm5h1DVXoX+WViwKkrkMpKBGk5hIwAUt1ax5mnXkvpXYvHUC0bcl9eQjs0Wq2XSqypWa9a4X0 dFbD9ed1Uigspf9mR6XU/v6eVL9lfgHWMI+lNpyiUBzuOIABSMbHdPTGrMNASRZhdCyvjG817XsY AFs2PJxQDcqSMxDxJklt33UkN4Ii1+iW/RVLApY+B3KVfqs9TC7XyvDf4Fg/LS8EmjijAQIDAQAB o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUBpOjCl4oaTeq YR3r6/wtbyPk86AwDQYJKoZIhvcNAQEMBQADggIBAJaAcgkGfpzMkwQWu6A6jZJOtxEaCnFxEM0E rX+lRVAQZk5KQaID2RFPeje5S+LGjzJmdSX7684/AykmjbgWHfYfM25I5uj4V7Ibed87hwriZLoA ymzvftAj63iP/2SbNDefNWWipAA9EiOWWF3KY4fGoweITedpdopTzfFP7ELyk+OZpDc8h7hi2/Ds Hzc/N19DzFGdtfCXwreFamgLRB7lUe6TzktuhsHSDCRZNhqfLJGP4xjblJUK7ZGqDpncllPjYYPG FrojutzdfhrGe0K22VoF3Jpf1d+42kd92jjbrDnVHmtsKheMYc2xbXIBw8MgAGJoFjHVdqqGuw6q nsb58Nn4DSEC5MUoFlkRudlpcyqSeLiSV5sI8jrlL5WwWLdrIBRtFO8KvH7YVdiI2i/6GaX7i+B/ OfVyK4XELKzvGUWSTLNhB9xNH27SgRNcmvMSZ4PPmz+Ln52kuaiWA3rF7iDeM9ovnhp6dB7h7sxa OgTdsxoEqBRjrLdHEoOabPXm6RUVkRqEGQ6UROcSjiVbgGcZ3GOTEAtlLor6CZpO2oYofaphNdgO pygau1LgePhsumywbrmHXumZNTfxPWQrqaA0k89jL9WB365jJ6UeTo3cKXhZ+PmhIIynJkBugnLN eLLIjzwec+fBH7/PzqUqm9tEZDKgu39cJRNItX+S -----END CERTIFICATE----- SecureSign Root CA15 ==================== -----BEGIN CERTIFICATE----- MIICIzCCAamgAwIBAgIUFhXHw9hJp75pDIqI7fBw+d23PocwCgYIKoZIzj0EAwMwUTELMAkGA1UE BhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMR0wGwYDVQQDExRTZWN1 cmVTaWduIFJvb3QgQ0ExNTAeFw0yMDA0MDgwODMyNTZaFw00NTA0MDgwODMyNTZaMFExCzAJBgNV BAYTAkpQMSMwIQYDVQQKExpDeWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2Vj dXJlU2lnbiBSb290IENBMTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQLUHSNZDKZmbPSYAi4Io5G dCx4wCtELW1fHcmuS1Iggz24FG1Th2CeX2yF2wYUleDHKP+dX+Sq8bOLbe1PL0vJSpSRZHX+AezB 2Ot6lHhWGENfa4HL9rzatAy2KZMIaY+jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD AgEGMB0GA1UdDgQWBBTrQciu/NWeUUj1vYv0hyCTQSvT9DAKBggqhkjOPQQDAwNoADBlAjEA2S6J fl5OpBEHvVnCB96rMjhTKkZEBhd6zlHp4P9mLQlO4E/0BdGF9jVg3PVys0Z9AjBEmEYagoUeYWmJ SwdLZrWeqrqgHkHZAXQ6bkU6iYAZezKYVWOr62Nuk22rGwlgMU4= -----END CERTIFICATE----- D-TRUST BR Root CA 2 2023 ========================= -----BEGIN CERTIFICATE----- MIIFqTCCA5GgAwIBAgIQczswBEhb2U14LnNLyaHcZjANBgkqhkiG9w0BAQ0FADBIMQswCQYDVQQG EwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEJSIFJvb3QgQ0Eg MiAyMDIzMB4XDTIzMDUwOTA4NTYzMVoXDTM4MDUwOTA4NTYzMFowSDELMAkGA1UEBhMCREUxFTAT BgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDIgMjAyMzCC AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK7/CVmRgApKaOYkP7in5Mg6CjoWzckjYaCT cfKri3OPoGdlYNJUa2NRb0kz4HIHE304zQaSBylSa053bATTlfrdTIzZXcFhfUvnKLNEgXtRr90z sWh81k5M/itoucpmacTsXld/9w3HnDY25QdgrMBM6ghs7wZ8T1soegj8k12b9py0i4a6Ibn08OhZ WiihNIQaJZG2tY/vsvmA+vk9PBFy2OMvhnbFeSzBqZCTRphny4NqoFAjpzv2gTng7fC5v2Xx2Mt6 ++9zA84A9H3X4F07ZrjcjrqDy4d2A/wl2ecjbwb9Z/Pg/4S8R7+1FhhGaRTMBffb00msa8yr5LUL QyReS2tNZ9/WtT5PeB+UcSTq3nD88ZP+npNa5JRal1QMNXtfbO4AHyTsA7oC9Xb0n9Sa7YUsOCIv x9gvdhFP/Wxc6PWOJ4d/GUohR5AdeY0cW/jPSoXk7bNbjb7EZChdQcRurDhaTyN0dKkSw/bSuREV MweR2Ds3OmMwBtHFIjYoYiMQ4EbMl6zWK11kJNXuHA7e+whadSr2Y23OC0K+0bpwHJwh5Q8xaRfX /Aq03u2AnMuStIv13lmiWAmlY0cL4UEyNEHZmrHZqLAbWt4NDfTisl01gLmB1IRpkQLLddCNxbU9 CZEJjxShFHR5PtbJFR2kWVki3PaKRT08EtY+XTIvAgMBAAGjgY4wgYswDwYDVR0TAQH/BAUwAwEB /zAdBgNVHQ4EFgQUZ5Dw1t61GNVGKX5cq/ieCLxklRAwDgYDVR0PAQH/BAQDAgEGMEkGA1UdHwRC MEAwPqA8oDqGOGh0dHA6Ly9jcmwuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfYnJfcm9vdF9jYV8y XzIwMjMuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQA097N3U9swFrktpSHxQCF16+tIFoE9c+CeJyrr d6kTpGoKWloUMz1oH4Guaf2Mn2VsNELZLdB/eBaxOqwjMa1ef67nriv6uvw8l5VAk1/DLQOj7aRv U9f6QA4w9QAgLABMjDu0ox+2v5Eyq6+SmNMW5tTRVFxDWy6u71cqqLRvpO8NVhTaIasgdp4D/Ca4 nj8+AybmTNudX0KEPUUDAxxZiMrcLmEkWqTqJwtzEr5SswrPMhfiHocaFpVIbVrg0M8JkiZmkdij YQ6qgYF/6FKC0ULn4B0Y+qSFNueG4A3rvNTJ1jxD8V1Jbn6Bm2m1iWKPiFLY1/4nwSPFyysCu7Ff /vtDhQNGvl3GyiEm/9cCnnRK3PgTFbGBVzbLZVzRHTF36SXDw7IyN9XxmAnkbWOACKsGkoHU6XCP pz+y7YaMgmo1yEJagtFSGkUPFaUA8JR7ZSdXOUPPfH/mvTWze/EZTN46ls/pdu4D58JDUjxqgejB WoC9EV2Ta/vH5mQ/u2kc6d0li690yVRAysuTEwrt+2aSEcr1wPrYg1UDfNPFIkZ1cGt5SAYqgpq/ 5usWDiJFAbzdNpQ0qTUmiteXue4Icr80knCDgKs4qllo3UCkGJCy89UDyibK79XH4I9TjvAA46jt n/mtd+ArY0+ew+43u3gJhJ65bvspmZDogNOfJA== -----END CERTIFICATE----- TrustAsia TLS ECC Root CA ========================= -----BEGIN CERTIFICATE----- MIICMTCCAbegAwIBAgIUNnThTXxlE8msg1UloD5Sfi9QaMcwCgYIKoZIzj0EAwMwWDELMAkGA1UE BhMCQ04xJTAjBgNVBAoTHFRydXN0QXNpYSBUZWNobm9sb2dpZXMsIEluYy4xIjAgBgNVBAMTGVRy dXN0QXNpYSBUTFMgRUNDIFJvb3QgQ0EwHhcNMjQwNTE1MDU0MTU2WhcNNDQwNTE1MDU0MTU1WjBY MQswCQYDVQQGEwJDTjElMCMGA1UEChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywgSW5jLjEiMCAG A1UEAxMZVHJ1c3RBc2lhIFRMUyBFQ0MgUm9vdCBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLh/ pVs/AT598IhtrimY4ZtcU5nb9wj/1WrgjstEpvDBjL1P1M7UiFPoXlfXTr4sP/MSpwDpguMqWzJ8 S5sUKZ74LYO1644xST0mYekdcouJtgq7nDM1D9rs3qlKH8kzsaNCMEAwDwYDVR0TAQH/BAUwAwEB /zAdBgNVHQ4EFgQULIVTu7FDzTLqnqOH/qKYqKaT6RAwDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49 BAMDA2gAMGUCMFRH18MtYYZI9HlaVQ01L18N9mdsd0AaRuf4aFtOJx24mH1/k78ITcTaRTChD15K eAIxAKORh/IRM4PDwYqROkwrULG9IpRdNYlzg8WbGf60oenUoWa2AaU2+dhoYSi3dOGiMQ== -----END CERTIFICATE----- TrustAsia TLS RSA Root CA ========================= -----BEGIN CERTIFICATE----- MIIFgDCCA2igAwIBAgIUHBjYz+VTPyI1RlNUJDxsR9FcSpwwDQYJKoZIhvcNAQEMBQAwWDELMAkG A1UEBhMCQ04xJTAjBgNVBAoTHFRydXN0QXNpYSBUZWNobm9sb2dpZXMsIEluYy4xIjAgBgNVBAMT GVRydXN0QXNpYSBUTFMgUlNBIFJvb3QgQ0EwHhcNMjQwNTE1MDU0MTU3WhcNNDQwNTE1MDU0MTU2 WjBYMQswCQYDVQQGEwJDTjElMCMGA1UEChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywgSW5jLjEi MCAGA1UEAxMZVHJ1c3RBc2lhIFRMUyBSU0EgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP ADCCAgoCggIBAMMWuBtqpERz5dZO9LnPWwvB0ZqB9WOwj0PBuwhaGnrhB3YmH49pVr7+NmDQDIPN lOrnxS1cLwUWAp4KqC/lYCZUlviYQB2srp10Zy9U+5RjmOMmSoPGlbYJQ1DNDX3eRA5gEk9bNb2/ mThtfWza4mhzH/kxpRkQcwUqwzIZheo0qt1CHjCNP561HmHVb70AcnKtEj+qpklz8oYVlQwQX1Fk zv93uMltrOXVmPGZLmzjyUT5tUMnCE32ft5EebuyjBza00tsLtbDeLdM1aTk2tyKjg7/D8OmYCYo zza/+lcK7Fs/6TAWe8TbxNRkoDD75f0dcZLdKY9BWN4ArTr9PXwaqLEX8E40eFgl1oUh63kd0Nyr z2I8sMeXi9bQn9P+PN7F4/w6g3CEIR0JwqH8uyghZVNgepBtljhb//HXeltt08lwSUq6HTrQUNoy IBnkiz/r1RYmNzz7dZ6wB3C4FGB33PYPXFIKvF1tjVEK2sUYyJtt3LCDs3+jTnhMmCWr8n4uIF6C FabW2I+s5c0yhsj55NqJ4js+k8UTav/H9xj8Z7XvGCxUq0DTbE3txci3OE9kxJRMT6DNrqXGJyV1 J23G2pyOsAWZ1SgRxSHUuPzHlqtKZFlhaxP8S8ySpg+kUb8OWJDZgoM5pl+z+m6Ss80zDoWo8SnT q1mt1tve1CuBAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLgHkXlcBvRG/XtZ ylomkadFK/hTMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQwFAAOCAgEAIZtqBSBdGBanEqT3 Rz/NyjuujsCCztxIJXgXbODgcMTWltnZ9r96nBO7U5WS/8+S4PPFJzVXqDuiGev4iqME3mmL5Dw8 veWv0BIb5Ylrc5tvJQJLkIKvQMKtuppgJFqBTQUYo+IzeXoLH5Pt7DlK9RME7I10nYEKqG/odv6L TytpEoYKNDbdgptvT+Bz3Ul/KD7JO6NXBNiT2Twp2xIQaOHEibgGIOcberyxk2GaGUARtWqFVwHx tlotJnMnlvm5P1vQiJ3koP26TpUJg3933FEFlJ0gcXax7PqJtZwuhfG5WyRasQmr2soaB82G39tp 27RIGAAtvKLEiUUjpQ7hRGU+isFqMB3iYPg6qocJQrmBktwliJiJ8Xw18WLK7nn4GS/+X/jbh87q qA8MpugLoDzga5SYnH+tBuYc6kIQX+ImFTw3OffXvO645e8D7r0i+yiGNFjEWn9hongPXvPKnbwb PKfILfanIhHKA9jnZwqKDss1jjQ52MjqjZ9k4DewbNfFj8GQYSbbJIweSsCI3zWQzj8C9GRh3sfI B5XeMhg6j6JCQCTl1jNdfK7vsU1P1FeQNWrcrgSXSYk0ly4wBOeY99sLAZDBHwo/+ML+TvrbmnNz FrwFuHnYWa8G5z9nODmxfKuU4CkUpijy323imttUQ/hHWKNddBWcwauwxzQ= -----END CERTIFICATE----- D-TRUST EV Root CA 2 2023 ========================= -----BEGIN CERTIFICATE----- MIIFqTCCA5GgAwIBAgIQaSYJfoBLTKCnjHhiU19abzANBgkqhkiG9w0BAQ0FADBIMQswCQYDVQQG EwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEVWIFJvb3QgQ0Eg MiAyMDIzMB4XDTIzMDUwOTA5MTAzM1oXDTM4MDUwOTA5MTAzMlowSDELMAkGA1UEBhMCREUxFTAT BgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBFViBSb290IENBIDIgMjAyMzCC AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANiOo4mAC7JXUtypU0w3uX9jFxPvp1sjW2l1 sJkKF8GLxNuo4MwxusLyzV3pt/gdr2rElYfXR8mV2IIEUD2BCP/kPbOx1sWy/YgJ25yE7CUXFId/ MHibaljJtnMoPDT3mfd/06b4HEV8rSyMlD/YZxBTfiLNTiVR8CUkNRFeEMbsh2aJgWi6zCudR3Mf vc2RpHJqnKIbGKBv7FD0fUDCqDDPvXPIEysQEx6Lmqg6lHPTGGkKSv/BAQP/eX+1SH977ugpbzZM lWGG2Pmic4ruri+W7mjNPU0oQvlFKzIbRlUWaqZLKfm7lVa/Rh3sHZMdwGWyH6FDrlaeoLGPaxK3 YG14C8qKXO0elg6DpkiVjTujIcSuWMYAsoS0I6SWhjW42J7YrDRJmGOVxcttSEfi8i4YHtAxq910 7PncjLgcjmgjutDzUNzPZY9zOjLHfP7KgiJPvo5iR2blzYfi6NUPGJ/lBHJLRjwQ8kTCZFZxTnXo nMkmdMV9WdEKWw9t/p51HBjGGjp82A0EzM23RWV6sY+4roRIPrN6TagD4uJ+ARZZaBhDM7DS3LAa QzXupdqpRlyuhoFBAUp0JuyfBr/CBTdkdXgpaP3F9ev+R/nkhbDhezGdpn9yo7nELC7MmVcOIQxF AZRl62UJxmMiCzNJkkg8/M3OsD6Onov4/knFNXJHAgMBAAGjgY4wgYswDwYDVR0TAQH/BAUwAwEB /zAdBgNVHQ4EFgQUqvyREBuHkV8Wub9PS5FeAByxMoAwDgYDVR0PAQH/BAQDAgEGMEkGA1UdHwRC MEAwPqA8oDqGOGh0dHA6Ly9jcmwuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfZXZfcm9vdF9jYV8y XzIwMjMuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQCTy6UfmRHsmg1fLBWTxj++EI14QvBukEdHjqOS Mo1wj/Zbjb6JzkcBahsgIIlbyIIQbODnmaprxiqgYzWRaoUlrRc4pZt+UPJ26oUFKidBK7GB0aL2 QHWpDsvxVUjY7NHss+jOFKE17MJeNRqrphYBBo7q3C+jisosketSjl8MmxfPy3MHGcRqwnNU73xD UmPBEcrCRbH0O1P1aa4846XerOhUt7KR/aypH/KH5BfGSah82ApB9PI+53c0BFLd6IHyTS9URZ0V 4U/M5d40VxDJI3IXcI1QcB9WbMy5/zpaT2N6w25lBx2Eof+pDGOJbbJAiDnXH3dotfyc1dZnaVuo dNv8ifYbMvekJKZ2t0dT741Jj6m2g1qllpBFYfXeA08mD6iL8AOWsKwV0HFaanuU5nCT2vFp4LJi TZ6P/4mdm13NRemUAiKN4DV/6PEEeXFsVIP4M7kFMhtYVRFP0OUnR3Hs7dpn1mKmS00PaaLJvOwi S5THaJQXfuKOKD62xur1NGyfN4gHONuGcfrNlUhDbqNPgofXNJhuS5N5YHVpD/Aa1VP6IQzCP+k/ HxiMkl14p3ZnGbuy6n/pcAlWVqOwDAstNl7F6cTVg8uGF5csbBNvh1qvSaYd2804BC5f4ko1Di1L +KIkBI3Y4WNeApI02phhXBxvWHZks/wCuPWdCg== -----END CERTIFICATE----- SwissSign RSA TLS Root CA 2022 - 1 ================================== -----BEGIN CERTIFICATE----- MIIFkzCCA3ugAwIBAgIUQ/oMX04bgBhE79G0TzUfRPSA7cswDQYJKoZIhvcNAQELBQAwUTELMAkG A1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzErMCkGA1UEAxMiU3dpc3NTaWduIFJTQSBU TFMgUm9vdCBDQSAyMDIyIC0gMTAeFw0yMjA2MDgxMTA4MjJaFw00NzA2MDgxMTA4MjJaMFExCzAJ BgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxKzApBgNVBAMTIlN3aXNzU2lnbiBSU0Eg VExTIFJvb3QgQ0EgMjAyMiAtIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDLKmji C8NXvDVjvHClO/OMPE5Xlm7DTjak9gLKHqquuN6orx122ro10JFwB9+zBvKK8i5VUXu7LCTLf5Im gKO0lPaCoaTo+nUdWfMHamFk4saMla+ju45vVs9xzF6BYQ1t8qsCLqSX5XH8irCRIFucdFJtrhUn WXjyCcplDn/L9Ovn3KlMd/YrFgSVrpxxpT8q2kFC5zyEEPThPYxr4iuRR1VPuFa+Rd4iUU1OKNlf GUEGjw5NBuBwQCMBauTLE5tzrE0USJIt/m2n+IdreXXhvhCxqohAWVTXz8TQm0SzOGlkjIHRI36q OTw7D59Ke4LKa2/KIj4x0LDQKhySio/YGZxH5D4MucLNvkEM+KRHBdvBFzA4OmnczcNpI/2aDwLO EGrOyvi5KaM2iYauC8BPY7kGWUleDsFpswrzd34unYyzJ5jSmY0lpx+Gs6ZUcDj8fV3oT4MM0ZPl EuRU2j7yrTrePjxF8CgPBrnh25d7mUWe3f6VWQQvdT/TromZhqwUtKiE+shdOxtYk8EXlFXIC+OC eYSf8wCENO7cMdWP8vpPlkwGqnj73mSiI80fPsWMvDdUDrtaclXvyFu1cvh43zcgTFeRc5JzrBh3 Q4IgaezprClG5QtO+DdziZaKHG29777YtvTKwP1H8K4LWCDFyB02rpeNUIMmJCn3nTsPBQIDAQAB o2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBRvjmKLk0Ow 4UD2p8P98Q+4DxU4pTAdBgNVHQ4EFgQUb45ii5NDsOFA9qfD/fEPuA8VOKUwDQYJKoZIhvcNAQEL BQADggIBAKwsKUF9+lz1GpUYvyypiqkkVHX1uECry6gkUSsYP2OprphWKwVDIqO310aewCoSPY6W lkDfDDOLazeROpW7OSltwAJsipQLBwJNGD77+3v1dj2b9l4wBlgzHqp41eZUBDqyggmNzhYzWUUo 8aWjlw5DI/0LIICQ/+Mmz7hkkeUFjxOgdg3XNwwQiJb0Pr6VvfHDffCjw3lHC1ySFWPtUnWK50Zp y1FVCypM9fJkT6lc/2cyjlUtMoIcgC9qkfjLvH4YoiaoLqNTKIftV+Vlek4ASltOU8liNr3Cjlvr zG4ngRhZi0Rjn9UMZfQpZX+RLOV/fuiJz48gy20HQhFRJjKKLjpHE7iNvUcNCfAWpO2Whi4Z2L6M OuhFLhG6rlrnub+xzI/goP+4s9GFe3lmozm1O2bYQL7Pt2eLSMkZJVX8vY3PXtpOpvJpzv1/THfQ wUY1mFwjmwJFQ5Ra3bxHrSL+ul4vkSkphnsh3m5kt8sNjzdbowhq6/TdAo9QAwKxuDdollDruF/U KIqlIgyKhPBZLtU30WHlQnNYKoH3dtvi4k0NX/a3vgW0rk4N3hY9A4GzJl5LuEsAz/+MF7psYC0n hzck5npgL7XTgwSqT0N1osGDsieYK7EOgLrAhV5Cud+xYJHT6xh+cHiudoO+cVrQkOPKwRYlZ0rw tnu64ZzZ -----END CERTIFICATE----- OISTE Server Root ECC G1 ======================== -----BEGIN CERTIFICATE----- MIICNTCCAbqgAwIBAgIQI/nD1jWvjyhLH/BU6n6XnTAKBggqhkjOPQQDAzBLMQswCQYDVQQGEwJD SDEZMBcGA1UECgwQT0lTVEUgRm91bmRhdGlvbjEhMB8GA1UEAwwYT0lTVEUgU2VydmVyIFJvb3Qg RUNDIEcxMB4XDTIzMDUzMTE0NDIyOFoXDTQ4MDUyNDE0NDIyN1owSzELMAkGA1UEBhMCQ0gxGTAX BgNVBAoMEE9JU1RFIEZvdW5kYXRpb24xITAfBgNVBAMMGE9JU1RFIFNlcnZlciBSb290IEVDQyBH MTB2MBAGByqGSM49AgEGBSuBBAAiA2IABBcv+hK8rBjzCvRE1nZCnrPoH7d5qVi2+GXROiFPqOuj vqQycvO2Ackr/XeFblPdreqqLiWStukhEaivtUwL85Zgmjvn6hp4LrQ95SjeHIC6XG4N2xml4z+c KrhAS93mT6NjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBQ3TYhlz/w9itWj8UnATgwQ b0K0nDAdBgNVHQ4EFgQUN02IZc/8PYrVo/FJwE4MEG9CtJwwDgYDVR0PAQH/BAQDAgGGMAoGCCqG SM49BAMDA2kAMGYCMQCpKjAd0MKfkFFRQD6VVCHNFmb3U2wIFjnQEnx/Yxvf4zgAOdktUyBFCxxg ZzFDJe0CMQCSia7pXGKDYmH5LVerVrkR3SW+ak5KGoJr3M/TvEqzPNcum9v4KGm8ay3sMaE641c= -----END CERTIFICATE----- OISTE Server Root RSA G1 ======================== -----BEGIN CERTIFICATE----- MIIFgzCCA2ugAwIBAgIQVaXZZ5Qoxu0M+ifdWwFNGDANBgkqhkiG9w0BAQwFADBLMQswCQYDVQQG EwJDSDEZMBcGA1UECgwQT0lTVEUgRm91bmRhdGlvbjEhMB8GA1UEAwwYT0lTVEUgU2VydmVyIFJv b3QgUlNBIEcxMB4XDTIzMDUzMTE0MzcxNloXDTQ4MDUyNDE0MzcxNVowSzELMAkGA1UEBhMCQ0gx GTAXBgNVBAoMEE9JU1RFIEZvdW5kYXRpb24xITAfBgNVBAMMGE9JU1RFIFNlcnZlciBSb290IFJT QSBHMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKqu9KuCz/vlNwvn1ZatkOhLKdxV YOPMvLO8LZK55KN68YG0nnJyQ98/qwsmtO57Gmn7KNByXEptaZnwYx4M0rH/1ow00O7brEi56rAU jtgHqSSY3ekJvqgiG1k50SeH3BzN+Puz6+mTeO0Pzjd8JnduodgsIUzkik/HEzxux9UTl7Ko2yRp g1bTacuCErudG/L4NPKYKyqOBGf244ehHa1uzjZ0Dl4zO8vbUZeUapU8zhhabkvG/AePLhq5Svdk NCncpo1Q4Y2LS+VIG24ugBA/5J8bZT8RtOpXaZ+0AOuFJJkk9SGdl6r7NH8CaxWQrbueWhl/pIzY +m0o/DjH40ytas7ZTpOSjswMZ78LS5bOZmdTaMsXEY5Z96ycG7mOaES3GK/m5Q9l3JUJsJMStR8+ lKXHiHUhsd4JJCpM4rzsTGdHwimIuQq6+cF0zowYJmXa92/GjHtoXAvuY8BeS/FOzJ8vD+HomnqT 8eDI278n5mUpezbgMxVz8p1rhAhoKzYHKyfMeNhqhw5HdPSqoBNdZH702xSu+zrkL8Fl47l6QGzw Brd7KJvX4V84c5Ss2XCTLdyEr0YconosP4EmQufU2MVshGYRi3drVByjtdgQ8K4p92cIiBdcuJd5 z+orKu5YM+Vt6SmqZQENghPsJQtdLEByFSnTkCz3GkPVavBpAgMBAAGjYzBhMA8GA1UdEwEB/wQF MAMBAf8wHwYDVR0jBBgwFoAU8snBDw1jALvsRQ5KH7WxszbNDo0wHQYDVR0OBBYEFPLJwQ8NYwC7 7EUOSh+1sbM2zQ6NMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQwFAAOCAgEANGd5sjrG5T33 I3K5Ce+SrScfoE4KsvXaFwyihdJ+klH9FWXXXGtkFu6KRcoMQzZENdl//nk6HOjG5D1rd9QhEOP2 8yBOqb6J8xycqd+8MDoX0TJD0KqKchxRKEzdNsjkLWd9kYccnbz8qyiWXmFcuCIzGEgWUOrKL+ml Sdx/PKQZvDatkuK59EvV6wit53j+F8Bdh3foZ3dPAGav9LEDOr4SfEE15fSmG0eLy3n31r8Xbk5l 8PjaV8GUgeV6Vg27Rn9vkf195hfkgSe7BYhW3SCl95gtkRlpMV+bMPKZrXJAlszYd2abtNUOshD+ FKrDgHGdPY3ofRRsYWSGRqbXVMW215AWRqWFyp464+YTFrYVI8ypKVL9AMb2kI5Wj4kI3Zaq5tNq qYY19tVFeEJKRvwDyF7YZvZFZSS0vod7VSCd9521Kvy5YhnLbDuv0204bKt7ph6N/Ome/msVuduC msuY33OhkKCgxeDoAaijFJzIwZqsFVAzje18KotzlUBDJvyBpCpfOZC3J8tRd/iWkx7P8nd9H0aT olkelUTFLXVksNb54Dxp6gS1HAviRkRNQzuXSXERvSS2wq1yVAb+axj5d9spLFKebXd7Yv0PTY6Y MjAwcRLWJTXjn/hvnLXrahut6hDTlhZyBiElxky8j3C7DOReIoMt0r7+hVu05L0= -----END CERTIFICATE----- e-Szigno TLS Root CA 2023 ========================= -----BEGIN CERTIFICATE----- MIICzzCCAjGgAwIBAgINAOhvGHvWOWuYSkmYCjAKBggqhkjOPQQDBDB1MQswCQYDVQQGEwJIVTER MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xFzAVBgNVBGEMDlZBVEhV LTIzNTg0NDk3MSIwIAYDVQQDDBllLVN6aWdubyBUTFMgUm9vdCBDQSAyMDIzMB4XDTIzMDcxNzE0 MDAwMFoXDTM4MDcxNzE0MDAwMFowdTELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRYw FAYDVQQKDA1NaWNyb3NlYyBMdGQuMRcwFQYDVQRhDA5WQVRIVS0yMzU4NDQ5NzEiMCAGA1UEAwwZ ZS1Temlnbm8gVExTIFJvb3QgQ0EgMjAyMzCBmzAQBgcqhkjOPQIBBgUrgQQAIwOBhgAEAGgP36J8 PKp0iGEKjcJMpQEiFNT3YHdCnAo4YKGMZz6zY+n6kbCLS+Y53wLCMAFSAL/fjO1ZrTJlqwlZULUZ wmgcAOAFX9pQJhzDrAQixTpN7+lXWDajwRlTEArRzT/vSzUaQ49CE0y5LBqcvjC2xN7cS53kpDzL Ltmt3999Cd8ukv+ho2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E FgQUWYQCYlpGePVd3I8KECgj3NXW+0UwHwYDVR0jBBgwFoAUWYQCYlpGePVd3I8KECgj3NXW+0Uw CgYIKoZIzj0EAwQDgYsAMIGHAkIBLdqu9S54tma4n7Zwf2Z0z+yOfP7AAXmazlIC58PRDHpty7Ve 7hekm9sEdu4pKeiv+62sUvTXK9Z3hBC9xdIoaDQCQTV2WnXzkoYI9bIeCvZlC9p2x1L/Cx6AcCIw wzPbGO2E14vs7dOoY4G1VnxHx1YwlGhza9IuqbnZLBwpvQy6uWWL -----END CERTIFICATE----- hex-2.4.2/lib/hex/http/certs.ex000066400000000000000000000007421517471540100163020ustar00rootroot00000000000000defmodule Hex.HTTP.Certs do @moduledoc false crt_file = Path.join(__DIR__, "ca-bundle.crt") crt = File.read!(crt_file) pems = :public_key.pem_decode(crt) ders = Enum.map(pems, fn {:Certificate, der, _} -> der end) @der_encoded ders @external_resource crt_file def cacerts do @der_encoded end def decode_runtime(path) do crt = File.read!(path) pems = :public_key.pem_decode(crt) Enum.map(pems, fn {:Certificate, der, _} -> der end) end end hex-2.4.2/lib/hex/http/ssl.ex000066400000000000000000000052551517471540100157670ustar00rootroot00000000000000defmodule Hex.HTTP.SSL do @moduledoc false require Record alias Hex.HTTP.Certs alias Hex.HTTP.VerifyHostname # From https://www.ssllabs.com/ssltest/clients.html Android 7 @default_ciphers [ ~c"AES128-GCM-SHA256", ~c"AES128-SHA", ~c"AES256-GCM-SHA384", ~c"AES256-SHA", ~c"DES-CBC3-SHA", ~c"ECDHE-ECDSA-AES128-GCM-SHA256", ~c"ECDHE-ECDSA-AES128-SHA", ~c"ECDHE-ECDSA-AES256-GCM-SHA384", ~c"ECDHE-ECDSA-AES256-SHA", ~c"ECDHE-ECDSA-CHACHA20-POLY1305-SHA256", ~c"ECDHE-RSA-AES128-GCM-SHA256", ~c"ECDHE-RSA-AES128-SHA", ~c"ECDHE-RSA-AES256-GCM-SHA384", ~c"ECDHE-RSA-AES256-SHA", ~c"ECDHE-RSA-CHACHA20-POLY1305-SHA256" ] @default_versions [:"tlsv1.2", :"tlsv1.1", :tlsv1] Record.defrecordp( :certificate, :OTPCertificate, Record.extract(:OTPCertificate, from_lib: "public_key/include/OTP-PUB-KEY.hrl") ) Record.defrecordp( :tbs_certificate, :OTPTBSCertificate, Record.extract(:OTPTBSCertificate, from_lib: "public_key/include/OTP-PUB-KEY.hrl") ) def get_ca_certs do case Hex.State.fetch!(:cacerts_path) do nil -> Certs.cacerts() path -> Certs.decode_runtime(path) end end def ssl_opts(url) do hostname = String.to_charlist(URI.parse(url).host) ciphers = filter_ciphers(@default_ciphers) partial_chain = &partial_chain(Certs.cacerts(), &1) [ verify: :verify_peer, depth: 4, partial_chain: partial_chain, cacerts: get_ca_certs(), server_name_indication: hostname, secure_renegotiate: true, reuse_sessions: true, versions: @default_versions, ciphers: ciphers ] |> customize_hostname_check() end def partial_chain(cacerts, certs) do certs = Enum.map(certs, &{&1, :public_key.pkix_decode_cert(&1, :otp)}) cacerts = Enum.map(cacerts, &:public_key.pkix_decode_cert(&1, :otp)) trusted = Enum.find_value(certs, fn {der, cert} -> trusted? = Enum.find(cacerts, fn cacert -> extract_public_key_info(cacert) == extract_public_key_info(cert) end) if trusted?, do: der end) if trusted do {:trusted_ca, trusted} else :unknown_ca end end defp extract_public_key_info(cert) do cert |> certificate(:tbsCertificate) |> tbs_certificate(:subjectPublicKeyInfo) end defp filter_ciphers(allowed) do [ssl_version | _] = :ssl.versions()[:supported] available = MapSet.new(:ssl.cipher_suites(:all, ssl_version, :openssl)) Enum.filter(allowed, &(&1 in available)) end defp customize_hostname_check(opts) do Keyword.put(opts, :customize_hostname_check, match_fun: &VerifyHostname.match_fun/2) end end hex-2.4.2/lib/hex/http/verify_hostname.ex000066400000000000000000000133721517471540100203670ustar00rootroot00000000000000defmodule Hex.HTTP.VerifyHostname do @moduledoc false # Based on https://github.com/deadtrickster/ssl_verify_hostname.erl require Record Record.defrecordp( :attribute_type_and_value, :AttributeTypeAndValue, Record.extract(:AttributeTypeAndValue, from_lib: "public_key/include/OTP-PUB-KEY.hrl") ) Record.defrecordp( :otp_tbs_certificate, :OTPTBSCertificate, Record.extract(:OTPTBSCertificate, from_lib: "public_key/include/OTP-PUB-KEY.hrl") ) Record.defrecordp( :otp_certificate, :OTPCertificate, Record.extract(:OTPCertificate, from_lib: "public_key/include/OTP-PUB-KEY.hrl") ) Record.defrecordp( :extension, :Extension, Record.extract(:Extension, from_lib: "public_key/include/OTP-PUB-KEY.hrl") ) @id_ce_subject_alt_name {2, 5, 29, 17} @id_at_common_name {2, 5, 4, 3} def verify_cert_hostname(otp_certificate(tbsCertificate: tbs_cert), hostname) do dns_names = extract_dns_names(tbs_cert) dns_name_matched = try_match_hostnames(dns_names, hostname) if maybe_check_subject_cn(dns_names, dns_name_matched, tbs_cert, hostname) do {:valid, hostname} else {:fail, :unable_to_match_altnames} end end def verify_fun(_, {:bad_cert, _} = reason, _) do {:fail, reason} end def verify_fun(_, {:extension, _}, state) do {:unknown, state} end def verify_fun(_, :valid, state) do {:valid, state} end def verify_fun(cert, :valid_peer, state) do if check_hostname = state[:check_hostname] do verify_cert_hostname(cert, check_hostname) else {:valid, state} end end def match_fun({:dns_id, reference_id}, {:dNSName, presented_id}) do try_match_hostname(presented_id, reference_id) end def match_fun(_reference, _presented), do: :default def validate_and_parse_wildcard_identifier(identifier, hostname) do wildcard_pos = :string.chr(identifier, ?*) valid? = wildcard_pos != 0 and length(hostname) >= length(identifier) and check_wildcard_in_leftmost_label(identifier, wildcard_pos) if valid? do before_w = :string.substr(identifier, 1, wildcard_pos - 1) after_w = :string.substr(identifier, wildcard_pos + 1) if :string.chr(after_w, ?*) == 0 do case check_two_labels_after_wildcard(after_w) do {:ok, dot_after_wildcard} -> single_char_w = dot_after_wildcard == wildcard_pos and length(before_w) == 0 {before_w, after_w, single_char_w} :error -> false end else false end else false end end def try_match_hostname(identifier, hostname) do identifier = :string.strip(identifier, :right, ?.) hostname = :string.strip(hostname, :right, ?.) if case_insensitve_match(identifier, hostname) do true else case validate_and_parse_wildcard_identifier(identifier, hostname) do {before_w, after_w, single_char_w} -> try_match_wildcard(before_w, after_w, single_char_w, hostname) false -> false end end end defp extract_cn({:rdnSequence, list}), do: do_extract_cn(list) defp do_extract_cn([[attribute_type_and_value(type: @id_at_common_name, value: cn)] | _]), do: cn defp do_extract_cn([_ | rest]), do: rest defp do_extract_cn([]), do: [] defp extract_dns_names(otp_tbs_certificate(extensions: extensions)) do extensions = :pubkey_cert.extensions_list(extensions) alt_subject = :pubkey_cert.select_extension(@id_ce_subject_alt_name, extensions) if alt_subject == :undefined do [] else extract_dns_names_from_alt_names(extension(alt_subject, :extnValue)) end end defp extract_dns_names_from_alt_names(extn_values) do Enum.reduce(extn_values, [], fn extn_value, acc -> case extn_value do {:dNSName, dns_name} -> [dns_name | acc] _ -> acc end end) end defp case_insensitve_match(string1, string2) do :string.to_lower(string1) == :string.to_lower(string2) end defp wildcard_not_in_label(before_w, after_w) do dot_pos = :string.chr(after_w, ?.) after_dot = :string.substr(after_w, 1, dot_pos) :string.str(before_w, ~c"xn--") == 0 and :string.str(after_dot, ~c"xn--") == 0 end defp try_match_wildcard(before_w, after_w, single_char_w, pattern) do dot_pos = :string.chr(pattern, ?.) if single_char_w do case_insensitve_match(after_w, :string.substr(pattern, dot_pos)) else if wildcard_not_in_label(before_w, after_w) do pattern_part1 = :string.substr(pattern, length(pattern) - length(after_w) + 1, length(after_w)) pattern_part2 = :string.substr(pattern, 1, length(before_w)) case_insensitve_match(after_w, pattern_part1) and case_insensitve_match(before_w, pattern_part2) else false end end end defp check_two_labels_after_wildcard(string) do {_, positions} = Enum.reduce(string, {1, []}, fn ?., {ix, acc} -> {ix + 1, [ix | acc]} _, {ix, acc} -> {ix + 1, acc} end) if length(positions) >= 2 do {:ok, List.last(positions)} else :error end end defp check_wildcard_in_leftmost_label(identifier, wildcard_pos) do dot_pos = :string.chr(identifier, ?.) dot_pos != 0 and dot_pos >= wildcard_pos end defp try_match_hostnames(dns_names, hostname) do Enum.any?(dns_names, &try_match_hostname(&1, hostname)) end defp maybe_check_subject_cn(_dns_names, true, _tbs_cert, _hostname) do true end defp maybe_check_subject_cn([_ | _], false, _tbs_cert, _hostname) do false end defp maybe_check_subject_cn(_dns_names, false, tbs_cert, hostname) do tbs_cert |> otp_tbs_certificate(:subject) |> extract_cn() |> try_match_hostname(hostname) end end hex-2.4.2/lib/hex/mix.ex000066400000000000000000000170311517471540100147770ustar00rootroot00000000000000defmodule Hex.Mix do @moduledoc false # Utility functions around Mix dependencies. @type deps :: %{String.t() => {boolean, deps}} def overridden_deps(deps) do for( dep <- deps, dep.opts[:override], do: dep.app ) |> Enum.uniq() end defp non_hex_overridden_deps(deps) do for( dep <- deps, dep.opts[:override], dep.scm != Hex.SCM, do: dep.app ) |> Enum.uniq() end @doc """ Converts a list of dependencies to a requests to the resolver. Skips dependencies overriding with another SCM (but include dependencies overriding with Hex) and dependencies that are not Hex packages. The returned flattened list is going to contain duplicated dependencies because we want to accumulate all of the different requirements. However we must skip overridden dependencies as their requirements are no longer relevant. We also skip dependencies that are not included in the original list of dependencies as they were likely filtered out due to options like `:only`. """ def deps_to_requests(all_deps) do overridden = non_hex_overridden_deps(all_deps) all_apps = Enum.map(all_deps, & &1.app) hex_deps_to_requests(all_deps, all_apps, overridden) ++ non_hex_deps_to_requests(all_deps, all_deps, all_apps, overridden) end defp deps_to_requests(deps, all_deps, all_apps, overridden) do hex_deps_to_requests(deps, all_apps, overridden) ++ non_hex_deps_to_requests(deps, all_deps, all_apps, overridden) end defp hex_deps_to_requests(deps, all_apps, overridden) do Enum.flat_map(deps, fn dep -> if dep.scm == Hex.SCM and dep.app in all_apps and (dep.top_level or dep.app not in overridden) do [ %{ repo: dep.opts[:repo], name: dep.opts[:hex], requirement: dep.requirement, app: Atom.to_string(dep.app), from: Path.relative_to_cwd(dep.from), dependencies: [], warn_if_outdated: dep.opts[:warn_if_outdated] } ] else [] end end) end defp non_hex_deps_to_requests(deps, all_deps, all_apps, overridden) do Enum.flat_map(deps, fn dep -> if dep.scm != Hex.SCM and dep.deps != [] and dep.app in all_apps do collect_non_hex_deps(dep, all_deps, all_apps, overridden) else [] end end) end defp collect_non_hex_deps(dep, all_deps, all_apps, overridden) do sub_apps = Enum.map(dep.deps, & &1.app) sub_deps = Enum.filter(dep.deps, &(&1.app in sub_apps)) dependencies = deps_to_requests(sub_deps, all_deps, all_apps, overridden) if dependencies != [] do [ %{ repo: nil, name: Atom.to_string(dep.app), requirement: nil, app: Atom.to_string(dep.app), from: Path.relative_to_cwd(dep.from), dependencies: dependencies } ] else [] end end @doc """ Returns all top level dependencies. """ @spec top_level([Mix.Dep.t()]) :: [atom] def top_level(deps) do deps |> Enum.filter(& &1.top_level) |> Enum.map(& &1.app) end @doc """ Normalises a dependency definition to its 3-tuple form. """ @spec dep(tuple) :: {String.t(), String.t(), Keyword.t()} def dep({app, opts}) when is_list(opts), do: {app, nil, opts} def dep({app, req}) when is_binary(req), do: {app, req, []} def dep({app, req, opts}), do: {app, req, opts} @doc """ Takes all Hex packages from the lock and returns them as `{name, app, version, repo}` tuples. """ @spec from_lock(%{}) :: [{String.t(), String.t(), String.t(), String.t()}] def from_lock(lock) do Enum.flat_map(lock, fn {app, info} -> case Hex.Utils.lock(info) do %{name: name, version: version, repo: repo} -> [%{repo: repo, name: name, app: Atom.to_string(app), version: version}] nil -> [] end end) end @doc """ Takes a map of `{name, version}` and returns them as a lock of Hex packages. """ def to_lock(result) do Map.new(result, fn {repo, name, app, version} -> inner_checksum = Hex.Registry.Server.inner_checksum(repo, name, version) |> Base.encode16(case: :lower) outer_checksum = Hex.Registry.Server.outer_checksum(repo, name, version) |> encode_outer_checksum() deps = Hex.Registry.Server.dependencies(repo, name, version) |> case(do: ({:ok, deps} -> deps)) |> Enum.map(®istry_dep_to_def/1) |> Enum.sort() managers = managers(app) |> Enum.sort() |> Enum.uniq() {String.to_atom(app), {:hex, String.to_atom(name), version, inner_checksum, managers, deps, repo, outer_checksum}} end) end defp encode_outer_checksum(nil) do nil end defp encode_outer_checksum(binary) do Base.encode16(binary, case: :lower) end # We need to get managers from manifest if a dependency is not in the lock # but it's already fetched. Without the manifest we would only get managers # from metadata during checkout or from the lock entry. defp managers(nil), do: [] defp managers(app) do path = Path.join([Mix.Project.deps_path(), app, ".hex"]) case File.read(path) do {:ok, file} -> case Hex.SCM.parse_manifest(file) do {:ok, %{managers: managers}} -> managers :error -> [] end _ -> [] end end defp registry_dep_to_def(%{ repo: repo, name: name, constraint: constraint, optional: optional, label: app }) do {String.to_atom(app), to_string(constraint), hex: String.to_atom(name), repo: repo || "hexpm", optional: optional} end def packages_from_lock(lock) do Enum.flat_map(lock, fn {_app, info} -> case Hex.Utils.lock(info) do %{name: name, repo: repo} -> [{repo, name}] nil -> [] end end) end def normalize_dep({app, opts}) when is_atom(app) and is_list(opts) do {app, nil, opts} end def normalize_dep({app, req}) when is_atom(app) do {app, req, []} end def normalize_dep({app, req, opts}) when is_atom(app) and is_list(opts) do {app, req, opts} end def top_level_deps() do config = Mix.Project.config() apps_paths = apps_paths(config) umbrella_deps = Enum.map(config[:deps], fn deps -> {"", deps} end) child_deps = Enum.flat_map(apps_paths || [], fn {app, path} -> Mix.Project.in_project(app, path, fn _module -> Enum.map(Mix.Project.config()[:deps], fn deps -> {path, deps} end) end) end) (umbrella_deps ++ child_deps) |> Enum.map(fn {src, dep} -> {src, normalize_dep(dep)} end) |> Enum.reduce(%{}, fn {src, {app, req, opts}}, acc -> Map.update(acc, app, [{src, req, opts}], &[{src, req, opts} | &1]) end) end def apps_paths(config) do if apps_path = config[:apps_path] do config[:apps] |> umbrella_apps(apps_path) |> to_apps_paths(apps_path) end end defp umbrella_apps(nil, apps_path) do case File.ls(apps_path) do {:ok, apps} -> Enum.map(apps, &String.to_atom/1) {:error, _} -> [] end end defp umbrella_apps(apps, _apps_path) when is_list(apps) do apps end defp to_apps_paths(apps, apps_path) do for app <- apps, path = path_with_mix_exs(app, apps_path), do: {app, path}, into: %{} end defp path_with_mix_exs(app, apps_path) do path = Path.join(apps_path, Atom.to_string(app)) if File.regular?(Path.join(path, "mix.exs")) do path end end end hex-2.4.2/lib/hex/netrc.ex000066400000000000000000000005151517471540100153140ustar00rootroot00000000000000defmodule Hex.Netrc do @moduledoc false alias Hex.Netrc.Cache alias Hex.Netrc.Parser def lookup(host, path \\ Parser.netrc_path()) when is_binary(host) and is_binary(path) do case Cache.fetch(path) do {:ok, %{} = machines} -> {:ok, Map.get(machines, host)} other -> other end end end hex-2.4.2/lib/hex/netrc/000077500000000000000000000000001517471540100147555ustar00rootroot00000000000000hex-2.4.2/lib/hex/netrc/cache.ex000066400000000000000000000012541517471540100163600ustar00rootroot00000000000000defmodule Hex.Netrc.Cache do @moduledoc false alias Hex.Netrc.Parser @agent_name __MODULE__ def start_link(_arg) do Agent.start_link(fn -> %{} end, name: @agent_name) end def child_spec(arg) do %{ id: __MODULE__, start: {__MODULE__, :start_link, [arg]} } end def fetch(path \\ Parser.netrc_path()) when is_binary(path) do Agent.get_and_update(@agent_name, fn cache -> case Map.fetch(cache, path) do {:ok, cached_parse_result} -> {cached_parse_result, cache} :error -> parse_result = Parser.parse(path) {parse_result, Map.put(cache, path, parse_result)} end end) end end hex-2.4.2/lib/hex/netrc/parser.ex000066400000000000000000000030121517471540100166030ustar00rootroot00000000000000defmodule Hex.Netrc.Parser do @moduledoc false def parse(path \\ netrc_path()) when is_binary(path) do case File.read(path) do {:ok, contents} -> parse_contents(contents) error -> error end end defp parse_contents(contents) when is_binary(contents) do parse_result = contents |> String.trim() |> String.split("\n", trim: true) |> Enum.map(&String.split/1) |> Enum.reduce({%{}, nil}, &parse_line/2) case parse_result do {machines, %{username: _, password: _} = current} -> {host, machine} = Map.pop(current, :host) {:ok, Map.put(machines, host, machine)} _ -> {:error, :parse} end end defp parse_line(_, :parse_error), do: :parse_error defp parse_line(["machine", host], {machines, nil}) do {machines, %{host: host}} end defp parse_line(["machine", next_host], {machines, %{username: _, password: _} = current}) do {host, machine} = Map.pop(current, :host) {Map.put(machines, host, machine), %{host: next_host}} end defp parse_line(["login", username], {machines, %{} = current}) do {machines, Map.put(current, :username, username)} end defp parse_line(["password", password], {machines, %{} = current}) do {machines, Map.put(current, :password, password)} end defp parse_line(_line, _parse_state), do: :parse_error def netrc_path() do System.get_env("NETRC") || default_path() end defp default_path() do Path.join(System.user_home!(), ".netrc") end end hex-2.4.2/lib/hex/oauth.ex000066400000000000000000000122551517471540100153250ustar00rootroot00000000000000defmodule Hex.OAuth do @moduledoc false alias Hex.API.OAuth @refresh_cache __MODULE__.RefreshCache @refresh_timeout 60_000 def start_link(_args) do Hex.OnceCache.start_link(name: @refresh_cache) end def child_spec(arg) do %{ id: __MODULE__, start: {__MODULE__, :start_link, [arg]} } end @doc """ Retrieves a valid access token. Automatically refreshes the token if it's expired. Returns {:error, :no_auth} if no tokens are available. Since we now use 2FA for write operations, we use a single token for both read and write. Uses Hex.OnceCache to ensure only one refresh happens per CLI invocation when multiple concurrent requests detect an expired token. Options: * :prompt_auth - if true, prompts for authentication when refresh fails (default: false) """ def get_token(opts \\ []) do # First, check if we have a valid token (read-only, fast path) case get_stored_token() do nil -> {:error, :no_auth} token_data -> if valid_token?(token_data) do {:ok, token_data["access_token"]} else # Token expired, use OnceCache to ensure only one refresh/auth happens Hex.OnceCache.fetch( @refresh_cache, fn -> do_refresh_or_authenticate(token_data, opts) end, timeout: @refresh_timeout ) end end end defp do_refresh_or_authenticate(token_data, opts) do case do_refresh_token(token_data) do {:ok, new_token_data} -> store_token(new_token_data) {:ok, new_token_data["access_token"]} {:error, :refresh_failed} -> if Keyword.get(opts, :prompt_auth, false) do reauthenticate("Token refresh failed. Re-authenticating...") else {:error, :refresh_failed} end {:error, :no_refresh_token} -> if Keyword.get(opts, :prompt_auth, false) do reauthenticate("Access token expired and could not be refreshed. Re-authenticating...") else {:error, :no_refresh_token} end end end defp reauthenticate(message) do Hex.Shell.info(message) if Hex.Shell.yes?("Do you want to authenticate now?") do case Mix.Tasks.Hex.auth() do {:ok, token_data} -> {:ok, token_data["access_token"]} :error -> {:error, :auth_failed} end else {:error, :auth_declined} end end @doc """ Stores OAuth token data. Since we now use 2FA for write operations, we only store a single token. Expected format: %{ "access_token" => "...", "refresh_token" => "...", "expires_at" => unix_timestamp } """ def store_token(token_data) do Hex.Config.update([{:"$oauth_token", token_data}]) Hex.State.put(:oauth_token, token_data) end @doc """ Clears all stored OAuth tokens and the refresh cache. """ def clear_tokens do Hex.Config.remove([:"$oauth_token"]) Hex.State.put(:oauth_token, nil) Hex.OnceCache.clear(@refresh_cache) end @doc """ Checks if we have any OAuth tokens stored. """ def has_tokens? do get_stored_token() != nil end @doc """ Refreshes the stored OAuth token. This is primarily for manual refresh operations. Most code should use get_token/0 which automatically refreshes when needed. """ def refresh_token do case get_stored_token() do nil -> {:error, :no_auth} token_data -> case do_refresh_token(token_data) do {:ok, new_token_data} -> # Update the token in state store_token(new_token_data) {:ok, new_token_data["access_token"]} error -> error end end end @doc """ Creates token data with expiration time from OAuth response. """ def create_token_data(oauth_response) do expires_at = System.system_time(:second) + oauth_response["expires_in"] oauth_response |> Map.put("expires_at", expires_at) |> Map.take(["access_token", "refresh_token", "expires_at"]) end defp get_stored_token do Hex.State.get(:oauth_token) end defp valid_token?(token_data) do case token_data do %{"access_token" => token, "expires_at" => expires_at} when is_binary(token) -> current_time = System.system_time(:second) # Consider token expired if it expires within the next 5 minutes expires_at > current_time + 300 _ -> false end end defp do_refresh_token(token_data) do if token_data["refresh_token"] do case OAuth.refresh_token(token_data["refresh_token"]) do {:ok, {200, _, new_token_data}} -> # Update the token data with new values expires_at = System.system_time(:second) + new_token_data["expires_in"] new_token_data = new_token_data |> Map.put("expires_at", expires_at) |> Map.take(["access_token", "refresh_token", "expires_at"]) {:ok, new_token_data} {:ok, {status, _, _error}} when status >= 400 -> {:error, :refresh_failed} {:error, _reason} -> {:error, :refresh_failed} end else # No refresh token available, return error {:error, :no_refresh_token} end end end hex-2.4.2/lib/hex/once_cache.ex000066400000000000000000000120151517471540100162460ustar00rootroot00000000000000defmodule Hex.OnceCache do @moduledoc """ A cache that computes values at most once and caches them. Supports both single-value caching via `fetch/3` and keyed caching via `fetch_key/4`. Computations run in the caller's process, allowing concurrent computations for different keys. Multiple callers requesting the same key will wait for the first caller's computation to complete. ## Example # Start the cache with a name {:ok, _} = Hex.OnceCache.start_link(name: MyCache) # First call computes and caches Hex.OnceCache.fetch(MyCache, fn -> IO.puts("Computing...") :expensive_result end) # => :expensive_result # Subsequent calls return cached value Hex.OnceCache.fetch(MyCache, fn -> IO.puts("Computing...") :expensive_result end) # => :expensive_result (no "Computing..." output) """ use GenServer @doc """ Starts a new OnceCache. ## Options * `:name` - The name to register the cache under (required) """ def start_link(opts) do name = Keyword.fetch!(opts, :name) GenServer.start_link(__MODULE__, :ok, name: name) end @doc """ Fetches the cached value or computes it if not yet cached. The compute function is only called once, even with concurrent access. All callers will receive the same computed value. ## Options * `:timeout` - The timeout in milliseconds for the fetch operation (default: 5000). Use `:infinity` for operations that may take a long time (e.g., user interaction). """ def fetch(name, compute_fun, opts \\ []) do fetch_key(name, :__single__, compute_fun, opts) end @doc """ Fetches a keyed cached value or computes it if not yet cached. Like `fetch/3`, but supports multiple independent cached values identified by key. The compute function is only called once per key, even with concurrent access. Computations for different keys run concurrently in their respective caller processes. Should not be mixed with `fetch/3` or `put/2` on the same cache. """ def fetch_key(name, key, compute_fun, opts \\ []) do timeout = Keyword.get(opts, :timeout, 5000) case GenServer.call(name, {:fetch, key}, timeout) do {:ok, value} -> value :compute -> try do value = compute_fun.() :ok = GenServer.call(name, {:computed, key, value}, timeout) value catch kind, reason -> GenServer.cast(name, {:failed, key}) :erlang.raise(kind, reason, __STACKTRACE__) end end end @doc """ Stores a value in the cache without computing it. """ def put(name, value) do GenServer.call(name, {:put, :__single__, value}) end @doc """ Clears the cache. """ def clear(name) do GenServer.call(name, :clear) end # GenServer callbacks @impl true def init(:ok) do {:ok, %{}} end @impl true def handle_call({:fetch, key}, {pid, _} = from, state) do case Map.get(state, key) do {:cached, value} -> {:reply, {:ok, value}, state} {:computing, _mon_ref, _waiters} -> {:noreply, update_waiters(state, key, from)} nil -> mon_ref = Process.monitor(pid) {:reply, :compute, Map.put(state, key, {:computing, mon_ref, []})} end end def handle_call({:computed, key, value}, _from, state) do case Map.get(state, key) do {:computing, mon_ref, waiters} -> Process.demonitor(mon_ref, [:flush]) for waiter <- waiters do GenServer.reply(waiter, {:ok, value}) end {:reply, :ok, Map.put(state, key, {:cached, value})} _ -> {:reply, :ok, Map.put(state, key, {:cached, value})} end end def handle_call({:put, key, value}, _from, state) do {:reply, :ok, Map.put(state, key, {:cached, value})} end def handle_call(:clear, _from, _state) do {:reply, :ok, %{}} end @impl true def handle_cast({:failed, key}, state) do case Map.get(state, key) do {:computing, mon_ref, waiters} -> Process.demonitor(mon_ref, [:flush]) {:noreply, hand_off_or_remove(state, key, waiters)} _ -> {:noreply, state} end end @impl true def handle_info({:DOWN, mon_ref, :process, _pid, _reason}, state) do case find_computing_key(state, mon_ref) do {key, waiters} -> {:noreply, hand_off_or_remove(state, key, waiters)} nil -> {:noreply, state} end end defp update_waiters(state, key, from) do Map.update!(state, key, fn {:computing, mon_ref, waiters} -> {:computing, mon_ref, [from | waiters]} end) end defp hand_off_or_remove(state, key, [{pid, _} = next | rest]) do new_mon_ref = Process.monitor(pid) GenServer.reply(next, :compute) Map.put(state, key, {:computing, new_mon_ref, rest}) end defp hand_off_or_remove(state, key, []) do Map.delete(state, key) end defp find_computing_key(state, mon_ref) do Enum.find_value(state, fn {key, {:computing, ^mon_ref, waiters}} -> {key, waiters} _ -> nil end) end end hex-2.4.2/lib/hex/package.ex000066400000000000000000000041631517471540100155770ustar00rootroot00000000000000defmodule Hex.Package do @moduledoc false def default_files() do ~w(lib priv .formatter.exs mix.exs README* readme* LICENSE* license* CHANGELOG* changelog* src c_src Makefile*) end def configuration_doc() do """ ## Configuration * `:app` - Package name (required). * `:version` - Package version (required). * `:deps` - List of package dependencies (see [Dependencies](#module-dependencies) below). * `:description` - Short description of the project. * `:package` - Hex specific configuration (see [Package configuration](#module-package-configuration) below). ## Dependencies Dependencies are defined in mix's dependency format. But instead of using `:git` or `:path` as the SCM `:package` is used. defp deps() do [ {:ecto, "~> 0.1.0"}, {:postgrex, "~> 0.3.0"}, {:cowboy, github: "extend/cowboy"} ] end As can be seen Hex package dependencies works alongside git dependencies. Important to note is that non-Hex dependencies will not be used during dependency resolution and neither will they be listed as dependencies of the package. ## Package configuration Additional metadata of the package can optionally be defined, but it is very recommended to do so. * `:name` - Set this if the package name is not the same as the application name. * `:files` - List of files and directories to include in the package, can include wildcards. Defaults to `#{inspect(default_files())}`. * `:exclude_patterns` - List of patterns matching files and directories to exclude from the package. * `:licenses` - List of licenses used by the package. * `:links` - Map of links relevant to the package. * `:build_tools` - List of build tools that can build the package. Hex will try to automatically detect the build tools based on the files in the package. If a `rebar` or `rebar.config` file is present Hex will mark it as able to build with rebar. This detection can be overridden by setting this field. """ end end hex-2.4.2/lib/hex/parallel.ex000066400000000000000000000065711517471540100160050ustar00rootroot00000000000000defmodule Hex.Parallel do @moduledoc false # Runs a number of jobs (with an upper bound) in parallel and # awaits them to finish. use GenServer require Logger def start_link([name]) do GenServer.start_link(__MODULE__, [], name: name) end def run(name, id, opts \\ [], fun) do GenServer.call(name, {:run, id, opts, fun}) end def await(name, id, timeout) do GenServer.call(name, {:await, id}, timeout) end def clear(name) do GenServer.call(name, :clear) end def init([]) do {:ok, new_state()} end def handle_call({:run, id, opts, fun}, {pid, _ref}, state) do await? = Keyword.get(opts, :await, true) state = run_task(id, fun, state) state = if await? do state else %{state | waiting_reply: Map.put(state.waiting_reply, id, {:send, pid})} end {:reply, :ok, state} end def handle_call({:await, id}, from, state) do if result = state.finished[id] do state = %{state | finished: Map.delete(state.finished, id)} {:reply, result, state} else state = %{state | waiting_reply: Map.put(state.waiting_reply, id, {:gen, from})} {:noreply, state} end end def handle_call(:clear, _from, state) do Enum.each(state.running, fn {%Task{pid: pid}, _} -> Process.unlink(pid) Process.exit(pid, :kill) end) state = %{state | running: %{}, finished: %{}, waiting: :queue.new(), waiting_reply: %{}} {:reply, :ok, state} end def handle_info({ref, message}, state) when is_reference(ref) do tasks = Map.keys(state.running) if task = Enum.find(tasks, &(&1.ref == ref)) do id = state.running[task] state = %{state | running: Map.delete(state.running, task)} |> reply(id, message) |> next_task() {:noreply, state} else Logger.error("[Hex] Hex.Parallel received unknown reply: #{inspect({ref, message})}") {:noreply, state} end end def handle_info({:DOWN, ref, _, proc, reason}, state) do tasks = Map.keys(state.running) if Enum.find(tasks, &(&1.ref == ref)) do Logger.error( "[Hex] Hex.Parallel task #{inspect(proc)} died with reason: #{inspect(reason)}" ) {:noreply, %{state | running: Map.delete(state.running, ref)}} else {:noreply, state} end end defp reply(state, id, message) do case state.waiting_reply[id] do {:gen, from} -> GenServer.reply(from, message) %{state | waiting_reply: Map.delete(state.waiting_reply, id)} {:send, pid} -> send(pid, message) %{state | waiting_reply: Map.delete(state.waiting_reply, id)} nil -> %{state | finished: Map.put(state.finished, id, message)} end end defp next_task(state) do case :queue.out(state.waiting) do {{:value, {id, fun}}, waiting} -> state = %{state | waiting: waiting} run_task(id, fun, state) {:empty, _} -> state end end defp run_task(id, fun, state) do if map_size(state.running) >= state.max_jobs do %{state | waiting: :queue.in({id, fun}, state.waiting)} else task = Task.async(fun) %{state | running: Map.put(state.running, task, id)} end end defp new_state() do %{ max_jobs: Hex.State.fetch!(:http_concurrency), running: %{}, finished: %{}, waiting: :queue.new(), waiting_reply: %{} } end end hex-2.4.2/lib/hex/registry/000077500000000000000000000000001517471540100155125ustar00rootroot00000000000000hex-2.4.2/lib/hex/registry/server.ex000066400000000000000000000361321517471540100173630ustar00rootroot00000000000000defmodule Hex.Registry.Server do @moduledoc false use GenServer @behaviour Hex.Solver.Registry @name __MODULE__ @filename "cache.ets" @timeout 60_000 @ets_version 3 @public_keys_html "https://hex.pm/docs/public_keys" def start_link(opts \\ []) do opts = Keyword.put_new(opts, :name, @name) GenServer.start_link(__MODULE__, [], opts) end def open(opts \\ []) do GenServer.call(@name, {:open, opts}, @timeout) end def close() do GenServer.call(@name, :close, @timeout) end def persist() do GenServer.call(@name, :persist, @timeout) end def prefetch(packages) do :ok = GenServer.call(@name, {:prefetch, packages}, @timeout) end def versions(repo, package) do GenServer.call(@name, {:versions, repo, package}, @timeout) end def dependencies(repo, package, version) do GenServer.call(@name, {:dependencies, repo, package, version}, @timeout) end def inner_checksum(repo, package, version) do GenServer.call(@name, {:inner_checksum, repo, package, version}, @timeout) end def outer_checksum(repo, package, version) do GenServer.call(@name, {:outer_checksum, repo, package, version}, @timeout) end def retired(repo, package, version) do GenServer.call(@name, {:retired, repo, package, version}, @timeout) end def last_update() do GenServer.call(@name, :last_update, @timeout) end def last_update(time) do GenServer.call(@name, {:last_update, time}, @timeout) end def init([]) do {:ok, state()} end defp state() do %{ ets: nil, path: nil, pending: MapSet.new(), fetched: MapSet.new(), waiting: %{}, pending_fun: nil } end def handle_call({:open, opts}, _from, %{ets: nil} = state) do if Keyword.get(opts, :check_version, true) do Hex.UpdateChecker.start_check() end path = opts[:registry_path] || path() ets = String.to_charlist(path) |> open_ets() |> check_version() |> set_version() state = %{state | ets: ets, path: path} {:reply, :ok, state} end def handle_call({:open, opts}, _from, state) do if Keyword.get(opts, :check_version, true) do Hex.UpdateChecker.start_check() end {:reply, :ok, state} end def handle_call(:close, from, state) do state = wait_pending(state, fn state -> if state.ets do persist(state.ets, state.path) :ets.delete(state.ets) end GenServer.reply(from, :ok) state() end) {:noreply, state} end def handle_call(:persist, _from, state) do state = wait_pending(state, fn state -> if state.ets do persist(state.ets, state.path) end state end) {:reply, :ok, state} end def handle_call({:prefetch, packages}, _from, state) do packages = packages |> Enum.map(fn {repo, package} -> {repo || "hexpm", package} end) |> Enum.uniq() |> Enum.reject(&(&1 in state.fetched)) |> Enum.reject(&(&1 in state.pending)) purge_repo_from_cache(packages, state) if Hex.State.fetch!(:offline) do prefetch_offline(packages, state) else prefetch_online(packages, state) end end def handle_call({:versions, repo, package}, from, state) do maybe_wait({repo, package}, from, state, fn -> case lookup(state.ets, {:versions, repo || "hexpm", package}) do nil -> :error versions -> versions = versions |> Enum.map(&Hex.Solver.parse_constraint!/1) |> Enum.sort(&(Version.compare(&1, &2) in [:lt, :eq])) {:ok, versions} end end) end def handle_call({:dependencies, repo, package, version}, from, state) do maybe_wait({repo, package}, from, state, fn -> case lookup(state.ets, {:deps, repo || "hexpm", package, to_string(version)}) do nil -> :error deps -> deps = Enum.map(deps, fn {repo, package, app, requirement, optional} -> %{ repo: if(repo != "hexpm", do: repo), name: package, constraint: Hex.Solver.parse_constraint!(requirement || ">= 0.0.0"), optional: optional, label: app } end) {:ok, deps} end end) end def handle_call({:inner_checksum, repo, package, version}, from, state) do maybe_wait({repo, package}, from, state, fn -> lookup(state.ets, {:inner_checksum, repo || "hexpm", package, version}) end) end def handle_call({:outer_checksum, repo, package, version}, from, state) do maybe_wait({repo, package}, from, state, fn -> lookup(state.ets, {:outer_checksum, repo || "hexpm", package, version}) end) end def handle_call({:retired, repo, package, version}, from, state) do maybe_wait({repo, package}, from, state, fn -> lookup(state.ets, {:retired, repo || "hexpm", package, version}) end) end def handle_call(:last_update, _from, state) do time = lookup(state.ets, :last_update) {:reply, time, state} end def handle_call({:last_update, time}, _from, state) do :ets.insert(state.ets, {:last_update, time}) {:reply, :ok, state} end def handle_info({:DOWN, _ref, :process, _pid, :normal}, state) do {:noreply, state} end def handle_info({:get_package, repo, package, result}, state) do repo = repo || "hexpm" repo_package = {repo, package} pending = MapSet.delete(state.pending, repo_package) fetched = MapSet.put(state.fetched, repo_package) {replys, waiting} = Map.pop(state.waiting, repo_package, []) write_result(result, repo, package, state) Enum.each(replys, fn {from, fun} -> GenServer.reply(from, fun.()) end) state = %{state | pending: pending, waiting: waiting, fetched: fetched} state = maybe_run_pending(state) {:noreply, state} end defp open_ets(path) do case :ets.file2tab(path, verify: true) do {:ok, tid} -> tid {:error, {:read_error, {:file_error, _path, :enoent}}} -> :ets.new(@name, []) {:error, reason} -> Hex.Shell.error("Error opening ETS file #{path}: #{inspect(reason)}") File.rm(path) :ets.new(@name, []) end end defp check_version(ets) do case :ets.lookup(ets, :version) do [{:version, @ets_version}] -> ets _ -> :ets.delete(ets) :ets.new(@name, []) end end defp set_version(ets) do :ets.insert(ets, {:version, @ets_version}) ets end defp persist(tid, path) do dir = Path.dirname(path) File.mkdir_p!(dir) :ok = :ets.tab2file(tid, String.to_charlist(path), extended_info: [:object_count, :md5sum], sync: true ) end defp purge_repo_from_cache(packages, %{ets: ets}) do Enum.each(packages, fn {repo, _package} -> repo = repo || "hexpm" config = Hex.Repo.get_repo(repo) url = config.url case :ets.lookup(ets, {:repo, repo}) do [{_key, ^url}] -> :ok [] -> :ok _ -> purge_repo(repo, ets) end :ets.insert(ets, {{:repo, repo}, url}) end) end # :ets.fun2ms(fn # {{:versions, ^repo, _package}, _} -> true # {{:deps, ^repo, _package, _version}, _} -> true # {{:inner_checksum, ^repo, _package, _version}, _} -> true # {{:outer_checksum, ^repo, _package, _version}, _} -> true # {{:retired, ^repo, _package, _version}, _} -> true # {{:registry_etag, ^repo, _package}, _} -> true # {{:timestamp, ^repo, _package}, _} -> true # {{:timestamp, ^repo, _package, _version}, _} -> true # _ -> false # end) defp purge_repo_matchspec(repo) do [ {{{:versions, :"$1", :"$2"}, :_}, [{:"=:=", {:const, repo}, :"$1"}], [true]}, {{{:deps, :"$1", :"$2", :"$3"}, :_}, [{:"=:=", {:const, repo}, :"$1"}], [true]}, {{{:inner_checksum, :"$1", :"$2", :"$3"}, :_}, [{:"=:=", {:const, repo}, :"$1"}], [true]}, {{{:outer_checksum, :"$1", :"$2", :"$3"}, :_}, [{:"=:=", {:const, repo}, :"$1"}], [true]}, {{{:retired, :"$1", :"$2", :"$3"}, :_}, [{:"=:=", {:const, repo}, :"$1"}], [true]}, {{{:registry_etag, :"$1", :"$2"}, :_}, [{:"=:=", {:const, repo}, :"$1"}], [true]}, {{{:timestamp, :"$1", :"$2"}, :_}, [{:"=:=", {:const, repo}, :"$1"}], [true]}, {{{:timestamp, :"$1", :"$2", :"$3"}, :_}, [{:"=:=", {:const, repo}, :"$1"}], [true]}, {:_, [], [false]} ] end defp purge_repo(repo, ets) do :ets.select_delete(ets, purge_repo_matchspec(repo)) end defp prefetch_online(packages, state) do Enum.each(packages, fn {repo, package} -> etag = package_etag(repo, package, state) Hex.Parallel.run(:hex_fetcher, {:registry, repo, package}, [await: false], fn -> {:get_package, repo, package, Hex.Repo.get_package(repo, package, etag)} end) end) pending = MapSet.union(MapSet.new(packages), state.pending) state = %{state | pending: pending} {:reply, :ok, state} end defp prefetch_offline(packages, state) do missing = Enum.find(packages, fn {repo, package} -> unless lookup(state.ets, {:versions, repo, package}) do package end end) if missing do {repo, package} = missing message = "Hex is running in offline mode and the registry entry for " <> "package #{Hex.Utils.package_name(repo, package)} is not cached locally" {:reply, {:error, message}, state} else fetched = MapSet.union(MapSet.new(packages), state.fetched) {:reply, :ok, %{state | fetched: fetched}} end end defp write_result({:ok, {code, headers, %{releases: releases}}}, repo, package, %{ets: tid}) when code in 200..299 do delete_package(repo, package, tid) now = :calendar.universal_time() Enum.each(releases, fn %{version: version} = release -> :ets.insert(tid, {{:timestamp, repo, package, version}, now}) :ets.insert(tid, {{:inner_checksum, repo, package, version}, release[:inner_checksum]}) :ets.insert(tid, {{:outer_checksum, repo, package, version}, release[:outer_checksum]}) :ets.insert(tid, {{:retired, repo, package, version}, release[:retired]}) deps = Enum.map(release[:dependencies], fn dep -> {dep[:repository] || repo, dep[:package], dep[:app] || dep[:package], dep[:requirement], !!dep[:optional]} end) :ets.insert(tid, {{:deps, repo, package, version}, deps}) end) :ets.insert(tid, {{:timestamp, repo, package}, now}) versions = Enum.map(releases, & &1[:version]) :ets.insert(tid, {{:versions, repo, package}, versions}) if etag = headers[~c"etag"] do :ets.insert(tid, {{:registry_etag, repo, package}, List.to_string(etag)}) end end defp write_result({:ok, {304, _, _}}, _repo, _package, _state) do :ok end defp write_result(other, repo, package, %{ets: tid}) do cached? = !!:ets.lookup(tid, {:versions, repo, package}) print_error(other, repo, package, cached?) unless cached? do raise "Stopping due to errors" end end defp print_error(result, repo, package, cached?) do cached_message = if cached?, do: " (using cache instead)" Hex.Shell.error( "Failed to fetch record for #{Hex.Utils.package_name(repo, package)} from registry#{cached_message}" ) if missing_or_unauthorized_status?(result) do Hex.Shell.error( "This could be because the package does not exist, it was spelled " <> "incorrectly or you don't have permissions to it" ) if unauthorized_status?(result) and not Hex.OAuth.has_tokens?() do Hex.Shell.error("No authenticated user found. Run `mix hex.user auth` to authenticate") end end if not missing_or_unauthorized_status?(result) or Mix.debug?() do case result do {:error, :bad_signature} -> Hex.Shell.error( "Could not verify authenticity of fetched registry file because signature verification failed. " <> "This may happen because a proxy or some entity is " <> "interfering with the download or because you don't have a " <> "public key to verify the registry.\n\nYou may try again " <> "later or check if a new public key has been released #{public_key_message(repo)}. " <> "Set HEX_UNSAFE_REGISTRY=1 to disable this check and allow insecure package downloads." ) {:error, :bad_repo_name} -> Hex.Shell.error( "The configured repository name for your dependency #{Hex.Utils.package_name(repo, package)} does not " <> "match the repository name in the registry. This could be because the repository name is incorrect or " <> "because the registry has not been updated to the latest registry format. " <> "Set HEX_NO_VERIFY_REPO_ORIGIN=1 to disable this check and allow insecure package downloads." ) _other -> Hex.Utils.print_error_result(result) end end end defp missing_or_unauthorized_status?({:ok, {status, _, _}}), do: status in [401, 403, 404] defp missing_or_unauthorized_status?(_), do: false defp unauthorized_status?({:ok, {status, _, _}}), do: status in [401, 403] defp unauthorized_status?(_), do: false defp public_key_message("hexpm:" <> _), do: "on our public keys page: #{@public_keys_html}" defp public_key_message("hexpm"), do: "on our public keys page: #{@public_keys_html}" defp public_key_message(repo), do: "for repo #{repo}" defp maybe_wait({repo, package}, from, state, fun) do repo = repo || "hexpm" cond do {repo, package} in state.fetched -> {:reply, fun.(), state} {repo, package} in state.pending -> tuple = {from, fun} waiting = Map.update(state.waiting, {repo, package}, [tuple], &[tuple | &1]) state = %{state | waiting: waiting} {:noreply, state} true -> repo = if repo, do: "#{repo}/" Mix.raise("Package #{repo}#{package} not prefetched, please report this issue") end end defp wait_pending(state, fun) do if MapSet.size(state.pending) == 0 do state = fun.(state) %{state | pending_fun: nil} else %{state | pending_fun: fun} end end defp maybe_run_pending(%{pending_fun: nil} = state) do state end defp maybe_run_pending(%{pending_fun: fun} = state) do wait_pending(state, fun) end defp package_etag(repo, package, %{ets: tid}) do case :ets.lookup(tid, {:registry_etag, repo, package}) do [{_, etag}] -> etag [] -> nil end end defp path do Path.join(Hex.State.fetch!(:cache_home), @filename) end defp delete_package(repo, package, tid) do :ets.delete(tid, {:registry_etag, repo, package}) versions = lookup(tid, {:versions, repo, package}) || [] :ets.delete(tid, {:versions, repo, package}) Enum.each(versions, fn version -> :ets.delete(tid, {:checksum, repo, package, version}) :ets.delete(tid, {:retired, repo, package, version}) :ets.delete(tid, {:deps, repo, package, version}) end) end defp lookup(tid, key) do case :ets.lookup(tid, key) do [{^key, element}] -> element [] -> nil end end end hex-2.4.2/lib/hex/remote_converger.ex000066400000000000000000000553461517471540100175620ustar00rootroot00000000000000defmodule Hex.RemoteConverger do @moduledoc false @behaviour Mix.RemoteConverger alias Hex.Registry.Server, as: Registry def post_converge() do Hex.UpdateChecker.check() if Hex.State.get(:print_sponsored_tip) do Hex.Shell.info( "You have added/upgraded packages you could sponsor, " <> "run `mix hex.sponsor` to learn more" ) Hex.State.put(:print_sponsored_tip, false) end Registry.persist() end def remote?(dep) do !!dep.opts[:hex] end def converge(deps, lock) do unless Code.ensure_loaded?(Hex.Mix) do Hex.Stdlib.ensure_application!(:hex) end Registry.open() # We cannot use given lock here, because all deps that are being # converged have been removed from the lock by Mix # We need the old lock to get the children of Hex packages old_lock = Mix.Dep.Lock.read() overridden = Hex.Mix.overridden_deps(deps) requests = Hex.Mix.deps_to_requests(deps) prefetches = [ Hex.Mix.packages_from_lock(lock), Hex.Mix.packages_from_lock(old_lock), packages_from_requests(requests) ] |> Enum.concat() |> verify_prefetches() # Only preflight user OAuth when one of the relevant repos would # actually rely on the stored OAuth token. Public-only deps.get # should not prompt just because an unrelated token expired. check_and_refresh_auth(prefetches) Registry.prefetch(prefetches) locked = prepare_locked(lock, old_lock, deps) verify_lock(lock) verify_deps(deps, Hex.Mix.top_level(deps)) verify_input(requests, locked) Hex.Shell.info("Resolving Hex dependencies...") run_solver(lock, old_lock, requests, locked, overridden) end defp run_solver(lock, old_lock, requests, locked, overridden) do start_time = System.monotonic_time(:millisecond) dependencies = Enum.map(requests, &request_to_dependency/1) locked = Enum.map(locked, &request_to_locked/1) overridden = Enum.map(overridden, &Atom.to_string/1) verify_otp_app_names(dependencies) level = Logger.level() Logger.configure(level: if(Hex.State.fetch!(:debug_solver), do: :debug, else: :info)) solution = try do Hex.Solver.run( Registry, dependencies, locked, overridden, ansi: Hex.Shell.ansi_enabled?() ) after Logger.configure(level: level) end current_time = System.monotonic_time(:millisecond) total_time = Float.round((current_time - start_time) / 1000, 3) Hex.Shell.info("Resolution completed in #{total_time}s") case solution do {:ok, resolved} -> resolved = normalize_resolved(resolved) solver_success(resolved, requests, lock, old_lock) {:error, message} -> Hex.Shell.info(message) Mix.raise("Hex dependency resolution failed") end end defp request_to_dependency(request) do constraint = if request.requirement do Hex.Solver.parse_constraint!(request.requirement) else %Hex.Solver.Constraints.Range{} end %{ repo: if(request.repo != "hexpm", do: request.repo), name: request.name, constraint: constraint, optional: false, label: request.app, from: request.from, dependencies: Enum.map(request.dependencies, &request_to_dependency/1) } end defp request_to_locked(request) do %{ repo: if(request.repo != "hexpm", do: request.repo), name: request.name, version: Hex.Solver.parse_constraint!(request.version), label: request.app } end def normalize_resolved(resolved) do resolved |> Enum.map(fn {name, {version, repo}} -> {repo || "hexpm", name, to_string(version)} end) |> Enum.sort() end defp solver_success(resolved, requests, lock, old_lock) do parent_deps = Enum.flat_map(requests, fn %{name: package, dependencies: deps} -> if deps == [] do [] else [package] end end) resolved = Enum.reject(resolved, fn {_repo, package, _version} -> package in parent_deps end) resolved = add_apps_to_resolved(resolved, requests) print_success(resolved, old_lock) verify_resolved(resolved, old_lock) new_lock = Hex.Mix.to_lock(resolved) Hex.SCM.prefetch(new_lock) deps_to_warn = for %{repo: repo, name: name, app: app, requirement: requirement, warn_if_outdated: true} <- requests do {:ok, requirement} = Version.parse_requirement(requirement) {:ok, versions} = Registry.versions(repo, name) versions = versions |> Enum.filter(fn vsn -> vsn.pre == [] and Version.match?(vsn, requirement) end) |> Enum.sort(&(Version.compare(&1, &2) == :gt)) with [latest_version | _] <- versions, {:hex, _name, version, _checksum1, _managers, _, ^repo, _checksum2} <- Map.fetch!(new_lock, String.to_atom(app)), :gt <- Version.compare(latest_version, version) do {name, latest_version} else _ -> nil end end |> Enum.filter(& &1) if deps_to_warn != [] do IO.warn( [ "the following deps are outdated and set \"warn_if_outdated: true\":\n\n", Enum.map_join(deps_to_warn, "\n", fn {name, version} -> " * #{name} #{version} is available" end) ], [] ) end lock_merge(lock, new_lock) end defp add_apps_to_resolved(resolved, requests) do resolved = Enum.map(resolved, fn {repo, package, version} -> apps = Enum.flat_map(resolved, fn {parent_repo, parent_package, parent_version} -> repo = if repo != "hexpm", do: repo deps = case Registry.dependencies(parent_repo, parent_package, parent_version) do {:ok, deps} -> deps :error -> [] end app = Enum.find_value(deps, fn %{repo: ^repo, name: ^package, label: app} -> app %{} -> nil end) if app do [{parent_repo, parent_package, app}] else [] end end) root_app = find_root_app_in_requests(requests, repo, package) apps = Enum.uniq_by(List.wrap(root_app) ++ apps, fn {_repo, _package, app} -> app end) case apps do [{_repo, _package, app}] -> {repo, package, app, version} apps when apps != [] -> name = Hex.Utils.package_name(repo, package) header = "Conflicting OTP application names in dependency definition of " <> "\"#{name} {version}\", in the following packages:\n\n" list = Enum.map_join(apps, "\n", fn {parent_repo, parent_package, app} -> name = Hex.Utils.package_name(parent_repo, parent_package) " * #{name} defined application :#{app}" end) Mix.raise(header <> list) end end) Enum.each(resolved, fn {_repo, _package, app, _version} -> conflicting = Enum.filter(resolved, fn {_repo, _package, ^app, _version} -> true {_repo, _package, _app, _version} -> false end) unless length(conflicting) == 1 do header = "Multiple packages resolved with the same OTP application name of \"#{app}\":\n\n" list = Enum.map_join(conflicting, "\n", fn {repo, package, _app, version} -> name = Hex.Utils.package_name(repo, package) " * #{name} #{version}" end) Mix.raise(header <> list) end end) resolved end defp find_root_app_in_requests(requests, repo, package) do Enum.find_value(requests, fn %{repo: ^repo, name: ^package, app: app} -> {"hexpm", "myapp", app} %{dependencies: dependencies} -> find_root_app_in_requests(dependencies, repo, package) %{} -> nil end) end defp packages_from_requests(deps) do Enum.flat_map(deps, fn %{repo: repo, name: package, dependencies: []} -> [{repo, package}] %{dependencies: dependencies} -> packages_from_requests(dependencies) end) end defp lock_merge(old, new) do Map.merge(old, new, fn _key, old_tuple, new_tuple -> if lock_tuple_needs_update?(old_tuple, new_tuple) do new_tuple else old_tuple end end) end defp lock_tuple_needs_update?(old_tuple, new_tuple) do old_info = Hex.Utils.lock(old_tuple) new_info = Hex.Utils.lock(new_tuple) not (old_info != nil and new_info != nil and old_info.name == new_info.name and old_info.version == new_info.version and old_info.inner_checksum == new_info.inner_checksum and old_info.outer_checksum == new_info.outer_checksum and old_info.repo == new_info.repo) end def deps(%Mix.Dep{app: app}, lock) do case Hex.Utils.lock(lock[app]) do %{name: name, version: version, deps: nil, repo: repo} -> Registry.open() Registry.prefetch([{repo, name}]) get_deps(repo, name, version) %{deps: deps} -> deps nil -> [] end end defp get_deps(repo, name, version) do deps = case Registry.dependencies(repo, name, version) do {:ok, deps} -> deps :error -> [] end for %{repo: repo, name: name, constraint: req, optional: optional, label: app} <- deps do app = String.to_atom(app) opts = [optional: optional, hex: name, repo: repo] {app, to_string(req), opts} end end # TODO: Use requests defp verify_deps(deps, top_level) do Enum.each(deps, fn dep -> if dep.app in top_level and dep.scm == Hex.SCM and dep.requirement == nil do Hex.Shell.warn( "#{dep.app} is missing its version requirement, " <> "use \">= 0.0.0\" if it should match any version" ) end end) end defp verify_input(requests, locked) do Enum.each(requests, fn %{repo: repo, name: name, requirement: req, from: from, dependencies: []} -> verify_package_req(repo, name, req, from) %{} -> nil end) Enum.each(locked, fn %{repo: repo, name: name, version: req} -> verify_package_req(repo, name, req, "mix.lock") end) end defp verify_package_req(repo, name, req, from) do if Registry.versions(repo, name) == :error do Mix.raise("No package with name #{name} (from: #{from}) in registry") end if req != nil and Version.parse_requirement(req) == :error do Mix.raise( "Required version #{inspect(req)} for package #{name} is incorrectly specified (from: #{from})" ) end end defp verify_otp_app_names(dependencies) do verify_otp_app_names(dependencies, %{}) end defp verify_otp_app_names([%{dependencies: []} = dependency | dependencies], map) do map = Map.update(map, {dependency.repo, dependency.name}, dependency, fn existing_dependency -> if existing_dependency.label != dependency.label do name = Hex.Utils.package_name(dependency.repo, dependency.name) Mix.raise(""" Conflicting OTP application names in dependency definition of #{inspect(name)}, in the following locations: * #{existing_dependency.from} defined application :#{existing_dependency.label} * #{dependency.from} defined application :#{dependency.label} """) end dependency end) verify_otp_app_names(dependencies, map) end defp verify_otp_app_names([%{dependencies: sub_dependencies} | dependencies], map) do verify_otp_app_names(sub_dependencies ++ dependencies, map) end defp verify_otp_app_names([], _map) do :ok end defp print_success(resolved, old_lock) do if resolved != [] do previously_locked_versions = dep_info_from_lock(old_lock) dep_changes = group_dependency_changes(resolved, previously_locked_versions) Enum.each(dep_changes, fn {mod, deps} -> unless length(deps) == 0, do: print_category(mod) print_dependency_group(deps, mod) end) end end defp group_dependency_changes(resolved, previously_locked_versions) do state = %{new: [], eq: [], gt: [], lt: []} resolved |> Enum.map(fn {repo, name, _app, version} -> {name, {repo, version}} end) |> Enum.sort() |> Enum.reduce(state, fn {name, {repo, version}}, acc -> previous_version = previously_locked_versions |> Map.get(name) |> version_string_or_nil() change = categorize_dependency_change(previous_version, version) warning = warning_message(previous_version, version) Map.put(acc, change, acc[change] ++ [{name, repo, previous_version, version, warning}]) end) end defp dep_info_from_lock(lock) do Enum.flat_map(lock, fn {_app, info} -> case Hex.Utils.lock(info) do %{name: name, repo: repo, version: version} -> [{name, {repo, version}}] nil -> [] end end) |> Map.new() end defp version_string_or_nil(nil), do: nil defp version_string_or_nil({_repo, version_string}), do: version_string defp categorize_dependency_change(nil, _version), do: :new defp categorize_dependency_change(previous_version, version) do Version.compare(version, previous_version) end defp warning_message(nil, _version), do: nil defp warning_message(previous_version, version) do prev_ver = Version.parse!(previous_version) new_ver = Version.parse!(version) cond do major_version_change?(prev_ver, new_ver) -> " (major)" breaking_minor_version_change?(prev_ver, new_ver) -> " (minor)" true -> nil end end defp print_category(mod) do case mod do :new -> Hex.Shell.info("New:") :eq -> Hex.Shell.info("Unchanged:") :lt -> Hex.Shell.info("Downgraded:") :gt -> Hex.Shell.info("Upgraded:") end end defp print_dependency_group(deps, mod) do Enum.each(deps, fn {name, repo, previous_version, version, warning} -> print_status( Registry.retired(repo, name, version), mod, name, previous_version, version, warning ) end) end defp print_status(nil, mod, name, previous_version, version, warning) do case mod do :new -> Hex.Shell.info(Hex.Shell.format([:green, " #{name} #{version}", :red, "#{warning}"])) :eq -> Hex.Shell.info(" #{name} #{version}") :lt -> Hex.Shell.info( Hex.Shell.format([ :yellow, " #{name} #{previous_version} => #{version}", :red, "#{warning}" ]) ) :gt -> Hex.Shell.info( Hex.Shell.format([ :green, " #{name} #{previous_version} => #{version}", :red, "#{warning}" ]) ) end end defp print_status(retired, mod, name, previous_version, version, _warning) do case mod do mod when mod in [:eq, :new] -> Hex.Shell.warn(" #{name} #{version} RETIRED!") Hex.Shell.warn(" #{Hex.Utils.package_retirement_message(retired)}") _ -> Hex.Shell.warn(" #{name} #{previous_version} => #{version} RETIRED!") Hex.Shell.warn(" #{Hex.Utils.package_retirement_message(retired)}") end end defp verify_prefetches(prefetches) do prefetches |> Enum.map(fn {repo, _package} -> repo end) |> Enum.uniq() |> Enum.each(&verify_repo/1) prefetches end defp verify_repo(repo) do case Hex.Repo.fetch_repo(repo) do {:ok, _} -> :ok :error -> case repo do "hexpm:" <> organization -> if Hex.Shell.yes?( "No authenticated organization found for #{organization}. Do you want to authenticate it now?" ) do Mix.Tasks.Hex.Organization.run(["auth", organization]) else Hex.Repo.get_repo(repo) end _ -> Mix.raise( "Unknown repository #{inspect(repo)}, add new repositories " <> "with the `mix hex.repo add` task" ) end end end defp verify_resolved(resolved, lock) do Enum.each(resolved, fn {repo, name, app, version} -> case Hex.Utils.lock(lock[String.to_atom(app)]) do %{name: ^name, version: ^version, repo: ^repo} = lock -> verify_inner_checksum(repo, name, version, lock.inner_checksum) verify_outer_checksum(repo, name, version, lock.outer_checksum) verify_deps(repo, name, version, lock.deps) _ -> :ok end end) end defp verify_inner_checksum(repo, name, version, checksum) do registry_checksum = Registry.inner_checksum(repo, name, version) if checksum && Base.decode16!(checksum, case: :lower) != registry_checksum do Mix.raise("Registry checksum mismatch against lock (#{name} #{version})") end end defp verify_outer_checksum(repo, name, version, checksum) do registry_checksum = Registry.outer_checksum(repo, name, version) if checksum && Base.decode16!(checksum, case: :lower) != registry_checksum do Mix.raise("Registry checksum mismatch against lock (#{name} #{version})") end end defp verify_deps(repo, name, version, deps) defp verify_deps(nil, _name, _version, _deps), do: :ok defp verify_deps(_repo, _name, _version, nil), do: [] defp verify_deps(repo, name, version, deps) do # TODO: Use requests? deps = Enum.map(deps, fn {app, req, opts} -> %{ repo: if(opts[:repo] != "hexpm", do: opts[:repo]), name: opts[:hex], constraint: Hex.Solver.parse_constraint!(req), optional: !!opts[:optional], label: Atom.to_string(app) } end) registry_deps = case Registry.dependencies(repo, name, version) do {:ok, deps} -> deps :error -> [] end if Enum.sort(deps) != Enum.sort(registry_deps) do Mix.raise("Registry dependencies mismatch against lock (#{name} #{version})") end end defp verify_lock(lock) do Enum.each(lock, fn {_app, info} -> case Hex.Utils.lock(info) do %{name: name, version: version, repo: repo} -> verify_dep(repo, name, version) nil -> :ok end end) end defp verify_dep(repo, name, version) do Hex.Repo.get_repo(repo) case Registry.versions(repo, name) do {:ok, versions} -> versions = Enum.map(versions, &to_string/1) unless version in versions do Mix.raise("Unknown package version #{name} #{version} in lockfile") end :error -> Mix.raise("Unknown package #{name} in lockfile") end end defp with_children(apps, lock) do [apps, do_with_children(apps, lock)] |> List.flatten() end defp do_with_children(names, lock) do Enum.map(names, fn name -> case Hex.Utils.lock(lock[String.to_atom(name)]) do %{name: name, version: version, deps: nil, repo: repo} -> # Do not error on bad data in the old lock because we should just # fix it automatically case Registry.dependencies(repo, name, version) do {:ok, deps} -> apps = Enum.map(deps, fn %{label: app} -> app end) [apps, do_with_children(apps, lock)] :error -> [] end %{deps: deps} -> apps = Enum.map(deps, &Atom.to_string(elem(&1, 0))) [apps, do_with_children(apps, lock)] nil -> [] end end) end defp prepare_locked(lock, old_lock, deps) do # Remove dependencies from the lock if: # 1. They are defined as git or path in mix.exs # 2. If the requirement or repo in mix.exs does not match the locked version # 3. If it's a child of another Hex package being unlocked/updated unlock_without_children = for {app, _} <- old_lock, not Map.has_key?(lock, app), do: Atom.to_string(app) unlock = (unlock_deps(deps, old_lock) ++ unlock_without_children) |> Enum.uniq() |> with_children(old_lock) |> Enum.uniq() old_lock |> Hex.Mix.from_lock() |> Enum.reject(fn %{app: app} -> app in unlock end) end defp unlock_deps(deps, old_lock) do Enum.filter(deps, fn %Mix.Dep{scm: Hex.SCM, app: app, requirement: req, opts: opts} -> name = opts[:hex] case Hex.Utils.lock(old_lock[app]) do %{name: ^name, version: version, repo: repo} -> (req && not Version.match?(version, req)) || repo != opts[:repo] %{} -> false nil -> true end %Mix.Dep{} -> true end) |> Enum.map(&Atom.to_string(&1.app)) end defp major_version_change?(%Version{} = version1, %Version{} = version2) do version1.major != version2.major end defp breaking_minor_version_change?(%Version{} = version1, %Version{} = version2) do version1.major == 0 and version2.major == 0 and version1.minor != version2.minor end defp check_and_refresh_auth(prefetches) do if auth_preflight_required?(prefetches) do # Try to get token with authentication prompting enabled # The OnceCache ensures only one process prompts even if multiple processes # detect the expired token concurrently case Hex.OAuth.get_token(prompt_auth: true) do {:ok, _access_token} -> # Token is valid, was successfully refreshed, or user authenticated :ok {:error, :auth_failed} -> Hex.Shell.warn( "Authentication failed. Private packages will not be available. " <> "Run `mix hex.user auth` to authenticate." ) {:error, :auth_declined} -> Hex.Shell.warn( "Private packages will not be available. " <> "Run `mix hex.user auth` to authenticate." ) {:error, :no_auth} -> # No stored OAuth token to preflight; continue unauthenticated # and let the repo fetch fail normally if authentication is required. :ok {:error, _other} -> # Do not fail dependency resolution during OAuth preflight; continue # unauthenticated and let the repo fetch surface any auth error. :ok end else :ok end end @doc false def auth_preflight_required?(prefetches) do prefetches |> Enum.map(fn {repo, _package} -> repo end) |> Enum.uniq() |> Enum.any?(&repo_requires_user_oauth?/1) end defp repo_requires_user_oauth?("hexpm:" <> _ = repo) do repo |> Hex.Repo.get_repo() |> repo_uses_user_oauth?() end defp repo_requires_user_oauth?(_repo) do false end defp repo_uses_user_oauth?(repo_config) do Map.get(repo_config, :trusted, true) && !repo_config.auth_key end end hex-2.4.2/lib/hex/repo.ex000066400000000000000000000277571517471540100151670ustar00rootroot00000000000000defmodule Hex.Repo do @moduledoc false @exchange_cache __MODULE__.ExchangeCache @exchange_timeout 60_000 @hexpm_url "https://repo.hex.pm" @hexpm_public_key """ -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApqREcFDt5vV21JVe2QNB Edvzk6w36aNFhVGWN5toNJRjRJ6m4hIuG4KaXtDWVLjnvct6MYMfqhC79HAGwyF+ IqR6Q6a5bbFSsImgBJwz1oadoVKD6ZNetAuCIK84cjMrEFRkELtEIPNHblCzUkkM 3rS9+DPlnfG8hBvGi6tvQIuZmXGCxF/73hU0/MyGhbmEjIKRtG6b0sJYKelRLTPW XgK7s5pESgiwf2YC/2MGDXjAJfpfCd0RpLdvd4eRiXtVlE9qO9bND94E7PgQ/xqZ J1i2xWFndWa6nfFnRxZmCStCOZWYYPlaxr+FZceFbpMwzTNs4g3d4tLNUcbKAIH4 0wIDAQAB -----END PUBLIC KEY----- """ def start_link(_args) do Hex.OnceCache.start_link(name: @exchange_cache) end def child_spec(arg) do %{ id: __MODULE__, start: {__MODULE__, :start_link, [arg]} } end def clear_exchange_cache do Hex.OnceCache.clear(@exchange_cache) end def fetch_repo(repo) do repo = repo || "hexpm" repos = Hex.State.fetch!(:repos) case Map.fetch(repos, repo) do {:ok, config} when repo == "hexpm" -> hexpm = hexpm_repo() url = hexpm.url || config.url auth_key = hexpm.auth_key || config.auth_key {:ok, %{config | url: url, trusted: hexpm.trusted, auth_key: auth_key}} {:ok, config} -> {:ok, config} :error -> fetch_organization_fallback(repo) end end def get_repo(repo) do case fetch_repo(repo) do {:ok, config} -> config :error -> unknown_repo_error(repo) end end defp default_organization(repo, source, name) do url = merge_values(Map.get(repo, :url), source.url <> "/repos/#{name}") public_key = merge_values(Map.get(repo, :public_key), source.public_key) auth_key = merge_values(Map.get(repo, :auth_key), source.auth_key) oauth_exchange = merge_values(Map.get(repo, :oauth_exchange), Map.get(source, :oauth_exchange)) oauth_exchange_url = merge_values(Map.get(repo, :oauth_exchange_url), Map.get(source, :oauth_exchange_url)) repo |> Map.put(:url, url) |> Map.put(:public_key, public_key) |> Map.put(:auth_key, auth_key) |> Map.put(:oauth_exchange, oauth_exchange) |> Map.put(:oauth_exchange_url, oauth_exchange_url) |> Map.put(:trusted, Map.has_key?(repo, :auth_key) or source.trusted) end def hexpm_repo() do trusted_mirror_url = Hex.State.fetch!(:trusted_mirror_url) mirror_url = Hex.State.fetch!(:mirror_url) auth_key = Hex.State.fetch!(:repos_key) %{ url: trusted_mirror_url || mirror_url, public_key: @hexpm_public_key, auth_key: auth_key, oauth_exchange: true, trusted: trusted_mirror_url != nil or mirror_url == nil } end def default_hexpm_repo() do %{ url: @hexpm_url, public_key: @hexpm_public_key, auth_key: nil, oauth_exchange: true, trusted: true } end defp fetch_organization_fallback(repo) do case String.split(repo, ":", parts: 2) do [source, organization] -> source = get_repo(source) {:ok, default_organization(%{}, source, organization)} _ -> :error end end defp unknown_repo_error("hexpm:" <> organization) do Mix.raise( "Unknown organization #{inspect(organization)}, authorize with `mix hex.user auth` " <> "or add new organizations with the `mix hex.organization auth` task" ) end defp unknown_repo_error(repo) do Mix.raise( "Unknown repository #{inspect(repo)}, add new repositories " <> "with the `mix hex.repo add` task" ) end def merge_hexpm(repos, hexpm \\ hexpm_repo()) do Map.update(repos, "hexpm", hexpm, &Map.merge(hexpm, &1)) end def update_organizations(repos) do Map.new(repos, fn {name, repo} -> case split_repo_name(name) do [source, organization] -> state_pid = Process.whereis(Hex.State) source = if state_pid && state_pid != self() do get_repo(source) else Map.fetch!(repos, source) end repo = default_organization(repo, source, organization) {name, repo} _ -> {name, Map.put(repo, :trusted, true)} end end) end def clean_organizations(repos) do Map.new(repos, fn {name, repo} -> case split_repo_name(name) do [source, organization] -> source = get_repo(source) repo = repo |> put_organization_url(organization, source) |> clean_repo(source) {name, repo} _ -> {name, Map.delete(repo, :trusted)} end end) end defp put_organization_url(repo, organization, source_repo) do if repo.url == source_repo.url <> "/repos/#{organization}" do Map.delete(repo, :url) else repo end end def clean_hexpm(repos) do hexpm = hexpm_repo() repo = Map.get(repos, "hexpm", hexpm) repo = clean_repo(repo, hexpm) if repo == %{} do Map.delete(repos, "hexpm") else Map.put(repos, "hexpm", repo) end end defp clean_repo(repo, default) do repo |> clean_expired_oauth_token() |> Map.delete(:trusted) |> Enum.reject(fn {key, value} -> value in [nil, Map.get(default, key)] end) |> Map.new() end defp clean_expired_oauth_token(repo) do case repo[:oauth_token] do %{"expires_at" => expires_at} -> current_time = System.system_time(:second) # Keep token if it's valid for more than 5 minutes (300 seconds) if expires_at > current_time + 300 do repo else Map.delete(repo, :oauth_token) end _ -> repo end end defp merge_values(nil, right), do: right defp merge_values(left, _right), do: left def get_package(repo, package, etag) do repo_config = get_repo(repo) config = build_hex_core_config(repo_config, repo, etag) :mix_hex_repo.get_package(config, package) end def get_docs(repo, package, version) do repo_config = get_repo(repo) config = build_hex_core_config(repo_config, repo) :mix_hex_repo.get_docs(config, package, version) end def get_tarball(repo, package, version) do repo_config = get_repo(repo) config = build_hex_core_config(repo_config, repo) :mix_hex_repo.get_tarball(config, package, version) end def get_public_key(repo_config) when is_map(repo_config) do config = build_hex_core_config(repo_config, "") :mix_hex_repo.get_public_key(config) end def get_installs() do repo = get_repo("hexpm") config = build_hex_core_config(repo, "") :mix_hex_repo.get_hex_installs(config) end def find_new_version_from_csv(body) do body |> parse_csv() |> find_latest_eligible_version() |> version_latest() end def tarball_url(repo, package, version) do config = get_repo(repo) config.url <> "/tarballs/#{URI.encode(package)}-#{URI.encode(version)}.tar" end defp parse_csv(body) do body |> :binary.split("\n", [:global, :trim]) |> Enum.map(&:binary.split(&1, ",", [:global, :trim])) end defp find_latest_eligible_version(entries) do elixir_version = Version.parse!(System.version()) entries |> Enum.reverse() |> Enum.find_value(&find_version(&1, elixir_version)) end defp find_version([hex_version, _digest | compatible_versions], elixir_version) do if Enum.find(compatible_versions, &(Version.compare(&1, elixir_version) != :gt)) do hex_version end end # Treat missing as latest defp version_latest(nil), do: :latest defp version_latest(hex_version) do if Version.compare(hex_version, Hex.version()) == :gt do {:version, hex_version} else :latest end end defp split_repo_name(name) do String.split(name, ":", parts: 2) end defp build_hex_core_config(repo_config, repo_name, etag \\ nil) do unsafe_registry = Hex.State.fetch!(:unsafe_registry) no_verify_repo_origin = Hex.State.fetch!(:no_verify_repo_origin) config = %{ :mix_hex_core.default_config() | http_adapter: {Hex.HTTP, %{}}, repo_name: hex_to_actual_repo_name(repo_name), repo_url: repo_config.url, repo_public_key: Map.get(repo_config, :public_key), repo_verify: !unsafe_registry, repo_verify_origin: !no_verify_repo_origin, http_user_agent_fragment: Hex.API.Client.user_agent_fragment() } config = cond do # First priority: explicit repo auth key with OAuth exchange disabled - use API key directly repo_config.auth_key && Map.get(repo_config, :trusted, true) && !Map.get(repo_config, :oauth_exchange, false) -> %{config | repo_key: repo_config.auth_key} # Second priority: Exchange API key for OAuth token if enabled repo_config.auth_key && Map.get(repo_config, :trusted, true) && Map.get(repo_config, :oauth_exchange, false) -> case exchange_api_key_for_token(repo_config, repo_name) do {:ok, access_token} -> %{config | repo_key: "Bearer #{access_token}"} {:error, reason} -> raise "Failed to exchange API key for OAuth token: #{inspect(reason)}" end # Third priority: fallback to OAuth token if available, but only for # trusted repos. Untrusted mirrors/custom repos must not receive the # user bearer token. Map.get(repo_config, :trusted, true) -> case Hex.OAuth.get_token() do {:ok, access_token} -> # Format as Bearer token for OAuth authentication %{config | repo_key: "Bearer #{access_token}"} {:error, _reason} -> # No authentication available - continue without auth # Server will return 401/403 if authentication is required config end true -> config end if etag do %{config | http_etag: etag} else config end end defp hex_to_actual_repo_name("hexpm:" <> repo), do: repo defp hex_to_actual_repo_name(repo), do: repo defp exchange_api_key_for_token(repo_config, repo_name) do case get_cached_token(repo_config) do {:ok, access_token} -> {:ok, access_token} _expired_or_not_found -> Hex.OnceCache.fetch_key( @exchange_cache, {repo_name, repo_config.auth_key}, fn -> do_exchange_api_key(repo_config, repo_name) end, timeout: @exchange_timeout ) end end defp get_cached_token(repo_config) do case Map.get(repo_config, :oauth_token) do %{"access_token" => token, "expires_at" => expires_at} -> current_time = System.system_time(:second) if expires_at > current_time + 300 do {:ok, token} else :expired end _ -> :not_found end end defp do_exchange_api_key(repo_config, repo_name) do api_key = repo_config.auth_key scopes = "repositories" oauth_url = Map.get(repo_config, :oauth_exchange_url) name = get_hostname() case Hex.API.OAuth.exchange_api_key(api_key, scopes, name, oauth_url) do {:ok, {200, _, response}} when is_map(response) -> access_token = response["access_token"] expires_in = response["expires_in"] || 1800 cache_token(repo_config, repo_name, access_token, expires_in) {:ok, access_token} {:ok, {status, _, _}} when status >= 400 -> {:error, :exchange_failed} {:error, reason} -> {:error, reason} end end defp get_hostname do case :inet.gethostname() do {:ok, hostname} -> to_string(hostname) {:error, _} -> nil end end defp cache_token(repo_config, repo_name, access_token, expires_in) do expires_at = System.system_time(:second) + expires_in token_data = %{ "access_token" => access_token, "expires_at" => expires_at } repos = Hex.State.fetch!(:repos) updated_repo = Map.put(repo_config, :oauth_token, token_data) updated_repos = Map.put(repos, repo_name, updated_repo) Hex.Config.update_repos(updated_repos) end end hex-2.4.2/lib/hex/scm.ex000066400000000000000000000307071517471540100147710ustar00rootroot00000000000000defmodule Hex.SCM do alias Hex.Registry.Server, as: Registry @moduledoc false import Hex.Utils, only: [safe_binary_to_term!: 1] @behaviour Mix.SCM @packages_dir "packages" @request_timeout 60_000 @fetch_timeout @request_timeout * 2 def fetchable? do true end def format(_opts) do "Hex package" end def format_lock(opts) do case Hex.Utils.lock(opts[:lock]) do %{outer_checksum: <>, repo: "hexpm"} = lock -> "#{lock.version} (#{lock.name}) #{checksum}" %{outer_checksum: <>} = lock -> "#{lock.version} (#{lock.repo}/#{lock.name}) #{checksum}" %{outer_checksum: nil, repo: "hexpm"} = lock -> "#{lock.version} (#{lock.name}) " <> ~s("Checksum missing in old lock, run mix deps.get to update") %{outer_checksum: nil} = lock -> "#{lock.version} (#{lock.repo}/#{lock.name}) " <> ~s("Checksum missing in old lock, run mix deps.get to update") nil -> nil end end def accepts_options(name, opts) do opts |> organization_to_repo() |> Keyword.put_new(:hex, name) |> Keyword.update(:repo, "hexpm", &(&1 || "hexpm")) |> Keyword.update!(:hex, &to_string/1) |> Keyword.update!(:repo, &to_string/1) end defp organization_to_repo(opts) do case Keyword.fetch(opts, :organization) do {:ok, org} -> opts |> Keyword.delete(:organization) |> Keyword.put(:repo, "hexpm:#{org}") :error -> opts end end def checked_out?(opts) do File.dir?(opts[:dest]) end def lock_status(opts) do case Hex.Utils.lock(opts[:lock]) do %{} = lock -> lock_status( opts[:dest], lock.name, lock.version, lock.inner_checksum, lock.outer_checksum, lock.repo ) nil -> :mismatch end end defp lock_status(dest, name, version, inner_checksum, outer_checksum, repo) do case File.read(Path.join(dest, ".hex")) do {:ok, file} -> case parse_manifest(file) do {:ok, manifest} -> match? = manifest.name == name and manifest.version == version and manifest.inner_checksum == inner_checksum and (is_nil(manifest.outer_checksum) or is_nil(outer_checksum) or manifest.outer_checksum == outer_checksum) and manifest.repo == repo if match?, do: :ok, else: :mismatch _ -> :mismatch end {:error, _} -> :mismatch end end def equal?(opts1, opts2) do opts1[:hex] == opts2[:hex] and opts1[:repo] == opts2[:repo] end def managers(opts) do case Hex.Utils.lock(opts[:lock]) do %{managers: managers} -> managers || [] nil -> [] end end # https://hexdocs.pm/mix/Mix.Tasks.Deps.html#module-dependency-definition-options mix_keys = ~w[app env compile optional only targets override manager runtime system_env]a # https://hex.pm/docs/usage#options hex_keys = ~w[hex repo organization warn_if_outdated]a internal_keys = ~w[dest lock build]a @allowed_keys mix_keys ++ hex_keys ++ internal_keys def update(opts) do Registry.open() lock = Hex.Utils.lock(opts[:lock]) |> ensure_lock(opts) name = opts[:hex] dest = opts[:dest] repo = opts[:repo] || "hexpm" path = cache_path(repo, name, lock.version) unknown_options = Keyword.keys(opts) -- @allowed_keys if unknown_options != [] do Hex.Shell.warn( "#{name} is using unknown options: " <> Enum.map_join(unknown_options, ", ", &inspect/1) ) end case Hex.Parallel.await(:hex_fetcher, {:tarball, repo, name, lock.version}, @fetch_timeout) do {:ok, :cached} -> Hex.Shell.debug(" Using locally cached package (#{path})") {:ok, :offline} -> Hex.Shell.debug(" [OFFLINE] Using locally cached package (#{path})") {:ok, :new} -> tarball_url = tarball_url(repo, name, lock.version) Hex.Shell.debug(" Fetched package (#{tarball_url})") {:error, reason} -> Hex.Shell.error(reason) unless File.exists?(path) do tarball_url = tarball_url(repo, name, lock.version) Mix.raise("Package fetch failed and no cached copy available (#{tarball_url})") end Hex.Shell.info(" Fetch failed. Using locally cached package (#{path})") end File.rm_rf!(dest) registry_inner_checksum = Registry.inner_checksum(repo, to_string(name), lock.version) registry_outer_checksum = Registry.outer_checksum(repo, to_string(name), lock.version) %{ inner_checksum: tarball_inner_checksum, outer_checksum: tarball_outer_checksum, metadata: meta } = try do Hex.Tar.unpack!(path, dest) rescue exception -> File.rm(path) reraise(exception, __STACKTRACE__) end if tarball_inner_checksum != registry_inner_checksum do File.rm(path) raise("Checksum mismatch against registry (inner)") end if tarball_outer_checksum != registry_outer_checksum do File.rm(path) raise("Checksum mismatch against registry (outer)") end build_tools = guess_build_tools(meta) managers = build_tools |> Enum.map(&String.to_atom/1) |> Enum.sort() manifest = encode_manifest( name, lock.version, lock.inner_checksum, lock.outer_checksum, repo, managers ) File.write!(Path.join(dest, ".hex"), manifest) if Hex.Sponsor.get_link(dest) != nil do Hex.State.put(:print_sponsored_tip, true) end deps = lock.deps |> Enum.map(fn {dep, req, opts} -> {dep, req, Keyword.update!(opts, :hex, &String.to_atom/1)} end) |> Enum.sort() {:hex, String.to_atom(lock.name), lock.version, lock.inner_checksum, managers, deps, lock.repo, lock.outer_checksum} end def checkout(opts) do # For checkout (deps.get) it's important the lock is not modified in the parts of # the lock entries that actually locks the dependency. Those entries are the package repo, # name, version and checksum. We have additional entries that describe the dependency, # the managers and the deps, these are used for optimizations such as avoiding # loading the `mix.exs` for dependencies. # # We need to retrieve the managers in the SCM because the RemoteConverger, where # the lock is initially is built does not have this information. # # Here we update the lock entry with additional information but make sure we only # change the lock when the dependency is being updated or changed in some way. fetched_lock = update(opts) maybe_use_fetched_lock(opts[:lock], fetched_lock) end defp maybe_use_fetched_lock(current_lock, fetched_lock) do case Hex.Utils.lock(current_lock) do %{managers: []} -> fetched_lock _ -> current_lock end end @build_tools [ {"mix.exs", "mix"}, {"rebar.config", "rebar"}, {"rebar", "rebar"}, {"Makefile", "make"}, {"Makefile.win", "make"} ] def guess_build_tools(%{"build_tools" => tools}) do if tools do Enum.uniq(tools) else [] end end def guess_build_tools(meta) do base_files = (meta["files"] || []) |> Enum.filter(&(Path.dirname(&1) == ".")) |> MapSet.new() Enum.flat_map(@build_tools, fn {file, tool} -> if file in base_files do [tool] else [] end end) |> Enum.uniq() end defp ensure_lock(nil, opts) do Mix.raise( "The lock is missing for package #{opts[:hex]}. This could be " <> "because another package has configured the application name " <> "for the dependency incorrectly. Verify with the maintainer of " <> "the parent application" ) end defp ensure_lock(lock, _opts), do: lock def parse_manifest(file) do case safe_binary_to_term!(file) do {{:hex, 1, _}, map} -> {:ok, add_outer_checksum(map)} {{:hex, 2, _}, map} -> {:ok, map} _other -> :error end rescue ArgumentError -> {:ok, parse_old_manifest(file)} end defp add_outer_checksum(%{outer_checksum: _} = map), do: map defp add_outer_checksum(map), do: Map.put(map, :outer_checksum, nil) defp parse_old_manifest(file) do lines = file |> String.trim() |> String.split("\n") case lines do [first] -> destructure [name, version, inner_checksum, repo], String.split(first, ",") %{ name: name, version: version, inner_checksum: inner_checksum, outer_checksum: nil, repo: repo, managers: [] } [first, managers] -> managers = managers |> String.split(",") |> Enum.map(&String.to_atom/1) destructure [name, version, inner_checksum, repo], String.split(first, ",") %{ name: name, version: version, inner_checksum: inner_checksum, outer_checksum: nil, repo: repo, managers: managers } end end defp encode_manifest(name, version, inner_checksum, outer_checksum, repo, managers) do map = %{ name: name, version: version, inner_checksum: inner_checksum, outer_checksum: outer_checksum, repo: repo, managers: managers || [] } :erlang.term_to_binary({{:hex, 2, 0}, map}) end def prefetch(lock) do fetch = fetch_from_lock(lock) Enum.each(fetch, fn {repo, package, version} -> Hex.Parallel.run(:hex_fetcher, {:tarball, repo || "hexpm", package, version}, fn -> fetch(repo, package, version) end) end) end defp fetch_from_lock(lock) do deps_path = Mix.Project.deps_path() Enum.flat_map(lock, fn {app, info} -> case Hex.Utils.lock(info) do %{name: name, version: version, repo: repo} -> dest = Path.join(deps_path, "#{app}") case lock_status(dest: dest, lock: info) do :ok -> [] :mismatch -> [{repo, name, version}] end nil -> [] end end) end defp tarball_url(repo, package, version) do filename = "#{package}-#{version}.tar" prune_uri_userinfo(Hex.Repo.get_repo(repo).url <> "/tarballs/#{filename}") end defp prune_uri_userinfo(url) do case URI.parse(url) do %URI{userinfo: nil} -> url uri -> URI.to_string(%{uri | userinfo: "******"}) end end def cache_path(repo, package, version) do repo = Hex.Utils.windows_repo_path_fix(repo) filename = "#{package}-#{version}.tar" Path.join([Hex.State.fetch!(:cache_home), @packages_dir, repo, filename]) end def fetch(repo, package, version) do if Hex.State.fetch!(:offline) do {:ok, :offline} else outer_checksum = Registry.outer_checksum(repo, package, version) path = cache_path(repo, package, version) case Hex.Tar.outer_checksum(path) do {:ok, ^outer_checksum} -> {:ok, :cached} {:ok, other_outer_checksum} -> Hex.Shell.warn(""" Checksum mismatch between registry and the cached package for #{package} #{version} Registry checksum: #{Base.encode16(outer_checksum, case: :lower)} Package checksum: #{Base.encode16(other_outer_checksum, case: :lower)} This may happen when the previously cached package got re-published, but it may also indicate a security issue so verify the new package. Re-fetching...\ """) do_fetch(path, repo, package, version) {:error, _reason} -> do_fetch(path, repo, package, version) end end end defp do_fetch(path, repo, package, version) do case Hex.Repo.get_tarball(repo, package, version) do {:ok, {200, _, body}} -> File.mkdir_p!(Path.dirname(path)) File.write!(path, body) {:ok, :new} {:ok, {304, _headers, _body}} -> {:ok, :cached} {:ok, {code, _headers, _body}} -> {:error, "Request failed (#{code})"} {:error, :timeout} -> reason = [ "Request failed (:timeout)", :reset, "\nIf this happens consistently, adjust your concurrency and timeout settings:", "\n\n HEX_HTTP_CONCURRENCY=1 HEX_HTTP_TIMEOUT=120 mix deps.get" ] {:error, reason} {:error, reason} -> {:error, "Request failed (#{inspect(reason)})"} end end end hex-2.4.2/lib/hex/server.ex000066400000000000000000000025121517471540100155060ustar00rootroot00000000000000defmodule Hex.Server do @moduledoc false use GenServer @name __MODULE__ def start_link(opts) do opts = Keyword.put_new(opts, :name, @name) GenServer.start_link(__MODULE__, [], opts) end def reset() do GenServer.call(@name, :reset) end def should_warn_lock_version?(name \\ @name) do GenServer.call(name, :should_warn_lock_version?) end def should_warn_registry_version?(name \\ @name) do GenServer.call(name, :should_warn_registry_version?) end def init([]) do {:ok, state()} end def handle_call(:reset, _from, _state) do {:reply, :ok, state()} end def handle_call(:should_warn_lock_version?, _from, %{warned_lock_version: false} = state) do {:reply, true, %{state | warned_lock_version: true}} end def handle_call(:should_warn_lock_version?, _from, %{warned_lock_version: true} = state) do {:reply, false, state} end def handle_call( :should_warn_registry_version?, _from, %{warned_registry_version: false} = state ) do {:reply, true, %{state | warned_registry_version: true}} end def handle_call(:should_warn_registry_version?, _from, %{warned_registry_version: true} = state) do {:reply, false, state} end defp state() do %{ warned_lock_version: false, warned_registry_version: false } end end hex-2.4.2/lib/hex/shell.ex000066400000000000000000000060641517471540100153150ustar00rootroot00000000000000defmodule Hex.Shell do @moduledoc false def info(output) do validate_output!(output) Mix.shell().info(output) end def warn(output) do validate_output!(output) Mix.shell().info([IO.ANSI.yellow(), output, IO.ANSI.reset()]) end def error(output) do validate_output!(output) Mix.shell().error(output) end def debug(output) do validate_output!(output) if Mix.debug?() do info(output) end end def yes?(output) do validate_output!(output) Mix.shell().yes?(output) end def prompt(output) do validate_output!(output) Mix.shell().prompt(output) end def format(output, emit? \\ Hex.Shell.ansi_enabled?()) do IO.ANSI.format(output, emit?) end def cmd(command, options \\ [], callback) when is_function(callback, 1) do callback = if Keyword.get(options, :quiet, false) do fn x -> x end else callback end env = validate_env(Keyword.get(options, :env, [])) args = if Keyword.get(options, :stderr_to_stdout, true) do [:stderr_to_stdout] else [] end args = case Keyword.fetch(options, :cd) do {:ok, cd} -> [{:cd, cd} | args] :error -> args end opts = [:stream, :binary, :exit_status, :hide, :use_stdio, {:env, env} | args] port = Port.open({:spawn, shell_command(command)}, opts) port_read(port, callback) end defp port_read(port, callback) do receive do {^port, {:data, data}} -> _ = callback.(data) port_read(port, callback) {^port, {:exit_status, status}} -> status end end # Finding shell command logic from :os.cmd in OTP # https://github.com/erlang/otp/blob/8deb96fb1d017307e22d2ab88968b9ef9f1b71d0/lib/kernel/src/os.erl#L184 defp shell_command(command) do case :os.type() do {:unix, _} -> command = command |> String.replace("\"", "\\\"") |> String.to_charlist() ~c"sh -c \"" ++ command ++ ~c"\"" {:win32, osname} -> command = ~c"\"" ++ String.to_charlist(command) ++ ~c"\"" case {System.get_env("COMSPEC"), osname} do {nil, :windows} -> ~c"command.com /s /c " ++ command {nil, _} -> ~c"cmd /s /c " ++ command {cmd, _} -> ~c"#{cmd} /s /c " ++ command end end end defp validate_env(enum) do Enum.map(enum, fn {k, nil} -> {String.to_charlist(k), false} {k, v} -> {String.to_charlist(k), String.to_charlist(v)} other -> raise ArgumentError, "invalid environment key-value #{inspect(other)}" end) end if Mix.env() == :test do defp validate_output!(output) do formatted_output = output |> IO.ANSI.format_fragment(true) |> IO.chardata_to_string() unless String.printable?(formatted_output) do raise ArgumentError, "string not printable" end end else defp validate_output!(_output), do: :ok end if Mix.env() == :test do def ansi_enabled?(), do: false else def ansi_enabled?(), do: IO.ANSI.enabled?() end end hex-2.4.2/lib/hex/shell/000077500000000000000000000000001517471540100147515ustar00rootroot00000000000000hex-2.4.2/lib/hex/shell/process.ex000066400000000000000000000031641517471540100167710ustar00rootroot00000000000000defmodule Hex.Shell.Process do @moduledoc false @behaviour Mix.Shell def flush(callback \\ fn x -> x end) do receive do {:mix_shell, _, _} = message -> callback.(message) flush(callback) {:mix_shell_input, _, _} = message -> callback.(message) flush(callback) after 0 -> :done end end def print_app do if name = Mix.Shell.printable_app_name() do send(process(), {:mix_shell, :info, ["==> #{name}"]}) end end def info(message) do print_app() send(process(), {:mix_shell, :info, [format(message)]}) :ok end def error(message) do print_app() send(process(), {:mix_shell, :error, [format(message)]}) :ok end defp format(message) do message |> IO.ANSI.format(false) |> IO.iodata_to_binary() end def prompt(message) do print_app() send(process(), {:mix_shell, :prompt, [message]}) receive do {:mix_shell_input, :prompt, response} -> response after 0 -> raise "no shell process input given for prompt/1" end end def yes?(message, _options \\ []) do print_app() send(process(), {:mix_shell, :yes?, [message]}) receive do {:mix_shell_input, :yes?, response} -> response after 0 -> raise "no shell process input given for yes?/1" end end def cmd(command, opts \\ []) do print_app? = Keyword.get(opts, :print_app, true) Hex.Shell.cmd(command, opts, fn data -> if print_app?, do: print_app() send(process(), {:mix_shell, :run, [data]}) end) end defp process() do Hex.State.fetch!(:shell_process) || self() end end hex-2.4.2/lib/hex/solver.ex000066400000000000000000000060601517471540100155140ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defmodule Hex.Solver do _ = """ A version solver. """ @type dependency() :: %{ repo: repo(), name: package(), constraint: constraint(), optional: optional(), label: label(), dependencies: [dependency()] } @type locked() :: %{ repo: repo(), name: package(), version: Version.t(), label: label() } @type repo() :: String.t() | nil @type package() :: String.t() @type label() :: String.t() @type optional() :: boolean() @type result() :: %{package() => {Version.t(), repo()}} @opaque constraint() :: Hex.Solver.Requirement.t() alias Hex.Solver.{Failure, Requirement, Solver} @doc """ Runs the version solver. Takes a `Hex.Solver.Registry` implementation, a list of root dependencies, a list of locked package versions, and a list of packages that are overridden by the root dependencies. Depdendencies with sub-dependencies in the `:dependencies` map field are not expected to be packages in the registry. These dependencies are used to give better error messages for when there are multiple declarations of the dependency with conflicting version requirements. For example: * Top dependency 1 * package ~> 1.0 * Top dependency 2 * package ~> 2.0 Locked dependencies are treated as optional dependencies with a single version as their constraint. The overrides are a set of labels. If a dependency with a matching label is declared the solver will ignore that dependency unless it's a root dependency. Returns a map of packages and their selected versions or a human readable explanation of why a solution could not be found. ## Options * `:ansi` - If `true` adds ANSI formatting to the failure message (Default: `false`) """ @spec run(module(), [dependency()], [locked()], [label()], ansi: boolean()) :: {:ok, result()} | {:error, String.t()} def run(registry, dependencies, locked, overrides, opts \\ []) do case Solver.run(registry, dependencies, locked, overrides) do {:ok, solution} -> {:ok, Map.drop(solution, ["$root", "$lock"])} {:error, incompatibility} -> {:error, Failure.write(incompatibility, opts)} end end @doc """ Parses or converts a SemVer version or Elixir version requirement to an internal solver constraint that can be returned by the `Hex.Solver.Registry` or passed to `Hex.Solver.run/4`. """ @spec parse_constraint(String.t() | Version.t() | Version.Requirement.t()) :: {:ok, constraint()} | :error def parse_constraint(string) do Requirement.to_constraint(string) end @doc """ Parses or converts a SemVer version or Elixir version requirement to an internal solver constraint that can be returned by the `Hex.Solver.Registry` or passed to `Hex.Solver.run/4`. """ @spec parse_constraint!(String.t() | Version.t() | Version.Requirement.t()) :: constraint() def parse_constraint!(string) do Requirement.to_constraint!(string) end end hex-2.4.2/lib/hex/solver/000077500000000000000000000000001517471540100151545ustar00rootroot00000000000000hex-2.4.2/lib/hex/solver/assignment.ex000066400000000000000000000020551517471540100176640ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defmodule Hex.Solver.Assignment do @moduledoc false alias Hex.Solver.{Assignment, Term} defstruct term: nil, decision_level: nil, index: nil, cause: nil def intersect(%Assignment{} = left, %Assignment{} = right) do %{left | term: Term.intersect(left.term, right.term)} end def decision?(%Assignment{cause: cause}) do cause == nil end def to_string(%Assignment{term: term}) do Kernel.to_string(term) end defimpl String.Chars do defdelegate to_string(assignment), to: Hex.Solver.Assignment end defimpl Inspect do def inspect( %{ term: term, decision_level: decision_level, index: index, cause: cause }, _opts ) do "#Assignment" end defp maybe(_prefix, nil), do: "" defp maybe(prefix, value), do: "#{prefix}#{value}" end end hex-2.4.2/lib/hex/solver/constraint.ex000066400000000000000000000006001517471540100176720ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defprotocol Hex.Solver.Constraint do @moduledoc false def any?(constraint) def empty?(constraint) def allows?(constraint, version) def allows_any?(left, right) def allows_all?(left, right) def difference(left, right) def intersect(left, right) def union(left, right) def compare(left, right) end hex-2.4.2/lib/hex/solver/constraints/000077500000000000000000000000001517471540100175235ustar00rootroot00000000000000hex-2.4.2/lib/hex/solver/constraints/empty.ex000066400000000000000000000021231517471540100212150ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defmodule Hex.Solver.Constraints.Empty do @moduledoc false use Hex.Solver.Constraints.Impl alias Hex.Solver.Constraint alias Hex.Solver.Constraints.Empty defstruct [] def any?(%Empty{}), do: false def empty?(%Empty{}), do: true def allows?(%Empty{}, %Version{}), do: false def allows_any?(%Empty{}, constraint), do: Constraint.empty?(constraint) def allows_all?(%Empty{}, constraint), do: Constraint.empty?(constraint) def difference(%Empty{}, _constraint), do: %Empty{} def intersect(%Empty{}, _constraint), do: %Empty{} def union(%Empty{}, constraint), do: constraint def compare(left, right) do raise FunctionClauseError, module: __MODULE__, function: :compare, arity: 2, kind: :def, args: [left, right], clauses: [] end def to_string(%Empty{}) do "empty" end defimpl String.Chars do defdelegate to_string(empty), to: Hex.Solver.Constraints.Empty end defimpl Inspect do def inspect(_, _opts) do "#Empty<>" end end end hex-2.4.2/lib/hex/solver/constraints/impl.ex000066400000000000000000000022711517471540100210240ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defmodule Hex.Solver.Constraints.Impl do @moduledoc false defmacro __using__(opts) do for = Keyword.get(opts, :for, __CALLER__.module) quote do defimpl Hex.Solver.Constraint, for: unquote(for) do def any?(constraint), do: unquote(__CALLER__.module).any?(constraint) def empty?(constraint), do: unquote(__CALLER__.module).empty?(constraint) def allows?(constraint, version), do: unquote(__CALLER__.module).allows?(constraint, version) def allows_any?(left, right), do: unquote(__CALLER__.module).allows_any?(left, right) def allows_all?(left, right), do: unquote(__CALLER__.module).allows_all?(left, right) def difference(left, right), do: unquote(__CALLER__.module).difference(left, right) def intersect(left, right), do: unquote(__CALLER__.module).intersect(left, right) def union(left, right), do: unquote(__CALLER__.module).union(left, right) def compare(left, right), do: unquote(__CALLER__.module).compare(left, right) end end end end hex-2.4.2/lib/hex/solver/constraints/range.ex000066400000000000000000000272421517471540100211640ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defmodule Hex.Solver.Constraints.Range do @moduledoc false use Hex.Solver.Constraints.Impl alias Hex.Solver.Constraint alias Hex.Solver.Constraints.{Empty, Range, Union, Util, Version} # %Range{min: nil, max: nil} allows any version. # %Range{min: min, max: max, include_min: true, include_max: true} # when min == max is a single version. defstruct min: nil, max: nil, include_min: false, include_max: false def valid?(%Range{min: min, max: max, include_min: include_min, include_max: include_max}) do case version_compare(min, max) do :lt -> true :eq -> include_min and include_max :gt -> false end end def any?(%Range{min: nil, max: nil}), do: true def any?(%Range{}), do: false def empty?(%Range{}), do: false def allows?(%Range{} = range, %Elixir.Version{} = version) do compare_min = version_compare(range.min, version) if compare_min == :lt or (compare_min == :eq and range.include_min) do compare_max = version_compare(version, range.max) compare_max == :lt or (compare_max == :eq and range.include_max) else false end end def allows_all?(%Range{}, %Empty{}), do: true def allows_all?(%Range{} = range, %Elixir.Version{} = version), do: allows?(range, version) def allows_all?(%Range{} = left, %Range{} = right) do not allows_lower?(right, left) and not allows_higher?(right, left) end def allows_all?(%Range{} = range, %Union{ranges: ranges}) do Enum.all?(ranges, &allows_all?(range, &1)) end def allows_any?(%Range{}, %Empty{}), do: true def allows_any?(%Range{} = range, %Elixir.Version{} = version), do: allows?(range, version) def allows_any?(%Range{} = left, %Range{} = right) do not strictly_lower?(right, left) and not strictly_higher?(right, left) end def allows_any?(%Range{} = range, %Union{ranges: ranges}) do Enum.any?(ranges, &allows_any?(range, &1)) end def allows_lower?(%Range{} = left, %Range{} = right) do cond do left.min == nil -> right.min != nil right.min == nil -> false true -> case Version.compare(left.min, right.min) do :lt -> true :gt -> false :eq -> left.include_min and not right.include_min end end end def allows_higher?(%Range{} = left, %Range{} = right) do cond do left.max == nil -> right.max != nil right.max == nil -> false true -> case Version.compare(left.max, right.max) do :lt -> false :gt -> true :eq -> left.include_max and not right.include_max end end end def strictly_lower?(%Elixir.Version{} = left, %Elixir.Version{} = right) do Version.compare(left, right) == :lt end def strictly_lower?(%Range{} = range, %Elixir.Version{} = version) do range.max != nil and Version.compare(range.max, version) == :lt end def strictly_lower?(%Elixir.Version{} = version, %Range{} = range) do range.min != nil and Version.compare(version, range.min) == :lt end def strictly_lower?(%Range{} = left, %Range{} = right) do if left.max == nil or right.min == nil do false else case Version.compare(left.max, right.min) do :lt -> true :gt -> false :eq -> not left.include_max or not right.include_min end end end def strictly_higher?(left, right) do strictly_lower?(right, left) end def difference(%Range{} = range, %Empty{}) do range end def difference(%Range{} = range, %Elixir.Version{} = version) do cond do not allows?(range, version) -> range range.min == version -> %{range | include_min: false} range.max == version -> %{range | include_max: false} true -> %Union{ ranges: [ %{range | max: version, include_max: false}, %{range | min: version, include_min: false} ] } end end def difference(%Range{} = left, %Range{} = right) do if allows_any?(left, right) do before_range = cond do not allows_lower?(left, right) -> nil left.min == right.min -> true = left.include_min and not right.include_min true = left.min != nil left.min true -> %Range{ min: left.min, max: right.min, include_min: left.include_min, include_max: not right.include_min } end after_range = cond do not allows_higher?(left, right) -> nil left.max == right.max -> true = left.include_max and not right.include_max true = left.max != nil left.max true -> %Range{ min: right.max, max: left.max, include_min: not right.include_max, include_max: left.include_max } end cond do before_range == nil and after_range == nil -> %Empty{} before_range == nil -> after_range after_range == nil -> before_range true -> %Union{ranges: [before_range, after_range]} end else left end end def difference(%Range{} = range, %Union{} = union) do {range, ranges} = Enum.reduce_while(union.ranges, {range, []}, fn union_range, {range, acc} -> cond do strictly_lower?(union_range, range) -> {:cont, {range, acc}} strictly_higher?(union_range, range) -> {:halt, {range, acc}} true -> case Constraint.difference(range, union_range) do %Empty{} -> {:halt, {range, %Empty{}}} %Union{ranges: [first, last]} -> {:cont, {last, [first | acc]}} constraint -> {:cont, {constraint, acc}} end end end) case ranges do %Empty{} -> %Empty{} [] -> range _ -> %Union{ranges: Enum.reverse([range | ranges])} end end def intersect(%Range{}, %Empty{}) do %Empty{} end def intersect(%Range{} = range, %Elixir.Version{} = version) do if allows?(range, version) do version else %Empty{} end end def intersect(%Range{} = left, %Range{} = right) do if strictly_lower?(left, right) or strictly_lower?(right, left) do %Empty{} else {intersect_min, intersect_include_min} = if allows_lower?(left, right) do {right.min, right.include_min} else {left.min, left.include_min} end {intersect_max, intersect_include_max} = if allows_higher?(left, right) do {right.max, right.include_max} else {left.max, left.include_max} end cond do intersect_min == nil and intersect_max == nil -> # Open range %Range{} intersect_min == intersect_max -> # There must be overlap since we already checked none of # the ranges are strictly lower true = intersect_include_min and intersect_include_max intersect_min true -> %Range{ min: intersect_min, max: intersect_max, include_min: intersect_include_min, include_max: intersect_include_max } end end end def intersect(%Range{} = range, %Union{} = union) do Union.intersect(union, range) end def union(%Range{} = range, %Empty{}) do range end def union(%Range{} = range, %Elixir.Version{} = version) do if allows?(range, version) do range else case range do %Range{min: ^version} -> %{range | include_min: true} %Range{max: ^version} -> %{range | include_max: true} _ -> Util.union([range, version]) end end end def union(%Range{} = left, %Range{} = right) do if not edges_touch?(left, right) and not Range.allows_any?(left, right) do Util.union([left, right]) else min = if allows_lower?(left, right), do: left, else: right max = if allows_higher?(left, right), do: left, else: right %Range{ min: min.min, max: max.max, include_min: min.include_min, include_max: max.include_max } end end def union(%Range{} = range, %Union{} = union) do Util.union([range, union]) end defp edges_touch?(left, right) do (left.max != nil and left.max == right.min and (left.include_max or right.include_min)) or (left.min != nil and left.min == right.max and (left.include_min or right.include_max)) end def compare(%Range{min: min, include_min: include_min}, %Elixir.Version{} = version) do if min == nil do :lt else case Version.compare(min, version) do :eq when include_min -> :eq :eq -> :gt :lt -> :lt :gt -> :gt end end end def compare(%Range{} = left, %Range{} = right) do cond do left.min == nil and right.min == nil -> :eq left.min == nil -> :lt right.min == nil -> :gt true -> left_include_min = left.include_min right_include_min = right.include_min case Version.compare(left.min, right.min) do :eq when left_include_min == right_include_min -> :eq :eq when left_include_min -> :lt :eq when right_include_min -> :gt :lt -> :lt :gt -> :gt end end end def compare(%Range{} = left, %Union{ranges: [right | _]}) do compare(left, right) end def single_version?(%Range{min: min, max: max, include_min: true, include_max: true}) do min == max end def single_version?(%Range{}) do false end defp version_compare(nil, _right), do: :lt defp version_compare(_left, nil), do: :lt defp version_compare(left, right), do: Version.compare(left, right) def normalize(%Range{min: version, max: version, include_min: true, include_max: true}), do: version def normalize(%Range{} = range), do: range def normalize(%Elixir.Version{} = version), do: version def to_string(%Range{min: nil, max: nil}) do "any" end def to_string(%Range{min: version, max: version, include_min: true, include_max: true}) do Kernel.to_string(version) end def to_string(%Range{ min: %Elixir.Version{major: min_major, minor: min_minor, patch: 0, pre: pre}, max: %Elixir.Version{major: max_major, minor: 0, patch: 0, pre: [0]}, include_min: true, include_max: false }) when min_major + 1 == max_major do "~> #{min_major}.#{min_minor}#{pre_to_string(pre)}" end def to_string(%Range{ min: %Elixir.Version{major: min_major, minor: min_minor, patch: min_patch, pre: pre}, max: %Elixir.Version{major: max_major, minor: max_minor, patch: 0, pre: [0]}, include_min: true, include_max: false }) when min_major == max_major and min_minor + 1 == max_minor do "~> #{min_major}.#{min_minor}.#{min_patch}#{pre_to_string(pre)}" end def to_string(%Range{min: min, max: max, include_min: include_min, include_max: include_max}) do min_string = if min, do: ">#{include(include_min)} #{min}" max_string = if max, do: "<#{include(include_max)} #{max}" if min_string && max_string do "#{min_string} and #{max_string}" else min_string || max_string end end defp pre_to_string([]), do: "" defp pre_to_string(pre), do: "-" <> Enum.join(pre, ".") defp include(true), do: "=" defp include(false), do: "" defimpl String.Chars do defdelegate to_string(range), to: Hex.Solver.Constraints.Range end defimpl Inspect do def inspect(range, _opts) do "#Range<#{range}>" end end end hex-2.4.2/lib/hex/solver/constraints/union.ex000066400000000000000000000117441517471540100212200ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defmodule Hex.Solver.Constraints.Union do @moduledoc false use Hex.Solver.Constraints.Impl alias Hex.Solver.Constraint alias Hex.Solver.Constraints.{Empty, Range, Union, Util, Version} # We will always work under the following assumptions for unions: # * Minimum of two elements # * Only %Range{} and %Version{}, no %Empty{} # * No "any range" (%Range{min: nil, max: nil}) # * Elements sorted by their minimum version # * Ranges do not overlap defstruct ranges: [] def empty?(%Union{}), do: false def any?(%Union{}), do: false def allows?(%Union{ranges: ranges}, %Elixir.Version{} = version) do Enum.any?(ranges, &Constraint.allows?(&1, version)) end def allows_all?(%Union{ranges: left}, %Union{ranges: right}) do do_allows_all?(left, right) end def allows_all?(%Union{ranges: left}, right) do do_allows_all?(left, [right]) end # We can recurse left and right together since they are # sorted on minimum version defp do_allows_all?([left | lefts], [right | rights]) do if Constraint.allows_all?(left, right) do do_allows_all?([left | lefts], rights) else do_allows_all?(lefts, [right | rights]) end end defp do_allows_all?(_lefts, []), do: true defp do_allows_all?([], _rights), do: false def allows_any?(%Union{}, %Empty{}), do: true def allows_any?(%Union{} = left, right) do do_allows_any?(to_ranges(left), to_ranges(right)) end # We can recurse left and right together since they are # sorted on minimum version defp do_allows_any?([left | lefts], [right | rights]) do cond do Range.allows_any?(left, right) -> true # Move forward with the range with the lower max value Range.allows_higher?(right, left) -> do_allows_any?(lefts, [right | rights]) true -> do_allows_any?([left | lefts], rights) end end defp do_allows_any?(_lefts, _rights), do: false def difference(%Union{} = left, right) do do_difference(to_ranges(left), to_ranges(right), []) end defp do_difference(lefts, [], acc) do # If there are no more "right" ranges, none of the rest needs to # be subtracted and can be added as-is Util.from_list(Enum.reverse(acc) ++ lefts) end defp do_difference([], _rights, acc) do Util.from_list(Enum.reverse(acc)) end defp do_difference([left | lefts], [right | rights], acc) do cond do Range.strictly_lower?(right, left) -> do_difference([left | lefts], rights, acc) Range.strictly_higher?(right, left) -> do_difference(lefts, [right | rights], [left | acc]) true -> # Left and right overlaps case maybe_to_range(Range.difference(left, right)) do %Union{ranges: [first, last]} -> # If right splits left in half, we only need to check future ranges # against the latter half do_difference([last | lefts], rights, [first | acc]) %Range{} = range -> # Move the constraint with the lower max value forward. Ensures # we keep both lists in sync as much as possible and that large # ranges have a chance to subtract or be subtracted of multiple # small ranges if Range.allows_higher?(range, right) do do_difference([range | lefts], rights, acc) else do_difference(lefts, [right | rights], [range | acc]) end %Empty{} -> do_difference(lefts, [right | rights], acc) end end end def intersect(%Union{} = left, right) do do_intersect(to_ranges(left), to_ranges(right), []) end defp do_intersect([left | lefts], [right | rights], acc) do acc = case Constraint.intersect(left, right) do %Empty{} -> acc intersection -> [intersection | acc] end if Range.allows_higher?(right, left) do do_intersect(lefts, [right | rights], acc) else do_intersect([left | lefts], rights, acc) end end defp do_intersect(_lefts, [], acc), do: Util.from_list(Enum.reverse(acc)) defp do_intersect([], _rights, acc), do: Util.from_list(Enum.reverse(acc)) def union(%Union{} = left, right) do Util.union([left, right]) end def compare(%Union{ranges: [range | _]}, right) do Constraint.compare(range, right) end defp to_ranges(%Empty{}), do: [] defp to_ranges(%Elixir.Version{} = version), do: [Version.to_range(version)] defp to_ranges(%Range{} = range), do: [range] defp to_ranges(%Union{ranges: ranges}), do: Enum.map(ranges, &Version.to_range/1) defp maybe_to_range(%Elixir.Version{} = version), do: Version.to_range(version) defp maybe_to_range(other), do: other def to_string(%Union{ranges: ranges}) do Enum.map_join(ranges, " or ", &Kernel.to_string/1) end defimpl String.Chars do defdelegate to_string(union), to: Hex.Solver.Constraints.Union end defimpl Inspect do def inspect(union, _opts) do "#Union<#{union}>" end end end hex-2.4.2/lib/hex/solver/constraints/util.ex000066400000000000000000000042751517471540100210460ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defmodule Hex.Solver.Constraints.Util do @moduledoc false alias Hex.Solver.{Constraint, Util} alias Hex.Solver.Constraints.{Empty, Range, Union, Version} def any(), do: %Range{} def from_list([]), do: %Empty{} def from_list([single]), do: Range.normalize(single) def from_list(acc), do: %Union{ranges: Enum.map(acc, &Range.normalize/1)} def union(list) do list = flatten(list) cond do list == [] -> %Empty{} Enum.any?(list, &Constraint.any?/1) -> %Range{} true -> list |> Enum.sort(Util.compare(Constraint)) |> Enum.reduce([], fn constraint, acc -> previous = List.first(acc) cond do acc == [] -> [constraint] not Constraint.allows_any?(previous, constraint) and not adjacent?(previous, constraint) -> [constraint | acc] true -> # Merge this constraint with previous, but only if they touch List.replace_at(acc, 0, Constraint.union(previous, constraint)) end end) |> Enum.reverse() |> from_list() end end defp flatten(list) do Enum.flat_map(list, fn %Union{ranges: ranges} -> ranges %Empty{} -> [] other -> [other] end) end @doc """ Returns `true` if `left` is immediately next to, but not overlapping, `right`. Assumes `left` is lower than `right`. """ def adjacent?(left, right) do left = Version.to_range(left) right = Version.to_range(right) left.max == right.min and ((left.include_max and not right.include_min) or (not left.include_max and right.include_min)) end def from_bounds(%Elixir.Version{} = lower, %Elixir.Version{} = upper) do case Version.compare(lower, upper) do :eq -> lower :lt -> %Range{min: lower, max: upper, include_min: true, include_max: true} end end def from_bounds(%Elixir.Version{} = lower, nil), do: %Range{min: lower, include_min: true} def from_bounds(nil, %Elixir.Version{} = upper), do: %Range{max: upper, include_max: true} def from_bounds(nil, nil), do: %Range{} end hex-2.4.2/lib/hex/solver/constraints/version.ex000066400000000000000000000060421517471540100215500ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defmodule Hex.Solver.Constraints.Version do @moduledoc false use Hex.Solver.Constraints.Impl, for: Version import Kernel, except: [match?: 2] alias Hex.Solver.Constraint alias Hex.Solver.Constraints.{Empty, Range, Union, Util} def any?(%Version{}), do: false def empty?(%Version{}), do: false def allows?(%Version{} = left, %Version{} = right), do: left == right def allows_any?(%Version{}, %Empty{}), do: true def allows_any?(%Version{} = left, right), do: Constraint.allows?(right, left) def allows_all?(%Version{}, %Empty{}) do true end def allows_all?(%Version{} = left, %Version{} = right) do left == right end def allows_all?(%Version{}, %Range{min: nil}) do false end def allows_all?(%Version{}, %Range{max: nil}) do false end def allows_all?(%Version{} = version, %Range{ min: min, max: max, include_min: true, include_max: true }) do version == min and version == max end def allows_all?(%Version{}, %Range{}) do false end def allows_all?(%Version{} = version, %Union{ranges: ranges}) do Enum.all?(ranges, &allows_all?(version, &1)) end def difference(%Version{} = version, constraint) do if Constraint.allows?(constraint, version), do: %Empty{}, else: version end def intersect(%Version{} = version, constraint) do if Constraint.allows?(constraint, version), do: version, else: %Empty{} end def union(%Version{} = version, constraint) do if Constraint.allows?(constraint, version) do constraint else case constraint do %Range{min: ^version} = range -> %{range | include_min: true} %Range{max: ^version} = range -> %{range | include_max: true} _ -> Util.union([version, constraint]) end end end def compare(%Version{} = left, %Version{} = right) do Version.compare(left, right) end def compare(%Version{} = version, %Range{min: min, include_min: include_min}) do if min == nil do :gt else case Version.compare(version, min) do :eq when include_min -> :eq :eq -> :lt :lt -> :lt :gt -> :gt end end end def compare(%Version{} = version, %Union{ranges: [range | _]}) do compare(version, range) end def min(left, right) do case compare(left, right) do :lt -> left :eq -> left :gt -> right end end def max(left, right) do case compare(left, right) do :lt -> right :eq -> left :gt -> left end end def to_range(%Version{} = version) do %Range{min: version, max: version, include_min: true, include_max: true} end def to_range(%Range{} = range) do range end def prioritize(%Version{} = left, %Version{} = right) do do_prioritize(left, right) == :gt end defp do_prioritize(left, right) do cond do left.pre != [] and right.pre == [] -> :lt left.pre == [] and right.pre != [] -> :gt true -> Version.compare(left, right) end end end hex-2.4.2/lib/hex/solver/failure.ex000066400000000000000000000173261517471540100171520ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defmodule Hex.Solver.Failure do @moduledoc false alias Hex.Solver.Incompatibility def write(root, opts \\ []) do derivations = count_derivations(%{}, root) state = state(root, derivations, opts) state = case root.cause do {:conflict, _, _} -> visit(state, root) _ -> write(state, root, false, [ "Because ", Incompatibility.to_string(root, ansi: state.ansi), " version solving failed." ]) end numbers = state.lines |> Enum.map(fn {number, _message} -> number end) |> Enum.filter(&is_integer/1) indent? = numbers != [] indent = if indent?, do: Enum.max_by(numbers, &byte_size(Integer.to_string(&1))) + 3 Enum.map_join(Enum.reverse(state.lines), "\n", fn {number, message} -> if number do "(#{number}) #{message}" else "#{message && indent && String.duplicate(" ", indent)}#{message}" end end) end defp count_derivations(derivations, incompatibility) do case Map.fetch(derivations, incompatibility) do {:ok, value} -> Map.put(derivations, incompatibility, value + 1) :error -> derivations = case incompatibility.cause do {:conflict, conflict, other} -> derivations |> count_derivations(conflict) |> count_derivations(other) _ -> derivations end Map.update(derivations, incompatibility, 1, &(&1 + 1)) end end defp visit(state, incompatibility, conclusion? \\ false) do numbered? = conclusion? or Map.fetch!(state.derivations, incompatibility) > 1 conjunction = if conclusion? or incompatibility == state.root, do: "So,", else: "And" incompatibility_string = Incompatibility.to_string(incompatibility, ansi: state.ansi) {:conflict, conflict, other} = incompatibility.cause case {conflict.cause, other.cause} do {{:conflict, _, _}, {:conflict, _, _}} -> conflict_line = state.line_numbers[conflict] other_line = state.line_numbers[other] single_line_conflict? = single_line?(conflict.cause) single_line_other? = single_line?(other.cause) cond do conflict_line && other_line -> write(state, incompatibility, numbered?, [ "Because ", Incompatibility.to_string_and(conflict, other, left_line: conflict_line, right_line: other_line, ansi: state.ansi ), ", ", incompatibility_string ]) conflict_line || other_line -> {with_line, without_line, line} = if conflict_line do {conflict, other, conflict_line} else {other, conflict, other_line} end state |> visit(without_line) |> write(incompatibility, numbered?, [ conjunction, " because ", Incompatibility.to_string(with_line, ansi: state.ansi), " (#{line}), ", incompatibility_string ]) single_line_conflict? or single_line_other? -> first = if single_line_other?, do: conflict, else: other second = if single_line_other?, do: other, else: conflict state |> visit(first) |> visit(second) |> write(incompatibility, numbered?, ["This, ", incompatibility_string]) true -> state |> visit(conflict, _conclusion? = true) |> add_line() |> visit(other) |> write(incompatibility, numbered?, fn state -> [ conjunction, " because ", Incompatibility.to_string(conflict, ansi: state.ansi), maybe_line(state.line_numbers[conflict]), ", ", incompatibility_string, "." ] end) end {left, right} when elem(left, 0) == :conflict when elem(right, 0) == :conflict -> derived = if match?({:conflict, _, _}, conflict.cause), do: conflict, else: other ext = if match?({:conflict, _, _}, conflict.cause), do: other, else: conflict cond do derived_line = state.line_numbers[derived] -> write(state, incompatibility, numbered?, [ "Because ", Incompatibility.to_string_and(ext, derived, right_line: derived_line, ansi: state.ansi ), ", ", incompatibility_string ]) collapsible?(state, derived) -> {:conflict, derived_conflict, derived_other} = derived.cause collapsed_derived = if match?({:conflict, _, _}, derived_conflict.cause), do: derived_conflict, else: derived_other collapsed_ext = if match?({:conflict, _, _}, derived_conflict.cause), do: derived_other, else: derived_conflict state |> visit(collapsed_derived) |> write(incompatibility, numbered?, [ conjunction, " because ", Incompatibility.to_string_and(collapsed_ext, ext, ansi: state.ansi), ", ", incompatibility_string, "." ]) true -> state |> visit(derived) |> write(incompatibility, numbered?, [ conjunction, " because ", Incompatibility.to_string(ext, ansi: state.ansi), ", ", incompatibility_string, "." ]) end _ -> write(state, incompatibility, numbered?, [ "Because ", Incompatibility.to_string_and(conflict, other, ansi: state.ansi), ", ", incompatibility_string, "." ]) end end defp write(state, incompatibility, numbered?, message) do message = maybe_fun(state, message) if numbered? do number = map_size(state.line_numbers) + 1 %{ state | lines: [{number, message} | state.lines], line_numbers: Map.put(state.line_numbers, incompatibility, number) } else %{state | lines: [{nil, message} | state.lines]} end end defp add_line(state, message \\ nil) do %{state | lines: [{nil, message} | state.lines]} end defp maybe_fun(state, fun) when is_function(fun, 1), do: fun.(state) defp maybe_fun(_state, other), do: other defp maybe_line(nil), do: "" defp maybe_line(line), do: " (#{line})" defp collapsible?( state, %Incompatibility{cause: {:conflict, conflict, other}} = incompatibility ) do complex = if match?({:conflict, _, _}, conflict), do: conflict, else: other cond do state.derivations[incompatibility] > 1 -> false match?({:conflict, _, _}, conflict) and match?({:conflict, _, _}, other) -> false not match?({:conflict, _, _}, conflict) and not match?({:conflict, _, _}, other) -> false true -> not Map.has_key?(state.line_numbers, complex) end end defp single_line?({ :conflict, %Incompatibility{cause: {:conflict, _, _}}, %Incompatibility{cause: {:conflict, _, _}} }), do: true defp single_line?({:conflict, %Incompatibility{}, %Incompatibility{}}), do: false defp state(root, derivations, opts) do %{ root: root, lines: [], line_numbers: %{}, derivations: derivations, ansi: Keyword.get(opts, :ansi, false) } end end hex-2.4.2/lib/hex/solver/incompatibility.ex000066400000000000000000000307701517471540100207210ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defmodule Hex.Solver.Incompatibility do @moduledoc false import Kernel, except: [to_string: 1] alias Hex.Solver.{Constraint, Incompatibility, PackageRange, Term} alias Hex.Solver.Constraints.Range defstruct terms: [], cause: nil # Causes: # * {:conflict, incompatibility, cause} # * :root # * :dependency # * :no_versions # * :package_not_found def new(terms, cause) do terms = if length(terms) != 1 and match?({:conflict, _, _}, cause) and Enum.any?(terms, &(&1.positive or &1.package_range.name == "$root")) do Enum.filter(terms, &(not &1.positive or &1.package_range.name != "$root")) else terms end case terms do [_first] -> %Incompatibility{terms: terms, cause: cause} [%{package_range: %{name: first}}, %{package_range: %{name: second}}] when first != second -> %Incompatibility{terms: terms, cause: cause} _ -> terms = Enum.reduce(terms, %{}, fn term, map -> Map.update(map, term.package_range.name, term, &Term.intersect(&1, term)) end) %Incompatibility{terms: Map.values(terms), cause: cause} end end def failure?(%Incompatibility{terms: []}), do: true def failure?(%Incompatibility{terms: [%{package_range: %{name: "$root"}}]}), do: true def failure?(%Incompatibility{}), do: false def to_string(incompatibilty, opts \\ []) def to_string( %Incompatibility{ cause: :dependency, terms: [ %Term{package_range: %PackageRange{name: "$lock"}, positive: true}, %Term{positive: false} = dependee ] }, opts ) do "\"the lock\" specifies #{bright_term_abs(dependee, opts)}" end def to_string( %Incompatibility{ cause: :dependency, terms: [%Term{positive: true} = depender, %Term{positive: false} = dependee] }, opts ) do "#{terse_every(depender, opts)} depends on #{bright_term_abs(dependee, opts)}" end def to_string(%Incompatibility{cause: :no_versions, terms: terms}, opts) do [%Term{positive: true} = term] = terms "no versions of #{package_name(term, opts)} match #{bright(term.package_range.constraint, opts)}" end def to_string(%Incompatibility{cause: :package_not_found, terms: terms}, opts) do [%Term{positive: true} = term] = terms "#{package_name(term, opts)} doesn't exist" end def to_string(%Incompatibility{cause: :root, terms: terms}, opts) do [%Term{positive: false} = term] = terms "#{package_name(term, opts)} is #{bright(term.package_range.constraint, opts)}" end def to_string(%Incompatibility{terms: []}, _opts) do "version solving failed" end def to_string( %Incompatibility{terms: [%Term{package_range: %PackageRange{name: "$root"}}]}, _opts ) do "version solving failed" end def to_string( %Incompatibility{ terms: [ %Term{ positive: true, package_range: %PackageRange{constraint: %Range{min: nil, max: nil}} } = term ] }, opts ) do "no version of #{package_name(term, opts)} is allowed" end def to_string(%Incompatibility{terms: [%Term{positive: true} = term]}, opts) do "#{terse_name(term, opts)} is forbidden" end def to_string(%Incompatibility{terms: [%Term{positive: false} = term]}, opts) do "#{terse_name(%Term{term | positive: true}, opts)} is required" end def to_string( %Incompatibility{terms: [%{positive: true} = left, %{positive: true} = right]}, opts ) do "#{terse_name(left, opts)} is incompatible with #{terse_name(right, opts)}" end def to_string( %Incompatibility{terms: [%{positive: false} = left, %{positive: false} = right]}, opts ) do "either #{bright_term_abs(left, opts)} or #{bright_term_abs(right, opts)}" end def to_string(%Incompatibility{terms: terms}, opts) do {positive, negative} = Enum.split_with(terms, & &1.positive) cond do positive != [] and negative != [] -> case positive do [term] -> "#{bright_term_abs(term, opts)} requires #{Enum.map_join(negative, " or ", &bright_term_abs(&1, opts))}" _ -> "if #{Enum.map_join(positive, " and ", &bright_term_abs(&1, opts))} then #{Enum.map_join(negative, " or ", &bright_term_abs(&1, opts))}" end positive != [] -> "one of #{Enum.map_join(positive, " or ", &bright_term_abs(&1, opts))} must be false" negative != [] -> "one of #{Enum.map_join(negative, " or ", &bright_term_abs(&1, opts))} must be true" end end def to_string_and(left, right, opts \\ []) def to_string_and( %Incompatibility{ terms: [%{package_range: %{name: "$lock"}}, _dependency], cause: :dependency } = left, %Incompatibility{ terms: [%{package_range: %{name: "$root"}}, %{package_range: %{name: "$lock"}}], cause: :dependency }, opts ) do to_string(left, opts) end def to_string_and(%Incompatibility{} = left, %Incompatibility{} = right, opts) do cond do requires_both = try_requires_both(left, right, opts) -> requires_both requires_through = try_requires_through(left, right, opts) -> requires_through requires_forbidden = try_requires_forbidden(left, right, opts) -> requires_forbidden true -> [ to_string(left, opts), maybe_line(opts[:left_line]), " and ", to_string(right, opts), maybe_line(opts[:right_line]) ] end |> IO.chardata_to_string() end defp try_requires_both(left, right, opts) do if length(left.terms) == 1 or length(right.terms) == 1 do throw({__MODULE__, :try_requires_both}) end left_positive = single_term(left, & &1.positive) right_positive = single_term(right, & &1.positive) if !left_positive || !right_positive || left_positive.package_range != right_positive.package_range do throw({__MODULE__, :try_requires_both}) end left_negatives = left.terms |> Enum.reject(& &1.positive) |> Enum.map_join(" or ", &bright_term_abs(&1, opts)) right_negatives = right.terms |> Enum.reject(& &1.positive) |> Enum.map_join(" or ", &bright_term_abs(&1, opts)) dependency? = left.cause == :dependency and right.cause == :dependency [ terse_every(left_positive, opts), " ", cause_verb(dependency?), " both ", left_negatives, maybe_line(opts[:left_line]), " and ", right_negatives, maybe_line(opts[:right_line]) ] catch {__MODULE__, :try_requires_both} -> nil end defp try_requires_through(right, left, opts) do if length(left.terms) == 1 or length(right.terms) == 1 do throw({__MODULE__, :try_requires_through}) end left_negative = single_term(left, &(not &1.positive)) right_negative = single_term(right, &(not &1.positive)) left_positive = single_term(left, & &1.positive) right_positive = single_term(right, & &1.positive) if !left_negative && !right_negative do throw({__MODULE__, :try_requires_through}) end {prior, prior_negative, prior_line, latter, latter_line} = cond do left_negative && right_positive && left_negative.package_range.name == right_positive.package_range.name && Term.satisfies?(Term.inverse(left_negative), right_positive) -> {left, left_negative, opts[:left_line], right, opts[:right_line]} right_negative && left_positive && right_negative.package_range.name == left_positive.package_range.name && Term.satisfies?(Term.inverse(right_negative), left_positive) -> {right, right_negative, opts[:right_line], left, opts[:left_line]} true -> throw({__MODULE__, :try_requires_through}) end prior_positives = Enum.filter(prior.terms, & &1.positive) buffer = if length(prior_positives) > 1 do prior_string = Enum.map_join(prior_positives, " or ", &bright_term_abs(&1, opts)) "if #{prior_string} then " else "#{terse_every(List.first(prior_positives), opts)} #{cause_verb(prior)} " end buffer = [ buffer, bright_term_abs(prior_negative, opts), maybe_line(prior_line), " which ", cause_verb(latter) ] latter_string = latter.terms |> Enum.reject(& &1.positive) |> Enum.map_join(" or ", &bright_term_abs(&1, opts)) [buffer, " ", latter_string, maybe_line(latter_line)] catch {__MODULE__, :try_requires_through} -> nil end defp try_requires_forbidden(left, right, opts) do if length(left.terms) != 1 and length(right.terms) != 1 do throw({__MODULE__, :try_requires_forbidden}) end {prior, prior_line, latter, latter_line} = if length(left.terms) == 1 do {right, opts[:right_line], left, opts[:left_line]} else {left, opts[:left_line], right, opts[:right_line]} end negative = single_term(prior, &(not &1.positive)) unless negative do throw({__MODULE__, :try_requires_forbidden}) end unless Term.satisfies?(Term.inverse(negative), List.first(latter.terms)) do throw({__MODULE__, :try_requires_forbidden}) end positives = Enum.filter(prior.terms, & &1.positive) buffer = case positives do [positive] -> [terse_every(positive, opts), " ", cause_verb(prior), " "] _ -> ["if ", Enum.map_join(positives, " or ", &bright_term_abs(&1, opts)), " then "] end buffer = [ buffer, bright_term_abs(List.first(latter.terms), opts), maybe_line(prior_line), " " ] buffer = case latter.cause do :no_versions -> [buffer, "which doesn't match any versions"] :package_not_found -> [buffer, "which doesn't exist"] _ -> [buffer, "which is forbidden"] end [buffer, maybe_line(latter_line)] catch {__MODULE__, :try_requires_forbidden} -> nil end defp bright(string, opts) do if Keyword.get(opts, :ansi, false) do [IO.ANSI.bright(), Kernel.to_string(string), IO.ANSI.reset()] else ~s("#{string}") end end defp cause_verb(true), do: "depends on" defp cause_verb(false), do: "requires" defp cause_verb(%Incompatibility{cause: :dependency}), do: "depends on" defp cause_verb(%Incompatibility{cause: _}), do: "requires" defp maybe_line(nil), do: "" defp maybe_line(line), do: " (#{line})" defp single_term(%Incompatibility{terms: terms}, fun) do Enum.reduce_while(terms, nil, fn term, found -> if fun.(term) do if found do {:halt, nil} else {:cont, term} end else {:cont, found} end end) end defp package_name(%Term{package_range: %PackageRange{name: "$root"}}, opts), do: bright("your app", opts) defp package_name(%Term{package_range: %PackageRange{name: "$lock"}}, opts), do: bright("the lock", opts) defp package_name(%Term{package_range: %PackageRange{repo: nil, name: name}}, opts), do: bright(name, opts) defp package_name(%Term{package_range: %PackageRange{repo: repo, name: name}}, opts), do: bright("#{repo}/#{name}", opts) defp terse_name(term, opts) do if Constraint.any?(term.package_range.constraint) do package_name(term, opts) else bright(PackageRange.to_string(term.package_range), opts) end end defp terse_every(%Term{package_range: %PackageRange{name: "$root"}}, opts), do: bright("your app", opts) defp terse_every(%Term{package_range: %PackageRange{name: "$lock"}}, opts), do: bright("the lock", opts) defp terse_every(term, opts) do if Constraint.any?(term.package_range.constraint) do "every version of #{package_name(term, opts)}" else bright(PackageRange.to_string(term.package_range), opts) end end defp term_abs(%Term{} = term), do: %Term{term | positive: true} defp bright_term_abs(term, opts), do: bright(term_abs(term), opts) defimpl String.Chars do defdelegate to_string(incompatibility), to: Hex.Solver.Incompatibility end defimpl Inspect do def inspect(%{terms: terms, cause: cause}, _opts) do "#Incompatibility<#{Enum.map_join(terms, ", ", &Kernel.inspect/1)}#{maybe(", cause: ", cause)}>" end defp maybe(_prefix, nil), do: "" defp maybe(prefix, value), do: "#{prefix}#{inspect(value)}" end end hex-2.4.2/lib/hex/solver/package_lister.ex000066400000000000000000000217731517471540100205010ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defmodule Hex.Solver.PackageLister do @moduledoc false alias Hex.Solver.{Constraint, Incompatibility, PackageLister, PackageRange, Term} alias Hex.Solver.Constraints.{Range, Version} defstruct registry: nil, root_dependencies: [], locked: [], overrides: [], already_prefetched: MapSet.new([{nil, "$lock"}]), already_returned: %{} # Prefer packages with few remaining versions so that if there is conflict # later it will be forced quickly def pick_package(lister, package_ranges) do {package_range, versions} = package_ranges |> Enum.sort_by(& &1.name) |> Enum.map(fn package_range -> case versions(lister, package_range.repo, package_range.name) do {:ok, versions} -> allowed = Enum.filter(versions, &Constraint.allows?(package_range.constraint, &1)) {package_range, allowed} :error -> throw({__MODULE__, :minimal_versions, package_range}) end end) |> Enum.min_by(fn {_package_range, versions} -> length(versions) end) {:ok, package_range, List.first(Enum.sort(versions, &Version.prioritize/2))} catch :throw, {__MODULE__, :minimal_versions, package_range} -> {:error, package_range} end def dependencies_as_incompatibilities(%PackageLister{} = lister, package_repo, package, version) do {:ok, versions} = versions(lister, package_repo, package) already_returned = Map.get(lister.already_returned, {package_repo, package}, %{}) # Dependencies of package keyed on package version versions_dependencies = Enum.map(versions, fn version -> {:ok, dependencies} = dependencies(lister, package_repo, package, version) {version, dependencies} end) {_version, dependencies} = List.keyfind(versions_dependencies, version, 0) # Dependencies of package version filtered of overrides and already returned dependencies dependencies = dependencies |> Enum.sort() |> Enum.reject(fn {_dependency, %{label: label}} -> package not in ["$root", "$lock"] and label in lister.overrides end) |> Enum.reject(fn {dependency, _} -> case Map.fetch(already_returned, dependency) do {:ok, returned_constraint} -> Constraint.allows?(returned_constraint, version) :error -> false end end) incompatibilities = Enum.map(dependencies, fn {dependency, %{ repo: dependency_repo, constraint: constraint, optional: optional }} -> version_constraints = Enum.map(versions_dependencies, fn {version, dependencies} -> case Map.fetch(dependencies, dependency) do {:ok, %{constraint: constraint, optional: optional}} -> {version, {constraint, optional}} :error -> {version, nil} end end) # Find range of versions around the current version for which the # constraint is the same to create an incompatibility based on a # larger set of versions for the parent package. # This optimization let us skip many versions during conflict resolution. lower = lower_bound(Enum.reverse(version_constraints), version, {constraint, optional}) upper = upper_bound(version_constraints, version, {constraint, optional}) range = %Range{min: lower, max: upper, include_min: !!lower} package_range = %PackageRange{repo: package_repo, name: package, constraint: range} package_term = %Term{positive: true, package_range: package_range} dependency_range = %PackageRange{ repo: dependency_repo, name: dependency, constraint: constraint } dependency_term = %Term{ positive: false, package_range: dependency_range, optional: optional } Incompatibility.new([package_term, dependency_term], :dependency) end) prefetch = dependencies |> Enum.filter(fn {_dependency, %{prefetch: prefetch}} -> prefetch end) |> MapSet.new(fn {dependency, %{repo: repo}} -> {repo, dependency} end) |> MapSet.difference(lister.already_prefetched) if MapSet.size(prefetch) > 0 do lister.registry.prefetch(Enum.to_list(prefetch)) end # Dependencies already returned already_returned = Enum.reduce(incompatibilities, already_returned, fn incompatibility, already_returned -> [package_term, dependency_term] = case incompatibility do %Incompatibility{terms: [single]} -> [single, single] %Incompatibility{terms: [package, dependency]} -> [package, dependency] end name = dependency_term.package_range.name constraint = package_term.package_range.constraint Map.update(already_returned, name, constraint, &Constraint.union(&1, constraint)) end) lister = %{ lister | already_prefetched: MapSet.union(lister.already_prefetched, prefetch), already_returned: Map.put(lister.already_returned, package, already_returned) } {lister, incompatibilities} end defp lower_bound(versions_dependencies, version, constraint) do [{version, _} | versions_dependencies] = skip_to_version(versions_dependencies, version) skip_to_last_constraint(versions_dependencies, constraint, version) end defp upper_bound(versions_dependencies, version, constraint) do versions_dependencies = skip_to_version(versions_dependencies, version) skip_to_after_constraint(versions_dependencies, constraint) end defp skip_to_version([{version, _constraint} | _] = versions_dependencies, version) do versions_dependencies end defp skip_to_version([_ | versions_dependencies], version) do skip_to_version(versions_dependencies, version) end defp skip_to_last_constraint( [{version, constraint} | versions_dependencies], {_constraint, _optional} = constraint, _last ) do skip_to_last_constraint(versions_dependencies, constraint, version) end defp skip_to_last_constraint([], _constraint, _last) do nil end defp skip_to_last_constraint(_versions_dependencies, _constraint, last) do last end defp skip_to_after_constraint( [{_, constraint}, {version, constraint} | versions_dependencies], {_constraint, _optional} = constraint ) do skip_to_after_constraint([{version, constraint} | versions_dependencies], constraint) end defp skip_to_after_constraint([_, {version, _} | _versions_dependencies], _) do version end defp skip_to_after_constraint(_, _constraint) do nil end @version_1 Elixir.Version.parse!("1.0.0") defp versions(_lister, _repo, "$root"), do: {:ok, [@version_1]} defp versions(_lister, _repo, "$lock"), do: {:ok, [@version_1]} defp versions(%PackageLister{} = lister, repo, package) do root_dependency = find_root_dependency(lister, repo, package) if root_dependency do {:ok, [@version_1]} else lister.registry.versions(repo, package) end end defp dependencies(%PackageLister{} = lister, _repo, "$root", @version_1) do lock_dependency = if lister.locked == [] do [] else [%{repo: nil, name: "$lock", constraint: @version_1, optional: false, label: "$lock"}] end root_dependencies = Enum.map(lister.root_dependencies, fn dependency -> Map.put(dependency, :prefetch, dependency.dependencies == []) end) {:ok, dependency_map(lock_dependency ++ root_dependencies)} end defp dependencies(%PackageLister{locked: locked}, _repo, "$lock", @version_1) do {:ok, Map.new(locked, fn dependency -> {dependency.name, %{ repo: dependency.repo, constraint: dependency.constraint, optional: true, label: dependency.label, prefetch: true }} end)} end defp dependencies(%PackageLister{} = lister, repo, package, version) do root_dependency = find_root_dependency(lister, repo, package) if root_dependency do {:ok, dependency_map(root_dependency.dependencies)} else case lister.registry.dependencies(repo, package, version) do {:ok, dependencies} -> {:ok, dependency_map(dependencies)} :error -> :error end end end defp dependency_map(dependencies) do Map.new(dependencies, fn dependency -> {dependency.name, %{ repo: dependency.repo, constraint: dependency.constraint, optional: dependency.optional, label: dependency.label, prefetch: Map.get(dependency, :prefetch, true) }} end) end defp find_root_dependency(%PackageLister{root_dependencies: dependencies}, repo, package) do Enum.find(dependencies, &(&1.repo == repo and &1.name == package and &1.dependencies != [])) end end hex-2.4.2/lib/hex/solver/package_range.ex000066400000000000000000000021721517471540100202630ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defmodule Hex.Solver.PackageRange do @moduledoc false alias Hex.Solver.PackageRange alias Hex.Solver.Constraints.Range defstruct repo: nil, name: nil, constraint: nil def to_string(%PackageRange{name: "$root"}), do: "your app" def to_string(%PackageRange{name: "$lock"}), do: "the lock" def to_string(%PackageRange{repo: nil, name: name, constraint: constraint}), do: "#{name}#{constraint(constraint)}" def to_string(%PackageRange{repo: repo, name: name, constraint: constraint}), do: "#{repo}/#{name}#{constraint(constraint)}" defp constraint(%Range{min: nil, max: nil}), do: "" defp constraint(constraint), do: " #{constraint}" defimpl String.Chars do defdelegate to_string(package_range), to: Hex.Solver.PackageRange end defimpl Inspect do def inspect(%{repo: nil, name: name, constraint: constraint}, _opts), do: "#PackageRange<#{name} #{constraint}>" def inspect(%{repo: repo, name: name, constraint: constraint}, _opts), do: "#PackageRange<#{repo}/#{name} #{constraint}>" end end hex-2.4.2/lib/hex/solver/partial_solution.ex000066400000000000000000000122351517471540100211050ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defmodule Hex.Solver.PartialSolution do @moduledoc false alias Hex.Solver.{Assignment, PackageRange, PartialSolution, Term} defstruct assignments: [], decisions: %{}, positive: %{}, negative: %{}, backtracking: false, attempted_solutions: 1 def relation(%PartialSolution{} = solution, %Term{} = term, opts \\ []) do name = term.package_range.name case Map.fetch(solution.positive, name) do {:ok, positive} -> Term.relation(positive.term, term, opts) :error -> case Map.fetch(solution.negative, name) do {:ok, negative} -> Term.relation(negative.term, term, opts) :error -> :overlapping end end end def satisfies?(%PartialSolution{} = solution, %Term{} = term, opts \\ []) do relation(solution, term, opts) == :subset end def satisfier(%PartialSolution{} = solution, term, opts \\ []) do {:satisfier, assignment} = Enum.reduce_while(Enum.reverse(solution.assignments), nil, fn assignment, assigned_term -> if assignment.term.package_range.name == term.package_range.name do if Term.compatible_package?(assignment.term, term) do assigned_term = if assigned_term do Assignment.intersect(assigned_term, assignment) else assignment end if Term.satisfies?(assigned_term.term, term, opts) do {:halt, {:satisfier, assignment}} else {:cont, assigned_term} end else if assignment.term.positive do false = term.positive {:halt, {:satisfier, assignment}} else {:cont, assigned_term} end end else {:cont, assigned_term} end end) assignment end def backtrack(%PartialSolution{} = solution, decision_level) do {removed, assignments} = Enum.split_while(solution.assignments, &(&1.decision_level > decision_level)) removed_names = Enum.map(removed, & &1.term.package_range.name) removed_decisions = Enum.filter(removed, &Assignment.decision?/1) decisions = Map.drop(solution.decisions, Enum.map(removed_decisions, & &1.term.package_range.name)) positive = Map.drop(solution.positive, removed_names) negative = Map.drop(solution.negative, removed_names) solution = %{ solution | assignments: assignments, decisions: decisions, positive: positive, negative: negative, backtracking: true } Enum.reduce(assignments, solution, fn assignment, solution -> if assignment.term.package_range.name in removed_names do register(solution, assignment) else solution end end) end def derive(%PartialSolution{} = solution, term, incompatibility) do {assignment, solution} = assign(solution, term, incompatibility) register(solution, assignment) end def decide( %PartialSolution{} = solution, %PackageRange{repo: repo, name: package, constraint: version} = package_range ) do attempted_solutions = solution.attempted_solutions + if solution.backtracking, do: 1, else: 0 decisions = Map.put(solution.decisions, package, {version, repo}) term = %Term{package_range: package_range, positive: true} solution = %{ solution | attempted_solutions: attempted_solutions, backtracking: false, decisions: decisions } {assignment, solution} = assign(solution, term, nil) register(solution, assignment) end defp assign(solution, term, incompatibility) do assignment = %Assignment{ term: term, decision_level: map_size(solution.decisions), index: length(solution.assignments), cause: incompatibility } solution = %{solution | assignments: [assignment | solution.assignments]} {assignment, solution} end defp register(solution, assignment) do name = assignment.term.package_range.name case Map.fetch(solution.positive, name) do {:ok, old_assignment} -> assignment = Assignment.intersect(old_assignment, assignment) positive = Map.put(solution.positive, name, assignment) %{solution | positive: positive} :error -> assignment = if old_assignment = Map.get(solution.negative, name) do Assignment.intersect(old_assignment, assignment) else assignment end if assignment.term.positive do negative = Map.delete(solution.negative, name) positive = Map.put(solution.positive, name, assignment) %{solution | negative: negative, positive: positive} else negative = Map.put(solution.negative, name, assignment) %{solution | negative: negative} end end end def unsatisfied(%PartialSolution{} = solution) do solution.positive |> Map.values() |> Enum.reject(&Map.has_key?(solution.decisions, &1.term.package_range.name)) |> Enum.reject(& &1.term.optional) |> Enum.map(& &1.term.package_range) end end hex-2.4.2/lib/hex/solver/registry.ex000066400000000000000000000016141517471540100173640ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defmodule Hex.Solver.Registry do _ = """ The registry is used by the solver to discover package versions and their dependencies. """ @doc """ Returns all versions of the given package sorted from lowest to highest or `:error` if the package does not exist. """ @callback versions(Hex.Solver.repo(), Hex.Solver.package()) :: {:ok, [Version.t()]} | :error @doc """ Returns all dependencies of the given package version or `:error` if the package or version does not exist. """ @callback dependencies(Hex.Solver.repo(), Hex.Solver.package(), Version.t()) :: {:ok, [Hex.Solver.dependency()]} | :error @doc """ Called when the solver first discovers a set of packages so that the registry can be lazily preloaded. """ @callback prefetch([{Hex.Solver.repo(), Hex.Solver.package()}]) :: :ok end hex-2.4.2/lib/hex/solver/requirement.ex000066400000000000000000000203651517471540100200600ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defmodule Hex.Solver.Requirement do @moduledoc false alias Hex.Solver.Constraints.{Range, Util} alias Hex.Solver.Requirement.Parser @allowed_range_ops [:>, :>=, :<, :<=, :~>] def to_constraint(string) when is_binary(string) do case Parser.parse(string) do {:ok, lexed} -> {:ok, delex(lexed, [])} :error -> :error end catch {__MODULE__, :invalid_constraint} -> :error end def to_constraint(%Elixir.Version{} = version) do {:ok, version} end def to_constraint(%Elixir.Version.Requirement{} = requirement) do to_constraint(to_string(requirement)) end def to_constraint!(string) when is_binary(string) do case Parser.parse(string) do {:ok, lexed} -> delex(lexed, []) :error -> raise Elixir.Version.InvalidRequirementError, string end catch {__MODULE__, :invalid_constraint} -> raise Elixir.Version.InvalidRequirementError, string end def to_constraint!(%Elixir.Version{} = version) do %{version | build: nil} end def to_constraint!(%Elixir.Version.Requirement{} = requirement) do to_constraint!(to_string(requirement)) end defp delex([], acc) do Util.union(acc) end defp delex([op | rest], acc) when op in [:||, :or] do delex(rest, acc) end defp delex([op1, version1, op, op2, version2 | rest], acc) when op in [:&&, :and] do range = to_range(op1, version1, op2, version2) delex(rest, [range | acc]) end defp delex([op, version | rest], acc) do range = to_range(op, version) delex(rest, [range | acc]) end defp to_range(:==, version) do to_version(version) end defp to_range(:~>, {major, minor, nil, pre, _build}) do %Range{ min: to_version({major, minor, 0, pre, nil}), max: to_version({major + 1, 0, 0, [0], nil}), include_min: true } end defp to_range(:~>, {major, minor, patch, pre, _build}) do %Range{ min: to_version({major, minor, patch, pre, nil}), max: to_version({major, minor + 1, 0, [0], nil}), include_min: true } end defp to_range(:>, version) do %Range{min: to_version(version)} end defp to_range(:>=, version) do %Range{min: to_version(version), include_min: true} end defp to_range(:<, version) do %Range{max: to_version(version)} end defp to_range(:<=, version) do %Range{max: to_version(version), include_max: true} end defp to_range(:~>, _version1, :~>, _version2) do throw({__MODULE__, :invalid_constraint}) end defp to_range(op1, version1, :~>, version2) do to_range(:~>, version2, op1, version1) end defp to_range(:~>, version1, op2, version2) do range1 = to_range(:~>, version1) range2 = to_range(op2, version2) range = Range.intersect(range1, range2) unless Range.valid?(range) and Range.allows_any?(range1, range2) do throw({__MODULE__, :invalid_constraint}) end range end defp to_range(op1, version1, op2, version2) when op1 in @allowed_range_ops and op2 in @allowed_range_ops do range = Map.merge(to_range(op1, version1), to_range(op2, version2), fn :__struct__, Range, Range -> Range :min, nil, value -> value :min, value, nil -> value :max, nil, value -> value :max, value, nil -> value :include_min, value, value -> value :include_min, false, value -> value :include_min, value, false -> value :include_max, value, value -> value :include_max, false, value -> value :include_max, value, false -> value end) unless Range.valid?(range) do throw({__MODULE__, :invalid_constraint}) end range end defp to_version({major, minor, patch, pre, _build}), do: %Elixir.Version{major: major, minor: minor, patch: patch, pre: pre} # Vendored from https://github.com/elixir-lang/elixir/blob/0ff6522/lib/elixir/lib/version.ex#L495 defmodule Parser do @moduledoc false operators = [ {">=", :>=}, {"<=", :<=}, {"~>", :~>}, {">", :>}, {"<", :<}, {"==", :==}, {" or ", :or}, {" and ", :and} ] def parse(string) do revert_lexed(lexer(string), []) end defp lexer(string) do lexer(string, "", []) end for {string_op, atom_op} <- operators do defp lexer(unquote(string_op) <> rest, buffer, acc) do lexer(rest, "", [unquote(atom_op) | maybe_prepend_buffer(buffer, acc)]) end end defp lexer(" " <> rest, buffer, acc) do lexer(rest, "", maybe_prepend_buffer(buffer, acc)) end defp lexer(<>, buffer, acc) do lexer(rest, <>, acc) end defp lexer(<<>>, buffer, acc) do maybe_prepend_buffer(buffer, acc) end defp maybe_prepend_buffer("", acc), do: acc defp maybe_prepend_buffer(buffer, [head | _] = acc) when is_atom(head) and head not in [:and, :or], do: [buffer | acc] defp maybe_prepend_buffer(buffer, acc), do: [buffer, :== | acc] defp revert_lexed([version, op, cond | rest], acc) when is_binary(version) and is_atom(op) and cond in [:or, :and] do with {:ok, version} <- validate_requirement(op, version) do revert_lexed(rest, [cond, op, version | acc]) end end defp revert_lexed([version, op], acc) when is_binary(version) and is_atom(op) do with {:ok, version} <- validate_requirement(op, version) do {:ok, [op, version | acc]} end end defp revert_lexed(_rest, _acc), do: :error defp validate_requirement(op, version) do case parse_version(version, true) do {:ok, version} when op == :~> -> {:ok, version} {:ok, {_, _, patch, _, _} = version} when is_integer(patch) -> {:ok, version} _ -> :error end end defp parse_version(string, approximate?) when is_binary(string) do destructure [version_with_pre, build], String.split(string, "+", parts: 2) destructure [version, pre], String.split(version_with_pre, "-", parts: 2) destructure [major, minor, patch, next], String.split(version, ".") with nil <- next, {:ok, major} <- require_digits(major), {:ok, minor} <- require_digits(minor), {:ok, patch} <- maybe_patch(patch, approximate?), {:ok, pre_parts} <- optional_dot_separated(pre), {:ok, pre_parts} <- convert_parts_to_integer(pre_parts, []), {:ok, build_parts} <- optional_dot_separated(build) do {:ok, {major, minor, patch, pre_parts, build_parts}} else _other -> :error end end defp require_digits(nil), do: :error defp require_digits(string) do if leading_zero?(string), do: :error, else: parse_digits(string, "") end defp leading_zero?(<>), do: true defp leading_zero?(_), do: false defp parse_digits(<>, acc) when char in ?0..?9, do: parse_digits(rest, <>) defp parse_digits(<<>>, acc) when byte_size(acc) > 0, do: {:ok, String.to_integer(acc)} defp parse_digits(_, _acc), do: :error defp maybe_patch(patch, approximate?) defp maybe_patch(nil, true), do: {:ok, nil} defp maybe_patch(patch, _), do: require_digits(patch) defp optional_dot_separated(nil), do: {:ok, []} defp optional_dot_separated(string) do parts = String.split(string, ".") if Enum.all?(parts, &(&1 != "" and valid_identifier?(&1))) do {:ok, parts} else :error end end defp convert_parts_to_integer([part | rest], acc) do case parse_digits(part, "") do {:ok, integer} -> if leading_zero?(part) do :error else convert_parts_to_integer(rest, [integer | acc]) end :error -> convert_parts_to_integer(rest, [part | acc]) end end defp convert_parts_to_integer([], acc) do {:ok, Enum.reverse(acc)} end defp valid_identifier?(<>) when char in ?0..?9 when char in ?a..?z when char in ?A..?Z when char == ?- do valid_identifier?(rest) end defp valid_identifier?(<<>>) do true end defp valid_identifier?(_other) do false end end end hex-2.4.2/lib/hex/solver/solver.ex000066400000000000000000000340541517471540100170320ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defmodule Hex.Solver.Solver do @moduledoc false alias Hex.Solver.{ Incompatibility, PackageLister, PackageRange, PartialSolution, Term } alias Hex.Solver.Constraints.Util require Logger def run(registry, dependencies, locked, overrides) do solve("$root", new_state(registry, dependencies, locked, overrides)) end defp solve(next, state) do case unit_propagation([next], state) do {:ok, state} -> case choose_package_version(state) do :done -> {:ok, state.solution.decisions} {:choice, package, state} -> solve(package, state) end {:error, incompatibility, _state} -> {:error, incompatibility} end end defp unit_propagation([], state) do {:ok, state} end defp unit_propagation([package | changed], state) do incompatibilities = Map.fetch!(state.incompatibilities, package) result = Enum.reduce_while(incompatibilities, {changed, state}, fn incompatibility, {changed, state} -> case propagate_incompatibility(incompatibility.terms, nil, incompatibility, state) do {:ok, result, state} -> {:cont, {changed ++ [result], state}} {:error, :none} -> {:cont, {changed, state}} {:error, :conflict} -> unit_propagation_conflict(incompatibility, state) end end) case result do {:error, incompatibility, state} -> {:error, incompatibility, state} {changed, state} -> unit_propagation(changed, state) end end defp unit_propagation_conflict(incompatibility, state) do case conflict_resolution(state, incompatibility) do {:ok, root_cause, state} -> {:ok, result, state} = propagate_incompatibility(root_cause.terms, nil, root_cause, state, optionals: false) {:halt, {[result], state}} {:error, incompatibility} -> {:halt, {:error, incompatibility, state}} end end defp propagate_incompatibility(terms, unsatisified, incompatibility, state, opts \\ []) defp propagate_incompatibility([term | terms], unsatisified, incompatibility, state, opts) do case PartialSolution.relation(state.solution, term, opts) do :disjoint -> # If the term is contradicted by the partial solution then the # incompatibility is also contradicted so we can deduce nothing {:error, :none} :overlapping when unsatisified != nil -> # If more than one term is inconclusive we can deduce nothing {:error, :none} :overlapping -> propagate_incompatibility(terms, term, incompatibility, state, opts) :subset -> propagate_incompatibility(terms, unsatisified, incompatibility, state, opts) end end defp propagate_incompatibility([], nil, _incompatibility, _state, _opts) do # All terms in the incompatibility are satisified by the partial solution # so we have a conflict {:error, :conflict} end defp propagate_incompatibility([], unsatisfied, incompatibility, state, _opts) do # Only one term in the incompatibility was unsatisfied unsatisfied = %{unsatisfied | positive: not unsatisfied.positive} Logger.debug("RESOLVER: derived #{unsatisfied}") solution = PartialSolution.derive(state.solution, unsatisfied, incompatibility) {:ok, unsatisfied.package_range.name, %{state | solution: solution}} end defp choose_package_version(state) do unsatisfied = PartialSolution.unsatisfied(state.solution) if unsatisfied == [] do :done else case PackageLister.pick_package(state.lister, unsatisfied) do {:ok, package_range, nil} -> # If no version satisfies the constraint then add an incompatibility that indicates that term = %Term{positive: true, package_range: package_range} incompatibility = Incompatibility.new([term], :no_versions) state = add_incompatibility(state, incompatibility) {:choice, package_range.name, state} {:ok, %PackageRange{} = package_range, version} -> {lister, incompatibilities} = PackageLister.dependencies_as_incompatibilities( state.lister, package_range.repo, package_range.name, version ) state = %{state | lister: lister} {state, conflict} = Enum.reduce(incompatibilities, {state, false}, fn incompatibility, {state, conflict} -> # If an incompatibility is already satisfied then selecting this version would cause # a conflict. We'll continue adding its dependencies then go back to unit propagation # that will eventually choose a better version. conflict = conflict or incompatibility_conflict?(state, incompatibility, package_range.name) state = add_incompatibility(state, incompatibility) {state, conflict} end) solution = if conflict do state.solution else package_range = %PackageRange{package_range | constraint: version} Logger.debug("RESOLVER: selecting #{package_range}") PartialSolution.decide(state.solution, package_range) end state = %{state | solution: solution} {:choice, package_range.name, state} {:error, %PackageRange{} = package_range} -> package_range = %PackageRange{package_range | constraint: Util.any()} term = %Term{positive: true, package_range: package_range} incompatibility = Incompatibility.new([term], :package_not_found) state = add_incompatibility(state, incompatibility) {:choice, package_range.name, state} end end end defp add_incompatibility(state, incompatibility) do Logger.debug("RESOLVER: fact #{incompatibility}") incompatibilities = Enum.reduce(incompatibility.terms, state.incompatibilities, fn term, incompatibilities -> Map.update( incompatibilities, term.package_range.name, [incompatibility], &[incompatibility | &1] ) end) %{state | incompatibilities: incompatibilities} end defp incompatibility_conflict?(state, incompatibility, name) do Enum.all?(incompatibility.terms, fn term -> term.package_range.name == name or PartialSolution.satisfies?(state.solution, term) end) end # Given an incompatibility that's satisified by the solution, construct a new # incompatibility that encapsulates the root cause of the conflict and backtracks # until the new incompatibility will allow propagation to deduce new assignments. defp conflict_resolution(state, incompatibility) do Logger.debug("RESOLVER: conflict #{incompatibility}") do_conflict_resolution(state, incompatibility, false) catch :throw, {__MODULE__, :conflict_resolution, incompatibility, state} -> {:ok, incompatibility, state} end defp do_conflict_resolution(state, incompatibility, new_incompatibility?) do if Incompatibility.failure?(incompatibility) do {:error, incompatibility} else resolution = Enum.reduce(incompatibility.terms, new_resolution_state(), fn term, resolution -> satisfier = PartialSolution.satisfier(state.solution, term, optionals: false) resolution = cond do resolution.most_recent_satisfier == nil -> %{resolution | most_recent_term: term, most_recent_satisfier: satisfier} resolution.most_recent_satisfier.index < satisfier.index -> %{ resolution | most_recent_term: term, most_recent_satisfier: satisfier, difference: nil, previous_satisfier_level: max( resolution.previous_satisfier_level, resolution.most_recent_satisfier.decision_level ) } true -> %{ resolution | previous_satisfier_level: max(resolution.previous_satisfier_level, satisfier.decision_level) } end if resolution.most_recent_term == term do # If most_recent_satisfier doesn't satisfy most_recent_term on its own, # then the next most recent satisfier may not be the one that satisfies # remainder difference = Term.difference(resolution.most_recent_satisfier.term, resolution.most_recent_term) if difference do satisfier = PartialSolution.satisfier( state.solution, Term.inverse(difference), optionals: false ) previous_satisfier_level = max(resolution.previous_satisfier_level, satisfier.decision_level) %{ resolution | difference: difference, previous_satisfier_level: previous_satisfier_level } else %{resolution | difference: difference} end else resolution end end) # If most_recent_satisfier is the only satisfier left at its decision level, # or if it has no cause (indicating that it's a decision rather than a # derivation), then the incompatibility is the root cause. We then backjump # to previous_satisfier_level, where the incompatibility is guaranteed to # allow propagation to produce more assignments if resolution.previous_satisfier_level < resolution.most_recent_satisfier.decision_level or resolution.most_recent_satisfier.cause == nil do solution = PartialSolution.backtrack(state.solution, resolution.previous_satisfier_level) state = %{state | solution: solution} state = if new_incompatibility?, do: add_incompatibility(state, incompatibility), else: state throw({__MODULE__, :conflict_resolution, incompatibility, state}) end # Create a new incompatibility by combining the given incompatibility with # the incompatibility that caused most_recent_satisfier to be assigned. # Doing this iteratively constructs a new new incompatibility that's guaranteed # to be true (we know for sure no solution will satisfy the incompatibility) # while also approximating the intuitive notion of the "root cause" of the conflict. new_terms = Enum.filter(incompatibility.terms, &(&1 != resolution.most_recent_term)) ++ Enum.filter( resolution.most_recent_satisfier.cause.terms, &(&1.package_range != resolution.most_recent_satisfier.term.package_range) ) # The most_recent_satisfier may not satisfy most_recent_term on its own if # there are a collection of constraints on most_recent_term that only satisfy # it together. For example, if most_recent_term is `foo ~> 1.0` and solution # contains `[foo >= 1.0.0, foo < 2.0.0]`, the most_recent_satisfier will be # `foo < 2.0.0` even though it doesn't totally satisfy `foo ~> 1.0`. # In this case we add `most_recent_satisfier \ most_recent_term` to the # incompatibility as well. See https://github.com/dart-lang/pub/tree/master/doc/solver.md#conflict-resolution # for more details. new_terms = if resolution.difference, do: new_terms ++ [Term.inverse(resolution.difference)], else: new_terms incompatibility = Incompatibility.new( new_terms, {:conflict, incompatibility, resolution.most_recent_satisfier.cause} ) partially = if resolution.difference, do: " partially" Logger.debug(""" RESOLVER: conflict resolution #{resolution.most_recent_term} is#{partially} satisfied by #{resolution.most_recent_satisfier} which is caused by #{resolution.most_recent_satisfier.cause} thus #{incompatibility}\ """) do_conflict_resolution(state, incompatibility, true) end end defp new_resolution_state() do %{ # The term in incompatibility.terms that was most recently satisfied by the solution. most_recent_term: nil, # The earliest assignment in the solution such that incompatibility is satisfied # by the solution up to and including this assignment. most_recent_satisfier: nil, # The difference between most_recent_satisfier and most_recent_term, that is, # the versions that are allowed by most_recent_satisfier but not by most_recent_term. # nil if most_recent_satisfier totally satisfies most_recent_term. difference: nil, # The decision level of the earliest assignment before most_recent_satisfier # such that incompatibility is satisfied by the solution up to and including # this assignment and most_recent_satisfier. # Decision level 1 is the level where the root package was selected. We can # go back to level 0 but level 1 tends to give better error messages, because # references to the root package end up closer to the final conclusion that # no solution exists. previous_satisfier_level: 1 } end defp new_state(registry, root_dependencies, locked, overrides) do version = Version.parse!("1.0.0") package_range = %PackageRange{name: "$root", constraint: version} root = Incompatibility.new([%Term{positive: false, package_range: package_range}], :root) locked = Enum.map(locked, fn %{repo: repo, name: package, version: version, label: label} -> %{repo: repo, name: package, constraint: version, label: label} end) lister = %PackageLister{ registry: registry, root_dependencies: root_dependencies, locked: locked, overrides: overrides } %{ solution: %PartialSolution{}, incompatibilities: %{}, lister: lister } |> add_incompatibility(root) end end hex-2.4.2/lib/hex/solver/term.ex000066400000000000000000000110231517471540100164560ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defmodule Hex.Solver.Term do @moduledoc false alias Hex.Solver.{Constraint, PackageRange, Term} alias Hex.Solver.Constraints.Empty defstruct positive: true, package_range: nil, optional: false def relation(left, right, opts \\ []) do if Keyword.get(opts, :optionals, true) do case do_relation(left, right) do :disjoint -> if left.optional and not right.optional, do: :overlapping, else: :disjoint other -> if left.optional and right.optional, do: :disjoint, else: other end else do_relation(left, right) end end def do_relation(%Term{} = left, %Term{} = right) do left_constraint = constraint(left) right_constraint = constraint(right) cond do right.positive and left.positive -> cond do not compatible_package?(left, right) -> :disjoint Constraint.allows_all?(right_constraint, left_constraint) -> :subset not Constraint.allows_any?(left_constraint, right_constraint) -> :disjoint true -> :overlapping end right.positive and not left.positive -> cond do not compatible_package?(left, right) -> :overlapping Constraint.allows_all?(left_constraint, right_constraint) -> :disjoint true -> :overlapping end not right.positive and left.positive -> cond do not compatible_package?(left, right) -> :subset not Constraint.allows_any?(right_constraint, left_constraint) -> :subset Constraint.allows_all?(right_constraint, left_constraint) -> :disjoint true -> :overlapping end not right.positive and not left.positive -> cond do not compatible_package?(left, right) -> :overlapping Constraint.allows_all?(left_constraint, right_constraint) -> :subset true -> :overlapping end end end def intersect(%Term{} = left, %Term{} = right) do if compatible_package?(left, right) do optional = left.optional and right.optional cond do left.positive != right.positive -> positive = if left.positive, do: left, else: right negative = if left.positive, do: right, else: left constraint = Constraint.difference(constraint(positive), constraint(negative)) non_empty_term(left, constraint, optional, true) left.positive and right.positive -> constraint = Constraint.intersect(constraint(left), constraint(right)) non_empty_term(left, constraint, optional, true) not left.positive and not right.positive -> constraint = Constraint.union(constraint(left), constraint(right)) non_empty_term(left, constraint, optional, false) end else nil end end def difference(%Term{} = left, %Term{} = right) do intersect(left, inverse(right)) end def satisfies?(%Term{} = left, %Term{} = right, opts \\ []) do compatible_package?(left, right) and relation(left, right, opts) == :subset end def inverse(%Term{} = term) do %{term | positive: not term.positive} end def compatible_package?(%Term{package_range: %PackageRange{name: "$root"}}, %Term{}) do true end def compatible_package?(%Term{}, %Term{package_range: %PackageRange{name: "$root"}}) do true end def compatible_package?(%Term{package_range: left}, %Term{package_range: right}) do {left.repo, left.name} == {right.repo, right.name} end defp constraint(%Term{package_range: %PackageRange{constraint: constraint}}) do constraint end defp non_empty_term(_term, %Empty{}, _optional, _positive) do nil end defp non_empty_term(term, constraint, optional, positive) do %Term{ package_range: %{term.package_range | constraint: constraint}, positive: positive, optional: optional } end def to_string(%Term{package_range: package_range, positive: positive}) do "#{positive(positive)}#{package_range}" end defp positive(true), do: "" defp positive(false), do: "not " defimpl String.Chars do defdelegate to_string(term), to: Hex.Solver.Term end defimpl Inspect do def inspect( %Term{package_range: package_range, positive: positive, optional: optional}, _opts ) do "#Term<#{positive(positive)}#{package_range}#{optional(optional)}>" end defp positive(true), do: "" defp positive(false), do: "not " defp optional(true), do: " (optional)" defp optional(false), do: "" end end hex-2.4.2/lib/hex/solver/util.ex000066400000000000000000000003301517471540100164630ustar00rootroot00000000000000# Vendored from hex_solver v0.2.3 (f702d44), do not edit manually defmodule Hex.Solver.Util do @moduledoc false def compare(module) when is_atom(module) do &(module.compare(&1, &2) in [:lt, :eq]) end end hex-2.4.2/lib/hex/sponsor.ex000066400000000000000000000014641517471540100157100ustar00rootroot00000000000000defmodule Hex.Sponsor do @moduledoc false @metadata_file "hex_metadata.config" @sponsor_link_name "sponsor" def get_link(package_path) do case read_metadata(package_path) do {:ok, metadata} -> sponsorship(metadata) _ -> nil end end def get_link(package_name, deps_path) do deps_path |> Path.join(package_name) |> get_link() end defp read_metadata(package_path) do package_path |> Path.join(@metadata_file) |> :file.consult() end defp sponsorship(metadata) do case Enum.find(metadata, fn {name, _} -> name == "links" end) do {_links, links} -> Enum.find_value(links, fn {link, value} -> if String.downcase(link) == @sponsor_link_name do value end end) _ -> nil end end end hex-2.4.2/lib/hex/state.ex000066400000000000000000000216431517471540100153260ustar00rootroot00000000000000defmodule Hex.State do @moduledoc false @name __MODULE__ @api_url "https://hex.pm/api" @pbkdf2_iters 32_768 def default_api_url(), do: @api_url @config %{ api_key: %{ env: ["HEX_API_KEY"], config: [:api_key] }, api_otp: %{ env: ["HEX_OTP"] }, oauth_token: %{ config: [:"$oauth_token"] }, api_url: %{ env: ["HEX_API_URL", "HEX_API"], config: [:api_url], default: @api_url, fun: {__MODULE__, :trim_slash} }, cache_home: %{ default: :user_cache, fun: {Hex.Config, :find_config_home} }, config_home: %{ default: :user_config, fun: {Hex.Config, :find_config_home} }, unsafe_https: %{ env: ["HEX_UNSAFE_HTTPS"], config: [:unsafe_https], default: false, fun: {__MODULE__, :to_boolean} }, unsafe_registry: %{ env: ["HEX_UNSAFE_REGISTRY"], config: [:unsafe_registry], default: false, fun: {__MODULE__, :to_boolean} }, no_verify_repo_origin: %{ env: ["HEX_NO_VERIFY_REPO_ORIGIN"], config: [:no_verify_repo_origin], default: false, fun: {__MODULE__, :to_boolean} }, http_concurrency: %{ env: ["HEX_HTTP_CONCURRENCY"], config: [:http_concurrency], default: 8, fun: {__MODULE__, :to_integer} }, http_proxy: %{ env: ["http_proxy", "HTTP_PROXY"], config: [:http_proxy] }, https_proxy: %{ env: ["https_proxy", "HTTPS_PROXY"], config: [:https_proxy] }, no_proxy: %{ env: ["no_proxy", "NO_PROXY"], config: [:no_proxy] }, http_timeout: %{ env: ["HEX_HTTP_TIMEOUT"], config: [:http_timeout], fun: {__MODULE__, :to_integer} }, data_home: %{ default: :user_data, fun: {Hex.Config, :find_config_home} }, mirror_url: %{ env: ["HEX_MIRROR_URL", "HEX_MIRROR"], config: [:mirror_url], fun: {__MODULE__, :trim_slash} }, trusted_mirror_url: %{ env: ["HEX_TRUSTED_MIRROR_URL", "HEX_TRUSTED_MIRROR"], config: [:trusted_mirror_url], fun: {__MODULE__, :trim_slash} }, offline: %{ env: ["HEX_OFFLINE"], config: [:offline], default: false, fun: {__MODULE__, :to_boolean} }, resolve_verbose: %{ env: ["HEX_RESOLVE_VERBOSE"], default: false, fun: {__MODULE__, :to_boolean} }, repos_key: %{ env: ["HEX_REPOS_KEY"], config: [:repos_key] }, diff_command: %{ env: ["HEX_DIFF_COMMAND"], config: [:diff_command], default: Mix.Tasks.Hex.Package.default_diff_command() }, cacerts_path: %{ env: ["HEX_CACERTS_PATH"], default: nil, config: [:cacerts_path] }, no_short_urls: %{ env: ["HEX_NO_SHORT_URLS"], config: [:no_short_urls], default: false, fun: {__MODULE__, :to_boolean} }, debug_solver: %{ env: ["HEX_DEBUG_SOLVER"], config: [:debug_solver], default: false, fun: {__MODULE__, :to_boolean} }, ci: %{ env: ["CI"], default: false, fun: {__MODULE__, :to_truthy_boolean} } } def start_link([]) do global_config = Hex.Config.read() Agent.start_link(__MODULE__, :init, [global_config], name: @name) end def child_spec(arg) do %{ id: __MODULE__, start: {__MODULE__, :start_link, [arg]} } end def stop() do Agent.stop(@name) end def init(global_config) do project_config = Keyword.get(Mix.Project.config(), :hex, []) state = Map.new(@config, fn {key, spec} -> {key, load_config_value(global_config, project_config, spec)} end) Map.merge(state, %{ clean_pass: {:computed, true}, httpc_profile: {:computed, :hex}, pbkdf2_iters: {:computed, @pbkdf2_iters}, repos: {:computed, Hex.Config.read_repos(global_config)}, ssl_version: {:computed, ssl_version()}, shell_process: {:computed, nil} }) end def refresh() do Agent.update(@name, fn _ -> init(Hex.Config.read()) end) end def get(key) do Agent.get(@name, fn state -> case Map.fetch(state, key) do {:ok, {_source, value}} -> value :error -> nil end end) end def fetch!(key) do Agent.get(@name, fn state -> case Map.fetch(state, key) do {:ok, {_source, value}} -> value :error -> raise KeyError, key: key, term: Hex.State end end) end def fetch!(key, transform) do key |> fetch!() |> transform.() end def fetch_source!(key) do Agent.get(@name, fn state -> case Map.fetch(state, key) do {:ok, {source, _value}} -> source :error -> raise KeyError, key: key, term: Hex.State end end) end def put(key, value) do Agent.update(@name, Map, :put, [key, {:computed, value}]) end def update!(key, fun) do Agent.update(@name, fn state -> Map.update!(state, key, fn {source, value} -> {source, fun.(value)} end) end) end def get_all() do Agent.get(@name, & &1) end def put_all(map) do Agent.update(@name, fn _ -> map end) end defp load_config_value(global_config, project_config, spec) do env = System.get_env() result = load_env(spec[:env], env) || load_project_config(project_config, spec[:config]) || load_global_config(global_config, spec[:config]) {module, func} = spec[:fun] || {__MODULE__, :ok_wrap} case result do nil -> case apply(module, func, [spec[:default]]) do {:ok, value} -> {:default, value} {source, value} -> {source, value} end {source, value} -> case apply(module, func, [value]) do {:ok, value} -> {source, value} :error -> print_invalid_config_error(value, source) {:ok, value} = apply(module, func, [spec[:default]]) {:default, value} end end end defp print_invalid_config_error(value, source) do value = inspect(value, pretty: true) message = "Invalid Hex config, falling back to default. Source: #{source(source)} #{value}" Hex.Shell.error(message) end defp source({:env, env_var}), do: "environment variable #{env_var}=" defp source({:project_config, key}), do: "mix.exs config #{key}: " defp source({:global_config, key}), do: "Hex config (location: #{config_path()}) #{key}: " defp config_path() do :config_home |> Hex.State.fetch!() |> Path.join("hex.config") end defp load_env(keys, env) do Enum.find_value(keys || [], fn key -> case Map.fetch(env, key) do {:ok, value} -> {{:env, key}, value} :error -> nil end end) end defp load_global_config(config, keys) do Enum.find_value(keys || [], fn key -> if value = Keyword.get(config, key) do {{:global_config, key}, value} end end) end defp load_project_config(config, keys) do Enum.find_value(keys || [], fn key -> if value = Keyword.get(config, key) do {{:project_config, key}, value} end end) end def to_boolean(nil), do: {:ok, nil} def to_boolean(false), do: {:ok, false} def to_boolean(true), do: {:ok, true} def to_boolean("0"), do: {:ok, false} def to_boolean("1"), do: {:ok, true} def to_boolean("false"), do: {:ok, false} def to_boolean("true"), do: {:ok, true} def to_boolean("FALSE"), do: {:ok, false} def to_boolean("TRUE"), do: {:ok, true} def to_boolean(""), do: {:ok, false} def to_boolean(_), do: :error def to_truthy_boolean(value) do value |> to_boolean() |> then(fn :error -> {:ok, true} boolean -> boolean end) end def to_integer(nil), do: {:ok, nil} def to_integer(""), do: {:ok, nil} def to_integer(integer) when is_integer(integer), do: {:ok, integer} def to_integer(string) when is_binary(string) do {int, _} = Integer.parse(string) {:ok, int} end def to_integer(_), do: :error def default(nil, value), do: value def default(value, _), do: value def trim_slash(nil), do: {:ok, nil} def trim_slash(string) when is_binary(string), do: {:ok, String.trim_leading(string, "/")} def trim_slash(_), do: :error def ssl_version() do {:ok, version} = :application.get_key(:ssl, :vsn) parse_ssl_version(version) end defp parse_ssl_version(version) do version |> List.to_string() |> String.split(".") |> Enum.take(3) |> Enum.map(&to_integer/1) |> version_pad() |> List.to_tuple() end defp version_pad([major]), do: [major, 0, 0] defp version_pad([major, minor]), do: [major, minor, 0] defp version_pad([major, minor, patch]), do: [major, minor, patch] defp version_pad([major, minor, patch | _]), do: [major, minor, patch] def path_expand(path) when is_binary(path) do {:ok, Path.expand(path)} end def path_expand(_), do: :error def ok_wrap(arg), do: {:ok, arg} def config, do: @config end hex-2.4.2/lib/hex/stdlib.ex000066400000000000000000000016131517471540100154620ustar00rootroot00000000000000defmodule Hex.Stdlib do @moduledoc false # TODO: Remove this once we require OTP 24.0 def ssh_hostkey_fingerprint(digset_type, key) do cond do # Requires Elixir 1.15.0 function_exported?(Mix, :ensure_application!, 1) -> apply(Mix, :ensure_application!, [:ssh]) apply(:ssh, :hostkey_fingerprint, [digset_type, key]) Code.ensure_loaded?(:ssh) and function_exported?(:ssh, :hostkey_fingerprint, 2) -> apply(:ssh, :hostkey_fingerprint, [digset_type, key]) true -> apply(:public_key, :ssh_hostkey_fingerprint, [digset_type, key]) end end # Compilation prunes code paths for isolation, which may remove archive # paths like Hex. Restore them so all Hex modules are available. def ensure_application!(app) do if function_exported?(Mix, :ensure_application!, 1) do apply(Mix, :ensure_application!, [app]) end end end hex-2.4.2/lib/hex/tar.ex000066400000000000000000000025341517471540100147720ustar00rootroot00000000000000defmodule Hex.Tar do @moduledoc false def create!(_metadata, [], _output), do: Mix.raise( "Stopping package build due to errors.\nCreating tarball failed: File list was empty." ) def create!(metadata, files, output) do files = Enum.map(files, fn {filename, contents} -> {String.to_charlist(filename), contents} filename -> String.to_charlist(filename) end) case :mix_hex_tarball.create(metadata, files) do {:ok, %{tarball: tarball} = result} -> if output != :memory, do: File.write!(output, tarball) result {:error, reason} -> Mix.raise("Creating tarball failed: #{:mix_hex_tarball.format_error(reason)}") end end def unpack!(path, dest) do tarball = case path do {:binary, tarball} -> tarball _ -> File.read!(path) end dest = if dest == :memory, do: dest, else: String.to_charlist(dest) case :mix_hex_tarball.unpack(tarball, dest) do {:ok, result} -> result {:error, reason} -> Mix.raise("Unpacking tarball failed: #{:mix_hex_tarball.format_error(reason)}") end end # TODO: Add this function to def outer_checksum(path) do case File.read(path) do {:ok, tarball} -> {:ok, :crypto.hash(:sha256, tarball)} {:error, reason} -> {:error, reason} end end end hex-2.4.2/lib/hex/update_checker.ex000066400000000000000000000067231517471540100171560ustar00rootroot00000000000000defmodule Hex.UpdateChecker do @moduledoc false use GenServer @name __MODULE__ @timeout 60_000 @update_interval 24 * 60 * 60 def start_link(opts \\ []) do {init_state, opts} = Keyword.pop(opts, :init_state, %{}) opts = Keyword.put_new(opts, :name, @name) GenServer.start_link(__MODULE__, init_state, opts) end def init(init_state) do {:ok, state(init_state)} end def start_check() do GenServer.cast(@name, :start_check) end def check() do GenServer.call(@name, :check, @timeout * 2) |> print_update_message() end def handle_cast(:start_check, state) do if not state.started and not Hex.State.fetch!(:offline) and check_update?() do Task.async(fn -> {:installs, Hex.Repo.get_installs()} end) {:noreply, %{state | started: true}} else {:noreply, %{state | started: true, done: true}} end end def handle_call(:check, _from, %{started: true, done: true} = state) do {:reply, :already_checked, state} end def handle_call(:check, from, %{started: true, reply: nil, check_timeout: timeout} = state) do {:noreply, %{state | from: from}, timeout} end def handle_call(:check, from, %{started: true} = state) do {:reply, state.reply, %{state | from: from, done: true}} end def handle_call(:check, _from, %{started: false} = state) do {:reply, :latest, state} end def handle_info(:timeout, state) do state = reply(:timeout, state) {:noreply, state} end def handle_info({_ref, {:installs, result}}, state) do result = case result do {:ok, {code, _headers, body}} when code in 200..299 -> Hex.Repo.find_new_version_from_csv(body) other -> Hex.Shell.error("Failed to check for new Hex version") Hex.Utils.print_error_result(other) # Treat failure as latest :latest end Hex.Registry.Server.last_update(:calendar.universal_time()) state = reply(result, state) {:noreply, state} end def handle_info({:DOWN, _ref, :process, _pid, :normal}, state) do {:noreply, state} end defp print_update_message(:already_checked), do: :ok defp print_update_message(:latest), do: :ok defp print_update_message({:http_error, reason}) do Hex.Shell.error("Hex update check failed, HTTP ERROR: #{inspect(reason)}") :ok end defp print_update_message(:timeout) do Hex.Shell.error("Hex update check failed due to a timeout") :ok end defp print_update_message({:status, status}) do Hex.Shell.error("Hex update check failed, status code: #{status}") :ok end defp print_update_message({:version, version}) do Hex.Shell.warn( "A new Hex version is available (#{Hex.version()} < #{version}), " <> "please update with `mix local.hex`" ) :ok end defp reply(reply, %{from: nil} = state) do %{state | reply: reply} end defp reply(reply, %{from: from} = state) do GenServer.reply(from, reply) %{state | from: nil, done: true} end defp check_update?() do if last = Hex.Registry.Server.last_update() do now = :calendar.universal_time() |> :calendar.datetime_to_gregorian_seconds() last = :calendar.datetime_to_gregorian_seconds(last) now - last > @update_interval else true end end defp state(init_state) do state = %{ from: nil, reply: nil, done: false, started: false, check_timeout: @timeout } Map.merge(state, init_state) end end hex-2.4.2/lib/hex/utils.ex000066400000000000000000000225211517471540100153420ustar00rootroot00000000000000defmodule Hex.Utils do @moduledoc false def package_name("hexpm", package), do: package def package_name(nil, package), do: package def package_name(repo, package), do: "#{repo}/#{package}" def safe_deserialize_erlang("") do nil end def safe_deserialize_erlang(binary) do case safe_binary_to_term(binary) do {:ok, term} -> term :error -> Mix.raise("Received malformed erlang from Hex API") end rescue ArgumentError -> Mix.raise("Received malformed erlang from Hex API") end def safe_serialize_erlang(term) do binarify(term) |> :erlang.term_to_binary() end def safe_binary_to_term!(binary, opts \\ []) do case safe_binary_to_term(binary, opts) do {:ok, term} -> term :error -> raise ArgumentError, "unsafe terms" end end def safe_binary_to_term(binary, opts \\ []) def safe_binary_to_term(binary, opts) when is_binary(binary) do term = :erlang.binary_to_term(binary, opts) safe_terms(term) {:ok, term} catch :throw, :safe_terms -> :error end defp safe_terms(list) when is_list(list) do safe_list(list) end defp safe_terms(tuple) when is_tuple(tuple) do safe_tuple(tuple, tuple_size(tuple)) end defp safe_terms(map) when is_map(map) do fun = fn key, value, acc -> safe_terms(key) safe_terms(value) acc end :maps.fold(fun, map, map) end defp safe_terms(other) when is_atom(other) or is_number(other) or is_bitstring(other) or is_pid(other) or is_reference(other) do other end defp safe_terms(_other) do throw(:safe_terms) end defp safe_list([]), do: :ok defp safe_list([h | t]) when is_list(t) do safe_terms(h) safe_list(t) end defp safe_list([h | t]) do safe_terms(h) safe_terms(t) end defp safe_tuple(_tuple, 0), do: :ok defp safe_tuple(tuple, n) do safe_terms(:erlang.element(n, tuple)) safe_tuple(tuple, n - 1) end def truncate(string, options \\ []) do length = options[:length] || 50 omission = options[:omission] || "..." cond do not String.valid?(string) -> string String.length(string) < length -> string true -> String.slice(string, 0, length) <> omission end end def binarify(term, opts \\ []) def binarify(binary, _opts) when is_binary(binary) do binary end def binarify(number, _opts) when is_number(number) do number end def binarify(atom, _opts) when is_nil(atom) or is_boolean(atom) do atom end def binarify(atom, _opts) when is_atom(atom) do Atom.to_string(atom) end def binarify(list, opts) when is_list(list) do for(elem <- list, do: binarify(elem, opts)) end def binarify(tuple, opts) when is_tuple(tuple) do for(elem <- Tuple.to_list(tuple), do: binarify(elem, opts)) |> List.to_tuple() end def binarify(map, opts) when is_map(map) do if Keyword.get(opts, :maps, true) do for(elem <- map, into: %{}, do: binarify(elem, opts)) else for(elem <- map, do: binarify(elem, opts)) end end def print_error_result({:error, reason}) do Hex.Shell.info(inspect(reason)) end def print_error_result({:ok, {status, _headers, nil}}) do print_http_code(status) end def print_error_result({:ok, {status, _headers, ""}}) do print_http_code(status) end def print_error_result({:ok, {_status, _headers, body}}) when is_binary(body) do Hex.Shell.info(body) end def print_error_result({:ok, {status, _headers, body}}) when is_map(body) do message = body["message"] errors = body["errors"] if message do Hex.Shell.info(message) end if errors do pretty_errors(errors) end unless message || errors do print_http_code(status) Hex.Shell.info(body) end end defp pretty_errors(errors, depth \\ 0) do Enum.each(errors, fn {key, map} when is_map(map) -> Hex.Shell.info(indent(depth) <> key <> ":") pretty_errors(map, depth + 1) {key, value} -> message = pretty_error_message(value, depth) Hex.Shell.info(indent(depth) <> key <> ": " <> message) end) end defp pretty_error_message(message, depth) do if message =~ "\n" do message = message |> String.trim() |> String.replace("\n", "\n" <> indent(depth + 1)) "\n" <> indent(depth + 1) <> message else message end end defp print_http_code(code), do: Hex.Shell.info(pretty_http_code(code)) defp pretty_http_code(401), do: "Authentication failed (401)" defp pretty_http_code(403), do: "Forbidden (403)" defp pretty_http_code(404), do: "Entity not found (404)" defp pretty_http_code(422), do: "Validation failed (422)" defp pretty_http_code(code), do: "HTTP status code: #{code}" defp indent(0), do: " " defp indent(depth), do: " " <> indent(depth - 1) def hexdocs_url(organization, package) when organization in ["hexpm", nil], do: "https://hexdocs.pm/#{package}" def hexdocs_url(organization, package), do: "https://#{organization}.hexorgs.pm/#{package}" def hexdocs_url(organization, package, version) when organization in ["hexpm", nil], do: "https://hexdocs.pm/#{package}/#{version}" def hexdocs_url(organization, package, version), do: "https://#{organization}.hexorgs.pm/#{package}/#{version}" def hexdocs_module_url(organization, package, module) when organization in ["hexpm", nil], do: "https://hexdocs.pm/#{package}/#{module}.html" def hexdocs_module_url(organization, package, module), do: "https://#{organization}.hexorgs.pm/#{package}/#{module}.html" def hexdocs_module_url(organization, package, version, module) when organization in ["hexpm", nil], do: "https://hexdocs.pm/#{package}/#{version}/#{module}.html" def hexdocs_module_url(organization, package, version, module), do: "https://#{organization}.hexorgs.pm/#{package}/#{version}/#{module}.html" def package_retirement_reason(:RETIRED_OTHER), do: "other" def package_retirement_reason(:RETIRED_INVALID), do: "invalid" def package_retirement_reason(:RETIRED_SECURITY), do: "security" def package_retirement_reason(:RETIRED_DEPRECATED), do: "deprecated" def package_retirement_reason(:RETIRED_RENAMED), do: "renamed" def package_retirement_reason(other), do: other def package_retirement_message(%{reason: reason_code, message: message}) do "(#{package_retirement_reason(reason_code)}) #{message}" end def package_retirement_message(%{reason: reason_code}) do "(#{package_retirement_reason(reason_code)})" end # From https://github.com/fishcakez/dialyze/blob/6698ae582c77940ee10b4babe4adeff22f1b7779/lib/mix/tasks/dialyze.ex#L168 def otp_version do major = :erlang.system_info(:otp_release) |> List.to_string() vsn_file = Path.join([:code.root_dir(), "releases", major, "OTP_VERSION"]) try do {:ok, contents} = File.read(vsn_file) String.split(contents, "\n", trim: true) else [full] -> full _ -> major catch :error, _ -> major end end def windows_repo_path_fix(path) do case :os.type() do {:win32, _name} -> String.replace(path, ":", "-") {_family, _name} -> path end end def lock(tuple) when elem(tuple, 0) == :hex do if tuple_size(tuple) > 8 and Hex.Server.should_warn_lock_version?() do Hex.Shell.warn( "The mix.lock file was generated with a newer version of Hex. Update " <> "your client by running `mix local.hex` to avoid losing data." ) end destructure( [:hex, name, version, inner_checksum, managers, deps, repo, outer_checksum], Tuple.to_list(tuple) ) %{ name: to_string(name), version: version, inner_checksum: inner_checksum, outer_checksum: outer_checksum, managers: managers, deps: lock_deps(deps), repo: repo || "hexpm" } end def lock(_) do nil end defp lock_deps(nil) do nil end defp lock_deps(deps) do Enum.map(deps, fn {app, req, opts} -> opts = opts |> Keyword.put_new(:repo, "hexpm") |> Keyword.update!(:hex, &to_string/1) {app, req, opts} end) end @doc """ Returns the appropriate command for opening a file or URL with the system's default handler. Returns a tuple of {command, args, options} suitable for use with System.cmd/3. """ def open_cmd(path) do case :os.type() do {:win32, _} -> {"cmd", win_cmd_args(path)} {:unix, :darwin} -> {"open", [path]} {:unix, _} -> cond do System.find_executable("xdg-open") -> {"xdg-open", [path]} # When inside WSL System.find_executable("cmd.exe") -> {"cmd.exe", win_cmd_args(path)} true -> {"open", [path]} end end end defp win_cmd_args(path) do ["/c", "start", String.replace(path, "&", "^&")] end @doc """ Opens a file or URL with the system's default handler. In test environment, sends a message instead of actually executing the command. """ def system_open(path) do path |> open_cmd() |> system_cmd() end if Mix.env() == :test do defp system_cmd({cmd, args}) do send(self(), {:hex_system_cmd, cmd, args}) end else defp system_cmd({cmd, args}) do System.cmd(cmd, args) end end end hex-2.4.2/lib/mix/000077500000000000000000000000001517471540100136535ustar00rootroot00000000000000hex-2.4.2/lib/mix/task_description.ex000066400000000000000000000002701517471540100175550ustar00rootroot00000000000000defmodule Hex.Mix.TaskDescription do @moduledoc false @type args :: String.t() @type docs :: String.t() @type task_spec :: {args, docs} @callback tasks() :: [task_spec] end hex-2.4.2/lib/mix/tasks/000077500000000000000000000000001517471540100150005ustar00rootroot00000000000000hex-2.4.2/lib/mix/tasks/hex.audit.ex000066400000000000000000000034461517471540100172360ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Audit do use Mix.Task alias Hex.Registry.Server, as: Registry @shortdoc "Shows retired Hex deps for the current project" @moduledoc """ Shows all Hex dependencies that have been marked as retired. Retired packages are no longer recommended to be used by their maintainers. The task will display a message describing the reason for retirement and exit with a non-zero code if any retired dependencies are found. > In a project, this task must be invoked before any other tasks > that may load or start your application. Otherwise, you must > explicitly list `:hex` as part of your `:extra_applications`. """ @behaviour Hex.Mix.TaskDescription @impl true def run(_) do Mix.Tasks.Deps.Loadpaths.run(["--no-compile", "--no-listeners"]) Hex.start() Registry.open() lock = Mix.Dep.Lock.read() lock |> Hex.Mix.packages_from_lock() |> Registry.prefetch() case retired_packages(lock) do [] -> Hex.Shell.info("No retired packages found") packages -> header = ["Dependency", "Version", "Retirement reason"] Mix.Tasks.Hex.print_table(header, packages) Hex.Shell.error("Found retired packages") Mix.Tasks.Hex.set_exit_code(1) end end @impl true def tasks() do [ {"", "Shows retired Hex deps for the current project"} ] end defp retired_packages(lock) do Enum.flat_map(lock, fn {_app, lock} -> retirement_status(Hex.Utils.lock(lock)) end) end defp retirement_status(%{repo: repo, name: package, version: version}) do retired = Registry.retired(repo, package, version) case retired do %{} -> [[package, version, Hex.Utils.package_retirement_message(retired)]] nil -> [] end end defp retirement_status(nil) do [] end end hex-2.4.2/lib/mix/tasks/hex.build.ex000066400000000000000000000310651517471540100172250ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Build do use Mix.Task @shortdoc "Builds a new package version locally" @moduledoc """ Builds a new local version of your package. The package .tar file is created in the current directory, but is not pushed to the repository. An app named `foo` at version `1.2.3` will be built as `foo-1.2.3.tar`. $ mix hex.build #{Hex.Package.configuration_doc()} > In a project, this task must be invoked before any other tasks > that may load or start your application. Otherwise, you must > explicitly list `:hex` as part of your `:extra_applications`. ### Command line options * `--unpack` - Builds the tarball and unpacks contents into a directory. Useful for making sure the tarball contains all needed files before publishing. See `--output` below for setting the output path. * `-o`, `--output` - Sets output path. When used with `--unpack` it means the directory (Default: `-`). Otherwise, it specifies tarball path (Default: `-.tar`) """ @behaviour Hex.Mix.TaskDescription @error_fields ~w(app name files version build_tools)a @warn_fields ~w(description licenses links)a @meta_fields @error_fields ++ @warn_fields ++ ~w(elixir extra)a @root_fields ~w(app version elixir description)a @max_description_length 300 @default_repo "hexpm" @metadata_config "hex_metadata.config" @switches [unpack: :boolean, output: :string] @aliases [o: :output] @impl true def run(args) do Hex.start() {opts, _args} = OptionParser.parse!(args, strict: @switches, aliases: @aliases) build = prepare_package() organization = build.organization meta = build.meta package = build.package exclude_deps = build.exclude_deps Hex.Shell.info("Building #{meta.name} #{meta.version}") print_info(meta, organization, exclude_deps, package[:files]) if opts[:unpack] do output = Keyword.get(opts, :output, "#{meta.name}-#{meta.version}") build_and_unpack_package(meta, output) else output = Keyword.get(opts, :output, "#{meta.name}-#{meta.version}.tar") build_package(meta, output) end end @impl true def tasks() do [ {"", "Builds a new package version locally"} ] end defp build_package(meta, output) do %{outer_checksum: outer_checksum} = Hex.Tar.create!(meta, meta.files, output) Hex.Shell.info("Package checksum: #{Base.encode16(outer_checksum, case: :lower)}") Hex.Shell.info("Saved to #{output}") end defp build_and_unpack_package(meta, output) do %{tarball: tarball, inner_checksum: inner_checksum, outer_checksum: outer_checksum} = Hex.Tar.create!(meta, meta.files, :memory) %{inner_checksum: ^inner_checksum, outer_checksum: ^outer_checksum} = Hex.Tar.unpack!({:binary, tarball}, output) Hex.Shell.info("Saved to #{output}") end @doc false def prepare_package() do Mix.Project.get!() config = Mix.Project.config() check_umbrella_project!(config) check_root_fields!(config) package = Map.new(config[:package] || []) check_misspellings!(package) {organization, package} = Map.pop(package, :organization) {deps, exclude_deps} = dependencies() meta = meta_for(config, package, deps) %{ config: config, package: package, deps: deps, exclude_deps: exclude_deps, meta: meta, organization: organization } end @doc false def print_info(meta, organization, exclude_deps, package_files) do if meta[:requirements] != [] do Hex.Shell.info(" Dependencies:") Enum.each(meta[:requirements], fn requirement -> %{name: name, app: app, requirement: req, optional: opt, repository: repo} = requirement app = if name != app, do: " (app: #{app})" opt = if opt, do: " (optional)" repo = if repo != @default_repo, do: " (repo: #{repo})" message = " #{name} #{req}#{app}#{repo}#{opt}" Hex.Shell.info(message) end) end if organization do Hex.Shell.info(" Organization: #{organization}") end Enum.each(@meta_fields, &print_metadata(meta, &1)) errors = Enum.concat([ check_missing_fields(meta, organization), check_description_length(meta), check_missing_files(package_files || []), check_reserved_files(package_files || []), check_excluded_deps(exclude_deps) ]) if errors != [] do ["Stopping package build due to errors." | errors] |> Enum.join("\n") |> Mix.raise() end if organization in [nil, "hexpm"] do licenses_valid_or_warn(meta.licenses) end end defp licenses_valid_or_warn([]), do: Hex.Shell.warn("\nYou have not included any licenses\n") defp licenses_valid_or_warn(licenses) do invalid_licenses = Enum.reject(licenses, fn lic -> :mix_hex_licenses.valid(lic) end) if invalid_licenses != [] do message = [ "The following licenses are not recognized by SPDX:\n", Enum.map(invalid_licenses, &" * #{&1}\n"), "\nValid license identifiers are available from https://spdx.org/licenses" ] Hex.Shell.warn(message) end end defp check_excluded_deps([]), do: [] defp check_excluded_deps(deps) do [ "Dependencies excluded from the package (only Hex packages can be dependencies): #{Enum.join(deps, ", ")}" ] end defp meta_for(config, package, deps) do config |> Keyword.take(@root_fields) |> Map.new() |> Map.merge(package) |> package(config) |> Map.put(:requirements, deps) end defp dependencies() do {include, exclude} = Mix.Project.config()[:deps] |> Enum.map(&Hex.Mix.normalize_dep/1) |> Enum.filter(&prod_dep?/1) |> Enum.split_with(&package_dep?/1) Enum.each(include, fn {app, _req, opts} -> if Keyword.has_key?(opts, :override) do Mix.raise( "Can't build package with overridden dependency #{app}, remove `override: true`" ) end if Keyword.has_key?(opts, :compile) do Mix.raise( "Can't build package when :compile is set for dependency #{app}, remove `compile: ...`" ) end if Keyword.has_key?(opts, :manager) do Mix.raise( "Can't build package when :manager is set for dependency #{app}, remove `manager: ...`" ) end if Keyword.has_key?(opts, :app) do Mix.raise("Can't build package when :app is set for dependency #{app}, remove `app: ...`") end if Keyword.get(opts, :system_env, []) != [] do Mix.raise( "Can't build package when :system_env is set for dependency #{app}, remove `system_env: ...`" ) end end) include = Enum.map(include, fn {app, req, opts} -> name = opts[:hex] || app repo = deorg_repo(opts[:repo] || opts[:organization] || @default_repo) %{ name: to_string(name), app: app, requirement: req, optional: opts[:optional] || false, repository: repo } end) exclude = for {app, _req, _opts} <- exclude, do: app {include, exclude} end defp deorg_repo(repo) do case String.split(to_string(repo), ":", parts: 2) do [_source, repo] -> repo [repo] -> repo end end @doc false def package(package, config) do files = package[:files] || Hex.Package.default_files() exclude_patterns = (package[:exclude_patterns] || []) ++ [~r/\W\.DS_Store$/] files = files |> expand_paths(File.cwd!()) |> Enum.reject(fn path -> Enum.any?(exclude_patterns, &(path =~ &1)) end) package |> Map.put(:files, files) |> maybe_put(:description, package[:description], &String.trim/1) |> maybe_put(:name, package[:name] || config[:app], &to_string(&1)) |> maybe_put(:build_tools, !package[:build_tools] && guess_build_tools(files), & &1) |> Map.take(@meta_fields) end defp maybe_put(map, key, value, transform) do if value do Map.put(map, key, transform.(value)) else map end end @doc false def check_umbrella_project!(config) do if Mix.Project.umbrella?(config) do Mix.raise("Hex does not support umbrella projects") end end defp check_misspellings!(opts) do if opts[:organisation] do Mix.raise("Invalid Hex package config :organisation, use spelling :organization") end end defp check_root_fields!(config) do package_only_fields = ([:organisation, :organization] ++ @meta_fields) -- (@root_fields ++ [:name]) config_keys = Keyword.keys(config) invalid_field = Enum.find(config_keys, &(&1 in package_only_fields)) if invalid_field do Hex.Shell.warn( "Mix project configuration #{inspect(invalid_field)} belongs under the :package key, did you misplace it?" ) end end @scm_keys [:git, :github, :path, :in_umbrella] defp package_dep?({_app, _req, opts}) do keys = Keyword.keys(opts) :hex in keys or not Enum.any?(@scm_keys, &(&1 in keys)) end defp prod_dep?({_app, _req, opts}) do if only = opts[:only], do: :prod in List.wrap(only), else: true end defp expand_paths(paths, dir) do expand_dir = Path.expand(dir) paths |> Enum.map(&Path.join(dir, &1)) |> Enum.flat_map(&Path.wildcard/1) |> Enum.flat_map(&dir_files/1) |> Enum.map(&Path.expand/1) |> Enum.uniq() |> Enum.map(&Path.relative_to(&1, expand_dir)) end defp dir_files(path) do case File.lstat(path) do {:ok, %File.Stat{type: :directory}} -> new_paths = path |> File.ls!() |> Enum.map(&Path.join(path, &1)) |> Enum.flat_map(&dir_files/1) [path | new_paths] _ -> [path] end end defp print_metadata(metadata, :files) do case metadata[:files] do [] -> Hex.Shell.error("No files") files -> Hex.Shell.info(" Files:") Enum.each(files, &Hex.Shell.info(" #{&1}")) end end defp print_metadata(metadata, key) do if value = metadata[key] do key = key |> Atom.to_string() |> String.replace("_", " ") |> String.capitalize() value = format_metadata_value(value) Hex.Shell.info(" #{key}: #{value}") end end defp format_metadata_value(list) when is_list(list) do Enum.join(list, ", ") end defp format_metadata_value(map) when is_map(map) do "\n " <> Enum.map_join(map, "\n ", fn {key, val} -> "#{key}: #{format_metadata_value(val)}" end) end defp format_metadata_value(value) do value end defp check_missing_fields(metadata, organization) do if organization in [nil, "hexpm"] do check_error_fields(metadata, @error_fields ++ @warn_fields) else check_warn_fields(metadata, @warn_fields) check_error_fields(metadata, @error_fields) end end defp check_warn_fields(metadata, warn_fields) do case check_error_fields(metadata, warn_fields) do [message] -> Hex.Shell.warn(message) [] -> :ok end end defp check_error_fields(metadata, error_fields) do taken_fields = Map.take(metadata, error_fields) |> Map.keys() missing = error_fields -- taken_fields if missing == [] do [] else ["Missing metadata fields: #{Enum.join(missing, ", ")}"] end end defp check_description_length(metadata) do descr = metadata[:description] || "" if String.length(descr) > @max_description_length do ["Package description is too long (exceeds #{@max_description_length} characters)"] else [] end end defp check_missing_files(package_files) do case Enum.filter(package_files, &(Path.wildcard(&1) == [])) do [] -> [] missing -> ["Missing files: #{Enum.join(missing, ", ")}"] end end defp check_reserved_files(package_files) do reserved_file = @metadata_config invalid_file = Enum.find(package_files, &(reserved_file in Path.wildcard(&1))) if invalid_file do ["Do not include this file: #{reserved_file}"] else [] end end @build_tools [ {"mix.exs", "mix"}, {"rebar", "rebar3"}, {"rebar.lock", "rebar3"}, {"rebar.config", "rebar3"}, {"rebar.config.script", "rebar3"}, {"erlang.mk", "make"}, {"Makefile", "make"}, {"Makefile.win", "make"} ] defp guess_build_tools(paths) do base_files = paths |> Enum.filter(&(Path.dirname(&1) == ".")) |> MapSet.new() for {file, tool} <- @build_tools, file in base_files do tool end |> default_build_tool() |> Enum.uniq() end defp default_build_tool([]), do: ["mix"] defp default_build_tool(other), do: other end hex-2.4.2/lib/mix/tasks/hex.config.ex000066400000000000000000000205311517471540100173670ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Config do use Mix.Task @shortdoc "Reads, updates or deletes local Hex config" @moduledoc """ Reads, updates or deletes local Hex config. ## List config keys and values $ mix hex.config ## Get or delete config value for KEY $ mix hex.config KEY [--delete] ## Set config KEY to VALUE $ mix hex.config KEY VALUE ## Config keys * `api_key` - Your API key. If you are authenticated this config will override the API key used for your authenticated user. Can be also be overridden by setting the environment variable `HEX_API_KEY` * `api_url` - Hex API URL. Can be overridden by setting the environment variable `HEX_API_URL` (Default: `#{inspect(Hex.State.default_api_url())}`) * `offline` - If set to true Hex will not fetch the registry or packages and will instead use locally cached files if they are available. Can be overridden by setting the environment variable `HEX_OFFLINE` (Default: `false`) * `unsafe_https` - If set to true Hex will not verify HTTPS certificates. Can be overridden by setting the environment variable `HEX_UNSAFE_HTTPS` (Default: `false`) * `unsafe_registry` - If set to true Hex will not verify the registry signature against the repository's public key. Can be overridden by setting the environment variable `HEX_UNSAFE_REGISTRY` (Default: `false`) * `no_verify_repo_origin` - If set to true Hex will not verify the registry origin. Can be overridden by setting the environment variable `HEX_NO_VERIFY_REPO_ORIGIN` (Default: `false`) * `http_proxy` - HTTP proxy server. Can be overridden by setting the environment variable `HTTP_PROXY` * `https_proxy` - HTTPS proxy server. Can be overridden by setting the environment variable `HTTPS_PROXY` * `no_proxy` - A comma separated list of hostnames that will not be proxied, asterisks can be used as wildcards. Can be overridden by setting the environment variable `no_proxy` or `NO_PROXY` * `http_concurrency` - Limits the number of concurrent HTTP requests in flight. Can be overridden by setting the environment variable `HEX_HTTP_CONCURRENCY` (Default: `8`) * `http_timeout` - Sets the timeout for HTTP requests in seconds. Can be overridden by setting the environment variable `HEX_HTTP_TIMEOUT` * `mirror_url` - Hex mirror URL. Can be overridden by setting the environment variable `HEX_TRUSTED_MIRROR_URL` * `trusted_mirror_url` - Hex mirror URL. Unlike `mirror_url`, this mirror is "trusted", secrets such as authentication information will be sent to the mirror. Can be overridden by setting the environment variable `HEX_TRUSTED_MIRROR_URL` * `cacerts_path` - Path to the CA certificate store PEM file. If not set, a CA bundle that ships with Hex is used. Can be overridden by setting the environment variable `HEX_CACERTS_PATH` * `no_short_urls` - If set to true Hex will not shorten any links. Can be overridden by setting the environment variable `HEX_NO_SHORT_URLS` (Default: `false`) Hex responds to these additional environment variables: * `HEX_HOME` - directory where Hex stores the cache and configuration (Default: `~/.hex`) * `MIX_XDG` - asks Hex to follow the [XDG Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) for its home directory and configuration files. `HEX_HOME` has higher preference than `MIX_XDG`. If none of the variables are set, the default directory `~/.hex` will be used. ## Config overrides All keys from the "Config keys" section above can be overridden. Hex uses the following order of precedence when computing a value for a given key: 1. System environment Setting for example `HEX_API_URL` environment variable has always the highest precedence for the `api_url` config key. 2. Project configuration Hex allows an optional, per-project configuration in the `mix.exs` file. For example, to override `api_url` config key, add the following: # mix.exs defmodule MyApp.MixProject def project() do [ # ... deps: deps(), hex: hex() ] end defp hex() do [ api_url: "https://hex.myorg/api" ] end end 3. Global configuration using `mix hex.config KEY VALUE` 4. Default value ## Command line options * `--delete` - Remove a specific config key """ @behaviour Hex.Mix.TaskDescription @switches [delete: :boolean] @impl true def run(args) do Hex.start() {opts, args} = OptionParser.parse!(args, strict: @switches) case args do [] -> list() ["$" <> _key | _] -> Mix.raise("Invalid key name") [key] -> if opts[:delete] do delete(key) else read(key) end [key, value] -> set(key, value) _ -> Mix.raise(""" Invalid arguments, expected: mix hex.config KEY [VALUE] """) end end @impl true def tasks() do [ {"", "Reads, updates or deletes local Hex config"}, {"KEY", "Get config value for KEY"}, {"KEY --delete", "Delete config value for KEY"}, {"KEY VALUE", "Set config KEY to VALUE"} ] end defp list() do Enum.each(valid_read_keys(), fn {config, _internal} -> read(config, true) end) end defp read(key, verbose \\ false) defp read(key, verbose) when is_binary(key) do key = String.to_atom(key) case Keyword.fetch(valid_read_keys(), key) do {:ok, internal} -> fetch_current_value_and_print(internal, key, verbose) _error -> Mix.raise("The key #{key} is not valid") end end defp read(key, verbose) when is_atom(key), do: read(to_string(key), verbose) defp fetch_current_value_and_print(internal, key, verbose) do case Map.fetch(Hex.State.get_all(), internal) do {:ok, {{:env, env_var}, value}} -> print_value(key, value, verbose, "(using `#{env_var}`)") {:ok, {{:global_config, _key}, value}} -> print_value(key, value, verbose, "(using `#{config_path()}`)") {:ok, {{:project_config, _key}, value}} -> print_value(key, value, verbose, "(using `mix.exs`)") {:ok, {kind, value}} when kind in [:default, :computed] -> print_value(key, value, verbose, "(default)") :error -> Mix.raise("Config does not contain the key #{key}") end end defp print_value(key, value, true, source), do: Hex.Shell.info("#{key}: #{inspect(value, pretty: true)} #{source}") defp print_value(_key, value, false, _source), do: Hex.Shell.info(inspect(value, pretty: true)) defp delete(key) do key = String.to_atom(key) if Keyword.has_key?(valid_write_keys(), key) do Hex.Config.remove([key]) end end defp set(key, value) do key = String.to_atom(key) if Keyword.has_key?(valid_write_keys(), key) do Hex.Config.update([{key, value}]) else Mix.raise("Invalid key #{key}") end end defp config_path() do :config_home |> Hex.State.fetch!() |> Path.join("hex.config") end defp valid_keys() do Enum.map(Hex.State.config(), fn {internal, map} -> [config | _] = Map.get(map, :config, [nil]) [env | _] = Map.get(map, :env, [nil]) cond do String.starts_with?(to_string(config), "$") -> {internal, config, :not_accessible} is_nil(config) and not is_nil(env) -> {internal, config, :env_only} is_nil(config) and is_nil(env) -> {internal, config, :read_only} true -> {internal, config, :read_and_write} end end) end defp valid_read_keys() do valid_keys() |> Enum.map(fn {internal, config, access} -> if access != :not_accessible, do: key_representation(internal, config) end) |> Enum.filter(&(&1 != nil)) end defp valid_write_keys() do valid_keys() |> Enum.map(fn {internal, config, access} -> if access == :read_and_write, do: key_representation(internal, config) end) |> Enum.filter(&(&1 != nil)) end defp key_representation(internal, nil), do: {internal, internal} defp key_representation(internal, config), do: {config, internal} end hex-2.4.2/lib/mix/tasks/hex.docs.ex000066400000000000000000000262651517471540100170640ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Docs do use Mix.Task @shortdoc "Fetches or opens documentation of a package" @moduledoc """ Fetches or opens documentation of a package. If no version is specified, defaults to version used in the current mix project. If called outside of a mix project or the dependency is not used in the current mix project, defaults to the latest version. ## Fetch documentation for all dependencies in the current mix project $ mix hex.docs fetch ## Fetch documentation for offline use Fetches documentation for the specified package that you can later open with `mix hex.docs offline`. $ mix hex.docs fetch PACKAGE [VERSION] ## Open a browser window with offline documentation $ mix hex.docs offline PACKAGE [VERSION] ## Open a browser window with online documentation $ mix hex.docs online PACKAGE [VERSION] ## Command line options * `--module Some.Module` - Open a specified module documentation page inside desired package * `--organization ORGANIZATION` - Set this for private packages belonging to an organization * `--latest` - Looks for the latest release of a package * `--format epub` - When opening documentation offline, use this flag to open the epub formatted version """ @behaviour Hex.Mix.TaskDescription @elixir_apps ~w(eex elixir ex_unit iex logger mix) @switches [module: :string, organization: :string, latest: :boolean, format: :string] @impl true def run(args) do Hex.start() {opts, args} = OptionParser.parse!(args, strict: @switches) opts = Keyword.put(opts, :mix_project, !!Mix.Project.get()) case args do ["fetch" | remaining] -> fetch_docs(remaining, opts) ["online" | remaining] -> open_docs(remaining, opts) ["offline" | remaining] -> open_docs_offline(remaining, opts) _ -> Mix.raise(""" Invalid arguments, expected one of: mix hex.docs fetch mix hex.docs fetch PACKAGE [VERSION] mix hex.docs offline PACKAGE [VERSION] mix hex.docs online PACKAGE [VERSION] """) end end @impl true def tasks() do [ {"fetch PACKAGE [VERSION]", "Fetch documentation for offline use"}, {"offline PACKAGE [VERSION]", "Open a browser window with offline documentation"}, {"online PACKAGE [VERSION]", "Open a browser window with online documentation"} ] end defp fetch_docs([] = _args, opts) do if !opts[:mix_project] do Mix.raise( "Specify a package name or run inside a Mix project " <> "to fetch docs for all dependencies" ) end Enum.each(deps_in_lock(), fn package -> fetch_docs([package.name, package.version], organization: package.repo) end) end defp fetch_docs([name], opts) when name in @elixir_apps do fetch_docs([name, System.version()], opts) end defp fetch_docs([name], opts) do locked_or_latest_version = find_package_locked_or_latest_version(name, opts) fetch_docs([name, locked_or_latest_version], opts) end defp fetch_docs([name, version], opts) do target_dir = Path.join([docs_dir(), org_to_path(opts[:organization]), name, version]) fallback_dir = Path.join([docs_dir(), name, version]) cond do File.exists?(target_dir) -> Hex.Shell.info("Docs already fetched: #{target_dir}") File.exists?(fallback_dir) -> Hex.Shell.info("Docs already fetched: #{fallback_dir}") true -> target = Path.join(target_dir, "#{name}-#{version}.tar.gz") success? = download_docs(opts[:organization], name, version, target) if success? do extract_docs(target, target_dir) end end end defp find_package_locked_or_latest_version(name, opts) do package_in_lock = package_in_lock(name) if opts[:mix_project] && !opts[:latest] && package_in_lock do package_in_lock.version else find_package_latest_version(opts[:organization], name) end end defp find_package_latest_version(organization, name) do %{"releases" => releases} = retrieve_package_info(organization, name) sorted_versions = Enum.sort(releases, &(Version.compare(&1["version"], &2["version"]) == :gt)) if Enum.all?(sorted_versions, &pre_release?/1) do sorted_versions |> List.first() |> Map.get("version") else sorted_versions |> Enum.reject(&pre_release?/1) |> List.first() |> Map.get("version") end end defp retrieve_package_info(organization, name) do auth = if organization, do: Mix.Tasks.Hex.auth_info(:read) case Hex.API.Package.get(organization, name, auth) do {:ok, {code, _, body}} when code in 200..299 -> body {:ok, {404, _, _}} -> Mix.raise("No package with name #{name}") other -> Hex.Shell.error("Failed to retrieve package information") Hex.Utils.print_error_result(other) end end defp open_docs([] = _args, _opts) do Mix.raise("You must specify the name of a package") end defp open_docs([name], opts) do package_in_lock = package_in_lock(name) if opts[:mix_project] && !opts[:latest] && package_in_lock do version = package_in_lock.version open_docs([name, version], opts) else open_latest_docs([name], opts) end end defp open_docs([name, version], opts) do get_docs_url([name, version], opts) |> browser_open() end defp open_latest_docs(args, opts) do args |> get_docs_url(opts) |> browser_open() end defp open_docs_offline([] = _args, _opts) do Mix.raise("You must specify the name of a package") end defp open_docs_offline([name], opts) do package_in_lock = package_in_lock(name) if opts[:mix_project] && !opts[:latest] && package_in_lock do latest_version = package_in_lock.version open_docs_offline([name, latest_version], opts) else open_latest_docs_offline(name, opts) end end defp open_docs_offline([name, version], opts) do docs_location = docs_location(opts[:organization], name, version, opts) if docs_location do open_file(docs_location) else fetch_docs([name, version], opts) docs_location = docs_location(opts[:organization], name, version, opts) if docs_location do open_file(docs_location) end end end defp open_latest_docs_offline(name, opts) do latest_version = find_package_version(opts[:organization], name) if latest_version do open_docs_offline([name, latest_version], opts) else fetch_docs([name], opts) latest_version = find_package_version(opts[:organization], name) if latest_version do open_docs_offline([name, latest_version], opts) end end end defp docs_location(organization, name, version, opts) do format = Keyword.get(opts, :format, "html") module = Keyword.get(opts, :module, "index") default_path = Path.join([docs_dir(), org_to_path(organization), name, version]) fallback_path = Path.join([docs_dir(), name, version]) case format do "epub" -> epub_file_location(default_path, fallback_path, organization) "html" -> html_file_location(default_path, fallback_path, module, organization) end end defp html_file_location(default_path, fallback_path, module, organization) do default_path = Path.join([default_path, module <> ".html"]) fallback_path = Path.join([fallback_path, module <> ".html"]) cond do File.exists?(default_path) -> default_path !organization && File.exists?(fallback_path) -> fallback_path true -> nil end end defp epub_file_location(default_path, fallback_path, organization) do default_path = Path.wildcard(Path.join([default_path, "*.epub"])) fallback_path = Path.wildcard(Path.join([fallback_path, "*.epub"])) cond do length(default_path) == 1 -> Enum.at(default_path, 0) !organization && length(fallback_path) == 1 -> Enum.at(fallback_path, 0) true -> Mix.raise("No documentation found in epub format.") end end defp find_package_version(organization, name) do default_path = Path.join([docs_dir(), org_to_path(organization), name]) fallback_path = Path.join([docs_dir(), name]) cond do File.exists?(default_path) -> find_latest_version(default_path) !organization && File.exists?(fallback_path) -> find_latest_version(fallback_path) true -> nil end end defp get_docs_url([name], opts) do if module = opts[:module] do Hex.Utils.hexdocs_module_url(opts[:organization], name, module) else Hex.Utils.hexdocs_url(opts[:organization], name) end end defp get_docs_url([name, version], opts) do if module = opts[:module] do Hex.Utils.hexdocs_module_url(opts[:organization], name, version, module) else Hex.Utils.hexdocs_url(opts[:organization], name, version) end end defp browser_open(path) do Hex.Utils.system_open(path) end defp open_file(path) do unless path do Mix.raise("Documentation not found") end unless File.exists?(path) do Mix.raise("Documentation file not found: #{path}") end browser_open(path) end defp find_latest_version(path) do sorted_versions = path |> File.ls!() |> Enum.sort(&(Version.compare(&1, &2) == :gt)) if Enum.all?(sorted_versions, &pre_release?/1) do List.first(sorted_versions) else sorted_versions |> Enum.reject(&pre_release?/1) |> List.first() end end defp download_docs(organization, package, version, target) do repo = org_to_repo(organization) case Hex.Repo.get_docs(repo, package, version) do {:ok, {200, _, body}} -> File.mkdir_p!(Path.dirname(target)) File.write!(target, body) true {:error, :timeout} -> message = """ Timed out trying to download docs. Try changing your timeout settings: HEX_HTTP_TIMEOUT=120 mix hex.docs fetch #{package} #{version} """ Hex.Shell.error(message) false _ -> message = "Couldn't find docs for package with name #{package} or version #{version}" Hex.Shell.error(message) false end end defp extract_docs(target, target_dir) do File.mkdir_p!(target_dir) :ok = :mix_hex_erl_tar.extract(target, [:compressed, cwd: Path.dirname(target)]) Hex.Shell.info("Docs fetched: #{target_dir}") end defp docs_dir() do Path.join(Hex.State.fetch!(:data_home), "docs") end defp package_in_lock(name) do Enum.find(deps_in_lock(), &(&1.name == name)) end defp deps_in_lock() do Mix.Dep.Lock.read() |> Enum.map(fn {_app, info} -> Hex.Utils.lock(info) end) |> Enum.reject(&is_nil/1) end defp org_to_repo(organization) when organization in [nil, "hexpm"], do: "hexpm" defp org_to_repo(organization), do: "hexpm:#{organization}" defp org_to_path(organization) do organization |> org_to_repo() |> Hex.Utils.windows_repo_path_fix() end defp pre_release?(%{"version" => version}), do: do_pre_release?(version) defp pre_release?(version), do: do_pre_release?(version) defp do_pre_release?(version) do case Version.parse(version) do {:ok, %Version{pre: []}} -> false {:ok, %Version{}} -> true _ -> false end end end hex-2.4.2/lib/mix/tasks/hex.ex000066400000000000000000000347421517471540100161340ustar00rootroot00000000000000defmodule Mix.Tasks.Hex do use Mix.Task @shortdoc "Prints Hex help information" @moduledoc """ Prints Hex tasks and their information. $ mix hex See `mix help hex.config` to see all available configuration options. """ @impl true def run(_args) do Hex.start() Hex.Shell.info("Hex v" <> Hex.version()) Hex.Shell.info("Hex is a package manager for the Erlang ecosystem.") print_available_tasks() Hex.Shell.info("Further information can be found here: https://hex.pm/docs") end defp print_available_tasks() do line_break() Hex.Shell.info("Available tasks:") line_break() pattern = "hex." modules = Enum.filter(load_tasks(), &String.contains?(Mix.Task.task_name(&1), pattern)) {docs, max} = build_task_doc_list(modules) display_doc_list(docs, max) line_break() end defp load_tasks() do Enum.filter(Mix.Task.load_all(), &(Mix.Task.moduledoc(&1) != false)) end defp build_task_doc_list(modules) do Enum.reduce(modules, {[], 0}, fn module, {docs, max} -> task = "mix " <> Mix.Task.task_name(module) task_list = if Keyword.has_key?(module.__info__(:functions), :tasks) do Enum.map(module.tasks(), fn {subtask, docs} -> {"#{task} #{subtask}", docs} end) else [] end max = Enum.reduce(task_list, max, fn {task, _}, max_now -> max(byte_size(task), max_now) end) if Enum.empty?(task_list) do {docs, max} else {docs ++ [task_list], max} end end) end defp display_doc_list(docs, max) do Enum.each(Enum.sort_by(docs, &List.first/1), fn tasks -> Enum.each(tasks, fn {task, doc} -> Mix.shell().info(format_task(task, max, doc)) end) line_break() end) end defp format_task(task, max, doc) do String.pad_trailing(task, max) <> " # " <> doc end defp line_break(), do: Hex.Shell.info("") @doc false def print_table(header, values) do header = Enum.map(header, &[:underline, &1]) widths = widths([header | values]) print_row(header, widths) Enum.each(values, &print_row(&1, widths)) end defp ansi_length(binary) when is_binary(binary) do byte_size(binary) end defp ansi_length(list) when is_list(list) do Enum.reduce(list, 0, &(ansi_length(&1) + &2)) end defp ansi_length(atom) when is_atom(atom) do 0 end defp print_row(strings, widths) do Enum.zip(strings, widths) |> Enum.map(fn {string, width} -> pad_size = width - ansi_length(string) + 2 pad = :lists.duplicate(pad_size, ?\s) [string || "", :reset, pad] end) |> IO.ANSI.format() |> Hex.Shell.info() end defp widths([head | tail]) do widths = Enum.map(head, &ansi_length/1) Enum.reduce(tail, widths, fn list, acc -> Enum.zip(list, acc) |> Enum.map(fn {string, width} -> max(width, ansi_length(string)) end) end) end @doc false def auth(opts \\ []) do auth_device(opts) end defp get_hostname() do case :inet.gethostname() do {:ok, hostname} -> to_string(hostname) {:error, _} -> nil end end @doc false def auth_device(_opts \\ []) do # Clean up any existing authentication revoke_existing_oauth_tokens() revoke_and_cleanup_old_api_keys() name = get_hostname() case Hex.API.OAuth.device_authorization("api repositories", name) do {:ok, {200, _, device_response}} -> perform_device_flow(device_response) {:ok, {status, _, error}} -> Hex.Shell.error("Device authorization failed (#{status}): #{inspect(error)}") :error {:error, reason} -> Hex.Shell.error("Device authorization error: #{inspect(reason)}") :error end end defp perform_device_flow(device_response) do device_code = device_response["device_code"] user_code = device_response["user_code"] verification_uri = device_response["verification_uri"] verification_uri_complete = device_response["verification_uri_complete"] interval = device_response["interval"] || 5 # Use the complete URI if available (has user code pre-filled), otherwise fall back to basic URI uri_to_open = verification_uri_complete || verification_uri Hex.Shell.info("To authenticate, visit: #{uri_to_open}") Hex.Shell.info("") Hex.Shell.info("Your verification code:") Hex.Shell.info("") Hex.Shell.info(" #{format_user_code(user_code)}") Hex.Shell.info("") Hex.Shell.info("Verify this code matches what is shown in your browser.") Hex.Shell.info("") # Automatically open the browser Hex.Utils.system_open(uri_to_open) Hex.Shell.info("Waiting for authentication...") case poll_for_token(device_code, interval) do {:ok, token} -> store_token(token) :error -> :error end end defp poll_for_token(device_code, interval, attempt \\ 1) do case Hex.API.OAuth.poll_device_token(device_code) do {:ok, {200, _, token_response}} -> {:ok, token_response} {:ok, {400, _, %{"error" => "authorization_pending"}}} -> if attempt > 120 do Hex.Shell.error("Authentication timed out. Please try again.") :error else Process.sleep(interval * 1000) poll_for_token(device_code, interval, attempt + 1) end {:ok, {400, _, %{"error" => "slow_down"}}} -> # Increase polling interval new_interval = min(interval * 2, 30) Process.sleep(new_interval * 1000) poll_for_token(device_code, new_interval, attempt + 1) {:ok, {400, _, %{"error" => "expired_token"}}} -> Hex.Shell.error("Device code expired. Please try again.") :error {:ok, {403, _, %{"error" => "access_denied"}}} -> Hex.Shell.error("Authentication was denied.") :error {:ok, {status, _, error}} -> Hex.Shell.error("Authentication failed (#{status}): #{inspect(error)}") :error {:error, reason} -> Hex.Shell.error("Authentication error: #{inspect(reason)}") :error end end defp format_user_code(user_code) do mid = div(String.length(user_code), 2) String.slice(user_code, 0, mid) <> "-" <> String.slice(user_code, mid, String.length(user_code)) end defp store_token(token) do # Create token data with expiration time token_data = Hex.OAuth.create_token_data(token) # Store a single token for both read and write operations # With 2FA now required for write operations, we don't need separate tokens Hex.OAuth.store_token(token_data) Hex.Shell.info("You are authenticated!") {:ok, token_data} end @doc false def generate_organization_key(organization_name, key_name, permissions, auth \\ nil) do auth = auth || auth_info(:write) case Hex.API.Key.Organization.new(organization_name, key_name, permissions, auth) do {:ok, {201, _, body}} -> {:ok, body["secret"]} other -> Mix.shell().error("Generation of key failed") Hex.Utils.print_error_result(other) :error end end @doc false def general_key_name(nil) do {:ok, hostname} = :inet.gethostname() List.to_string(hostname) end def general_key_name(key) do key end @doc false def api_key_name(key, extra \\ nil) do {:ok, hostname} = :inet.gethostname() name = "#{key || hostname}-api" if extra, do: "#{name}-#{extra}", else: name end @doc false def repository_key_name(organization, key) do {:ok, hostname} = :inet.gethostname() "#{key || hostname}-repository-#{organization}" end @doc false def repositories_key_name(key) do {:ok, hostname} = :inet.gethostname() "#{key || hostname}-repositories" end @doc false def revoke_existing_oauth_tokens do case Hex.Config.read()[:"$oauth_token"] do nil -> :ok token_data when is_map(token_data) -> if access_token = token_data["access_token"] do case Hex.API.OAuth.revoke_token(access_token) do {:ok, {code, _, _}} when code in 200..299 -> :ok _ -> :ok end end Hex.Config.remove([:"$oauth_token"]) _ -> :ok end end @doc false def revoke_and_cleanup_old_api_keys do config = Hex.Config.read() # Check for old write key if write_key = config[:"$write_key"] do # Try to revoke on server (might fail if already revoked or invalid) _ = Hex.API.Key.delete(write_key, key: write_key) end # Check for old read key (only if different from write key) if read_key = config[:"$read_key"] do if read_key != config[:"$write_key"] do _ = Hex.API.Key.delete(read_key, key: read_key) end end # Remove from config if they existed if config[:"$write_key"] || config[:"$read_key"] do Hex.Config.remove([:"$write_key", :"$read_key"]) end end @doc false def auth_organization(name, key) do repo = Hex.Repo.get_repo(name) || Hex.Repo.default_hexpm_repo() repo = Map.put(repo, :auth_key, key) Hex.State.fetch!(:repos) |> Map.put(name, repo) |> Hex.Config.update_repos() end defp prompt_otp() do Hex.Shell.info("") Hex.Shell.prompt("Enter your 2FA code:") |> String.trim() end @doc """ Returns authentication info for the given operation type. The permission parameter determines whether to include OTP for 2FA: - :write - includes OTP if available (required for write operations with 2FA) - :read - does not include OTP Both read and write operations use the same OAuth token. """ def auth_info(permission, opts \\ []) def auth_info(:write, opts) do # Try OAuth tokens first case Hex.OAuth.get_token() do {:ok, access_token} -> # Don't prompt for OTP upfront - will be prompted if server requires it otp = Hex.State.fetch!(:api_otp) if otp do [key: access_token, oauth: true, otp: otp] else [key: access_token, oauth: true] end {:error, :refresh_failed} -> Hex.Shell.info("Token refresh failed. Please re-authenticate.") if Keyword.get(opts, :auth_inline, true) do authenticate_inline() else [] end {:error, :no_refresh_token} -> Hex.Shell.info("Access token expired and could not be refreshed. Please re-authenticate.") if Keyword.get(opts, :auth_inline, true) do authenticate_inline() else [] end {:error, :no_auth} -> # Fall back to API key from config/env case Hex.State.fetch!(:api_key) do nil -> if Keyword.get(opts, :auth_inline, true) do authenticate_inline() else [] end api_key -> [key: api_key] end end end def auth_info(:read, opts) do # Try OAuth tokens first case Hex.OAuth.get_token() do {:ok, access_token} -> [key: access_token, oauth: true] {:error, :refresh_failed} -> Hex.Shell.info("Token refresh failed. Please re-authenticate.") if Keyword.get(opts, :auth_inline, true) do authenticate_inline() else [] end {:error, :no_refresh_token} -> Hex.Shell.info("Access token expired and could not be refreshed. Please re-authenticate.") if Keyword.get(opts, :auth_inline, true) do authenticate_inline() else [] end {:error, :no_auth} -> # Fall back to API key from config/env (write key can be used for read) case Hex.State.fetch!(:api_key) do nil -> if Keyword.get(opts, :auth_inline, true) do authenticate_inline() else [] end api_key -> [key: api_key] end end end defp authenticate_inline() do authenticate? = Hex.Shell.yes?("No authenticated user found. Do you want to authenticate now?") if authenticate? do case auth() do {:ok, _tokens} -> # Auth succeeded, try to get token case Hex.OAuth.get_token() do {:ok, access_token} -> # Don't prompt for OTP upfront - will be prompted if server requires it otp = Hex.State.fetch!(:api_otp) if otp do [key: access_token, oauth: true, otp: otp] else [key: access_token, oauth: true] end {:error, _} -> no_auth_error() end :error -> no_auth_error() end else no_auth_error() end end defp no_auth_error() do Mix.raise("No authenticated user found. Run `mix hex.user auth`") end @doc false def with_otp_retry(auth, fun) when is_function(fun, 1) do case fun.(auth) do {:error, :otp_required} -> otp = prompt_otp() Hex.State.put(:api_otp, otp) auth_with_otp = Keyword.put(auth, :otp, otp) with_otp_retry(auth_with_otp, fun) {:error, :invalid_totp} -> Hex.Shell.error("Invalid two-factor authentication code") otp = prompt_otp() Hex.State.put(:api_otp, otp) auth_with_otp = Keyword.put(auth, :otp, otp) with_otp_retry(auth_with_otp, fun) result -> result end end @doc false def required_opts(opts, required) do Enum.map(required, fn req -> unless Keyword.has_key?(opts, req) do Mix.raise("Missing command line option: #{req}") end end) end @doc false def convert_permissions([]) do nil end @doc false def convert_permissions(permissions) do Enum.map(permissions, fn permission -> permission = String.downcase(permission) destructure [domain, resource], String.split(permission, ":", parts: 2) %{"domain" => domain, "resource" => resource} end) end @progress_steps 25 @doc false def progress(nil) do fn _ -> nil end end def progress(max) do fn size -> fraction = size / max completed = trunc(fraction * @progress_steps) put_progress(completed, trunc(fraction * 100)) size end end defp put_progress(completed, percent) do unfilled = @progress_steps - completed str = "\r[#{String.duplicate("#", completed)}#{String.duplicate(" ", unfilled)}]" IO.write(:stderr, str <> " #{percent}%") end if Mix.env() == :test do @doc false def set_exit_code(code), do: throw({:exit_code, code}) else @doc false def set_exit_code(code), do: System.at_exit(fn _ -> System.halt(code) end) end end hex-2.4.2/lib/mix/tasks/hex.info.ex000066400000000000000000000200701517471540100170530ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Info do use Mix.Task @shortdoc "Prints Hex information" @moduledoc """ Prints Hex package or system information. $ mix hex.info [PACKAGE [VERSION]] If `package` is not given, print system information. This includes when registry was last updated and current system version. If `package` is given, print information about the package. This includes all released versions and package metadata. If `package` and `version` is given, print release information. ## Command line options * `--organization ORGANIZATION` - Set this for private packages belonging to an organization """ @behaviour Hex.Mix.TaskDescription @switches [organization: :string] @impl true def run(args) do Hex.start() {opts, args} = OptionParser.parse!(args, strict: @switches) case args do [] -> general() [package] -> package(opts[:organization], package) [package, version] -> release(opts[:organization], package, version) _ -> Mix.raise(""" Invalid arguments, expected: mix hex.info [PACKAGE [VERSION]] """) end end @impl true def tasks() do [ {"", "Prints Hex information"}, {"PACKAGE [VERSION]", "Prints package information"} ] end defp general() do Hex.Shell.info("Hex: #{Hex.version()}") Hex.Shell.info("Elixir: #{System.version()}") Hex.Shell.info("OTP: #{Hex.Utils.otp_version()}") Hex.Shell.info("") Hex.Shell.info("Built with: Elixir #{Hex.elixir_version()} and OTP #{Hex.otp_version()}") Hex.Registry.Server.open() Hex.UpdateChecker.check() Hex.Registry.Server.close() end defp package(_organization, "") do Hex.Shell.error("Package name is empty") Mix.Tasks.Hex.set_exit_code(1) end defp package(organization, package) do auth = organization && Mix.Tasks.Hex.auth_info(:read) case Hex.API.Package.get(organization, package, auth) do {:ok, {code, _, body}} when code in 200..299 -> print_package(body, locked_dep(package)) {:ok, {404, _, _}} -> Hex.Shell.error("No package with name #{package}") Mix.Tasks.Hex.set_exit_code(1) other -> Hex.Shell.error("Failed to retrieve package information") Hex.Utils.print_error_result(other) Mix.Tasks.Hex.set_exit_code(1) end end defp release(organization, package, version) do auth = organization && Mix.Tasks.Hex.auth_info(:read) case Hex.API.Release.get(organization, package, version, auth) do {:ok, {code, _, body}} when code in 200..299 -> print_release(organization, package, body) {:ok, {404, _, _}} -> Hex.Shell.error("No release with name #{package} #{version}") Mix.Tasks.Hex.set_exit_code(1) other -> Hex.Shell.error("Failed to retrieve release information") Hex.Utils.print_error_result(other) Mix.Tasks.Hex.set_exit_code(1) end end defp print_package(package, locked_package) do meta = package["meta"] desc = meta["description"] || "No description provided" Hex.Shell.info(desc <> "\n") releases = package["releases"] || [] retirements = package["retirements"] || %{} Hex.Shell.info("Config: " <> package["configs"]["mix.exs"]) print_locked_package(locked_package) Hex.Shell.info(["\nRecent releases:\n" | format_releases(releases, Map.keys(retirements))]) print_downloads(package["downloads"]) print_meta(meta) end defp format_releases(releases, retirements) do {releases, rest} = Enum.split(releases, 8) releases |> Enum.map(&[" ", format_version(&1, retirements), "\n"]) |> add_ellipsis(rest) end defp format_version(%{"version" => version} = release, retirements) do date = format_release_date(release["inserted_at"]) if version in retirements do [:yellow, version, date, " (retired)", :reset] else [version, date] end end defp format_release_date(nil), do: "" defp format_release_date(date_string) do case parse_date(date_string) do {:ok, date} -> " (#{date})" _ -> "" end end defp add_ellipsis(output, []), do: output defp add_ellipsis(output, _rest), do: output ++ [" ...\n"] defp print_downloads(nil), do: :ok defp print_downloads(downloads) do parts = Enum.reject( [ format_download_count("Yesterday", downloads["day"]), format_download_count("Last 7 days", downloads["week"]), format_download_count("All time", downloads["all"]) ], &is_nil/1 ) if parts != [] do Hex.Shell.info("Downloads:\n " <> Enum.join(parts, "\n ") <> "\n") end end defp format_download_count(_label, nil), do: nil defp format_download_count(label, count), do: "#{label}: #{add_thousands_separators(count)}" defp add_thousands_separators(count, separator \\ " ") do count |> Integer.to_string() |> String.reverse() |> String.codepoints() |> Enum.chunk_every(3) |> Enum.join(separator) |> String.reverse() end defp print_meta(meta) do print_list(meta, "licenses") print_dict(meta, "links") end defp print_release(organization, package, release) do version = release["version"] print_retirement(release) Hex.Shell.info("Config: " <> release["configs"]["mix.exs"]) print_release_published_at(release["inserted_at"]) if release["has_docs"] do Hex.Shell.info("Documentation at: #{Hex.Utils.hexdocs_url(organization, package, version)}") end print_release_downloads(release["downloads"]) if requirements = release["requirements"] do Hex.Shell.info("Dependencies:") Enum.each(requirements, fn {name, req} -> app = req["app"] app = if app && app != name, do: " (app: #{app})" optional = if req["optional"], do: " (optional)" Hex.Shell.info(" #{name} #{req["requirement"]}#{app}#{optional}") end) end print_publisher(release) end defp print_release_downloads(count) when is_integer(count) do Hex.Shell.info("Downloads: #{add_thousands_separators(count)}") end defp print_release_downloads(_), do: :ok defp print_release_published_at(nil), do: :ok defp print_release_published_at(date_string) do case parse_date(date_string) do {:ok, date} -> Hex.Shell.info("Released: #{date}") _ -> :ok end end defp parse_date(date_string) do case DateTime.from_iso8601(date_string) do {:ok, datetime, _offset} -> {:ok, DateTime.to_date(datetime)} _ -> Date.from_iso8601(date_string) end end defp print_locked_package(nil), do: nil defp print_locked_package(locked_package) do Hex.Shell.info(["Locked version: #{locked_package.version}"]) end defp print_retirement(%{"retirement" => nil}), do: "" defp print_retirement(release) do retirement = %{ reason: release["retirement"]["reason"], message: release["retirement"]["message"] } Hex.Shell.warn([ [:bright, "This version has been retired"], [:normal, ": "], [:normal, Hex.Utils.package_retirement_message(retirement)] ]) end defp print_publisher(release) do publisher_username = release["publisher"]["username"] publisher_email = release["publisher"]["email"] email_or_empty = if publisher_email, do: " (#{publisher_email})", else: "" Hex.Shell.info("Published by: #{publisher_username}#{email_or_empty}") end defp print_list(meta, name) do list = Map.get(meta, name, []) if list != [] do Hex.Shell.info(String.capitalize(name) <> ": " <> Enum.join(list, ", ")) end end defp print_dict(meta, name) do title = String.capitalize(name) dict = Map.get(meta, name, []) if dict != [] do Hex.Shell.info(title <> ":") Enum.each(dict, fn {key, val} -> Hex.Shell.info(" #{key}: #{val}") end) end end # Pull out the locked dependency version, if it exists defp locked_dep(package_name) do Mix.Dep.Lock.read() |> Enum.map(fn {_app, info} -> Hex.Utils.lock(info) end) |> Enum.find(fn locked -> locked && locked.name == package_name end) end end hex-2.4.2/lib/mix/tasks/hex.organization.ex000066400000000000000000000214101517471540100206230ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Organization do use Mix.Task @shortdoc "Manages Hex.pm organizations" @moduledoc """ Manages the list of authorized Hex.pm organizations. Organizations is a feature of Hex.pm to host and manage private packages. See for more information. By default you will be authorized to all your applications when running `mix hex.user auth` and this is the recommended approach. This task is mainly provided for a CI and build systems where access to an organization is needed without authorizing a user. By authorizing a new organization a new key is created for fetching packages from the organizations repository and the repository key is stored on the local machine. To use a package from an organization add `organization: "my_organization"` to the dependency declaration in `mix.exs`: {:plug, "~> 1.0", organization: "my_organization"} ## Authorize an organization This command will generate an API key used to authenticate access to the organization. See the `hex.user` tasks to list and control all your active API keys. $ mix hex.organization auth ORGANIZATION [--key KEY] [--key-name KEY_NAME] ## Deauthorize and remove an organization $ mix hex.organization deauth NAME ## List all authorized organizations This command will only list organizations you have authorized with this task, it will not list organizations you have access to by having authorized with `mix hex.user auth`. $ mix hex.organization list ## Generate organization key This command is useful to pre-generate keys for use with `mix hex.organization auth ORGANIZATION --key KEY` on CI servers or similar systems. It returns the hash of the generated key that you can pass to `auth ORGANIZATION --key KEY`. Unlike the `hex.user key` commands, a key generated with this command is owned by the organization directly, and not the user that generated it. This makes it ideal for shared environments such as CI where you don't want to give access to user-specific resources and the user's organization membership status won't affect key. By default this command sets the `repository:organization_name` permission which allows read-only access to the organization's repository, it can be overridden with the `--permission` flag. $ mix hex.organization key ORGANIZATION generate [--key-name KEY_NAME] [--permission PERMISSION] ## Revoke key Removes given key from organization. The key can no longer be used to authenticate API requests. $ mix hex.organization key ORGANIZATION revoke KEY_NAME ## Revoke all keys Revoke all keys from the organization. $ mix hex.organization key ORGANIZATION revoke --all ## List keys Lists all keys associated with the organization. $ mix hex.organization key ORGANIZATION list ## Command line options * `--key KEY` - Hash of key used to authenticate HTTP requests to repository, if omitted will generate a new key with your account credentials. This flag is useful if you have a key pre-generated with `mix hex.organization key` and want to authenticate on a CI server or similar system * `--key-name KEY_NAME` - By default Hex will base the key name on your machine's hostname and the organization name, use this option to give your own name. * `--permission PERMISSION` - Sets the permissions on the key, this option can be given multiple times, possibly values are: * `api:read` - API read access. * `api:write` - API write access. * `repository:organization_name` - Access to the organization's repository (this is the default permission). """ @behaviour Hex.Mix.TaskDescription @switches [ all: :boolean, key_name: :string, key: :string, permission: [:string, :keep] ] @impl true def run(args) do Hex.start() {opts, args} = OptionParser.parse!(args, switches: @switches) case args do ["auth", name] -> auth(name, opts) ["deauth", name] -> deauth(name) ["key", name, "generate"] -> key_generate(name, opts) ["key", name, "revoke", key_name] -> key_revoke(name, key_name) ["key", name, "revoke"] -> if opts[:all], do: key_revoke_all(name), else: invalid_args() ["key", name, "list"] -> key_list(name) ["list"] -> list() _ -> invalid_args() end end @impl true def tasks() do [ {"auth ORGANIZATION", "Authorize an organization"}, {"deauth ORGANIZATION", "Deauthorize and remove organization"}, {"list", "List all authorized organizations"}, {"key ORGANIZATION generate", "Generate organization key"}, {"key ORGANIZATION revoke KEY_NAME", "Revoke key"}, {"key ORGANIZATION revoke --all", "Revoke all keys"}, {"key ORGANIZATION list", "List keys"} ] end defp invalid_args() do Mix.raise(""" Invalid arguments, expected one of: mix hex.organization auth ORGANIZATION mix hex.organization deauth ORGANIZATION mix hex.organization list mix hex.organization key ORGANIZATION generate mix hex.organization key ORGANIZATION revoke KEY_NAME mix hex.organization key ORGANIZATION revoke --all mix hex.organization key ORGANIZATION list """) end defp auth(organization, opts) do key = opts[:key] key = if key do test_key(key, organization) key else key_name = Mix.Tasks.Hex.repository_key_name(organization, opts[:key_name]) permissions = [%{"domain" => "repository", "resource" => organization}] auth = Mix.Tasks.Hex.auth_info(:write) case Hex.API.Key.new(key_name, permissions, auth) do {:ok, {201, _, body}} -> body["secret"] other -> Mix.shell().error("Generation of key failed") Hex.Utils.print_error_result(other) nil end end if key do Mix.Tasks.Hex.auth_organization("hexpm:#{organization}", key) end end defp deauth(name) do Hex.State.fetch!(:repos) |> Map.delete("hexpm:#{name}") |> Hex.Config.update_repos() end defp key_revoke_all(organization) do auth = Mix.Tasks.Hex.auth_info(:write) Hex.Shell.info("Revoking all keys...") case Hex.API.Key.Organization.delete_all(organization, auth) do {:ok, {code, _headers, _body}} when code in 200..299 -> :ok other -> Hex.Shell.error("Key revocation failed") Hex.Utils.print_error_result(other) end end defp key_revoke(organization, key) do auth = Mix.Tasks.Hex.auth_info(:write) Hex.Shell.info("Revoking key #{key}...") case Hex.API.Key.Organization.delete(organization, key, auth) do {:ok, {code, _headers, _body}} when code in 200..299 -> :ok other -> Hex.Shell.error("Key revocation failed") Hex.Utils.print_error_result(other) end end # TODO: print permissions defp key_list(organization) do auth = Mix.Tasks.Hex.auth_info(:read) case Hex.API.Key.Organization.get(organization, auth) do {:ok, {code, _headers, body}} when code in 200..299 -> values = Enum.map(body, fn %{"name" => name, "inserted_at" => time} -> [name, time] end) Mix.Tasks.Hex.print_table(["Name", "Created at"], values) other -> Hex.Shell.error("Key fetching failed") Hex.Utils.print_error_result(other) end end defp key_generate(organization, opts) do key_name = Mix.Tasks.Hex.general_key_name(opts[:key_name]) default_permission = [%{"domain" => "repository", "resource" => organization}] permissions = Keyword.get_values(opts, :permission) permissions = Mix.Tasks.Hex.convert_permissions(permissions) || default_permission result = Mix.Tasks.Hex.generate_organization_key( organization, key_name, permissions ) case result do {:ok, secret} -> Hex.Shell.info(secret) :error -> :ok end end defp list() do Enum.each(Hex.State.fetch!(:repos), fn {name, _repo} -> case String.split(name, ":", parts: 2) do ["hexpm", name] -> Hex.Shell.info(name) _ -> :ok end end) end defp test_key(key, name) do case Hex.API.Auth.get("repository", name, key: key) do {:ok, {code, _, _body}} when code in 200..299 -> :ok {:ok, {code, _, %{"message" => message}}} when code in [401, 403] -> Hex.Shell.error( "Failed to authenticate against organization repository with given key because of: #{message}" ) Mix.Tasks.Hex.set_exit_code(1) other -> Hex.Utils.print_error_result(other) Hex.Shell.error("Failed to verify authentication key") Mix.Tasks.Hex.set_exit_code(1) end end end hex-2.4.2/lib/mix/tasks/hex.outdated.ex000066400000000000000000000270111517471540100177330ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Outdated do use Mix.Task alias Hex.Registry.Server, as: Registry @shortdoc "Shows outdated Hex deps for the current project" @moduledoc """ Shows all Hex dependencies that have newer versions in the registry. $ mix hex.outdated [APP] By default, it only shows top-level packages explicitly listed in the `mix.exs` file. All outdated packages can be displayed by using the `--all` command line option. By default, `hex.outdated` will exit with a non-zero exit code (1) if there are any outdated dependencies. You can override this to respect the requirements as specified in your `mix.exs` file, with the `--within-requirements` command line option, so it only exits with non-zero exit code if the update is possible. For example, if your version requirement is "~> 2.0" but the latest version is `3.0`, with `--within-requirements` it will exit successfully, but if the latest version is `2.8`, then `--within-requirements` will exit with non-zero exit code (1). One scenario this could be useful is to ensure you always have the latest version of your dependencies, except for major version bumps. If a dependency name is given all requirements on that dependency, from the entire dependency tree, are listed. This is useful if you are trying to figure why a package isn't updating when you run `mix deps.update`. Note that when this task determines if a package is updatable it only looks at the project's current set of dependency requirements and what version they are locked to. When `mix deps.update` is called multiple packages may be updated that in turn update their own dependencies, which may cause the package you want to update to not be able to update. If you want to force a dependency to be updated to a given version, you can directly update it in your `mix.exs`. > In a project, this task must be invoked before any other tasks > that may load or start your application. Otherwise, you must > explicitly list `:hex` as part of your `:extra_applications`. ## Command line options * `--all` - shows all outdated packages, including children of packages defined in `mix.exs` * `--pre` - include pre-releases when checking for newer versions * `--within-requirements` - exit with non-zero code only if requirements specified in `mix.exs` is met. * `--sort ` - sort results by the given column. Currently supports `status`. * `--only ANYVALUE` - show only dependencies with the given only value (comma-separated for multiple values) """ @behaviour Hex.Mix.TaskDescription @switches [ all: :boolean, pre: :boolean, within_requirements: :boolean, sort: :string, only: :string ] @impl true def run(args) do Mix.Tasks.Deps.Loadpaths.run(["--no-compile", "--no-listeners"]) Hex.start() {opts, args} = OptionParser.parse!(args, strict: @switches) Registry.open() lock = Mix.Dep.Lock.read() lock |> Hex.Mix.packages_from_lock() |> Hex.Registry.Server.prefetch() case args do [app] -> single(lock, app, opts) [] -> all(lock, opts) _ -> Mix.raise(""" Invalid arguments, expected: mix hex.outdated [APP] """) end end @impl true def tasks() do [ {"", "Shows outdated Hex deps for the current project"}, {"[APP]", "Shows outdated Hex deps for the given dependency"} ] end defp single(lock, app, opts) do app = String.to_atom(app) deps = Hex.Mix.top_level_deps() {repo, package, current} = case Hex.Utils.lock(lock[app]) do %{repo: repo, name: package, version: version} -> {repo, package, version} nil -> Mix.raise("Dependency #{app} not locked as a Hex package") end latest = latest_version(repo, package, current, opts[:pre]) outdated? = Version.compare(current, latest) == :lt lock_requirements = get_requirements_from_lock(app, lock) deps_requirements = get_requirements_from_deps(app, deps) requirements = deps_requirements ++ lock_requirements if outdated? do [ "There is newer version of the dependency available ", [:bright, latest, " > ", current, :reset, "!"] ] |> IO.ANSI.format_fragment() |> Hex.Shell.info() else ["Current version ", :bright, current, :reset, " of dependency is up to date!"] |> IO.ANSI.format_fragment() |> Hex.Shell.info() end header = ["Source", "Requirement", "Up-to-date"] values = Enum.map(requirements, &format_single_row(&1, latest)) Hex.Shell.info("") Mix.Tasks.Hex.print_table(header, values) message = "Up-to-date indicates if the requirement matches the latest version." Hex.Shell.info(["\n", message]) if outdated?, do: Mix.Tasks.Hex.set_exit_code(1) end defp get_requirements_from_lock(app, lock) do Enum.flat_map(lock, fn {source, lock} -> case Hex.Utils.lock(lock) do %{deps: nil} -> [] %{deps: deps} -> Enum.flat_map(deps, fn {dep_app, req, _opts} -> if app == dep_app, do: [[Atom.to_string(source), req]], else: [] end) nil -> [] end end) end defp get_requirements_from_deps(app, deps) do # TODO: Path to umbrella child's mix.exs case Map.fetch(deps, app) do {:ok, deps} -> Enum.map(deps, fn {src, req, _opts} -> [Path.join([src, "mix.exs"]), req] end) :error -> [] end end defp format_single_row([source, req], latest) do req_matches? = version_match?(latest, req) req_color = if req_matches?, do: :green, else: :red up_to_date? = if req_matches?, do: "Yes", else: "No" [[:bright, source], [req_color, req || ""], [req_color, up_to_date?]] end defp all(lock, opts) do deps = Hex.Mix.top_level_deps() dep_names = if opts[:all], do: Map.keys(lock), else: Map.keys(deps) versions = dep_names |> Enum.sort() |> get_versions(deps, lock, opts[:pre]) |> filter_by_only(opts) values = versions |> Enum.map(&format_all_row/1) |> maybe_sort_by(opts[:sort]) diff_links = Enum.map(versions, &build_diff_link/1) |> Enum.reject(&is_nil/1) if Enum.empty?(values) do Hex.Shell.info("No hex dependencies") else header = ["Dependency", "Only", "Current", "Latest", "Status"] Mix.Tasks.Hex.print_table(header, values) base_message = "Run `mix hex.outdated APP` to see requirements for a specific dependency." diff_message = maybe_diff_message(diff_links) diff_command_message = maybe_diff_command_message(diff_links) Hex.Shell.info(["\n", base_message, diff_message, diff_command_message]) outdated = outdated(versions) any_updatable? = any_possible_to_update?(outdated) if outdated != [] && (!opts[:within_requirements] || any_updatable?) do Mix.Tasks.Hex.set_exit_code(1) end end end defp maybe_sort_by(values, "status") do status_order = %{ "Up-to-date" => 1, "Update not possible" => 2, "Update possible" => 3 } Enum.sort_by(values, fn [_package, _dep_only, _lock, _latest, [_color, status]] -> Map.fetch!(status_order, status) end) end defp maybe_sort_by(values, nil) do values end defp filter_by_only(versions, opts) do case opts[:only] do nil -> versions only_value -> # deps can have multiple `only` values, so we separate by `,` only_values = String.split(only_value, ",", trim: true) Enum.filter(versions, fn [_package, dep_only, _lock, _latest, _requirements] -> dep_only_parts = String.split(dep_only, ",") Enum.any?(dep_only_parts, &(&1 in only_values)) end) end end defp get_versions(dep_names, deps, lock, pre?) do Enum.flat_map(dep_names, fn name -> case Hex.Utils.lock(lock[name]) do %{repo: repo, name: package, version: lock_version} -> latest_version = latest_version(repo, package, lock_version, pre?) lock_requirements = get_requirements_from_lock(name, lock) deps_requirements = get_requirements_from_deps(name, deps) requirements = (deps_requirements ++ lock_requirements) |> Enum.map(fn [_, req_version] -> req_version end) dep_only = get_dep_only(deps, name) [[Atom.to_string(name), dep_only, lock_version, latest_version, requirements]] _ -> [] end end) end defp latest_version(repo, package, default, pre?) do {:ok, default} = Version.parse(default) pre? = pre? || default.pre != [] {:ok, versions} = Registry.versions(repo, package) latest = highest_version(versions, pre?) to_string(latest || default) end defp highest_version(versions, pre?) do versions = if pre? do versions else Enum.filter(versions, fn version -> version.pre == [] end) end List.last(versions) end defp format_all_row([package, dep_only, lock, latest, requirements]) do outdated? = Version.compare(lock, latest) == :lt latest_color = if outdated?, do: :red, else: :green req_matches? = req_matches?(requirements, latest) status = case {outdated?, req_matches?} do {true, true} -> [:yellow, "Update possible"] {true, false} -> [:red, "Update not possible"] {false, _} -> [:green, "Up-to-date"] end [ [:bright, package], dep_only, lock, [latest_color, latest], status ] end defp build_diff_link([package, _dep_only, lock, latest, requirements]) do outdated? = Version.compare(lock, latest) == :lt req_matches? = Enum.all?(requirements, &version_match?(latest, &1)) case {outdated?, req_matches?} do {true, true} -> "diffs[]=#{package}:#{lock}:#{latest}" {_, _} -> nil end end defp version_match?(_version, nil), do: true defp version_match?(version, req), do: Version.match?(version, req) defp outdated(versions) do Enum.filter(versions, fn [_package, _dep_only, lock, latest, _requirements] -> Version.compare(lock, latest) == :lt end) end defp maybe_diff_message([]), do: "" defp maybe_diff_message(diff_links) do "\n\nTo view the diffs in each available update, visit:\n" <> diff_link(diff_links) end defp maybe_diff_command_message([]), do: "" defp maybe_diff_command_message(_diff_links) do "\n\nTo view the diff of a specific update, run `mix hex.package diff APP FROM..TO`." end defp diff_link(diff_links) do long_url = "https://diff.hex.pm/diffs?" <> Enum.join(diff_links, "&") if Hex.State.fetch!(:no_short_urls) do long_url else maybe_get_short_link(long_url) end end defp maybe_get_short_link(long_url) do case Hex.API.ShortURL.create(long_url) do :error -> long_url {:ok, short_url} -> short_url end end defp any_possible_to_update?(outdated_versions) do Enum.any?(outdated_versions, fn [_package, _dep_only, _lock, latest, requirements] -> req_matches?(requirements, latest) end) end defp req_matches?(requirements, latest) do Enum.all?(requirements, &version_match?(latest, &1)) end defp get_dep_only(deps, dep_name) do dep_configs = Map.get(deps, dep_name, []) # Get the opts from the first (main project) dependency config case List.first(dep_configs) do {_src, _req, opts} -> dep_only_from_opts(opts) _ -> "" end end defp dep_only_from_opts(opts) do Keyword.get(opts, :only, []) |> List.wrap() |> Enum.map(&to_string/1) |> Enum.join(",") end end hex-2.4.2/lib/mix/tasks/hex.owner.ex000066400000000000000000000131611517471540100172550ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Owner do use Mix.Task @shortdoc "Manages Hex package ownership" @moduledoc """ Adds, removes or lists package owners. Package owners have full permissions to the package. They can publish and revert releases and even remove other package owners. ## Add owner Adds an owner to package by specifying the package name and email or username of the new owner. This command also takes a `--level` option, see below for more details. $ mix hex.owner add PACKAGE EMAIL_OR_USERNAME ## Transfer ownership Like `mix hex.owner add` but also removes all existing owners of the package. This task is required to use when transferring ownership of the package to an organization. $ mix hex.owner transfer PACKAGE EMAIL_OR_USERNAME ## Remove owner Removes an owner to package by specifying the package name and email or username of the new owner. $ mix hex.owner remove PACKAGE EMAIL_OR_USERNAME ## List owners Lists all owners of given package. $ mix hex.owner list PACKAGE ## List owned packages Lists all packages owned by the current user. $ mix hex.owner packages ## Command line options * `--level maintainer` - Maintainer level owners have all the powers of package ownership, except they cannot add or remove other package owners * `--level full` - Over the maintainer level, full owners can also add and remove other package owners (default) * `--organization ORGANIZATION` - Set this for private packages belonging to an organization """ @behaviour Hex.Mix.TaskDescription @switches [organization: :string, level: :string] @impl true def run(args) do Hex.start() {opts, args} = OptionParser.parse!(args, strict: @switches) organization = opts[:organization] level = opts[:level] || "full" case args do ["add", package, owner] -> add_owner(organization, package, owner, level) ["transfer", package, owner] -> transfer_owner(organization, package, owner) ["remove", package, owner] -> remove_owner(organization, package, owner) ["list", package] -> list_owners(organization, package) ["packages"] -> list_owned_packages() _ -> Mix.raise(""" Invalid arguments, expected one of: mix hex.owner add PACKAGE EMAIL_OR_USERNAME mix hex.owner transfer PACKAGE EMAIL_OR_USERNAME mix hex.owner remove PACKAGE EMAIL_OR_USERNAME mix hex.owner list PACKAGE mix hex.owner packages """) end end @impl true def tasks() do [ {"add PACKAGE EMAIL_OR_USERNAME", "Adds an owner to package"}, {"transfer PACKAGE EMAIL_OR_USERNAME", "Transfers ownership of a package to another user or organization"}, {"remove PACKAGE EMAIL_OR_USERNAME", "Removes an owner from package"}, {"list PACKAGE", "List all owners of a given package"}, {"packages", "List all packages owned by the current user"} ] end defp add_owner(organization, package, owner, level) when level in ~w[full maintainer] do auth = Mix.Tasks.Hex.auth_info(:write) Hex.Shell.info("Adding owner #{owner} with ownership level #{level} to #{package}") case Hex.API.Package.Owner.add(organization, package, owner, level, false, auth) do {:ok, {code, _headers, _body}} when code in 200..299 -> :ok other -> Hex.Shell.error("Adding owner failed") Hex.Utils.print_error_result(other) end end defp add_owner(_organization, _package, _owner, _level) do Mix.raise("Invalid ownership level, expected one of: full, maintainer") end defp transfer_owner(organization, package, owner) do auth = Mix.Tasks.Hex.auth_info(:write) Hex.Shell.info("Transferring ownership to #{owner} for #{package}") case Hex.API.Package.Owner.add(organization, package, owner, "full", true, auth) do {:ok, {code, _headers, _body}} when code in 200..299 -> :ok other -> Hex.Shell.error("Transferring ownership failed") Hex.Utils.print_error_result(other) end end defp remove_owner(organization, package, owner) do auth = Mix.Tasks.Hex.auth_info(:write) Hex.Shell.info("Removing owner #{owner} from #{package}") case Hex.API.Package.Owner.delete(organization, package, owner, auth) do {:ok, {code, _headers, _body}} when code in 200..299 -> :ok other -> Hex.Shell.error("Removing owner failed") Hex.Utils.print_error_result(other) end end defp list_owners(organization, package) do auth = Mix.Tasks.Hex.auth_info(:read) case Hex.API.Package.Owner.get(organization, package, auth) do {:ok, {code, _headers, body}} when code in 200..299 -> header = ["Email", "Level"] owners = Enum.map(body, &[&1["email"], &1["level"]]) Mix.Tasks.Hex.print_table(header, owners) other -> Hex.Shell.error("Package owner fetching failed") Hex.Utils.print_error_result(other) end end defp list_owned_packages() do auth = Mix.Tasks.Hex.auth_info(:read) case Hex.API.User.me(auth) do {:ok, {code, _headers, body}} when code in 200..299 -> Enum.each(body["packages"], fn package -> name = package_name(package["repository"], package["name"]) Hex.Shell.info("#{name} - #{package["html_url"]}") end) other -> Hex.Shell.error("Listing owned packages failed") Hex.Utils.print_error_result(other) end end defp package_name("hexpm", package_name), do: package_name defp package_name(repository_name, package_name), do: repository_name <> "/" <> package_name end hex-2.4.2/lib/mix/tasks/hex.package.ex000066400000000000000000000255361517471540100175270ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Package do use Mix.Task alias Hex.Registry.Server, as: Registry @shortdoc "Fetches or diffs packages" @default_diff_command "git diff --no-index --no-prefix __PATH1__ __PATH2__" @doc false def default_diff_command(), do: @default_diff_command @moduledoc """ Fetches or diffs packages. ## Fetch package Fetch a package tarball to the current directory. $ mix hex.package fetch PACKAGE [VERSION] [--unpack] [--output PATH] If `version` is not given, use the latest version. You can pipe the fetched tarball to stdout by setting `--output -`. ## Diff package versions $ mix hex.package diff APP VERSION This command compares the project's dependency `APP` against the target package version, unpacking the target version into temporary directory and running a diff command. ## Fetch and diff package contents between versions $ mix hex.package diff PACKAGE VERSION1 VERSION2 $ mix hex.package diff PACKAGE VERSION1..VERSION2 This command fetches package tarballs for both versions, unpacks them into temporary directories and runs a diff command. Afterwards, the temporary directories are automatically deleted. Note, similarly to when tarballs are fetched with `mix deps.get`, a `hex_metadata.config` is placed in each unpacked directory. This file contains package's metadata as Erlang terms and so we can additionally see the diff of that. The exit code of the task is that of the underlying diff command. ### Diff command The diff command can be customized by setting `diff_command` configuration option, see `mix help hex.config` for more information. The default diff command is: $ #{@default_diff_command} The `__PATH1__` and `__PATH2__` placeholders will be interpolated with paths to directories of unpacked tarballs for each version. Many diff commands supports coloured output but because we execute the command in non-interactive mode, they'd usually be disabled. On Unix systems you can pipe the output to more commands, for example: $ mix hex.package diff decimal 1.0.0..1.1.0 | colordiff | less -R Here, the output of `mix hex.package diff` is piped to the `colordiff` utility to adds colours, which in turn is piped to `less -R` which "pages" it. (`-R` preserves escape codes which allows colours to work.) Another option is to configure the diff command itself. For example, to force Git to always colour the output we can set the `--color=always` option: $ mix hex.config diff_command "git diff --color=always --no-index __PATH1__ __PATH2__" $ mix hex.package diff decimal 1.0.0..1.1.0 ## Command line options * `--unpack` - Unpacks the tarball after fetching it * `-o`, `--output` - Sets output path. When used with `--unpack` it means the directory (Default: `-`). Otherwise, it specifies tarball path (Default: `-.tar`) * `--organization ORGANIZATION` - Set this for private packages belonging to an organization * `--repo REPO` - Set this for self-hosted Hex instances, default: `hexpm` """ @behaviour Hex.Mix.TaskDescription @switches [unpack: :boolean, organization: :string, output: :string, repo: :string] @aliases [o: :output] @impl true def run(args) do Hex.start() {opts, args} = OptionParser.parse!(args, strict: @switches, aliases: @aliases) unpack = Keyword.get(opts, :unpack, false) output = Keyword.get(opts, :output, nil) case args do ["fetch", package] -> fetch(repo(opts), package, nil, unpack, output) ["fetch", package, version] -> fetch(repo(opts), package, version, unpack, output) ["diff", package, version1, version2] -> diff(repo(opts), package, parse_version!(version1, version2)) ["diff", package, version] -> diff(repo(opts), package, parse_version!(version)) _ -> Mix.raise(""" Invalid arguments, expected one of: mix hex.package fetch PACKAGE [VERSION] [--unpack] mix hex.package diff APP VERSION mix hex.package diff PACKAGE VERSION1 VERSION2 mix hex.package diff PACKAGE VERSION1..VERSION2 """) end end @impl true def tasks() do [ {"fetch PACKAGE [VERSION] [--unpack]", "Fetch the package"}, {"diff APP VERSION", "Diff dependency against version"}, {"diff PACKAGE VERSION1 VERSION2", "Diff package versions"}, {"diff PACKAGE VERSION1..VERSION2", "Diff package versions"} ] end defp fetch(repo, package, nil, unpack?, output) do version = find_package_latest_version(repo, package) fetch(repo, package, version, unpack?, output) end defp fetch(repo, package, version, false, "-") do Hex.Registry.Server.open() Hex.Registry.Server.prefetch([{repo, package}]) tarball = fetch_tarball!(repo, package, version) IO.binwrite(tarball) Hex.Registry.Server.close() end defp fetch(_repo, _package, _version, true, "-") do Mix.raise("Cannot unpack the package while output destination is stdout") end defp fetch(repo, package, version, unpack?, output) do Hex.Registry.Server.open() Hex.Registry.Server.prefetch([{repo, package}]) tarball = fetch_tarball!(repo, package, version) if output, do: File.mkdir_p!(output) abs_name = Path.absname("#{package}-#{version}") {abs_path, tar_path} = if output do {output, Path.join(output, "#{package}-#{version}.tar")} else {abs_name, "#{abs_name}.tar"} end File.write!(tar_path, tarball) if unpack? do %{inner_checksum: inner_checksum, outer_checksum: outer_checksum} = Hex.Tar.unpack!(tar_path, abs_path) verify_inner_checksum!(repo, package, version, inner_checksum) verify_outer_checksum!(repo, package, version, outer_checksum) else {:ok, outer_checksum} = Hex.Tar.outer_checksum(tar_path) verify_outer_checksum!(repo, package, version, outer_checksum) end message = if unpack? do File.rm!(tar_path) "#{package} v#{version} extracted to #{abs_path}" else "#{package} v#{version} downloaded to #{tar_path}" end Hex.Shell.info(message) Hex.Registry.Server.close() end defp fetch_tarball!(repo, package, version) do path = Hex.SCM.cache_path(repo, package, version) case Hex.SCM.fetch(repo, package, version) do {:ok, _} -> File.read!(path) {:error, reason} -> if File.exists?(path) do File.read!(path) else Mix.raise( "Downloading " <> Hex.Repo.tarball_url(repo, package, version) <> " failed:\n\n" <> reason ) end end end defp verify_inner_checksum!(repo, package, version, checksum) do registry_checksum = Registry.inner_checksum(repo, package, version) if checksum != registry_checksum do Mix.raise("Checksum mismatch against registry (inner)") end end defp verify_outer_checksum!(repo, package, version, checksum) do registry_checksum = Registry.outer_checksum(repo, package, version) if checksum != registry_checksum do Mix.raise("Checksum mismatch against registry (outer)") end end defp diff(repo, app, version) when is_binary(version) do Mix.Tasks.Deps.Loadpaths.run(["--no-compile", "--no-listeners"]) {path_lock, package} = case Map.get(Mix.Dep.Lock.read(), String.to_atom(app)) do nil -> Mix.raise( "Cannot find the app \"#{app}\" in \"mix.lock\" file, " <> "please ensure it has been specified in \"mix.exs\" and run \"mix deps.get\"" ) lock -> path = Path.join(Mix.Project.deps_path(), app) package = Hex.Utils.lock(lock).name {path, package} end path = tmp_path("#{package}-#{version}-") try do fetch_and_unpack!(repo, package, [{path, version}]) code = run_diff_path!(path_lock, path) Mix.Tasks.Hex.set_exit_code(code) after File.rm_rf!(path) end end defp diff(repo, package, {version1, version2}) do parent = tmp_path("hex-diff-") name1 = "#{package}-#{version1}" name2 = "#{package}-#{version2}" path1 = Path.join(parent, name1) path2 = Path.join(parent, name2) try do File.mkdir_p!(parent) fetch_and_unpack!(repo, package, [{path1, version1}, {path2, version2}]) code = run_diff_path!(name1, name2, cd: parent) Mix.Tasks.Hex.set_exit_code(code) after File.rm_rf!(parent) end end defp fetch_and_unpack!(repo, package, versions) do Hex.Registry.Server.open() Hex.Registry.Server.prefetch([{repo, package}]) try do Enum.each(versions, fn {path, version} -> tarball = fetch_tarball!(repo, package, version) %{inner_checksum: inner_checksum, outer_checksum: outer_checksum} = Hex.Tar.unpack!({:binary, tarball}, path) verify_inner_checksum!(repo, package, version, inner_checksum) verify_outer_checksum!(repo, package, version, outer_checksum) end) after Hex.Registry.Server.close() end end defp run_diff_path!(path1, path2, opts \\ []) do cmd = Hex.State.fetch!(:diff_command) |> String.replace("__PATH1__", escape_and_quote_path(path1)) |> String.replace("__PATH2__", escape_and_quote_path(path2)) Mix.shell().cmd(cmd, opts) end defp escape_and_quote_path(path) do escaped = String.replace(path, "\"", "\\\"") ~s("#{escaped}") end defp tmp_path(prefix) do random_string = Base.encode16(:crypto.strong_rand_bytes(4)) Path.join(System.tmp_dir!(), prefix <> random_string) end defp parse_version!(string) do case String.split(string, "..", trim: true) do [version1, version2] -> parse_two_versions!(version1, version2) [version] -> version |> Version.parse!() |> to_string() end end defp parse_version!(version1, version2) do parse_two_versions!(version1, version2) end defp parse_two_versions!(version1, version2) do version1 = Version.parse!(version1) version2 = Version.parse!(version2) {to_string(version1), to_string(version2)} end defp repo(opts) do repo = Keyword.get(opts, :repo, "hexpm") if organization = opts[:organization] do Enum.join([repo, organization], ":") else repo end end defp find_package_latest_version(organization, name) do %{"latest_stable_version" => latest_stable_version} = retrieve_package_info(organization, name) latest_stable_version end defp retrieve_package_info(organization, name) do case Hex.API.Package.get(organization, name) do {:ok, {code, _, body}} when code in 200..299 -> body {:ok, {404, _, _}} -> Mix.raise("No package with name #{name}") other -> Hex.Shell.error("Failed to retrieve package information") Hex.Utils.print_error_result(other) end end end hex-2.4.2/lib/mix/tasks/hex.publish.ex000066400000000000000000000400771517471540100175770ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Publish do use Mix.Task alias Mix.Tasks.Hex.Build @shortdoc "Publishes a new package version" @moduledoc """ Publishes a new version of the package. $ mix hex.publish The current authenticated user will be the package owner. Only package owners can publish the package, new owners can be added with the `mix hex.owner` task. Packages and documentation sizes are limited to 8mb compressed, and 64mb uncompressed. ## Publishing documentation Documentation will be generated by running the `mix docs` task. `ex_doc` provides this task by default, but any library can be used. Or an alias can be used to extend the documentation generation. The expected result of the task is the generated documentation located in the `doc/` directory with an `index.html` file. The documentation will be accessible at `https://hexdocs.pm/my_package/1.0.0`, `https://hexdocs.pm/my_package` will always redirect to the latest published version. Documentation will be built and published automatically. To publish a package without documentation run `mix hex.publish package` or to only publish documentation run `mix hex.publish docs`. ## Reverting a package A new package can be reverted or updated within 24 hours of its initial publish. A new version of an existing package can be reverted or updated within one hour. Documentation has no limitations on when it can be updated. To update the package simply run the `mix hex.publish` task again. To revert run `mix hex.publish --revert VERSION` or to only revert the documentation run `mix hex.publish docs --revert VERSION`. If the last version is reverted, the package is removed. ## Command line options * `--organization ORGANIZATION` - Set this for private packages belonging to an organization * `--yes` - Publishes the package without any confirmation prompts * `--dry-run` - Builds package and performs local checks without publishing, use `mix hex.build --unpack` to inspect package contents before publishing * `--replace` - Allows overwriting an existing package version if it exists. Private packages can always be overwritten, public packages can only be overwritten within one hour after they were initially published. * `--revert VERSION` - Revert given version. If the last version is reverted, the package is removed. #{Hex.Package.configuration_doc()} """ @behaviour Hex.Mix.TaskDescription @switches [ revert: :string, progress: :boolean, organization: :string, organisation: :string, yes: :boolean, dry_run: :boolean, replace: :boolean ] @impl true def run(args) do Mix.Tasks.Deps.Loadpaths.run(["--no-compile", "--no-listeners"]) Hex.start() {opts, args} = OptionParser.parse!(args, strict: @switches) build = Build.prepare_package() revert_version = opts[:revert] revert = !!revert_version organization = opts[:organization] || build.organization case args do ["package"] when revert -> auth = Mix.Tasks.Hex.auth_info(:write) revert_package(build, organization, revert_version, auth) ["docs"] when revert -> auth = Mix.Tasks.Hex.auth_info(:write) revert_docs(build, organization, revert_version, auth) [] when revert -> auth = Mix.Tasks.Hex.auth_info(:write) revert_package(build, organization, revert_version, auth) ["package"] -> case proceed_with_owner(build, organization, opts) do {:ok, owner} -> auth = Mix.Tasks.Hex.auth_info(:write) Hex.Shell.info("Publishing package...") case create_release(build, organization, auth, opts) do :ok -> transfer_owner(build, owner, auth, opts) _ -> Mix.Tasks.Hex.set_exit_code(1) end :error -> :ok end ["docs"] -> docs_task() auth = Mix.Tasks.Hex.auth_info(:write) create_docs(build, organization, auth, opts) [] -> create(build, organization, opts) _ -> Mix.raise(""" Invalid arguments, expected one of: mix hex.publish mix hex.publish package mix hex.publish docs """) end end @impl true def tasks() do [ {"", "Publishes a new package version"}, {"package", "Publish current package"}, {"docs", "Publish current docs"}, {"package --revert VERSION", "Reverts package on given version"}, {"docs --revert VERSION", "Reverts docs on given version"}, {"--revert VERSION", "Reverts given version"} ] end defp create(build, organization, opts) do case proceed_with_owner(build, organization, opts) do {:ok, owner} -> Hex.Shell.info("Building docs...") docs_task() auth = Mix.Tasks.Hex.auth_info(:write) Hex.Shell.info("Publishing package...") case create_release(build, organization, auth, opts) do :ok -> Hex.Shell.info("Publishing docs...") # Refresh auth to pick up cached OTP from package publish auth = Mix.Tasks.Hex.auth_info(:write) create_docs(build, organization, auth, opts) transfer_owner(build, owner, auth, opts) _ -> Mix.Tasks.Hex.set_exit_code(1) end :error -> :ok end end defp create_docs(build, organization, auth, opts) do directory = docs_dir() name = build.meta.name version = build.meta.version unless File.exists?("#{directory}/index.html") do Mix.raise("File not found: #{directory}/index.html") end progress? = Keyword.get(opts, :progress, true) dry_run? = Keyword.get(opts, :dry_run, false) tarball = build_docs_tarball(directory) if dry_run? do :ok else send_tarball(organization, name, version, tarball, auth, progress?) end end defp docs_task() do # Elixir v1.15 prunes the loadpaths on compilation and # docs will compile code. So we add all original code paths back. path = :code.get_path() try do Mix.Task.run("docs", []) rescue ex in [Mix.NoTaskError] -> Mix.shell().error(""" Publication failed because the "docs" task is unavailable. You may resolve this by: 1. Adding {:ex_doc, ">= 0.0.0", only: :dev, runtime: false} to your dependencies in your mix.exs and trying again 2. If ex_doc was already added, make sure you run "mix hex.publish" in the same environment as the ex_doc package 3. Publishing the package without docs by running "mix hex.publish package" (not recommended) """) reraise ex, __STACKTRACE__ after :code.add_pathsz(path) end end defp proceed_with_owner(build, organization, opts) do meta = build.meta exclude_deps = build.exclude_deps package = build.package Hex.Shell.info("Building #{meta.name} #{meta.version}") Build.print_info(meta, organization, exclude_deps, package[:files]) print_link_to_coc() print_public_private(organization) print_owner_prompt(build, organization, opts) end defp print_public_private(organization) do api_url = Hex.State.fetch!(:api_url) default_api_url? = api_url == Hex.State.default_api_url() using_api = if default_api_url? do "" else " using #{api_url}" end to_repository = cond do !organization and !default_api_url? -> "" public_organization?(organization) -> [" to ", :bright, "public", :reset, " repository hexpm"] true -> [" to ", :bright, "private", :reset, " repository #{organization}"] end Hex.Shell.info( Hex.Shell.format([ "Publishing package", to_repository, using_api, "." ]) ) end defp print_owner_prompt(build, organization, opts) do auth = Mix.Tasks.Hex.auth_info(:read) organizations = user_organizations(auth) owner_prompt? = public_organization?(organization) and not Keyword.get(opts, :yes, false) and organizations != [] and not package_exists?(build) Hex.Shell.info("") if owner_prompt? do do_print_owner_prompt(organizations) else if Keyword.get(opts, :yes, false) or Hex.Shell.yes?("Proceed?") do {:ok, nil} else :error end end end defp do_print_owner_prompt(organizations) do Hex.Shell.info( "You are a member of one or multiple organizations. Would you like to publish " <> "the package with yourself as owner or an organization as owner? " <> "If you publish with an organization as owner your package will " <> "be public but managed by the selected organization." ) Hex.Shell.info("") Hex.Shell.info(" [1] Yourself") numbers = Stream.map(Stream.iterate(2, &(&1 + 1)), &Integer.to_string/1) organizations = Stream.zip(numbers, organizations) Enum.each(organizations, fn {ix, organization} -> Hex.Shell.info(" [#{ix}] #{organization}") end) Hex.Shell.info("") owner_prompt_selection(Map.new(organizations)) end defp owner_prompt_selection(organizations) do selection = String.trim(Hex.Shell.prompt("Your selection:")) if selection == "1" do {:ok, nil} else case Map.fetch(organizations, selection) do {:ok, organization} -> {:ok, organization} :error -> owner_prompt_selection(organizations) end end end defp package_exists?(build) do case Hex.API.Package.get("hexpm", build.meta.name) do {:ok, {200, _headers, _body}} -> true {:ok, {404, _headers, _body}} -> false other -> Hex.Utils.print_error_result(other) true end end defp user_organizations(auth) do case Hex.API.User.me(auth) do {:ok, {200, _header, body}} -> Enum.map(body["organizations"], & &1["name"]) other -> Hex.Utils.print_error_result(other) [] end end defp public_organization?(organization), do: organization in [nil, "hexpm"] defp transfer_owner(_build, nil, _auth, _opts) do :ok end defp transfer_owner(build, owner, auth, opts) do Hex.Shell.info("Transferring ownership to #{owner}...") dry_run? = Keyword.get(opts, :dry_run, false) if dry_run? do :ok else case Hex.API.Package.Owner.add("hexpm", build.meta.name, owner, "full", true, auth) do {:ok, {status, _header, _body}} when status in 200..299 -> :ok other -> Hex.Shell.error("Failed to transfer ownership") Hex.Utils.print_error_result(other) end end end defp print_link_to_coc() do Hex.Shell.info( "Before publishing, please read the Code of Conduct: " <> "https://hex.pm/policies/codeofconduct\n" ) end defp revert_package(build, organization, version, auth) do name = build.meta.name case Hex.API.Release.delete(organization, name, version, auth) do {:ok, {code, _, _}} when code in 200..299 -> Hex.Shell.info("Reverted #{name} #{version}") other -> Hex.Shell.error("Reverting #{name} #{version} failed") Hex.Utils.print_error_result(other) end end defp revert_docs(build, organization, version, auth) do name = build.meta.name case Hex.API.ReleaseDocs.delete(organization, name, version, auth) do {:ok, {code, _, _}} when code in 200..299 -> Hex.Shell.info("Reverted docs for #{name} #{version}") {:ok, {404, _, _}} -> Hex.Shell.info("Docs do not exist") other -> Hex.Shell.error("Reverting docs for #{name} #{version} failed") Hex.Utils.print_error_result(other) end end defp build_docs_tarball(directory) do files = files(directory) raise_if_file_matches_semver(files) {:ok, data} = :mix_hex_tarball.create_docs(files) data end defp raise_if_file_matches_semver(files) do Enum.map(files, fn {filename, _contents} -> filename_matches_semver!(filename) filename -> filename_matches_semver!(filename) end) end defp filename_matches_semver!(filename) do top_level = filename |> Path.split() |> List.first() case Version.parse(to_string(top_level)) do {:ok, _struct} -> Mix.raise("Invalid filename: top-level filenames cannot match a semantic version pattern") _ -> :ok end end defp send_tarball(organization, name, version, tarball, auth, progress?) do progress = progress_fun(progress?, byte_size(tarball)) case Hex.API.ReleaseDocs.publish(organization, name, version, tarball, auth, progress) do {:ok, {code, headers, _body}} when code in 200..299 -> api_url = Hex.State.fetch!(:api_url) default_api_url? = api_url == Hex.State.default_api_url() location = if !default_api_url? && headers["location"] do headers["location"] else Hex.Utils.hexdocs_url(organization, name, version) end Hex.Shell.info("") Hex.Shell.info(["Docs will soon be available at ", location]) :ok {:ok, {404, _, _}} -> Hex.Shell.info("") Hex.Shell.error("Publishing docs failed due to the package not being published yet") :error other -> Hex.Shell.info("") Hex.Shell.error("Publishing docs failed") Hex.Utils.print_error_result(other) :error end end defp files(directory) do "#{directory}/**" |> Path.wildcard() |> Enum.filter(&File.regular?/1) |> Enum.map(&{relative_path(&1, directory), File.read!(&1)}) end defp relative_path(file, dir) do Path.relative_to(file, dir) |> String.to_charlist() end defp docs_dir do docs_output = docs_output_from_config() cond do docs_output -> docs_output File.exists?("doc") -> "doc" File.exists?("docs") -> "docs" true -> Mix.raise( "Documentation could not be found. " <> "Please ensure documentation is in the doc/ or docs/ directory" ) end end defp docs_output_from_config() do case Keyword.get(Mix.Project.config(), :docs) do nil -> nil config when is_list(config) -> config[:output] config when is_function(config, 0) -> config.()[:output] end end defp create_release(build, organization, auth, opts) do meta = build.meta %{tarball: tarball, outer_checksum: checksum} = Hex.Tar.create!(meta, meta.files, :memory) dry_run? = Keyword.get(opts, :dry_run, false) opts = [{:name, Map.fetch!(build.meta, :name)} | opts] if dry_run? do :ok else send_release(tarball, checksum, organization, auth, opts) end end defp send_release(tarball, checksum, organization, auth, opts) do progress? = Keyword.get(opts, :progress, true) progress = progress_fun(progress?, byte_size(tarball)) replace? = Keyword.get(opts, :replace, false) case Hex.API.Release.publish(organization, tarball, auth, progress, replace?) do {:ok, {code, _, body}} when code in 200..299 -> location = body["html_url"] || body["url"] checksum = String.downcase(Base.encode16(checksum, case: :lower)) Hex.Shell.info("") Hex.Shell.info("Package published to #{location} (#{checksum})") :ok {:ok, {403, _, _}} = result -> Hex.Shell.info("") Hex.Shell.error("Publishing failed") package = Keyword.fetch!(opts, :name) case Hex.API.Package.get(organization, package, auth) do {:ok, {code, _, _}} when code in 200..299 -> Hex.Shell.error(""" Package with name #{Keyword.fetch!(opts, :name)} already exists. \ Make sure you are authenticated and have permissions to publish the package.\ """) _ -> Hex.Utils.print_error_result(result) end :error other -> Hex.Shell.info("") Hex.Shell.error("Publishing failed") Hex.Utils.print_error_result(other) :error end end defp progress_fun(true, size), do: Mix.Tasks.Hex.progress(size) defp progress_fun(false, _size), do: Mix.Tasks.Hex.progress(nil) end hex-2.4.2/lib/mix/tasks/hex.registry.ex000066400000000000000000000164061517471540100200000ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Registry do use Mix.Task @behaviour Hex.Mix.TaskDescription @switches [ name: :string, private_key: :string ] @shortdoc "Manages local Hex registries" @moduledoc """ Manages local Hex registries. ## Build a local registry $ mix hex.registry build PUBLIC_DIR To build a registry you need a name, a directory that will be used to store public registry files, and a private key to sign the registry: $ mix hex.registry build public --name=acme --private-key=private_key.pem * creating public/public_key * creating public/tarballs * creating public/names * creating public/versions You can generate a random private key using the following command: $ openssl genrsa -out private_key.pem Let's say you have a package `foo-1.0.0.tar`. To publish it, simply copy it to the appropriate directory and re-build the registry: $ cp foo-1.0.0.tar public/tarballs/ $ mix hex.registry build public --name=acme --private-key=private_key.pem * creating public/packages/foo * updating public/names * updating public/versions You can test the repository by starting the built-in Erlang/OTP HTTP server, adding the repository, and retrieving the package that you just published. $ erl -s inets -eval 'inets:start(httpd,[{port,8000},{server_name,"localhost"},{server_root,"."},{document_root,"public"}]).' # replace "acme" with the name of your repository $ mix hex.repo add acme http://localhost:8000 --public-key=public/public_key $ mix hex.package fetch foo 1.0.0 --repo=acme To use the package in your Mix project, add it as a dependency and set the `:repo` option to your repository name: defp deps() do {:decimal, "~> 2.0", repo: "acme"} end ### Command line options * `--name` - The name of the registry * `--private-key` - Path to the private key """ @impl true def run(args) do Hex.start() {opts, args} = OptionParser.parse!(args, strict: @switches) case args do ["build", public_dir] -> build(public_dir, opts) _ -> Mix.raise(""" Invalid arguments, expected one of: mix hex.registry build PUBLIC_DIR """) end end @impl true def tasks() do [ {"build PUBLIC_DIR", "Build a local registry"} ] end defp build(public_dir, opts) do repo_name = opts[:name] || raise "missing --name" private_key_path = opts[:private_key] || raise "missing --private-key" private_key = private_key_path |> File.read!() |> decode_private_key() build(repo_name, public_dir, private_key) end defp build(repo_name, public_dir, private_key) do ensure_public_key(private_key, public_dir) create_directory(Path.join(public_dir, "tarballs")) paths_per_name = Enum.group_by(Path.wildcard("#{public_dir}/tarballs/*.tar"), fn path -> [name | _rest] = String.split(Path.basename(path), ["-", ".tar"], trim: true) name end) versions = Enum.map(paths_per_name, fn {name, paths} -> releases = paths |> Enum.map(&build_release(repo_name, &1)) |> Enum.sort(&(Version.compare(&1.version, &2.version) == :lt)) updated_at = paths |> Enum.map(&File.stat!(&1).mtime) |> Enum.sort() |> Enum.at(-1) updated_at = updated_at && %{seconds: to_unix(updated_at), nanos: 0} package = :mix_hex_registry.build_package( %{repository: repo_name, name: name, releases: releases}, private_key ) write_file("#{public_dir}/packages/#{name}", package) versions = Enum.map(releases, & &1.version) {name, %{updated_at: updated_at, versions: versions}} end) for path <- Path.wildcard("#{public_dir}/packages/*"), not Enum.member?(Map.keys(paths_per_name), Path.basename(path)) do remove_file(path) end names = for {name, %{updated_at: updated_at}} <- versions do %{name: name, updated_at: updated_at} end payload = %{repository: repo_name, packages: names} names = :mix_hex_registry.build_names(payload, private_key) write_file("#{public_dir}/names", names) versions = for {name, %{versions: versions}} <- versions do %{name: name, versions: versions} end payload = %{repository: repo_name, packages: versions} versions = :mix_hex_registry.build_versions(payload, private_key) write_file("#{public_dir}/versions", versions) end @unix_epoch :calendar.datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}) @doc false def to_unix(erl_datetime) do :calendar.datetime_to_gregorian_seconds(erl_datetime) - @unix_epoch end defp build_release(repo_name, tarball_path) do tarball = File.read!(tarball_path) {:ok, result} = :mix_hex_tarball.unpack(tarball, :memory) dependencies = for {package, map} <- Map.get(result.metadata, "requirements", []) do app = Map.fetch!(map, "app") requirement = Map.fetch!(map, "requirement") optional = map["optional"] == true repository = map["repository"] release = %{ package: package, app: app, optional: optional, requirement: requirement } if !repository or repository == repo_name do release else Map.put(release, :repository, repository) end end %{ version: result.metadata["version"], inner_checksum: result.inner_checksum, outer_checksum: result.outer_checksum, dependencies: dependencies } end defp ensure_public_key(private_key, public_dir) do path = "#{public_dir}/public_key" encoded_public_key = private_key |> extract_public_key() |> encode_public_key() case File.read(path) do {:ok, ^encoded_public_key} -> :ok {:ok, _} -> Hex.Shell.info("* public key at #{path} does not match private key, overwriting") write_file(path, encoded_public_key) {:error, :enoent} -> write_file(path, encoded_public_key) end end defp create_directory(path) do unless File.dir?(path) do Hex.Shell.info(["* creating ", path]) File.mkdir_p!(path) end end defp write_file(path, data) do if File.exists?(path) do Hex.Shell.info(["* updating ", path]) else File.mkdir_p!(Path.dirname(path)) Hex.Shell.info(["* creating ", path]) end File.write!(path, data) end defp remove_file(path) do Hex.Shell.info(["* removing ", path]) File.rm!(path) end ## Key utilities require Record Record.defrecordp( :rsa_private_key, :RSAPrivateKey, Record.extract(:RSAPrivateKey, from_lib: "public_key/include/OTP-PUB-KEY.hrl") ) Record.defrecordp( :rsa_public_key, :RSAPublicKey, Record.extract(:RSAPublicKey, from_lib: "public_key/include/OTP-PUB-KEY.hrl") ) defp extract_public_key(rsa_private_key(modulus: m, publicExponent: e)) do rsa_public_key(modulus: m, publicExponent: e) end defp encode_public_key(key) do :public_key.pem_encode([:public_key.pem_entry_encode(:RSAPublicKey, key)]) end defp decode_private_key(data) do [entry] = :public_key.pem_decode(data) :public_key.pem_entry_decode(entry) end end hex-2.4.2/lib/mix/tasks/hex.repo.ex000066400000000000000000000203351517471540100170710ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Repo do use Mix.Task @shortdoc "Manages Hex repositories" @moduledoc """ Manages the list of available Hex repositories. The repository is where packages and the registry of packages is stored. You can fetch packages from multiple different repositories and packages can depend on packages from other repositories. To use a package from another repository than the global default `hexpm` add `repo: "my_repo"` to the dependency declaration in `mix.exs`: {:plug, "~> 1.0", repo: "my_repo"} By default all dependencies of plug will also be fetched from `my_repo` unless plug has declared otherwise in its dependency definition. To use packages from `my_repo` you need to add it to your configuration first. You do that by calling `mix hex.repo add my_repo https://myrepo.example.com`. The default repo is called `hexpm` and points to https://repo.hex.pm. This can be overridden by using `mix hex.repo set ...`. A repository configured from an organization will have `hexpm:` prefixed to its name. To depend on packages from an organization add `repo: "hexpm:my_organization"` to the dependency declaration or simply `organization: "my_organization"`. To configure organizations, see the `hex.organization` task. ## Add a repo $ mix hex.repo add NAME URL ### Command line options * `--public-key PATH` - Path to public key used to verify the registry (optional). * `--auth-key KEY` - Key used to authenticate HTTP requests to repository (optional). * `--fetch-public-key FINGERPRINT` - Download public key from the repository and verify against the fingerprint (optional). * `--oauth-exchange` - Enable OAuth token exchange for API keys. Exchange the API key for a short-lived OAuth token instead of using the API key directly. Defaults to enabled for hexpm, disabled for other repositories. In the future, this will default to enabled for all repositories (optional). * `--no-oauth-exchange` - Disable OAuth token exchange for API keys. Use the API key directly instead of exchanging it for a short-lived OAuth token. Currently a no-op, but will disable OAuth token exchange in the future (optional). * `--oauth-exchange-url URL` - Custom URL for OAuth token exchange. By default, the API URL is used (optional). ## Set config for repo $ mix hex.repo set NAME --url URL $ mix hex.repo set NAME --public-key PATH $ mix hex.repo set NAME --auth-key KEY $ mix hex.repo set NAME --oauth-exchange $ mix hex.repo set NAME --no-oauth-exchange $ mix hex.repo set NAME --oauth-exchange-url URL ## Remove repo $ mix hex.repo remove NAME ## Show repo config $ mix hex.repo show NAME $ mix hex.repo show NAME --url ## List all repos $ mix hex.repo list """ @behaviour Hex.Mix.TaskDescription @add_switches [ public_key: :string, auth_key: :string, fetch_public_key: :string, oauth_exchange: :boolean, oauth_exchange_url: :string ] @set_switches [ url: :string, public_key: :string, auth_key: :string, oauth_exchange: :boolean, oauth_exchange_url: :string ] @show_switches [ url: :boolean, public_key: :boolean, auth_key: :boolean, oauth_exchange: :boolean, oauth_exchange_url: :boolean ] @impl true def run(all_args) do Hex.start() {_opts, args} = OptionParser.parse!(all_args, switches: []) case args do ["add", name, url] -> {opts, _args} = OptionParser.parse!(all_args, strict: @add_switches) add(name, url, opts) ["set", name] -> {opts, _args} = OptionParser.parse!(all_args, strict: @set_switches) set(name, opts) ["remove", name] -> remove(name) ["show", name] -> {opts, _args} = OptionParser.parse!(all_args, strict: @show_switches) show(name, opts) ["list"] -> list() _ -> invalid_args() end end defp invalid_args() do Mix.raise(""" Invalid arguments, expected one of: mix hex.repo add NAME URL mix hex.repo set NAME mix hex.repo remove NAME mix hex.repo show NAME mix hex.repo list """) end @impl true def tasks() do [ {"add NAME URL", "Add a repo"}, {"set NAME", "Set config for repo"}, {"remove NAME", "Remove repo"}, {"show NAME", "Show repo config"}, {"list", "List all repos"} ] end defp add(name, url, opts) do # Default oauth_exchange to true for hexpm/repo.hex.pm, false for others default_oauth_exchange = name == "hexpm" oauth_exchange = Keyword.get(opts, :oauth_exchange, default_oauth_exchange) public_key = read_public_key(opts[:public_key]) || fetch_public_key( opts[:fetch_public_key], url, opts[:auth_key], oauth_exchange ) repo = %{ url: url, public_key: nil, fetch_public_key: nil, auth_key: nil, oauth_exchange: oauth_exchange, oauth_exchange_url: nil, trusted: true } |> Map.merge(Map.new(opts)) |> Map.put(:public_key, public_key) Hex.State.fetch!(:repos) |> Map.put(name, repo) |> Hex.Config.update_repos() end defp set(name, opts) do opts = if public_key = opts[:public_key] do Keyword.put(opts, :public_key, read_public_key(public_key)) else opts end Hex.State.fetch!(:repos) |> Map.update!(name, &Map.merge(&1, Map.new(opts))) |> Hex.Config.update_repos() end defp remove(name) do Hex.State.fetch!(:repos) |> Map.delete(name) |> Hex.Config.update_repos() end defp list() do header = ["Name", "URL", "Public key", "Auth key"] values = Enum.map(Hex.State.fetch!(:repos), fn {name, config} -> [ name, config[:url], show_public_key(config[:public_key]), config[:auth_key] ] end) Mix.Tasks.Hex.print_table(header, values) end defp read_public_key(nil) do nil end defp read_public_key(path) do key = path |> Path.expand() |> File.read!() decode_public_key(key) key end defp decode_public_key(key) do [pem_entry] = :public_key.pem_decode(key) :public_key.pem_entry_decode(pem_entry) rescue _ -> Mix.raise(""" Could not decode public key. The public key contents are shown below. #{key} Public keys must be valid and be in the PEM format. """) end defp show_public_key(nil), do: nil defp show_public_key(public_key) do [pem_entry] = :public_key.pem_decode(public_key) public_key = :public_key.pem_entry_decode(pem_entry) Hex.Stdlib.ssh_hostkey_fingerprint(:sha256, public_key) |> List.to_string() end defp fetch_public_key(nil, _, _, _), do: nil defp fetch_public_key(fingerprint, repo_url, auth_key, oauth_exchange) do repo_config = %{ url: repo_url, auth_key: auth_key, trusted: true, oauth_exchange: oauth_exchange } case Hex.Repo.get_public_key(repo_config) do {:ok, {200, _, key}} -> if show_public_key(key) == fingerprint do key else Mix.raise("Public key fingerprint mismatch") end {:ok, {code, _, _}} -> Hex.Shell.error("Downloading public key failed with code \"#{inspect(code)}\"") Mix.Tasks.Hex.set_exit_code(1) other -> Hex.Shell.error("Downloading public key failed") Hex.Utils.print_error_result(other) Mix.Tasks.Hex.set_exit_code(1) end end defp show(name, [{key, _} | _]) do case Map.fetch(Hex.State.fetch!(:repos), name) do {:ok, config} -> value = Map.get(config, key, "") value = if is_boolean(value), do: to_string(value), else: value Hex.Shell.info(value) :error -> Mix.raise("Config does not contain repo #{name}") end end defp show(name, []) do case Map.fetch(Hex.State.fetch!(:repos), name) do {:ok, repo} -> header = ["URL", "Public key", "Auth key"] rows = [[repo.url, show_public_key(repo.public_key), repo.auth_key]] Mix.Tasks.Hex.print_table(header, rows) :error -> Mix.raise("Config does not contain repo #{name}") end end end hex-2.4.2/lib/mix/tasks/hex.retire.ex000066400000000000000000000063401517471540100174160ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Retire do use Mix.Task @shortdoc "Retires a package version" @moduledoc """ Retires a package version. $ mix hex.retire PACKAGE VERSION REASON $ mix hex.retire PACKAGE VERSION --unretire Mark a package as retired when you no longer recommend its usage. A retired package is still resolvable and usable but it will be flagged as retired in the repository and a message will be displayed to users when they use the package. ## Retirement reasons * **renamed** - The package has been renamed, including the new package name in the message * **deprecated** - The package has been deprecated, if there's a replacing package include it in the message * **security** - There are security issues with this package * **invalid** - The package is invalid, for example it does not compile correctly * **other** - Any other reason not included above, clarify the reason in the message ## Command line options * `--message "MESSAGE"` - Required message (up to 140 characters) clarifying the retirement reason * `--organization ORGANIZATION` - Set this for private packages belonging to an organization ## Example $ mix hex.retire your_app 0.1.1 invalid --message "Package has a breaking bug" """ @behaviour Hex.Mix.TaskDescription @switches [message: :string, unretire: :boolean, organization: :string] @impl true def run(args) do Hex.start() {opts, args} = OptionParser.parse!(args, strict: @switches) retire? = !opts[:unretire] organization = opts[:organization] case args do [package, version, reason] when retire? -> retire(organization, package, version, reason, opts) [package, version] when not retire? -> unretire(organization, package, version) _ -> Mix.raise(""" Invalid arguments, expected one of: mix hex.retire PACKAGE VERSION REASON mix hex.retire PACKAGE VERSION --unretire """) end end @impl true def tasks() do [ {"PACKAGE VERSION REASON", "Retires a package version"}, {"PACKAGE VERSION --unretire", "Unretires a package"} ] end defp retire(organization, package, version, reason, opts) do auth = Mix.Tasks.Hex.auth_info(:write) body = %{reason: reason, message: message_option(opts[:message])} case Hex.API.Release.retire(organization, package, version, body, auth) do {:ok, {code, _headers, _body}} when code in 200..299 -> Hex.Shell.info("#{package} #{version} has been retired\n") other -> Hex.Shell.error("Retiring package failed") Hex.Utils.print_error_result(other) end end defp unretire(organization, package, version) do auth = Mix.Tasks.Hex.auth_info(:write) case Hex.API.Release.unretire(organization, package, version, auth) do {:ok, {code, _headers, _body}} when code in 200..299 -> Hex.Shell.info("#{package} #{version} has been unretired") :ok other -> Hex.Shell.error("Unretiring package failed") Hex.Utils.print_error_result(other) end end defp message_option(nil) do Mix.raise("Missing required flag --message") end defp message_option(message) do message end end hex-2.4.2/lib/mix/tasks/hex.search.ex000066400000000000000000000110621517471540100173660ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Search do use Mix.Task @shortdoc "Open and perform searches" @moduledoc """ Open and perform searches. When invoked without arguments, it opens up a search page on https://hexdocs.pm with all of your dependencies plus Elixir standard library selected: $ mix hex.search $ mix hex.search --no-stdlib You may also pass command line flags, to execute searches via the command line, according to the modes below. ## Package search Specify `--package PACKAGE` to search for a given package. $ mix hex.search --package PACKAGE If you are authenticated, it will additionally search all organizations you are member of. ### Options * `--organization ORGANIZATION` - Set this for private packages belonging to an organization * `--print-url` - Print the docs URL instead of opening it in the browser """ @behaviour Hex.Mix.TaskDescription @switches [organization: :string, package: :string, stdlib: :boolean, print_url: :boolean] @impl true def run(args) do {opts, args} = OptionParser.parse!(args, strict: @switches) case args do [] -> cond do package = opts[:package] -> package_search(package, opts[:organization]) true -> hexdocs_search(opts) end [package] -> Mix.shell().error("mix hex.search PACKAGE is deprecated, use --package PACKAGE instead") package_search(package, opts[:organization]) _ -> Mix.raise(""" Invalid arguments, expected: mix hex.search mix hex.search --package PACKAGE """) end end @impl true def tasks() do [ {"", "Opens up hexdocs.pm with your dependencies"}, {"--package PACKAGE", "Searches for package names"} ] end defp hexdocs_search(opts) do Mix.Tasks.Deps.Loadpaths.run(["--no-compile", "--no-listeners"]) Hex.start() deps = for {_app, info} <- Mix.Dep.Lock.read(), %{repo: "hexpm", name: name, version: version} <- [Hex.Utils.lock(info)] do "#{name}:#{version}" end stdlib = if Keyword.get(opts, :stdlib, true) do [:elixir, :iex, :logger, :ex_unit, :eex, :mix] |> Enum.map(&"#{&1}:#{System.version()}") else [] end packages = (deps ++ stdlib) |> Enum.sort() |> Enum.join(",") |> URI.encode_www_form() url = "https://hexdocs.pm/?packages=#{packages}&q=" if opts[:print_url] do Hex.Shell.info(url) else Hex.Utils.system_open(url) end end defp package_search(package, organization) do Hex.start() auth = Mix.Tasks.Hex.auth_info(:read, auth_inline: false) Hex.API.Package.search(organization, package, auth) |> lookup_packages() end defp lookup_packages({:ok, {200, _headers, []}}) do Hex.Shell.info("No packages found") end defp lookup_packages({:ok, {200, _headers, packages}}) do include_organizations? = Enum.any?(packages, &(&1["repository"] != "hexpm")) if include_organizations? do print_with_organizations(packages) else print_without_organizations(packages) end end defp lookup_packages({:ok, {_status, _headers, _body}}) do Hex.Shell.info("No packages found") end defp print_with_organizations(packages) do values = Enum.map(packages, fn package -> [ if(package["repository"] != "hexpm", do: package["repository"]), package["name"], package["meta"]["description"] |> trim_heredoc() |> Hex.Utils.truncate(), latest_stable(package["releases"]), package["html_url"] || package["url"] ] end) Mix.Tasks.Hex.print_table( ["Organization", "Package", "Description", "Version", "URL"], values ) end defp print_without_organizations(packages) do values = Enum.map(packages, fn package -> [ package["name"], package["meta"]["description"] |> trim_heredoc() |> Hex.Utils.truncate(), latest_stable(package["releases"]), package["html_url"] || package["url"] ] end) Mix.Tasks.Hex.print_table( ["Package", "Description", "Version", "URL"], values ) end defp latest_stable(releases) do %{"version" => version} = Enum.find( releases, %{"version" => nil}, &(Version.parse!(&1["version"]).pre == []) ) version end defp trim_heredoc(nil), do: "" defp trim_heredoc(string) do string |> String.split("\n", trim: true) |> Enum.map_join(" ", &String.trim/1) end end hex-2.4.2/lib/mix/tasks/hex.sponsor.ex000066400000000000000000000031771517471540100176340ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Sponsor do use Mix.Task @shortdoc "Show Hex packages accepting sponsorships" @moduledoc """ Show Hex packages in your dependencies that accept sponsorships. $ mix hex.sponsor Sponsorship plays an important role to maintain some open source projects. This task will display all packages that are accepting sponsorship from the current project. You can add sponsorship links to your projects by adding the following to your mix.exs: links: %{ "GitHub" => "[your-repo-link]", "Sponsor" => "[your-sponsorship-link]" } """ @behaviour Hex.Mix.TaskDescription @impl true def run(_) do unless Mix.Project.get() do raise Mix.raise( "The sponsor task only works inside a Mix project. " <> "Please ensure you are in a directory with a mix.exs file." ) end Mix.Tasks.Deps.Loadpaths.run(["--no-compile", "--no-listeners"]) sponsor_links = Mix.Dep.Lock.read() |> Hex.Mix.packages_from_lock() |> sponsor_links(Mix.Project.deps_path()) case sponsor_links do [] -> Hex.Shell.info("No dependencies with sponsorship link found.") deps_links -> header = ["Dependency", "Sponsorship"] Mix.Tasks.Hex.print_table(header, deps_links) end end defp sponsor_links(packages, deps_path) do Enum.flat_map(packages, fn {_repo, package_name} -> case Hex.Sponsor.get_link(package_name, deps_path) do nil -> [] value -> [[package_name, value]] end end) end @impl true def tasks() do [ {"", @shortdoc} ] end end hex-2.4.2/lib/mix/tasks/hex.user.ex000066400000000000000000000040331517471540100170770ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.User do use Mix.Task @shortdoc "Manages your Hex user account" @moduledoc """ Hex user tasks. ## Print the current user $ mix hex.user whoami ## Authorize a new user Authorizes a new user on the local machine. $ mix hex.user auth ## Deauthorize the user Deauthorizes the user from the local machine. $ mix hex.user deauth """ @behaviour Hex.Mix.TaskDescription @switches [] @impl true def run(args) do Hex.start() {_opts, args} = OptionParser.parse!(args, strict: @switches) case args do ["whoami"] -> whoami() ["auth"] -> auth() ["deauth"] -> deauth() _ -> invalid_args() end end @impl true def tasks() do [ {"whoami", "Prints the current user"}, {"auth", "Authorize using OAuth device flow"}, {"deauth", "Deauthorize the user"} ] end defp invalid_args() do Mix.raise(""" Invalid arguments, expected one of: mix hex.user whoami mix hex.user auth mix hex.user deauth """) end defp whoami() do auth = Mix.Tasks.Hex.auth_info(:read) case Hex.API.User.me(auth) do {:ok, {code, _, body}} when code in 200..299 -> Hex.Shell.info(body["username"]) other -> Hex.Shell.error("Failed to auth") Hex.Utils.print_error_result(other) end end defp auth() do Mix.Tasks.Hex.auth() end defp deauth() do # Revoke and clear OAuth tokens Mix.Tasks.Hex.revoke_existing_oauth_tokens() Hex.OAuth.clear_tokens() # Revoke and cleanup old API keys Mix.Tasks.Hex.revoke_and_cleanup_old_api_keys() deauth_organizations() Hex.Shell.info( "Authentication credentials removed from the local machine. " <> "To authenticate again, run `mix hex.user auth`" ) end defp deauth_organizations() do Hex.State.fetch!(:repos) |> Enum.reject(fn {name, _config} -> String.starts_with?(name, "hexpm:") end) |> Map.new() |> Hex.Config.update_repos() end end hex-2.4.2/mix.exs000066400000000000000000000074371517471540100136410ustar00rootroot00000000000000defmodule Hex.MixProject do use Mix.Project @version "2.4.2" def project do [ app: :hex, version: @version, elixir: "~> 1.12", aliases: aliases(), # TODO: Remove when we only support Elixir 1.15+ preferred_cli_env: ["deps.get": :test], config_path: "config/config.exs", compilers: [:leex] ++ Mix.compilers(), deps: deps(Mix.env()), elixirc_options: elixirc_options(Mix.env()), elixirc_paths: elixirc_paths(Mix.env()), test_ignore_filters: [ ~r"^test/fixtures/", "test/setup_hexpm.exs" ] ] end def cli do [preferred_envs: ["deps.get": :test]] end def application do [ extra_applications: [:ssl, :inets, :logger], mod: {Hex.Application, []} ] end defp deps(:test) do [ {:bypass, "~> 2.0"}, {:cowboy, "~> 2.14"}, {:mime, "~> 1.0"}, {:mox, "~> 1.0"}, {:plug, "~> 1.18"}, {:plug_cowboy, "~> 2.7"} ] end defp deps(_) do [] end defp elixirc_options(:prod), do: [debug_info: false] defp elixirc_options(_), do: [] defp elixirc_paths(:test), do: ["lib", "test/support"] defp elixirc_paths(_), do: ["lib"] defp aliases do [ "compile.elixir": [&unload_hex/1, "compile.elixir"], run: [&unload_hex/1, "run"], install: ["archive.build -o hex.ez", "archive.install hex.ez --force"], certdata: [&certdata/1] ] end defp unload_hex(_) do update_cached_deps(__MODULE__) Application.stop(:hex) Application.unload(:hex) paths = Path.wildcard(Path.join(archives_path(), "hex*")) Enum.each(paths, fn archive -> ebin = archive_ebin(archive) Code.delete_path(ebin) {:ok, files} = ebin |> :unicode.characters_to_list() |> :erl_prim_loader.list_dir() Enum.each(files, fn file -> file = List.to_string(file) if String.ends_with?(file, ".beam") do name = String.trim_trailing(file, ".beam") module = String.to_atom(name) :code.delete(module) :code.purge(module) end end) end) end defp update_cached_deps(module) do case Mix.State.read_cache({:cached_deps, module}) do nil -> :ok {env_target, cached_deps} -> cached_deps = Enum.map(cached_deps, &change_scm/1) Mix.State.write_cache({:cached_deps, module}, {env_target, cached_deps}) end end defp change_scm(%Mix.Dep{deps: deps} = dep) do %Mix.Dep{dep | scm: Hex.FakeSCM, deps: Enum.map(deps, &change_scm/1)} end @mk_ca_bundle_url "https://raw.githubusercontent.com/bagder/curl/master/scripts/mk-ca-bundle.pl" @mk_ca_bundle_cmd "mk-ca-bundle.pl" @ca_bundle "ca-bundle.crt" @ca_bundle_target Path.join("lib/hex/http", @ca_bundle) defp certdata(_) do cmd("wget", [@mk_ca_bundle_url]) File.chmod!(@mk_ca_bundle_cmd, 0o755) cmd(Path.expand(@mk_ca_bundle_cmd), ["-u"]) File.cp!(@ca_bundle, @ca_bundle_target) File.rm!(@ca_bundle) File.rm!(@mk_ca_bundle_cmd) end defp cmd(cmd, args) do {_, result} = System.cmd(cmd, args, into: IO.stream(:stdio, :line), stderr_to_stdout: true) if result != 0 do raise "Non-zero result (#{result}) from: #{cmd} #{Enum.map_join(args, " ", &inspect/1)}" end end cond do function_exported?(Mix, :path_for, 1) -> defp archives_path(), do: Mix.path_for(:archives) function_exported?(Mix.Local, :path_for, 1) -> defp archives_path(), do: Mix.Local.path_for(:archive) true -> defp archives_path(), do: Mix.Local.archives_path() end if function_exported?(Mix.Local, :archive_ebin, 1) do defp archive_ebin(archive), do: Mix.Local.archive_ebin(archive) else defp archive_ebin(archive), do: Mix.Archive.ebin(archive) end end defmodule Hex.FakeSCM do def fetchable?, do: true end hex-2.4.2/mix.lock000066400000000000000000000066761517471540100137760ustar00rootroot00000000000000%{ "bypass": {:hex, :bypass, "2.1.0", "909782781bf8e20ee86a9cabde36b259d44af8b9f38756173e8f5e2e1fabb9b1", [:mix], [{:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "d9b5df8fa5b7a6efa08384e9bbecfe4ce61c77d28a4282f79e02f1ef78d96b80"}, "cowboy": {:hex, :cowboy, "2.14.0", "565dcf221ba99b1255b0adcec24d2d8dbe79e46ec79f30f8373cceadc6a41e2a", [:make, :rebar3], [{:cowlib, ">= 2.16.0 and < 3.0.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, ">= 1.8.0 and < 3.0.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "ea99769574550fe8a83225c752e8a62780a586770ef408816b82b6fe6d46476b"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowlib": {:hex, :cowlib, "2.16.0", "54592074ebbbb92ee4746c8a8846e5605052f29309d3a873468d76cdf932076f", [:make, :rebar3], [], "hexpm", "7f478d80d66b747344f0ea7708c187645cfcc08b11aa424632f78e25bf05db51"}, "mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"}, "mox": {:hex, :mox, "1.2.0", "a2cd96b4b80a3883e3100a221e8adc1b98e4c3a332a8fc434c39526babafd5b3", [:mix], [{:nimble_ownership, "~> 1.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}], "hexpm", "c7b92b3cc69ee24a7eeeaf944cd7be22013c52fcb580c1f33f50845ec821089a"}, "nimble_ownership": {:hex, :nimble_ownership, "1.0.1", "f69fae0cdd451b1614364013544e66e4f5d25f36a2056a9698b793305c5aa3a6", [:mix], [], "hexpm", "3825e461025464f519f3f3e4a1f9b68c47dc151369611629ad08b636b73bb22d"}, "plug": {:hex, :plug, "1.18.1", "5067f26f7745b7e31bc3368bc1a2b818b9779faa959b49c934c17730efc911cf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "57a57db70df2b422b564437d2d33cf8d33cd16339c1edb190cd11b1a3a546cc2"}, "plug_cowboy": {:hex, :plug_cowboy, "2.7.4", "729c752d17cf364e2b8da5bdb34fb5804f56251e88bb602aff48ae0bd8673d11", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "9b85632bd7012615bae0a5d70084deb1b25d2bcbb32cab82d1e9a1e023168aa3"}, "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, "ranch": {:hex, :ranch, "1.8.1", "208169e65292ac5d333d6cdbad49388c1ae198136e4697ae2f474697140f201c", [:make, :rebar3], [], "hexpm", "aed58910f4e21deea992a67bf51632b6d60114895eb03bb392bb733064594dd0"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, } hex-2.4.2/scripts/000077500000000000000000000000001517471540100137775ustar00rootroot00000000000000hex-2.4.2/scripts/release_hex.sh000077500000000000000000000121171517471540100166240ustar00rootroot00000000000000#!/usr/bin/env bash # Usage: # # $ ELIXIR_PEM=/path/to/elixir.pem \ # HEX_FASTLY_KEY=... \ # HEX_FASTLY_BUILDS_SERVICE_ID=... \ # release_hex.sh HEX_VERSION # # Pass --dry-run to build archives without uploading: # # $ release_hex.sh --dry-run HEX_VERSION # # After running, can be locally tested: # # $ (cd tmp && erl -S httpd) # $ HEX_BUILDS_URL=http://localhost:8000 mix local.hex --force set -e -u -o pipefail function main { dry_run=false local args=() for arg in "$@"; do if [ "$arg" = "--dry-run" ]; then dry_run=true else args+=("$arg") fi done hex_version=${args[0]} installs_dir="$PWD/tmp/installs" hex_csv="${installs_dir}/hex.csv" hex_1x_csv="${installs_dir}/hex-1.x.csv" rm -rf "${installs_dir}" mkdir "${installs_dir}" s3down hex.csv "${hex_csv}" touch "${hex_csv}" sed -i.bak "/^${hex_version},/d" "${hex_csv}" s3down hex-1.x.csv "${hex_1x_csv}" || true touch "${hex_1x_csv}" sed -i.bak "/^${hex_version},/d" "${hex_1x_csv}" # UPDATE THIS FOR EVERY RELEASE, ORDER MATTERS # Elixir v1.12-v1.16 (old CSV format) build_old ${hex_version} 22.3 1.12.3 1.12.0 xenial-20200212 build_old ${hex_version} 22.3 1.13.4 1.13.0 xenial-20200212 build_old ${hex_version} 23.3 1.14.2 1.14.0 xenial-20210114 build_old ${hex_version} 24.3.4.17 1.15.8 1.15.0 focal-20240427 build_old ${hex_version} 24.3.4.17 1.16.3 1.16.0 focal-20240427 # Elixir v1.17 build ${hex_version} 25.3.2.20 1.17.3 1.17.0 noble-20250404 build ${hex_version} 26.2.5.11 1.17.3 1.17.0 noble-20250404 build ${hex_version} 27.3.3 1.17.3 1.17.0 noble-20250404 # Elixir v1.18 build ${hex_version} 25.3.2.18 1.18.0 1.18.0 noble-20260210.1 # need to use exactly 1.18.0 build ${hex_version} 26.2.5.18 1.18.0 1.18.0 noble-20260210.1 # ditto build ${hex_version} 27.3.4.9 1.18.0 1.18.0 noble-20260210.1 # ditto # Elixir v1.19 build ${hex_version} 26.2.5.18 1.19.5 1.19.0 noble-20260210.1 build ${hex_version} 27.3.4.9 1.19.5 1.19.0 noble-20260210.1 build ${hex_version} 28.4.1 1.19.5 1.19.0 noble-20260210.1 rm -rf _build rm "${hex_csv}.bak" rm "${hex_1x_csv}.bak" if [ "${dry_run}" = true ]; then echo "Dry run complete, archives in ${installs_dir}" elif [ -n "${ELIXIR_PEM}" ]; then openssl dgst -sha512 -sign "${ELIXIR_PEM}" "${hex_csv}" | openssl base64 > "${hex_csv}.signed" openssl dgst -sha512 -sign "${ELIXIR_PEM}" "${hex_1x_csv}" | openssl base64 > "${hex_1x_csv}.signed" cd $installs_dir for path in $(find . -type f | sort); do path="${path#./}" echo "uploading ${path}..." s3up "${path}" "${path}" done purge_key "${HEX_FASTLY_BUILDS_SERVICE_ID}" "installs" sleep 5 purge_key "${HEX_FASTLY_BUILDS_SERVICE_ID}" "installs" else echo "ELIXIR_PEM is empty, skipping" exit 1 fi } # $1 = hex version # $2 = erlang version # $3 = elixir version # $4 = saved elixir version # $5 = ubuntu version function build_old { hex_version=$1 otp_version=$2 elixir_version=$3 saved_elixir_version=$4 ubuntu_version=$5 echo "Building ${elixir_version} ${otp_version} ${ubuntu_version} (old format)" rm -rf _build src/mix_safe_erl_term.erl hex_ez=hex-${hex_version}.ez mkdir -p "$installs_dir/${saved_elixir_version}" docker run -v $(pwd):/hex hexpm/elixir:${elixir_version}-erlang-${otp_version}-ubuntu-${ubuntu_version} sh -c " \ cd /hex && \ MIX_ENV=prod mix archive.build -o ${hex_ez}" mv "${hex_ez}" "${installs_dir}/${saved_elixir_version}/${hex_ez}" sha=$(shasum -a 512 "${installs_dir}/${saved_elixir_version}/${hex_ez}") sha=($sha) echo "${hex_version},${sha},${saved_elixir_version}" >> "${hex_1x_csv}" } # $1 = hex version # $2 = erlang version # $3 = elixir version # $4 = saved elixir version # $5 = ubuntu version function build { hex_version=$1 otp_version=$2 otp_release=${otp_version%%.*} elixir_version=$3 saved_elixir_version=$4 ubuntu_version=$5 echo "Building ${elixir_version} ${otp_version} ${ubuntu_version}" rm -rf _build src/mix_safe_erl_term.erl hex_ez=hex-${hex_version}-otp-${otp_release}.ez mkdir -p "$installs_dir/${saved_elixir_version}" docker run -v $(pwd):/hex hexpm/elixir:${elixir_version}-erlang-${otp_version}-ubuntu-${ubuntu_version} sh -c " \ cd /hex && \ MIX_ENV=prod mix archive.build -o ${hex_ez}" mv "${hex_ez}" "${installs_dir}/${saved_elixir_version}/${hex_ez}" sha=$(shasum -a 512 "${installs_dir}/${saved_elixir_version}/${hex_ez}") sha=($sha) echo "${hex_version},${sha},${saved_elixir_version},${otp_release}" >> "${hex_csv}" } # $1 = source # $2 = target function s3up { aws s3 cp "${1}" "s3://s3.hex.pm/installs/${2}" --acl public-read --cache-control "public, max-age=604800" --metadata "surrogate-key=installs" } # $1 = source # $2 = target function s3down { aws s3 cp "s3://s3.hex.pm/installs/${1}" "${2}" } # $1 = service # $2 = key function purge_key() { curl \ --fail \ -X POST \ -H "Fastly-Key: ${HEX_FASTLY_KEY}" \ -H "Accept: application/json" \ -H "Content-Length: 0" \ "https://api.fastly.com/service/$1/purge/$2" } main $* hex-2.4.2/scripts/release_rebar.sh000077500000000000000000000103121517471540100171260ustar00rootroot00000000000000#!/usr/bin/env bash # Usage: # # $ ELIXIR_PEM=/path/to/elixir.pem \ # HEX_FASTLY_KEY=... \ # HEX_FASTLY_BUILDS_SERVICE_ID=... \ # release_rebar.sh # Unless HEX_FASTLY_KEY is set, nothing is uploaded. After running, can be locally tested: # # $ (cd tmp && erl -S httpd) # $ HEX_BUILDS_URL=http://localhost:8000 mix local.rebar --force set -e -u -o pipefail function main { installs_dir="$PWD/tmp/installs" rebar_csv="${installs_dir}/rebar.csv" rebar_1x_csv="${installs_dir}/rebar3-1.x.csv" rm -rf "${installs_dir}" mkdir "${installs_dir}" touch "${rebar_csv}" s3down rebar3-1.x.csv "${rebar_1x_csv}" || true touch "${rebar_1x_csv}" # UPDATE THIS FOR EVERY RELEASE # Old Elixir versions (old CSV format) build_old 3.13.3 17.5.6.10 1.0.0 xenial-20200326 build_old 3.15.2 21.3.8.21 1.11.4 xenial-20201014 build_old 3.15.2 22.3.4.22 1.13.0 xenial-20210114 build_old 3.22.0 23.3.4.18 1.14.5 xenial-20210804 build_old 3.22.0 24.3.4.11 1.15.0-rc.0 xenial-20210804 # New Elixir versions (new CSV format) build 3.22.0 25.3.2.20 1.18.3 noble-20250404 build 3.22.0 26.2.5.11 1.18.3 noble-20250404 build 3.24.0 25.3.2.20 1.18.3 noble-20250404 build 3.24.0 26.2.5.11 1.18.3 noble-20250404 build 3.24.0 27.3.3 1.18.3 noble-20250404 build 3.25.1 28.0.1 1.18.4 noble-20250714 rm -f "${rebar_1x_csv}.bak" openssl dgst -sha512 -sign "${ELIXIR_PEM}" "${rebar_csv}" | openssl base64 > "${rebar_csv}.signed" openssl dgst -sha512 -sign "${ELIXIR_PEM}" "${rebar_1x_csv}" | openssl base64 > "${rebar_1x_csv}.signed" cd $installs_dir for path in $(find . -type f | sort); do path="${path#./}" if [ -n "${HEX_FASTLY_KEY-}" ]; then echo "uploading ${path}..." s3up "${path}" "${path}" else echo "[skip] uploading ${path}..." fi done if [ -n "${HEX_FASTLY_KEY-}" ]; then purge_key "${HEX_FASTLY_BUILDS_SERVICE_ID}" "installs" fi } # $1 = rebar version # $2 = erlang version # $3 = elixir version # $4 = ubuntu version function build_old { rebar_version=$1 otp_version=$2 elixir_version=$3 ubuntu_version=$4 echo "Building ${rebar_version} ${otp_version} ${ubuntu_version} (old format)" mkdir -p "${installs_dir}/${elixir_version}" docker run -v "${installs_dir}/${elixir_version}":/installs hexpm/erlang:${otp_version}-ubuntu-${ubuntu_version} sh -c "\ apt update && apt -y install git && \ git clone https://github.com/erlang/rebar3.git -b ${rebar_version} && \ cd rebar3 && \ ./bootstrap && \ cp rebar3 /installs/rebar3 && \ cp rebar3 /installs/rebar3-${rebar_version} " sed -i.bak "/,${elixir_version}\$/d" "${rebar_1x_csv}" sha=$(shasum -a 512 "${installs_dir}/${elixir_version}/rebar3") sha=($sha) echo "${rebar_version},${sha},${elixir_version}" >> "${rebar_1x_csv}" } # $1 = rebar version # $2 = erlang version # $3 = elixir version # $4 = ubuntu version function build { rebar_version=$1 otp_version=$2 otp_release=${otp_version%%.*} elixir_version=$3 ubuntu_version=$4 echo "Building ${rebar_version} ${otp_version} ${ubuntu_version}" mkdir -p "${installs_dir}/${elixir_version}" rebar="rebar3-${rebar_version}-otp-${otp_release}" docker run -v "${installs_dir}/${elixir_version}":/installs hexpm/erlang:${otp_version}-ubuntu-${ubuntu_version} sh -c "\ apt update && apt -y install git && \ git clone https://github.com/erlang/rebar3.git -b ${rebar_version} && \ cd rebar3 && \ ./bootstrap && \ cp rebar3 /installs/$rebar " sha=$(shasum -a 512 "${installs_dir}/${elixir_version}/$rebar") sha=($sha) echo "${rebar_version},${sha},${elixir_version},${otp_release}" >> "${rebar_csv}" } # $1 = source # $2 = target function s3up { aws s3 cp "${1}" "s3://s3.hex.pm/installs/${2}" --acl public-read --cache-control "public, max-age=604800" --metadata "surrogate-key=installs" } # $1 = source # $2 = target function s3down { aws s3 cp "s3://s3.hex.pm/installs/${1}" "${2}" } # $1 = service # $2 = key function purge_key() { curl \ --fail \ -X POST \ -H "Fastly-Key: ${HEX_FASTLY_KEY}" \ -H "Accept: application/json" \ -H "Content-Length: 0" \ "https://api.fastly.com/service/$1/purge/$2" } main $* hex-2.4.2/scripts/vendor_hex_core.sh000077500000000000000000000041211517471540100175050ustar00rootroot00000000000000#!/bin/bash set -e if [[ -z "$1" ]]; then echo "Usage: vendor_hex_core.sh PATH_TO_HEX_CORE" exit 1 fi source_dir=$1/src target_dir=src prefix=mix_ version=`cat $source_dir/hex_core.hrl | grep HEX_CORE_VERSION | cut -d'"' -f2` shortref=`cd $source_dir && git rev-parse --short HEAD` filenames="hex_api_auth.erl \ hex_api_key.erl \ hex_api_oauth.erl \ hex_api_organization_member.erl \ hex_api_organization.erl \ hex_api_package_owner.erl \ hex_api_package.erl \ hex_api_release.erl \ hex_api_short_url.erl \ hex_api_user.erl \ hex_api.erl \ hex_core.hrl \ hex_core.erl \ hex_erl_tar.erl \ hex_erl_tar.hrl \ hex_http.erl \ hex_http_httpc.erl \ hex_licenses.erl \ hex_pb_names.erl \ hex_pb_package.erl \ hex_pb_signed.erl \ hex_pb_versions.erl \ hex_registry.erl \ hex_repo.erl \ hex_safe_binary_to_term.erl \ hex_tarball.erl \ safe_erl_term.xrl" search_to_replace="hex_core: \ hex_core) \ hex_core.hrl \ hex_erl_tar \ hex_filename \ hex_licenses \ hex_pb_names \ hex_pb_package \ hex_pb_signed \ hex_pb_versions \ hex_registry \ hex_tarball \ hex_safe_binary_to_term \ hex_http \ hex_repo \ hex_api \ safe_erl_term" rm -f $target_dir/$prefix* for filename in $filenames; do source_path=$source_dir/$filename target_path=$target_dir/$prefix$filename echo "%% Vendored from hex_core v$version ($shortref), do not edit manually" > $target_path echo >> $target_path cat $source_path >> $target_path for word in $search_to_replace; do sed -i.bak s/$word/$prefix$word/g $target_path rm $target_path.bak done done hex-2.4.2/scripts/vendor_hex_solver.sh000077500000000000000000000015761517471540100201020ustar00rootroot00000000000000#!/bin/bash set -e if [[ -z "$1" ]]; then echo "Usage: vendor_hex_solver.sh PATH_TO_HEX_SOLVER" exit 1 fi dir=$1 pushd $dir mix compile version=$(mix run -e 'IO.puts(Mix.Project.config[:version])') shortref=$(git rev-parse --short HEAD) popd rm -f lib/hex/solver.ex rm -rf lib/hex/solver skip_filenames="hex_solver/dev.ex" for filename in $(find $dir/lib -type f); do target_filename=${filename#$dir/lib/} target_path=lib/hex/${target_filename/hex_solver/solver} if [[ $skip_filenames == *$target_filename* ]]; then continue fi mkdir -p $(dirname $target_path) echo "# Vendored from hex_solver v$version ($shortref), do not edit manually" > $target_path echo >> $target_path cat $filename >> $target_path sed -i.bak 's/@moduledoc """/_ = """/g' $target_path rm $target_path.bak sed -i.bak s/HexSolver/Hex.Solver/g $target_path rm $target_path.bak done hex-2.4.2/src/000077500000000000000000000000001517471540100130775ustar00rootroot00000000000000hex-2.4.2/src/mix_hex_api.erl000066400000000000000000000133541517471540100161030ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @doc %% Hex HTTP API -module(mix_hex_api). -export([ delete/2, get/2, post/3, put/3, encode_query_string/1, build_repository_path/2, build_organization_path/2, join_path_segments/1 ]). -define(ERL_CONTENT_TYPE, <<"application/vnd.hex+erlang">>). -export_type([response/0]). -type response() :: {ok, {mix_hex_http:status(), mix_hex_http:headers(), body() | nil}} | {error, term()}. -type body() :: [body()] | #{binary() => body() | binary()}. %% @private get(Config, Path) -> request(Config, get, Path, undefined). %% @private post(Config, Path, Body) -> request(Config, post, Path, encode_body(Body)). %% @private put(Config, Path, Body) -> request(Config, put, Path, encode_body(Body)). %% @private delete(Config, Path) -> request(Config, delete, Path, undefined). %% @private encode_query_string(List) -> Pairs = lists:map(fun({K, V}) -> {to_list(K), to_list(V)} end, List), list_to_binary(compose_query(Pairs)). %% OTP 21+ %% @private -ifdef(OTP_RELEASE). compose_query(Pairs) -> uri_string:compose_query(Pairs). -else. compose_query(Pairs) -> String = join("&", lists:map(fun({K, V}) -> K ++ "=" ++ V end, Pairs)), http_uri:encode(String). -endif. %% @private build_repository_path(#{api_repository := Repo}, Path) when is_binary(Repo) -> ["repos", Repo | Path]; build_repository_path(#{api_repository := undefined}, Path) -> Path. %% @private build_organization_path(#{api_organization := Org}, Path) when is_binary(Org) -> ["orgs", Org | Path]; build_organization_path(#{api_organization := undefined}, Path) -> Path. %% @private join_path_segments(Segments) -> iolist_to_binary(recompose(Segments)). %% OTP 21+ %% @private -ifdef(OTP_RELEASE). recompose(Segments) -> Concatenated = join(<<"/">>, Segments), %% uri_string:recompose/1 accepts path segments as a list, %% both strings and binaries uri_string:recompose(#{path => Concatenated}). -else. recompose(Segments) -> join(<<"/">>, lists:map(fun encode_segment/1, Segments)). %% @private encode_segment(Binary) when is_binary(Binary) -> encode_segment(binary_to_list(Binary)); encode_segment(String) when is_list(String) -> http_uri:encode(String). -endif. %%==================================================================== %% Internal functions %%==================================================================== request(Config, Method, PathSegments, Body) when is_list(PathSegments) -> Path = join_path_segments(PathSegments), request(Config, Method, Path, Body); request(Config, Method, Path, Body) when is_binary(Path) and is_map(Config) -> DefaultHeaders = make_headers(Config), ReqHeaders = maps:merge(maps:get(http_headers, Config, #{}), DefaultHeaders), ReqHeaders2 = put_new(<<"accept">>, ?ERL_CONTENT_TYPE, ReqHeaders), case mix_hex_http:request(Config, Method, build_url(Path, Config), ReqHeaders2, Body) of {ok, {Status, RespHeaders, RespBody}} -> ContentType = maps:get(<<"content-type">>, RespHeaders, <<"">>), Response = case binary:match(ContentType, ?ERL_CONTENT_TYPE) of {_, _} -> case mix_hex_safe_binary_to_term:safe_binary_to_term(RespBody) of {ok, Term} -> {ok, {Status, RespHeaders, Term}}; {error, Reason} -> {error, Reason} end; nomatch -> {ok, {Status, RespHeaders, nil}} end, detect_otp_error(Response); Other -> Other end. %% TODO: not needed after exdoc is fixed %% @private build_url(Path, #{api_url := URI}) -> <>. %% TODO: not needed after exdoc is fixed %% @private encode_body({_ContentType, _Body} = Body) -> Body; encode_body(Body) -> {binary_to_list(?ERL_CONTENT_TYPE), term_to_binary(Body)}. %% TODO: not needed after exdoc is fixed %% @private %% TODO: copy-pasted from mix_hex_repo make_headers(Config) -> maps:fold(fun set_header/3, #{}, Config). %% TODO: not needed after exdoc is fixed %% @private set_header(api_key, Token, Headers) when is_binary(Token) -> maps:put(<<"authorization">>, Token, Headers); set_header(api_otp, OTP, Headers) when is_binary(OTP) -> maps:put(<<"x-hex-otp">>, OTP, Headers); set_header(_, _, Headers) -> Headers. %% TODO: not needed after exdoc is fixed %% @private put_new(Key, Value, Map) -> case maps:find(Key, Map) of {ok, _} -> Map; error -> maps:put(Key, Value, Map) end. %% TODO: not needed after exdoc is fixed %% @private %% https://github.com/erlang/otp/blob/OTP-20.3/lib/stdlib/src/lists.erl#L1449:L1453 join(_Sep, []) -> []; join(Sep, [H | T]) -> [H | join_prepend(Sep, T)]. %% TODO: not needed after exdoc is fixed %% @private join_prepend(_Sep, []) -> []; join_prepend(Sep, [H | T]) -> [Sep, H | join_prepend(Sep, T)]. %% TODO: not needed after exdoc is fixed %% @private to_list(A) when is_atom(A) -> atom_to_list(A); to_list(B) when is_binary(B) -> unicode:characters_to_list(B); to_list(I) when is_integer(I) -> integer_to_list(I); to_list(Str) -> unicode:characters_to_list(Str). %% TODO: not needed after exdoc is fixed %% @private detect_otp_error({ok, {401, Headers, Body}}) -> case maps:get(<<"www-authenticate">>, Headers, nil) of <<"Bearer realm=\"hex\", error=\"totp_required\"", _/binary>> -> {error, otp_required}; <<"Bearer realm=\"hex\", error=\"invalid_totp\"", _/binary>> -> {error, invalid_totp}; _ -> {ok, {401, Headers, Body}} end; detect_otp_error(Response) -> Response. hex-2.4.2/src/mix_hex_api_auth.erl000066400000000000000000000012511517471540100171150ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @doc %% Hex HTTP API - Authentication. -module(mix_hex_api_auth). -export([test/2]). %% @doc %% Test an auth key against a given domain and resource. %% %% Examples: %% %% ``` %% 1> Params = #{domain => <<"repository">>, resource => <<"gustafson_motors">>}. %% 2> mix_hex_api_auth:test_key(mix_hex_core:default_config(), Params). %% {ok,{204, ..., nil}} %% ''' %% @end -spec test(mix_hex_core:config(), map()) -> mix_hex_api:response(). test(Config, #{domain := Domain, resource := Resource}) -> URI = ["auth", "?domain=", Domain, "&resource=", Resource], mix_hex_api:get(Config, list_to_binary(URI)). hex-2.4.2/src/mix_hex_api_key.erl000066400000000000000000000127611517471540100167540ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @doc %% Hex HTTP API - Keys. -module(mix_hex_api_key). -export([ list/1, get/2, add/3, delete/2, delete_all/1 ]). -export_type([permission/0]). -type permission() :: #{binary() := binary()}. %% @doc %% Lists the user's or organization's API and repository keys. %% %% Examples: %% %% ``` %% > mix_hex_api_key:list(mix_hex_core:default_config()). %% {ok, {200, ..., [#{ %% <<"authing_key">> => true, %% <<"inserted_at">> => <<"2019-02-27T11:15:32Z">>, %% <<"last_use">> => %% #{<<"ip">> => <<"1.2.3.4">>, %% <<"used_at">> => <<"2019-02-27T14:38:54Z">>, %% <<"user_agent">> => <<"hex_core/0.5.0 (httpc) (OTP/21) (erts/10.2)">>}, %% <<"name">> => <<"hex_core">>, %% <<"permissions">> => [#{<<"domain">> => <<"api">>,<<"resource">> => <<"read">>}], %% <<"revoked_at">> => nil, %% <<"updated_at">> => <<"2019-02-27T14:38:54Z">>, %% <<"url">> => <<"https://hex.pm/api/keys/test">>}, %% }]}} %% ''' %% @end -spec list(mix_hex_core:config()) -> mix_hex_api:response(). list(Config) when is_map(Config) -> Path = mix_hex_api:build_organization_path(Config, ["keys"]), mix_hex_api:get(Config, Path). %% @doc %% Gets an API or repository key by name. %% %% Examples: %% %% ``` %% > mix_hex_api_key:get(mix_hex_core:default_config(), <<"test">>). %% {ok, {200, ..., #{ %% <<"authing_key">> => true, %% <<"inserted_at">> => <<"2019-02-27T11:15:32Z">>, %% <<"last_use">> => %% #{<<"ip">> => <<"1.2.3.4">>, %% <<"used_at">> => <<"2019-02-27T14:38:54Z">>, %% <<"user_agent">> => <<"hex_core/0.5.0 (httpc) (OTP/21) (erts/10.2)">>}, %% <<"name">> => <<"hex_core">>, %% <<"permissions">> => [#{<<"domain">> => <<"api">>,<<"resource">> => <<"read">>}], %% <<"revoked_at">> => nil, %% <<"updated_at">> => <<"2019-02-27T14:38:54Z">>, %% <<"url">> => <<"https://hex.pm/api/keys/test">>}, %% }}} %% ''' %% @end -spec get(mix_hex_core:config(), binary()) -> mix_hex_api:response(). get(Config, Name) when is_map(Config) and is_binary(Name) -> Path = mix_hex_api:build_organization_path(Config, ["keys", Name]), mix_hex_api:get(Config, Path). %% @doc %% Adds a new API or repository key. %% %% A permission is a map of `#{<<"domain">> => Domain, <<"resource"> => Resource}'. %% %% Valid `Domain' values: `<<"api">> | <<"repository">> | <<"repositories">>'. %% %% Valid `Resource' values: `<<"read">> | <<"write">>'. %% %% Examples: %% %% ``` %% > mix_hex_api_key:add(mix_hex_core:default_config(), <<"test">>, [...]). %% {ok, {200, ..., #{ %% <<"authing_key">> => true, %% <<"inserted_at">> => <<"2019-02-27T11:15:32Z">>, %% <<"last_use">> => %% #{<<"ip">> => <<"1.2.3.4">>, %% <<"used_at">> => <<"2019-02-27T14:38:54Z">>, %% <<"user_agent">> => <<"hex_core/0.5.0 (httpc) (OTP/21) (erts/10.2)">>}, %% <<"name">> => <<"hex_core">>, %% <<"permissions">> => [#{<<"domain">> => <<"api">>,<<"resource">> => <<"read">>}], %% <<"revoked_at">> => nil, %% <<"updated_at">> => <<"2019-02-27T14:38:54Z">>, %% <<"url">> => <<"https://hex.pm/api/keys/test">>}, %% }}} %% ''' %% @end -spec add(mix_hex_core:config(), binary(), [permission()]) -> mix_hex_api:response(). add(Config, Name, Permissions) when is_map(Config) and is_binary(Name) and is_list(Permissions) -> Path = mix_hex_api:build_organization_path(Config, ["keys"]), Params = #{<<"name">> => Name, <<"permissions">> => Permissions}, mix_hex_api:post(Config, Path, Params). %% @doc %% Deletes an API or repository key. %% %% Examples: %% %% ``` %% > mix_hex_api_key:delete(mix_hex_core:default_config(), <<"test">>). %% {ok, {200, ..., #{ %% <<"authing_key">> => true, %% <<"inserted_at">> => <<"2019-02-27T11:15:32Z">>, %% <<"last_use">> => %% #{<<"ip">> => <<"1.2.3.4">>, %% <<"used_at">> => <<"2019-02-27T14:38:54Z">>, %% <<"user_agent">> => <<"hex_core/0.5.0 (httpc) (OTP/21) (erts/10.2)">>}, %% <<"name">> => <<"hex_core">>, %% <<"permissions">> => [#{<<"domain">> => <<"api">>,<<"resource">> => <<"read">>}], %% <<"revoked_at">> => nil, %% <<"updated_at">> => <<"2019-02-27T14:38:54Z">>, %% <<"url">> => <<"https://hex.pm/api/keys/test">>}, %% }}} %% ''' %% @end -spec delete(mix_hex_core:config(), binary()) -> mix_hex_api:response(). delete(Config, Name) when is_map(Config) and is_binary(Name) -> Path = mix_hex_api:build_organization_path(Config, ["keys", Name]), mix_hex_api:delete(Config, Path). %% @doc %% Deletes all API and repository keys associated with the account. %% %% Examples: %% %% ``` %% > mix_hex_api_key:delete_all(mix_hex_core:default_config()). %% {ok, {200, ..., [#{ %% <<"authing_key">> => true, %% <<"inserted_at">> => <<"2019-02-27T11:15:32Z">>, %% <<"last_use">> => %% #{<<"ip">> => <<"1.2.3.4">>, %% <<"used_at">> => <<"2019-02-27T14:38:54Z">>, %% <<"user_agent">> => <<"hex_core/0.5.0 (httpc) (OTP/21) (erts/10.2)">>}, %% <<"name">> => <<"hex_core">>, %% <<"permissions">> => [#{<<"domain">> => <<"api">>,<<"resource">> => <<"read">>}], %% <<"revoked_at">> => nil, %% <<"updated_at">> => <<"2019-02-27T14:38:54Z">>, %% <<"url">> => <<"https://hex.pm/api/keys/test">>}, %% }]}} %% ''' %% @end -spec delete_all(mix_hex_core:config()) -> mix_hex_api:response(). delete_all(Config) when is_map(Config) -> Path = mix_hex_api:build_organization_path(Config, ["keys"]), mix_hex_api:delete(Config, Path). hex-2.4.2/src/mix_hex_api_oauth.erl000066400000000000000000000150701517471540100173000ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @doc %% Hex HTTP API - OAuth. -module(mix_hex_api_oauth). -export([ device_authorization/3, device_authorization/4, poll_device_token/3, refresh_token/3, revoke_token/3, client_credentials_token/4, client_credentials_token/5 ]). %% @doc %% Initiates the OAuth device authorization flow. %% %% @see device_authorization/4 %% @end -spec device_authorization(mix_hex_core:config(), binary(), binary()) -> mix_hex_api:response(). device_authorization(Config, ClientId, Scope) -> device_authorization(Config, ClientId, Scope, []). %% @doc %% Initiates the OAuth device authorization flow with optional parameters. %% %% Returns device code, user code, and verification URIs for user authentication. %% %% Options: %% * `name' - A name to identify the token (e.g., hostname of the device) %% %% Examples: %% %% ``` %% 1> Config = mix_hex_core:default_config(). %% 2> mix_hex_api_oauth:device_authorization(Config, <<"cli">>, <<"api:write">>). %% {ok,{200, ..., #{ %% <<"device_code">> => <<"...">>, %% <<"user_code">> => <<"ABCD-1234">>, %% <<"verification_uri">> => <<"https://hex.pm/oauth/device">>, %% <<"verification_uri_complete">> => <<"https://hex.pm/oauth/device?user_code=ABCD-1234">>, %% <<"expires_in">> => 600, %% <<"interval">> => 5 %% }}} %% %% 3> mix_hex_api_oauth:device_authorization(Config, <<"cli">>, <<"api:write">>, [{name, <<"MyMachine">>}]). %% ''' %% @end -spec device_authorization(mix_hex_core:config(), binary(), binary(), proplists:proplist()) -> mix_hex_api:response(). device_authorization(Config, ClientId, Scope, Opts) -> Path = <<"oauth/device_authorization">>, Params0 = #{ <<"client_id">> => ClientId, <<"scope">> => Scope }, Params = case proplists:get_value(name, Opts) of undefined -> Params0; Name -> Params0#{<<"name">> => Name} end, mix_hex_api:post(Config, Path, Params). %% @doc %% Polls the OAuth token endpoint for device authorization completion. %% %% Returns: %% - `{ok, {200, _, Token}}` - Authorization complete %% - `{ok, {400, _, #{<<"error">> => <<"authorization_pending">>}}}` - Still waiting %% - `{ok, {400, _, #{<<"error">> => <<"slow_down">>}}}` - Polling too fast %% - `{ok, {400, _, #{<<"error">> => <<"expired_token">>}}}` - Code expired %% - `{ok, {403, _, #{<<"error">> => <<"access_denied">>}}}` - User denied %% %% Examples: %% %% ``` %% 1> Config = mix_hex_core:default_config(). %% 2> mix_hex_api_oauth:poll_device_token(Config, <<"cli">>, DeviceCode). %% {ok, {200, _, #{ %% <<"access_token">> => <<"...">>, %% <<"refresh_token">> => <<"...">>, %% <<"token_type">> => <<"Bearer">>, %% <<"expires_in">> => 3600 %% }}} %% ''' %% @end -spec poll_device_token(mix_hex_core:config(), binary(), binary()) -> mix_hex_api:response(). poll_device_token(Config, ClientId, DeviceCode) -> Path = <<"oauth/token">>, Params = #{ <<"grant_type">> => <<"urn:ietf:params:oauth:grant-type:device_code">>, <<"device_code">> => DeviceCode, <<"client_id">> => ClientId }, mix_hex_api:post(Config, Path, Params). %% @doc %% Refreshes an access token using a refresh token. %% %% Examples: %% %% ``` %% 1> Config = mix_hex_core:default_config(). %% 2> mix_hex_api_oauth:refresh_token(Config, <<"cli">>, RefreshToken). %% {ok, {200, _, #{ %% <<"access_token">> => <<"...">>, %% <<"refresh_token">> => <<"...">>, %% <<"token_type">> => <<"Bearer">>, %% <<"expires_in">> => 3600 %% }}} %% ''' %% @end -spec refresh_token(mix_hex_core:config(), binary(), binary()) -> mix_hex_api:response(). refresh_token(Config, ClientId, RefreshToken) -> Path = <<"oauth/token">>, Params = #{ <<"grant_type">> => <<"refresh_token">>, <<"refresh_token">> => RefreshToken, <<"client_id">> => ClientId }, mix_hex_api:post(Config, Path, Params). %% @doc %% Exchanges an API key for an OAuth access token using the client credentials grant. %% %% @see client_credentials_token/5 %% @end -spec client_credentials_token(mix_hex_core:config(), binary(), binary(), binary()) -> mix_hex_api:response(). client_credentials_token(Config, ClientId, ApiKey, Scope) -> client_credentials_token(Config, ClientId, ApiKey, Scope, []). %% @doc %% Exchanges an API key for an OAuth access token using the client credentials grant with optional parameters. %% %% This grant type allows exchanging a long-lived API key for a short-lived OAuth access token. %% The API key is sent as the client_secret parameter. %% %% Options: %% * `name' - A name to identify the token (e.g., hostname of the client) %% %% Returns: %% - `{ok, {200, _, Token}}` - Token exchange successful %% - `{ok, {400, _, #{<<"error">> => ...}}}` - Invalid request or scope %% - `{ok, {401, _, #{<<"error">> => ...}}}` - Invalid API key %% %% Examples: %% %% ``` %% 1> Config = mix_hex_core:default_config(). %% 2> mix_hex_api_oauth:client_credentials_token(Config, <<"cli">>, ApiKey, <<"api">>). %% {ok, {200, _, #{ %% <<"access_token">> => <<"...">>, %% <<"token_type">> => <<"bearer">>, %% <<"expires_in">> => 1800, %% <<"scope">> => <<"api">> %% }}} %% %% 3> mix_hex_api_oauth:client_credentials_token(Config, <<"cli">>, ApiKey, <<"api">>, [{name, <<"MyMachine">>}]). %% ''' %% @end -spec client_credentials_token( mix_hex_core:config(), binary(), binary(), binary(), proplists:proplist() ) -> mix_hex_api:response(). client_credentials_token(Config, ClientId, ApiKey, Scope, Opts) -> Path = <<"oauth/token">>, Params0 = #{ <<"grant_type">> => <<"client_credentials">>, <<"client_id">> => ClientId, <<"client_secret">> => ApiKey, <<"scope">> => Scope }, Params = case proplists:get_value(name, Opts) of undefined -> Params0; Name -> Params0#{<<"name">> => Name} end, mix_hex_api:post(Config, Path, Params). %% @doc %% Revokes an OAuth token (RFC 7009). %% %% Can revoke either access tokens or refresh tokens. %% Returns 200 OK regardless of whether the token was found, %% following RFC 7009 security recommendations. %% %% Examples: %% %% ``` %% 1> Config = mix_hex_core:default_config(). %% 2> mix_hex_api_oauth:revoke_token(Config, <<"cli">>, Token). %% {ok, {200, ..., nil}} %% ''' %% @end -spec revoke_token(mix_hex_core:config(), binary(), binary()) -> mix_hex_api:response(). revoke_token(Config, ClientId, Token) -> Path = <<"oauth/revoke">>, Params = #{ <<"token">> => Token, <<"client_id">> => ClientId }, mix_hex_api:post(Config, Path, Params). hex-2.4.2/src/mix_hex_api_organization.erl000066400000000000000000000036601517471540100206660ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @doc %% Hex HTTP API - Organizations. -module(mix_hex_api_organization). -export([ get/1, list/1, update/2 ]). %% @doc %% Lists all organizations you are member of. %% %% Examples: %% %% ``` %% > mix_hex_api_organization:list(mix_hex_core:default_config()). %% {ok, {200, ..., [#{ %% <<"billing_active">> => true, %% <<"inserted_at">> => <<"2017-08-22T22:19:53Z">>, %% <<"name">> => <<"acme">>, %% <<"updated_at">> => <<"2019-01-18T08:47:29Z">>} %% }]}} %% ''' -spec list(mix_hex_core:config()) -> mix_hex_api:response(). list(Config) when is_map(Config) -> mix_hex_api:get(Config, ["orgs"]). %% @doc %% Gets an organzation. %% %% Examples: %% %% ``` %% > mix_hex_api_organization:get(mix_hex_core:default_config()). %% {ok, {200, ..., #{ %% <<"billing_active">> => true, %% <<"inserted_at">> => <<"2017-08-22T22:19:53Z">>, %% <<"name">> => <<"acme">>, %% <<"seats">> => 42, %% <<"updated_at">> => <<"2019-01-18T08:47:29Z">>} %% }}} %% ''' -spec get(mix_hex_core:config()) -> mix_hex_api:response(). get(Config) when is_map(Config) -> Path = mix_hex_api:build_organization_path(Config, []), mix_hex_api:get(Config, Path). %% @doc %% Updates the number of seats in an organzation. %% %% Examples: %% %% ``` %% > mix_hex_api_organization:get(mix_hex_core:default_config(), 42). %% {ok, {200, ..., #{ %% <<"billing_active">> => true, %% <<"inserted_at">> => <<"2017-08-22T22:19:53Z">>, %% <<"name">> => <<"acme">>, %% <<"seats">> => 42, %% <<"updated_at">> => <<"2019-01-18T08:47:29Z">>} %% }}} %% ''' -spec update(mix_hex_core:config(), pos_integer()) -> mix_hex_api:response(). update(Config, Seats) when is_map(Config) and is_integer(Seats) -> Path = mix_hex_api:build_organization_path(Config, []), Params = #{<<"seats">> => Seats}, mix_hex_api:post(Config, Path, Params). hex-2.4.2/src/mix_hex_api_organization_member.erl000066400000000000000000000071211517471540100222110ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @doc %% Hex HTTP API - Organization Members. -module(mix_hex_api_organization_member). -export([ add/3, delete/2, get/2, list/1, update/3 ]). -type role() :: admin | write | read. %% @doc %% Lists the organization's members. %% %% Examples: %% %% ``` %% > mix_hex_api_organization_member:list(mix_hex_core:default_config(), #{api_organization => <<"acme">>}). %% {ok, {200, ..., [#{ %% <<"email">> => <<"user@example.com">>, %% <<"role">> => <<"admin">>, %% <<"url">> => <<"https://hex.pm/api/users/user">>, %% <<"username">> => <<"user">> %% }]}} %% ''' %% @end -spec list(mix_hex_core:config()) -> mix_hex_api:response(). list(Config) when is_map(Config) -> Path = mix_hex_api:build_organization_path(Config, ["members"]), mix_hex_api:get(Config, Path). %% @doc %% Gets an organization member. %% %% Examples: %% %% ``` %% > mix_hex_api_organization_member:get(mix_hex_core:default_config(), #{api_organization => <<"acme">>}, <<"user">>). %% {ok, {200, ..., #{ %% <<"email">> => <<"user@example.com">>, %% <<"role">> => <<"admin">>, %% <<"url">> => <<"https://hex.pm/api/users/user">>, %% <<"username">> => <<"user">> %% }}} %% ''' %% @end -spec get(mix_hex_core:config(), binary()) -> mix_hex_api:response(). get(Config, UsernameOrEmail) when is_map(Config) and is_binary(UsernameOrEmail) -> Path = mix_hex_api:build_organization_path(Config, ["members", UsernameOrEmail]), mix_hex_api:get(Config, Path). %% @doc %% Adds an organization member. %% %% Examples: %% %% ``` %% > mix_hex_api_organization_member:add(mix_hex_core:default_config(), #{api_organization => <<"acme">>}, <<"user">>, write). %% {ok, {200, ..., #{ %% <<"email">> => <<"user@example.com">>, %% <<"role">> => <<"write">>, %% <<"url">> => <<"https://hex.pm/api/users/user">>, %% <<"username">> => <<"user">> %% }}} %% ''' %% @end -spec add(mix_hex_core:config(), binary(), role()) -> mix_hex_api:response(). add(Config, UsernameOrEmail, Role) when is_map(Config) and is_binary(UsernameOrEmail) and is_atom(Role) -> Path = mix_hex_api:build_organization_path(Config, ["members"]), Params = #{<<"name">> => UsernameOrEmail, <<"role">> => Role}, mix_hex_api:post(Config, Path, Params). %% @doc %% Updates an organization member's role. %% %% Examples: %% %% ``` %% > mix_hex_api_organization_member:update(mix_hex_core:default_config(), #{api_organization => <<"acme">>}, <<"user">>, read). %% {ok, {200, ..., #{ %% <<"email">> => <<"user@example.com">>, %% <<"role">> => <<"read">>, %% <<"url">> => <<"https://hex.pm/api/users/user">>, %% <<"username">> => <<"user">> %% }}} %% ''' %% @end -spec update(mix_hex_core:config(), binary(), role()) -> mix_hex_api:response(). update(Config, UsernameOrEmail, Role) when is_map(Config) and is_binary(UsernameOrEmail) and is_atom(Role) -> Path = mix_hex_api:build_organization_path(Config, ["members", UsernameOrEmail]), Params = #{<<"role">> => Role}, mix_hex_api:post(Config, Path, Params). %% @doc %% Deletes an organization member. %% %% Examples: %% %% ``` %% > mix_hex_api_organization_member:delete(mix_hex_core:default_config(), #{api_organization => <<"acme">>}, <<"user">>). %% {ok, {204, ..., nil}} %% ''' %% @end -spec delete(mix_hex_core:config(), binary()) -> mix_hex_api:response(). delete(Config, UsernameOrEmail) when is_map(Config) and is_binary(UsernameOrEmail) -> Path = mix_hex_api:build_organization_path(Config, ["members", UsernameOrEmail]), mix_hex_api:delete(Config, Path). hex-2.4.2/src/mix_hex_api_package.erl000066400000000000000000000032371517471540100175550ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @doc %% Hex HTTP API - Packages. -module(mix_hex_api_package). -export([get/2, search/3]). %% @doc %% Gets a package. %% %% Examples: %% %% ``` %% > mix_hex_api_package:get(mix_hex_core:default_config(), <<"package">>). %% {ok, {200, ..., #{ %% <<"name">> => <<"package1">>, %% <<"meta">> => #{ %% <<"description">> => ..., %% <<"licenses">> => ..., %% <<"links">> => ..., %% <<"maintainers">> => ... %% }, %% ..., %% <<"releases">> => [ %% #{<<"url">> => ..., <<"version">> => <<"0.5.0">>}], %% #{<<"url">> => ..., <<"version">> => <<"1.0.0">>}], %% ... %% ]}}} %% ''' %% @end -spec get(mix_hex_core:config(), binary()) -> mix_hex_api:response(). get(Config, Name) when is_map(Config) and is_binary(Name) -> Path = mix_hex_api:build_repository_path(Config, ["packages", Name]), mix_hex_api:get(Config, Path). %% @doc %% Searches packages. %% %% Examples: %% %% ``` %% > mix_hex_api_package:search(mix_hex_core:default_config(), <<"package">>, [{page, 1}]). %% {ok, {200, ..., [ %% #{<<"name">> => <<"package1">>, ...}, %% ... %% ]}} %% ''' -spec search(mix_hex_core:config(), binary(), [{term(), term()}]) -> mix_hex_api:response(). search(Config, Query, SearchParams) when is_map(Config) and is_binary(Query) and is_list(SearchParams) -> QueryString = mix_hex_api:encode_query_string([{search, Query} | SearchParams]), Path = mix_hex_api:join_path_segments(mix_hex_api:build_repository_path(Config, ["packages"])), PathQuery = <>, mix_hex_api:get(Config, PathQuery). hex-2.4.2/src/mix_hex_api_package_owner.erl000066400000000000000000000070571517471540100207730ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @doc %% Hex HTTP API - Package Owners. -module(mix_hex_api_package_owner). -export([ add/5, delete/3, get/3, list/2 ]). %% @doc %% Lists the packages owners. %% %% Examples: %% %% ``` %% > mix_hex_api_package_owner:list(mix_hex_core:default_config(), <<"package">>). %% {ok, {200, ..., [#{ %% <<"email">> => <<"user@example.com">>, %% <<"full_name">> => <<"John Doe">>, %% <<"handles">> => #{...}, %% <<"inserted_at">> => <<"2014-04-21T17:20:12Z">>, %% <<"level">> => <<"full">>, %% <<"updated_at">> => <<"2019-08-04T19:28:05Z">>, %% <<"url">> => <<"https://hex.pm/api/users/user">>, %% <<"username">> => <<"user">> %% }]}} %% ''' %% @end -spec list(mix_hex_core:config(), binary()) -> mix_hex_api:response(). list(Config, PackageName) when is_binary(PackageName) -> Path = mix_hex_api:build_repository_path(Config, ["packages", PackageName, "owners"]), mix_hex_api:get(Config, Path). %% @doc %% Gets a packages owner. %% %% Examples: %% %% ``` %% > mix_hex_api_package_owner:get(mix_hex_core:default_config(), <<"package">>, <<"user">>). %% {ok, {200, ..., #{ %% <<"email">> => <<"user@example.com">>, %% <<"full_name">> => <<"John Doe">>, %% <<"handles">> => #{...}, %% <<"inserted_at">> => <<"2014-04-21T17:20:12Z">>, %% <<"level">> => <<"full">>, %% <<"updated_at">> => <<"2019-08-04T19:28:05Z">>, %% <<"url">> => <<"https://hex.pm/api/users/user">>, %% <<"username">> => <<"user">> %% }}} %% ''' %% @end -spec get(mix_hex_core:config(), binary(), binary()) -> mix_hex_api:response(). get(Config, PackageName, UsernameOrEmail) when is_map(Config) and is_binary(PackageName) and is_binary(UsernameOrEmail) -> Path = mix_hex_api:build_repository_path(Config, [ "packages", PackageName, "owners", UsernameOrEmail ]), mix_hex_api:get(Config, Path). %% @doc %% Adds a packages owner. %% %% Examples: %% %% ``` %% > mix_hex_api_package_owner:add(mix_hex_core:default_config(), <<"package">>, <<"user">>, <<"full">>, false). %% {ok, {200, ..., #{ %% <<"email">> => <<"user@example.com">>, %% <<"full_name">> => <<"John Doe">>, %% <<"handles">> => #{...}, %% <<"inserted_at">> => <<"2014-04-21T17:20:12Z">>, %% <<"level">> => <<"full">>, %% <<"updated_at">> => <<"2019-08-04T19:28:05Z">>, %% <<"url">> => <<"https://hex.pm/api/users/user">>, %% <<"username">> => <<"user">> %% }}} %% ''' %% @end -spec add(mix_hex_core:config(), binary(), binary(), binary(), boolean()) -> mix_hex_api:response(). add(Config, PackageName, UsernameOrEmail, Level, Transfer) when is_binary(PackageName) and is_binary(UsernameOrEmail) and is_map(Config) and is_binary(Level) and is_boolean(Transfer) -> Path = mix_hex_api:build_repository_path(Config, [ "packages", PackageName, "owners", UsernameOrEmail ]), mix_hex_api:put(Config, Path, #{<<"level">> => Level, <<"transfer">> => Transfer}). %% @doc %% Deletes a packages owner. %% %% Examples: %% %% ``` %% > mix_hex_api_package_owner:delete(mix_hex_core:default_config(), <<"package">>, <<"user">>). %% {ok, {204, ..., nil}} %% ''' %% @end -spec delete(mix_hex_core:config(), binary(), binary()) -> mix_hex_api:response(). delete(Config, PackageName, UsernameOrEmail) when is_map(Config) and is_binary(PackageName) and is_binary(UsernameOrEmail) -> Path = mix_hex_api:build_repository_path(Config, [ "packages", PackageName, "owners", UsernameOrEmail ]), mix_hex_api:delete(Config, Path). hex-2.4.2/src/mix_hex_api_release.erl000066400000000000000000000161171517471540100176030ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @doc %% Hex HTTP API - Releases. -module(mix_hex_api_release). -export([ delete/3, get/3, publish/2, publish/3, retire/4, unretire/3 ]). -export_type([publish_params/0, retirement_params/0]). -type publish_params() :: [{replace, boolean()}]. -type retirement_params() :: #{binary() := binary()}. %% @doc %% Gets a package release. %% %% Examples: %% %% ``` %% > mix_hex_api_release:get(mix_hex_core:default_config(), <<"package">>, <<"1.0.0">>). %% {ok, {200, ..., #{ %% <<"checksum">> => <<"540d210d81f56f17f64309a4896430e727972499b37bd59342dc08d61dff74d8">>, %% <<"docs_html_url">> => <<"https://hexdocs.pm/package/1.0.0/">>, %% <<"downloads">> => 740,<<"has_docs">> => true, %% <<"html_url">> => <<"https://hex.pm/packages/package/1.0.0">>, %% <<"inserted_at">> => <<"2014-12-09T18:32:03Z">>, %% <<"meta">> => %% #{<<"app">> => <<"package">>, %% <<"build_tools">> => [<<"mix">>]}, %% <<"package_url">> => <<"https://hex.pm/api/packages/package">>, %% <<"publisher">> => nil,<<"requirements">> => #{}, %% <<"retirement">> => nil, %% <<"updated_at">> => <<"2019-07-28T21:12:11Z">>, %% <<"url">> => <<"https://hex.pm/api/packages/package/releases/1.0.0">>, %% <<"version">> => <<"1.0.0">> %% }}} %% ''' %% @end -spec get(mix_hex_core:config(), binary(), binary()) -> mix_hex_api:response(). get(Config, Name, Version) when is_map(Config) and is_binary(Name) and is_binary(Version) -> Path = mix_hex_api:build_repository_path(Config, ["packages", Name, "releases", Version]), mix_hex_api:get(Config, Path). %% @doc %% Publishes a new package release. %% %% Examples: %% %% ``` %% > mix_hex_api_release:publish(mix_hex_core:default_config(), Tarball). %% {ok, {200, ..., #{ %% <<"checksum">> => <<"540d210d81f56f17f64309a4896430e727972499b37bd59342dc08d61dff74d8">>, %% <<"docs_html_url">> => <<"https://hexdocs.pm/package/1.0.0/">>, %% <<"downloads">> => 740,<<"has_docs">> => true, %% <<"html_url">> => <<"https://hex.pm/packages/package/1.0.0">>, %% <<"inserted_at">> => <<"2014-12-09T18:32:03Z">>, %% <<"meta">> => %% #{<<"app">> => <<"package">>, %% <<"build_tools">> => [<<"mix">>]}, %% <<"package_url">> => <<"https://hex.pm/api/packages/package">>, %% <<"publisher">> => nil,<<"requirements">> => #{}, %% <<"retirement">> => nil, %% <<"updated_at">> => <<"2019-07-28T21:12:11Z">>, %% <<"url">> => <<"https://hex.pm/api/packages/package/releases/1.0.0">>, %% <<"version">> => <<"1.0.0">> %% }}} %% ''' %% @end -spec publish(mix_hex_core:config(), binary()) -> mix_hex_api:response(). publish(Config, Tarball) -> publish(Config, Tarball, []). %% @doc %% Publishes a new package release with query parameters. %% %% Supported query params : %% - replace : boolean %% %% Examples: %% %% ``` %% > mix_hex_api_release:publish(mix_hex_core:default_config(), Tarball, [{replace, true}]). %% {ok, {201, ..., #{ %% <<"checksum">> => <<"540d210d81f56f17f64309a4896430e727972499b37bd59342dc08d61dff74d8">>, %% <<"docs_html_url">> => <<"https://hexdocs.pm/package/1.0.0/">>, %% <<"downloads">> => 740,<<"has_docs">> => true, %% <<"html_url">> => <<"https://hex.pm/packages/package/1.0.0">>, %% <<"inserted_at">> => <<"2014-12-09T18:32:03Z">>, %% <<"meta">> => %% #{<<"app">> => <<"package">>, %% <<"build_tools">> => [<<"mix">>]}, %% <<"package_url">> => <<"https://hex.pm/api/packages/package">>, %% <<"publisher">> => nil,<<"requirements">> => #{}, %% <<"retirement">> => nil, %% <<"updated_at">> => <<"2019-07-28T21:12:11Z">>, %% <<"url">> => <<"https://hex.pm/api/packages/package/releases/1.0.0">>, %% <<"version">> => <<"1.0.0">> %% }}} %% ''' %% @end -spec publish(mix_hex_core:config(), binary(), publish_params()) -> mix_hex_api:response(). publish(Config, Tarball, Params) when is_map(Config) andalso is_binary(Tarball) andalso is_list(Params) -> case mix_hex_tarball:unpack(Tarball, memory) of {ok, #{metadata := Metadata}} -> PackageName = maps:get(<<"name">>, Metadata), QueryString = mix_hex_api:encode_query_string([ {replace, proplists:get_value(replace, Params, false)} ]), Path = mix_hex_api:join_path_segments( mix_hex_api:build_repository_path(Config, ["packages", PackageName, "releases"]) ), PathWithQuery = <>, TarballContentType = "application/octet-stream", Config2 = put_header( <<"content-length">>, integer_to_binary(byte_size(Tarball)), Config ), Config3 = maybe_put_expect_header(Config2), Body = {TarballContentType, Tarball}, mix_hex_api:post(Config3, PathWithQuery, Body); {error, Reason} -> {error, {tarball, Reason}} end. %% @doc %% Deletes a package release. %% %% Examples: %% %% ``` %% > mix_hex_api_release:delete(mix_hex_core:default_config(), <<"package">>, <<"1.0.0">>). %% {ok, {204, ..., nil}} %% ''' %% @end -spec delete(mix_hex_core:config(), binary(), binary()) -> mix_hex_api:response(). delete(Config, Name, Version) when is_map(Config) and is_binary(Name) and is_binary(Version) -> Path = mix_hex_api:build_repository_path(Config, ["packages", Name, "releases", Version]), mix_hex_api:delete(Config, Path). %% @doc %% Retires a package release. %% %% Examples: %% %% ``` %% > mix_hex_api_release:retire(mix_hex_core:default_config(), <<"package">>, <<"1.0.0">>, Params). %% {ok, {204, ..., nil}} %% ''' %% @end -spec retire(mix_hex_core:config(), binary(), binary(), retirement_params()) -> mix_hex_api:response(). retire(Config, Name, Version, Params) when is_map(Config) and is_binary(Name) and is_binary(Version) -> Path = mix_hex_api:build_repository_path(Config, ["packages", Name, "releases", Version, "retire"]), mix_hex_api:post(Config, Path, Params). %% @doc %% Unretires a package release. %% %% Examples: %% %% ``` %% > mix_hex_api_release:unretire(mix_hex_core:default_config(), <<"package">>, <<"1.0.0">>). %% {ok, {204, ..., nil}} %% ''' %% @end -spec unretire(mix_hex_core:config(), binary(), binary()) -> mix_hex_api:response(). unretire(Config, Name, Version) when is_map(Config) and is_binary(Name) and is_binary(Version) -> Path = mix_hex_api:build_repository_path(Config, ["packages", Name, "releases", Version, "retire"]), mix_hex_api:delete(Config, Path). %%==================================================================== %% Internal functions %%==================================================================== %% @private put_header(Name, Value, Config) -> Headers = maps:get(http_headers, Config, #{}), Headers2 = maps:put(Name, Value, Headers), maps:put(http_headers, Headers2, Config). %% @private maybe_put_expect_header(Config) -> case maps:get(send_100_continue, Config, true) of true -> put_header(<<"expect">>, <<"100-continue">>, Config); false -> Config end. hex-2.4.2/src/mix_hex_api_short_url.erl000066400000000000000000000011551517471540100202000ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @doc %% Hex HTTP API - Short URLs. -module(mix_hex_api_short_url). -export([create/2]). %% @doc %% Creates a short URL. %% %% Examples: %% %% ``` %% > mix_hex_api_short_url:create(mix_hex_core:default_config(), <<"https://hex.pm/packages/example">>). %% {ok, {201, ..., #{<<"url">> => <<"https://hex.pm/l/XXXXX">>}}} %% ''' %% @end -spec create(mix_hex_core:config(), binary()) -> mix_hex_api:response(). create(Config, URL) when is_map(Config) and is_binary(URL) -> Body = #{<<"url">> => URL}, mix_hex_api:post(Config, ["short_url"], Body). hex-2.4.2/src/mix_hex_api_user.erl000066400000000000000000000056671517471540100171510ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @doc %% Hex HTTP API - Users. -module(mix_hex_api_user). -export([ create/4, get/2, me/1, reset_password/2 ]). %% @doc %% Gets the authenticated user. %% %% Examples: %% %% ``` %% > mix_hex_api_user:me(mix_hex_core:default_config()). %% {ok, {200, ..., #{ %% <<"email">> => <<"user@example.com">>, %% <<"full_name">> => <<"John Doe">>, %% <<"handles">> => #{...}, %% <<"inserted_at">> => <<"2014-04-21T17:20:12Z">>, %% <<"level">> => <<"full">>, %% <<"updated_at">> => <<"2019-08-04T19:28:05Z">>, %% <<"url">> => <<"https://hex.pm/api/users/user">>, %% <<"username">> => <<"user">> %% }}} %% ''' %% @end -spec me(mix_hex_core:config()) -> mix_hex_api:response(). me(Config) when is_map(Config) -> mix_hex_api:get(Config, ["users", "me"]). %% @doc %% Creates a new user account. %% %% Examples: %% %% ``` %% > mix_hex_api_user:create(mix_hex_core:default_config(), <<"user">>, <<"hunter42">>, <<"user@example.com">>). %% {ok, {201, ..., #{ %% <<"email">> => <<"user@example.com">>, %% <<"full_name">> => <<"John Doe">>, %% <<"handles">> => #{...}, %% <<"inserted_at">> => <<"2014-04-21T17:20:12Z">>, %% <<"level">> => <<"full">>, %% <<"updated_at">> => <<"2019-08-04T19:28:05Z">>, %% <<"url">> => <<"https://hex.pm/api/users/user">>, %% <<"username">> => <<"user">> %% }}} %% ''' %% @end -spec create(mix_hex_core:config(), binary(), binary(), binary()) -> mix_hex_api:response(). create(Config, Username, Password, Email) when is_map(Config) and is_binary(Username) and is_binary(Password) and is_binary(Email) -> Params = #{ <<"username">> => Username, <<"password">> => Password, <<"email">> => Email }, mix_hex_api:post(Config, ["users"], Params). %% @doc %% Resets the user's password. %% %% Examples: %% %% ``` %% > mix_hex_api_user:reset_password(mix_hex_core:default_config(), <<"user">>). %% {ok, {204, ..., nil}} %% ''' %% @end -spec reset_password(mix_hex_core:config(), binary()) -> mix_hex_api:response(). reset_password(Config, Username) when is_map(Config) and is_binary(Username) -> mix_hex_api:post(Config, ["users", Username, "reset"], #{}). %% @doc %% Gets a user. %% %% Examples: %% %% ``` %% > mix_hex_api_user:get(mix_hex_core:default_config()). %% {ok, {200, ..., #{ %% <<"email">> => <<"user@example.com">>, %% <<"full_name">> => <<"John Doe">>, %% <<"handles">> => #{...}, %% <<"inserted_at">> => <<"2014-04-21T17:20:12Z">>, %% <<"level">> => <<"full">>, %% <<"updated_at">> => <<"2019-08-04T19:28:05Z">>, %% <<"url">> => <<"https://hex.pm/api/users/user">>, %% <<"username">> => <<"user">> %% }}} %% ''' %% @end -spec get(mix_hex_core:config(), binary()) -> mix_hex_api:response(). get(Config, Username) when is_map(Config) and is_binary(Username) -> mix_hex_api:get(Config, ["users", Username]). hex-2.4.2/src/mix_hex_core.erl000066400000000000000000000142011517471540100162520ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @doc %% `hex_core' entrypoint module. %% %% === Config === %% %% Most functions in the `hex_core' API takes a configuration. The configuration sets things %% like HTTP client to use, and API and repository URL. Some of these configuration options %% will likely be static for your application and some may change depending on the function %% you call. %% %% === Options === %% %% * `api_key' - Authentication key used when accessing the HTTP API. %% %% * `api_otp' - TOTP (Time-based One-Time Password) code for two-factor authentication. %% When using OAuth tokens, write operations require 2FA if the user has it enabled. %% If required, the server returns one of: %% - `{error, otp_required}' - Retry the request with a 6-digit TOTP code in this option %% - `{error, invalid_totp}' - The provided TOTP code was incorrect, retry with correct code %% - `{ok, {403, _, #{<<"message">> => <<"Two-factor authentication must be enabled for API write access">>}}}' - User must enable 2FA first %% - `{ok, {429, _, _}}' - Too many failed TOTP attempts, rate limited %% API keys do not require TOTP validation. %% %% * `api_organization' - Name of the organization endpoint in the API, this should %% for example be set when accessing key for a specific organization. %% %% * `api_repository' - Name of the repository endpoint in the API, this should %% for example be set when accessing packages from a specific repository. %% %% * `api_url' - URL to the HTTP API (default: `https://hex.pm/api'). %% %% * `http_adapter' - A tuple of a callback module and its configuration used %% for HTTP requests (default: `{mix_hex_http_httpc, #{profile => default}}'). See %% {@link mix_hex_http} and {@link mix_hex_http_httpc} for more information. %% %% * `http_etag' - Sets the `if-none-match' HTTP header with the given value to do a %% conditional HTTP request. %% %% * `http_user_agent_fragment' - Will be appended to the `user-agent' HTTP header (default: `<<"(httpc)">>'). %% %% * `repo_key' - Authentication key used when accessing the repository. %% %% * `repo_name' - Name of the repository, used for verifying the repository signature %% authenticity (default: `hexpm'). %% %% * `repo_public_key' - Public key used to verify the repository signature %% (defaults to hexpm public key `https://hex.pm/docs/public_keys'). %% %% * `repo_url' - URL to the repository (default: `https://repo.hex.pm'). %% %% * `repo_organization' - Name of the organization repository, appends `/repos/:name' %% to the repository URL and overrides the `repo_name' option. %% %% * `repo_verify' - If `true' will verify the repository signature (default: `true'). %% %% * `repo_verify_origin' - If `true' will verify the repository signature origin, %% requires protobuf messages as of hex_core v0.4.0 (default: `true'). %% %% * `send_100_continue' - If `true' will send `Expect: 100-continue' header for %% publish operations. This allows the server to validate authentication and %% authorization before the client sends the request body (default: `true'). %% %% * `tarball_max_size' - Maximum size of package tarball, defaults to %% `16_777_216' (16 MiB). Set to `infinity' to not enforce the limit. %% %% * `tarball_max_uncompressed_size' - Maximum size of uncompressed package tarball, defaults to %% `134_217_728' (128 MiB). Set to `infinity' to not enforce the limit. %% %% * `docs_tarball_max_size' - Maximum size of docs tarball, defaults to %% `16_777_216' (16 MiB). Set to `infinity' to not enforce the limit. %% %% * `docs_tarball_max_uncompressed_size' - Maximum size of uncompressed docs tarball, defaults to %% `134_217_728' (128 MiB). Set to `infinity' to not enforce the limit. -module(mix_hex_core). -export([default_config/0]). -export_type([config/0]). %% https://hex.pm/docs/public_keys -define(HEXPM_PUBLIC_KEY, << "-----BEGIN PUBLIC KEY-----\n" "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApqREcFDt5vV21JVe2QNB\n" "Edvzk6w36aNFhVGWN5toNJRjRJ6m4hIuG4KaXtDWVLjnvct6MYMfqhC79HAGwyF+\n" "IqR6Q6a5bbFSsImgBJwz1oadoVKD6ZNetAuCIK84cjMrEFRkELtEIPNHblCzUkkM\n" "3rS9+DPlnfG8hBvGi6tvQIuZmXGCxF/73hU0/MyGhbmEjIKRtG6b0sJYKelRLTPW\n" "XgK7s5pESgiwf2YC/2MGDXjAJfpfCd0RpLdvd4eRiXtVlE9qO9bND94E7PgQ/xqZ\n" "J1i2xWFndWa6nfFnRxZmCStCOZWYYPlaxr+FZceFbpMwzTNs4g3d4tLNUcbKAIH4\n" "0wIDAQAB\n" "-----END PUBLIC KEY-----" >>). -type config() :: #{ api_key => binary() | undefined, api_otp => binary() | undefined, api_organization => binary() | undefined, api_repository => binary() | undefined, api_url => binary(), http_adapter => {module(), map()}, http_etag => binary() | undefined, http_headers => map(), http_user_agent_fragment => binary(), repo_key => binary() | undefined, repo_name => binary(), repo_public_key => binary(), repo_url => binary(), repo_organization => binary() | undefined, repo_verify => boolean(), repo_verify_origin => boolean(), send_100_continue => boolean(), tarball_max_size => pos_integer() | infinity, tarball_max_uncompressed_size => pos_integer() | infinity, docs_tarball_max_size => pos_integer() | infinity, docs_tarball_max_uncompressed_size => pos_integer() | infinity }. -spec default_config() -> config(). default_config() -> #{ api_key => undefined, api_otp => undefined, api_organization => undefined, api_repository => undefined, api_url => <<"https://hex.pm/api">>, http_adapter => {mix_hex_http_httpc, #{profile => default}}, http_etag => undefined, http_headers => #{}, http_user_agent_fragment => <<"(httpc)">>, repo_key => undefined, repo_name => <<"hexpm">>, repo_public_key => ?HEXPM_PUBLIC_KEY, repo_url => <<"https://repo.hex.pm">>, repo_organization => undefined, repo_verify => true, repo_verify_origin => true, send_100_continue => true, tarball_max_size => 16 * 1024 * 1024, tarball_max_uncompressed_size => 128 * 1024 * 1024, docs_tarball_max_size => 16 * 1024 * 1024, docs_tarball_max_uncompressed_size => 128 * 1024 * 1024 }. hex-2.4.2/src/mix_hex_core.hrl000066400000000000000000000001501517471540100162530ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually -define(HEX_CORE_VERSION, "0.15.0"). hex-2.4.2/src/mix_hex_erl_tar.erl000066400000000000000000002411551517471540100167640ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% This file is a copy of erl_tar.erl from OTP with the following modifications: %% 1. Module renamed from erl_tar to mix_hex_erl_tar %% 2. -include changed from erl_tar.hrl to mix_hex_erl_tar.hrl %% 3. -doc and -moduledoc attributes removed for OTP 24 compatibility %% 4. safe_link_name/2 fixed to validate symlink targets relative to symlink's %% parent directory instead of in isolation %% 5. When extracting to disk (cwd option), stream file entries in chunks %% instead of loading them fully into memory %% 6. Default chunk_size to 65536 in add_opts instead of 0 with special case %% 7. Use compressed instead of compressed_one for file:open for OTP 24 compat %% 8. Added {max_size, N} extraction option for zip bomb protection %% %% OTP commit: 013041bd68c2547848e88963739edea7f0a1a90f %% %% %CopyrightBegin% %% %% SPDX-License-Identifier: Apache-2.0 %% %% Copyright Ericsson AB 1997-2025. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% %% This module implements extraction/creation of tar archives. %% It supports reading most common tar formats, namely V7, STAR, %% USTAR, GNU, BSD/libarchive, and PAX. It produces archives in USTAR %% format, unless it must use PAX headers, in which case it produces PAX %% format. %% %% The following references where used: %% http://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5 %% http://www.gnu.org/software/tar/manual/html_node/Standard.html %% http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html -module(mix_hex_erl_tar). -export([init/3, create/2, create/3, extract/1, extract/2, table/1, table/2, t/1, tt/1, open/2, close/1, add/3, add/4, format_error/1]). -include_lib("kernel/include/file.hrl"). -include("mix_hex_erl_tar.hrl"). %% Converts the short error reason to a descriptive string. -spec format_error(term()) -> string(). format_error(invalid_tar_checksum) -> "Checksum failed"; format_error(bad_header) -> "Unrecognized tar header format"; format_error({bad_header, Reason}) -> lists:flatten(io_lib:format("Unrecognized tar header format: ~p", [Reason])); format_error({invalid_header, negative_size}) -> "Invalid header: negative size"; format_error(invalid_sparse_header_size) -> "Invalid sparse header: negative size"; format_error(invalid_sparse_map_entry) -> "Invalid sparse map entry"; format_error({invalid_sparse_map_entry, Reason}) -> lists:flatten(io_lib:format("Invalid sparse map entry: ~p", [Reason])); format_error(invalid_end_of_archive) -> "Invalid end of archive"; format_error(eof) -> "Unexpected end of file"; format_error(integer_overflow) -> "Failed to parse numeric: integer overflow"; format_error({misaligned_read, Pos}) -> lists:flatten(io_lib:format("Read a block which was misaligned: block_size=~p pos=~p", [?BLOCK_SIZE, Pos])); format_error(invalid_gnu_1_0_sparsemap) -> "Invalid GNU sparse map (version 1.0)"; format_error({invalid_gnu_0_1_sparsemap, Format}) -> lists:flatten(io_lib:format("Invalid GNU sparse map (version ~s)", [Format])); format_error(unsafe_path) -> "The path points above the current working directory"; format_error(too_big) -> "Extraction size exceeds the configured max_size limit"; format_error({Name,Reason}) -> lists:flatten(io_lib:format("~ts: ~ts", [Name,format_error(Reason)])); format_error(Atom) when is_atom(Atom) -> file:format_error(Atom); format_error(Term) -> lists:flatten(io_lib:format("~tp", [Term])). %% Initializes a new reader given a custom file handle and I/O wrappers -spec init(UserData :: user_data(), write | read, file_op()) -> {ok, tar_descriptor()} | {error, badarg}. init(UserData, AccessMode, Fun) when is_function(Fun, 2) -> Reader = #reader{handle=UserData,access=AccessMode,func=Fun}, {ok, Pos, Reader2} = do_position(Reader, {cur, 0}), {ok, Reader2#reader{pos=Pos}}; init(_UserData, _AccessMode, _Fun) -> {error, badarg}. %%%================================================================ %% Extracts all files from the tar file Name. -spec extract(Open :: open_type()) -> ok | {error, term()}. extract(Name) -> extract(Name, []). %% Extracts (all) files from the tar file Name. %% Options accepted: %% - cooked: Opens the tar file without mode `raw` %% - compressed: Uncompresses the tar file when reading %% - memory: Returns the tar contents as a list of tuples {Name, Bin} %% - keep_old_files: Extracted files will not overwrite the destination %% - {files, ListOfFilesToExtract}: Only extract ListOfFilesToExtract %% - verbose: Prints verbose information about the extraction, %% - {cwd, AbsoluteDir}: Sets the current working directory for the extraction -spec extract(Open :: open_type(), []) -> ok | {error, term()}; (Open :: open_type(), [extract_opt(), ...]) -> ok | {ok, [{string(), binary()}]} | {error, term()}. extract({binary, Bin}, Opts) when is_list(Opts) -> do_extract({binary, Bin}, Opts); extract({file, Fd}, Opts) when is_list(Opts) -> do_extract({file, Fd}, Opts); extract(#reader{}=Reader, Opts) when is_list(Opts) -> do_extract(Reader, Opts); extract(Name, Opts) when is_list(Name); is_binary(Name), is_list(Opts) -> do_extract(Name, Opts). do_extract(Handle, Opts) when is_list(Opts) -> Opts2 = extract_opts(Opts), case maybe_inflate_with_limit(Handle, Opts2) of {error, _} = Err -> Err; {ok, Handle2, Opts3} -> Acc0 = if Opts3#read_opts.output =:= memory -> []; true -> ok end, Acc = case Opts3#read_opts.max_size of infinity -> Acc0; _ -> {size_tracked, 0, Acc0} end, foldl_read(Handle2, fun extract1/4, Acc, Opts3) end. maybe_inflate_with_limit({binary, Bin}, #read_opts{max_size=MaxSize}=Opts) when is_integer(MaxSize), is_binary(Bin) -> case lists:member(compressed, Opts#read_opts.open_mode) of true -> case inflate_with_limit(Bin, MaxSize) of {ok, Inflated} -> OpenMode = Opts#read_opts.open_mode -- [compressed], {ok, {binary, Inflated}, Opts#read_opts{open_mode=OpenMode}}; {error, too_big} -> {error, too_big} end; false -> {ok, {binary, Bin}, Opts} end; maybe_inflate_with_limit(Handle, Opts) -> {ok, Handle, Opts}. inflate_with_limit(Bin, MaxSize) -> Z = zlib:open(), try zlib:inflateInit(Z, 31, cut), inflate_with_limit_loop(Z, Bin, MaxSize, 0, []) catch _:_ -> {ok, Bin} after zlib:close(Z) end. inflate_with_limit_loop(Z, Bin, MaxSize, Total, Acc) -> case zlib:safeInflate(Z, Bin) of {finished, Chunks} -> Size = iolist_size(Chunks), NewTotal = Total + Size, if NewTotal > MaxSize -> {error, too_big}; true -> {ok, iolist_to_binary(lists:reverse(Acc, Chunks))} end; {continue, Chunks} -> Size = iolist_size(Chunks), NewTotal = Total + Size, if NewTotal > MaxSize -> {error, too_big}; true -> inflate_with_limit_loop(Z, <<>>, MaxSize, NewTotal, [Chunks|Acc]) end end. extract1(eof, Reader, _, {size_tracked, _, Acc}) when is_list(Acc) -> {ok, {ok, lists:reverse(Acc)}, Reader}; extract1(eof, Reader, _, {size_tracked, _, leading_slash}) -> error_logger:info_msg("erl_tar: removed leading '/' from member names\n"), {ok, ok, Reader}; extract1(eof, Reader, _, {size_tracked, _, Acc}) -> {ok, Acc, Reader}; extract1(#tar_header{size=Size}=Header, Reader0, Opts, {size_tracked, Total, InnerAcc}) -> NewTotal = Total + Size, case NewTotal > Opts#read_opts.max_size of true -> throw({error, too_big}); false -> ok end, case extract1(Header, Reader0, Opts, InnerAcc) of {ok, NewInnerAcc, Reader1} -> {ok, {size_tracked, NewTotal, NewInnerAcc}, Reader1} end; extract1(eof, Reader, _, Acc) when is_list(Acc) -> {ok, {ok, lists:reverse(Acc)}, Reader}; extract1(eof, Reader, _, leading_slash) -> error_logger:info_msg("erl_tar: removed leading '/' from member names\n"), {ok, ok, Reader}; extract1(eof, Reader, _, Acc) -> {ok, Acc, Reader}; extract1(#tar_header{name=Name,size=Size}=Header, Reader0, Opts, Acc0) -> case check_extract(Name, Opts) of true -> case Opts#read_opts.output of memory -> case do_read(Reader0, Size) of {ok, Bin, Reader1} -> Acc = extract2(Header, Bin, Opts, Acc0), {ok, Acc, Reader1}; {error, _} = Err -> throw(Err) end; file -> Reader1 = extract_to_file(Header, Reader0, Opts), {ok, Acc0, Reader1} end; false -> {ok, Acc0, skip_file(Reader0)} end. extract2(Header, Bin, Opts, Acc) -> case write_extracted_element(Header, Bin, Opts) of ok -> case Header of #tar_header{name="/"++_} -> leading_slash; #tar_header{} -> Acc end; {ok, NameBin} when is_list(Acc) -> [NameBin | Acc]; {error, _} = Err -> throw(Err) end. extract_to_file(#tar_header{name=Name0}=Header, Reader0, Opts) -> case typeflag(Header#tar_header.typeflag) of regular -> Name1 = make_safe_path(Name0, Opts), case stream_to_file(Name1, Reader0, Opts) of {ok, Reader1} -> read_verbose(Opts, "x ~ts~n", [Name0]), _ = set_extracted_file_info(Name1, Header), Reader1; {error, _} = Err -> throw(Err) end; _ -> Reader1 = skip_file(Reader0), _ = write_extracted_element(Header, <<>>, Opts), Reader1 end. stream_to_file(Name, Reader0, Opts) -> Write = case Opts#read_opts.keep_old_files of true -> case file:read_file_info(Name) of {ok, _} -> false; _ -> true end; false -> true end, case Write of true -> ChunkSize = Opts#read_opts.chunk_size, case open_output_file(Name) of {ok, Fd} -> try stream_to_file_loop(Fd, Reader0, ChunkSize) after file:close(Fd) end; {error, _} = Err -> Err end; false -> {ok, skip_file(Reader0)} end. open_output_file(Name) -> case file:open(Name, [write, raw, binary]) of {ok, _} = Ok -> Ok; {error, enoent} -> ok = make_dirs(Name, file), file:open(Name, [write, raw, binary]); {error, _} = Err -> Err end. stream_to_file_loop(_Fd, #reg_file_reader{num_bytes=0}=Reader, _ChunkSize) -> {ok, Reader}; stream_to_file_loop(_Fd, #sparse_file_reader{num_bytes=0}=Reader, _ChunkSize) -> {ok, Reader}; stream_to_file_loop(Fd, Reader, ChunkSize) -> case do_read(Reader, ChunkSize) of {ok, Bin, Reader1} -> case file:write(Fd, Bin) of ok -> stream_to_file_loop(Fd, Reader1, ChunkSize); {error, _} = Err -> Err end; {error, _} = Err -> Err end. %% Checks if the file Name should be extracted. check_extract(_, #read_opts{files=all}) -> true; check_extract(Name, #read_opts{files=Files}) -> ordsets:is_element(Name, Files). %%%================================================================ %% The following table functions produce a list of information about %% the files contained in the archive. -type typeflag() :: regular | link | symlink | char | block | directory | fifo | reserved | unknown. -type mode() :: non_neg_integer(). -type uid() :: non_neg_integer(). -type gid() :: non_neg_integer(). -type tar_entry() :: {Name :: name_in_archive(), Type :: typeflag(), Size :: non_neg_integer(), MTime :: tar_time(), Mode :: mode(), Uid :: uid(), Gid :: gid()}. %% Returns a list of names of the files in the tar file Name. -spec table(Open :: open_type()) -> {ok, [name_in_archive()]} | {error, term()}. table(Name) -> table(Name, []). %% Returns a list of names of the files in the tar file Name. %% Options accepted: compressed, verbose, cooked. -spec table(Open :: open_type(), [compressed | verbose | cooked]) -> {ok, [name_in_archive() | tar_entry()]} | {error, term()}. table(Name, Opts) when is_list(Opts) -> foldl_read(Name, fun table1/4, [], table_opts(Opts)). table1(eof, Reader, _, Result) -> {ok, {ok, lists:reverse(Result)}, Reader}; table1(#tar_header{}=Header, Reader, #read_opts{verbose=Verbose}, Result) -> Attrs = table1_attrs(Header, Verbose), Reader2 = skip_file(Reader), {ok, [Attrs|Result], Reader2}. %% Extracts attributes relevant to table1's output table1_attrs(#tar_header{typeflag=Typeflag,mode=Mode}=Header, true) -> Type = typeflag(Typeflag), Name = Header#tar_header.name, Mtime = Header#tar_header.mtime, Uid = Header#tar_header.uid, Gid = Header#tar_header.gid, Size = Header#tar_header.size, {Name, Type, Size, Mtime, Mode, Uid, Gid}; table1_attrs(#tar_header{name=Name}, _Verbose) -> Name. typeflag(?TYPE_REGULAR) -> regular; typeflag(?TYPE_REGULAR_A) -> regular; typeflag(?TYPE_GNU_SPARSE) -> regular; typeflag(?TYPE_CONT) -> regular; typeflag(?TYPE_LINK) -> link; typeflag(?TYPE_SYMLINK) -> symlink; typeflag(?TYPE_CHAR) -> char; typeflag(?TYPE_BLOCK) -> block; typeflag(?TYPE_DIR) -> directory; typeflag(?TYPE_FIFO) -> fifo; typeflag(_) -> unknown. %%%================================================================ %% Comments for printing the contents of a tape archive, %% meant to be invoked from the shell. %% Prints each filename in the archive -spec t(file:filename()) -> ok | {error, term()}. t(Name) when is_list(Name); is_binary(Name) -> case table(Name) of {ok, List} -> lists:foreach(fun(N) -> ok = io:format("~ts\n", [N]) end, List); Error -> Error end. %% Prints verbose information about each file in the archive -spec tt(open_type()) -> ok | {error, term()}. tt(Name) -> case table(Name, [verbose]) of {ok, List} -> lists:foreach(fun print_header/1, List); Error -> Error end. %% Used by tt/1 to print a tar_entry tuple -spec print_header(tar_entry()) -> ok. print_header({Name, Type, Size, Mtime, Mode, Uid, Gid}) -> io:format("~s~s ~4w/~-4w ~7w ~s ~s\n", [type_to_string(Type), mode_to_string(Mode), Uid, Gid, Size, time_to_string(Mtime), Name]). type_to_string(regular) -> "-"; type_to_string(directory) -> "d"; type_to_string(link) -> "l"; type_to_string(symlink) -> "s"; type_to_string(char) -> "c"; type_to_string(block) -> "b"; type_to_string(fifo) -> "f"; type_to_string(unknown) -> "?". %% Converts a numeric mode to its human-readable representation mode_to_string(Mode) -> mode_to_string(Mode, "xwrxwrxwr", []). mode_to_string(Mode, [C|T], Acc) when Mode band 1 =:= 1 -> mode_to_string(Mode bsr 1, T, [C|Acc]); mode_to_string(Mode, [_|T], Acc) -> mode_to_string(Mode bsr 1, T, [$-|Acc]); mode_to_string(_, [], Acc) -> Acc. %% Converts a tar_time() (POSIX time) to a readable string time_to_string(Secs0) -> Epoch = calendar:datetime_to_gregorian_seconds(?EPOCH), Secs = Epoch + Secs0, DateTime0 = calendar:gregorian_seconds_to_datetime(Secs), DateTime = calendar:universal_time_to_local_time(DateTime0), {{Y, Mon, Day}, {H, Min, _}} = DateTime, io_lib:format("~s ~2w ~s:~s ~w", [month(Mon), Day, two_d(H), two_d(Min), Y]). two_d(N) -> tl(integer_to_list(N + 100)). month(1) -> "Jan"; month(2) -> "Feb"; month(3) -> "Mar"; month(4) -> "Apr"; month(5) -> "May"; month(6) -> "Jun"; month(7) -> "Jul"; month(8) -> "Aug"; month(9) -> "Sep"; month(10) -> "Oct"; month(11) -> "Nov"; month(12) -> "Dec". %%%================================================================ %% The open function with friends is to keep the file and binary api of this module -type open_type() :: file:filename_all() | {binary, binary()} | {file, file:io_device()}. -spec open(Open :: open_type(), [write | compressed | cooked]) -> {ok, tar_descriptor()} | {error, term()}. open({binary, Bin}, Mode) when is_binary(Bin) -> do_open({binary, Bin}, Mode); open({file, Fd}, Mode) -> do_open({file, Fd}, Mode); open(Name, Mode) when is_list(Name); is_binary(Name) -> do_open(Name, Mode). do_open(Name, Mode) when is_list(Mode) -> case open_mode(Mode) of {ok, Access, Raw, Opts} -> open1(Name, Access, Raw, Opts); {error, Reason} -> {error, {Name, Reason}} end. open1({binary,Bin0}=Handle, read, _Raw, Opts) when is_binary(Bin0) -> Bin = case lists:member(compressed, Opts) of true -> %% emulate file:open with Modes = [compressed_one ...] Z = zlib:open(), zlib:inflateInit(Z, 31, cut), try IoList = zlib:inflate(Z, Bin0), zlib:inflateEnd(Z), iolist_to_binary(IoList) catch _:_ -> Bin0 after zlib:close(Z) end; false -> Bin0 end, case file:open(Bin, [ram,binary,read]) of {ok,File} -> {ok, #reader{handle=File,access=read,func=fun file_op/2}}; {error, Reason} -> {error, {Handle, Reason}} end; open1({file, Fd}=Handle, read, [raw], Opts) -> case not lists:member(compressed, Opts) of true -> Reader = #reader{handle=Fd,access=read,func=fun file_op/2}, case do_position(Reader, {cur, 0}) of {ok, Pos, Reader2} -> {ok, Reader2#reader{pos=Pos}}; {error, Reason} -> {error, {Handle, Reason}} end; false -> {error, {Handle, {incompatible_option, compressed}}} end; open1({file, _Fd}=Handle, read, [], _Opts) -> {error, {Handle, {incompatible_option, cooked}}}; open1(Name, Access, Raw, Opts) when is_list(Name); is_binary(Name) -> case file:open(Name, Raw ++ [binary, Access|Opts]) of {ok, File} -> {ok, #reader{handle=File,access=Access,func=fun file_op/2}}; {error, Reason} -> {error, {Name, Reason}} end. open_mode(Mode) -> open_mode(Mode, false, [raw], []). open_mode(read, _, Raw, _) -> {ok, read, Raw, []}; open_mode(write, _, Raw, _) -> {ok, write, Raw, []}; open_mode([read|Rest], false, Raw, Opts) -> open_mode(Rest, read, Raw, Opts); open_mode([write|Rest], false, Raw, Opts) -> open_mode(Rest, write, Raw, Opts); open_mode([compressed|Rest], Access, Raw, Opts) -> open_mode(Rest, Access, Raw, [compressed,read_ahead|Opts]); open_mode([cooked|Rest], Access, _Raw, Opts) -> open_mode(Rest, Access, [], Opts); open_mode([], Access, Raw, Opts) -> {ok, Access, Raw, Opts}; open_mode(_, _, _, _) -> {error, einval}. file_op(write, {Fd, Data}) -> file:write(Fd, Data); file_op(position, {Fd, Pos}) -> file:position(Fd, Pos); file_op(read2, {Fd, Size}) -> file:read(Fd, Size); file_op(close, Fd) -> file:close(Fd). %% Closes a tar archive. -spec close(TarDescriptor :: tar_descriptor()) -> ok | {error, term()}. close(#reader{access=read}=Reader) -> ok = do_close(Reader); close(#reader{access=write}=Reader) -> {ok, Reader2} = pad_file(Reader), ok = do_close(Reader2), ok; close(_) -> {error, einval}. pad_file(#reader{pos=Pos}=Reader) -> %% There must be at least two zero blocks at the end. PadCurrent = skip_padding(Pos+?BLOCK_SIZE), Padding = <<0:PadCurrent/unit:8>>, do_write(Reader, [Padding, ?ZERO_BLOCK, ?ZERO_BLOCK]). %%%================================================================ %% Creation/modification of tar archives %% Creates a tar file Name containing the given files. -spec create(file:filename_all(), filelist()) -> ok | {error, {string(), term()}}. create(Name, FileList) when is_list(Name); is_binary(Name) -> create(Name, FileList, []). %% Creates a tar archive Name containing the given files. %% Accepted options: verbose, compressed, cooked -spec create(file:filename_all(), filelist(), [create_opt()]) -> ok | {error, term()} | {error, {string(), term()}}. create(Name, FileList, Options) when is_list(Name); is_binary(Name) -> Mode = lists:filter(fun(X) -> X =:= compressed orelse X =:= cooked end, Options), case open(Name, [write|Mode]) of {ok, TarFile} -> do_create(TarFile, FileList, Options); {error, _} = Err -> Err end. do_create(TarFile, [], _Opts) -> close(TarFile); do_create(TarFile, [{NameInArchive, NameOrBin}|Rest], Opts) -> case add(TarFile, NameOrBin, NameInArchive, Opts) of ok -> do_create(TarFile, Rest, Opts); {error, _} = Err -> _ = close(TarFile), Err end; do_create(TarFile, [Name|Rest], Opts) -> case add(TarFile, Name, Name, Opts) of ok -> do_create(TarFile, Rest, Opts); {error, _} = Err -> _ = close(TarFile), Err end. %% Adds a file to a tape archive. -spec add(TarDescriptor, Name, Options) -> ok | {error, term()} when TarDescriptor :: tar_descriptor(), Name :: name_in_archive() | {name_in_archive(), file:filename_all()}, Options :: [add_opt()]. add(Reader, {NameInArchive, Name}, Opts) when is_list(NameInArchive), is_list(Name) -> do_add(Reader, Name, NameInArchive, Opts); add(Reader, {NameInArchive, Bin}, Opts) when is_list(NameInArchive), is_binary(Bin) -> do_add(Reader, Bin, NameInArchive, Opts); add(Reader, Name, Opts) when is_list(Name) -> do_add(Reader, Name, Name, Opts). -spec add(TarDescriptor, Filename, NameInArchive, Options) -> ok | {error, term()} when TarDescriptor :: tar_descriptor(), Filename :: file:filename_all(), NameInArchive :: name_in_archive(), Options :: [add_opt()]. add(Reader, NameOrBin, NameInArchive, Options) when is_list(NameOrBin); is_binary(NameOrBin), is_list(NameInArchive), is_list(Options) -> do_add(Reader, NameOrBin, NameInArchive, Options). do_add(#reader{access=write}=Reader, Name, NameInArchive, Options) when is_list(NameInArchive), is_list(Options) -> RF = apply_file_info_opts_fun(Options, read_link_info), Opts = #add_opts{read_info=RF}, add1(Reader, Name, NameInArchive, add_opts(Options, Options, Opts)); do_add(#reader{access=read},_,_,_) -> {error, eacces}; do_add(Reader,_,_,_) -> {error, {badarg, Reader}}. add_opts([dereference|T], AllOptions, Opts) -> RF = apply_file_info_opts_fun(AllOptions, read_file_info), add_opts(T, AllOptions, Opts#add_opts{read_info=RF}); add_opts([verbose|T], AllOptions, Opts) -> add_opts(T, AllOptions, Opts#add_opts{verbose=true}); add_opts([{chunks,N}|T], AllOptions, Opts) -> add_opts(T, AllOptions, Opts#add_opts{chunk_size=N}); add_opts([{atime,Value}|T], AllOptions, Opts) -> add_opts(T, AllOptions, Opts#add_opts{atime=Value}); add_opts([{mtime,Value}|T], AllOptions, Opts) -> add_opts(T, AllOptions, Opts#add_opts{mtime=Value}); add_opts([{ctime,Value}|T], AllOptions, Opts) -> add_opts(T, AllOptions, Opts#add_opts{ctime=Value}); add_opts([{mode,Value}|T], AllOptions, Opts) -> add_opts(T, AllOptions, Opts#add_opts{mode=Value}); add_opts([{uid,Value}|T], AllOptions, Opts) -> add_opts(T, AllOptions, Opts#add_opts{uid=Value}); add_opts([{gid,Value}|T], AllOptions, Opts) -> add_opts(T, AllOptions, Opts#add_opts{gid=Value}); add_opts([_|T], AllOptions, Opts) -> add_opts(T, AllOptions, Opts); add_opts([], _AllOptions, Opts) -> Opts. apply_file_info_opts(Opts, {ok, FileInfo}) -> {ok, do_apply_file_info_opts(Opts, FileInfo)}; apply_file_info_opts(_Opts, Other) -> Other. do_apply_file_info_opts([{atime,Value}|T], FileInfo) -> do_apply_file_info_opts(T, FileInfo#file_info{atime=Value}); do_apply_file_info_opts([{mtime,Value}|T], FileInfo) -> do_apply_file_info_opts(T, FileInfo#file_info{mtime=Value}); do_apply_file_info_opts([{ctime,Value}|T], FileInfo) -> do_apply_file_info_opts(T, FileInfo#file_info{ctime=Value}); do_apply_file_info_opts([{mode,Value}|T], FileInfo) -> do_apply_file_info_opts(T, FileInfo#file_info{mode=Value}); do_apply_file_info_opts([{uid,Value}|T], FileInfo) -> do_apply_file_info_opts(T, FileInfo#file_info{uid=Value}); do_apply_file_info_opts([{gid,Value}|T], FileInfo) -> do_apply_file_info_opts(T, FileInfo#file_info{gid=Value}); do_apply_file_info_opts([_|T], FileInfo) -> do_apply_file_info_opts(T, FileInfo); do_apply_file_info_opts([], FileInfo) -> FileInfo. apply_file_info_opts_fun(Options, InfoFunction) -> fun(F) -> apply_file_info_opts(Options, file:InfoFunction(F, [{time, posix}])) end. add1(#reader{}=Reader, Name, NameInArchive, #add_opts{read_info=ReadInfo}=Opts) when is_list(Name) -> Res = case ReadInfo(Name) of {error, Reason0} -> {error, {Name, Reason0}}; {ok, #file_info{type=symlink}=Fi} -> add_verbose(Opts, "a ~ts~n", [NameInArchive]), {ok, Linkname} = file:read_link(Name), Header = fileinfo_to_header(NameInArchive, Fi, Linkname), add_header(Reader, Header, Opts); {ok, #file_info{type=regular}=Fi} -> add_verbose(Opts, "a ~ts~n", [NameInArchive]), Header = fileinfo_to_header(NameInArchive, Fi, false), {ok, Reader2} = add_header(Reader, Header, Opts), FileSize = Header#tar_header.size, {ok, FileSize, Reader3} = do_copy(Reader2, Name, Opts), Padding = skip_padding(FileSize), Pad = <<0:Padding/unit:8>>, do_write(Reader3, Pad); {ok, #file_info{type=directory}=Fi} -> add_directory(Reader, Name, NameInArchive, Fi, Opts); {ok, #file_info{}=Fi} -> add_verbose(Opts, "a ~ts~n", [NameInArchive]), Header = fileinfo_to_header(NameInArchive, Fi, false), add_header(Reader, Header, Opts) end, case Res of ok -> ok; {ok, _Reader} -> ok; {error, _Reason} = Err -> Err end; add1(Reader, Bin, NameInArchive, Opts) when is_binary(Bin) -> add_verbose(Opts, "a ~ts~n", [NameInArchive]), Now = os:system_time(seconds), Header = #tar_header{ name = NameInArchive, size = byte_size(Bin), typeflag = ?TYPE_REGULAR, atime = add_opts_time(Opts#add_opts.atime, Now), mtime = add_opts_time(Opts#add_opts.mtime, Now), ctime = add_opts_time(Opts#add_opts.ctime, Now), uid = Opts#add_opts.uid, gid = Opts#add_opts.gid, mode = Opts#add_opts.mode}, {ok, Reader2} = add_header(Reader, Header, Opts), Padding = skip_padding(byte_size(Bin)), Data = [Bin, <<0:Padding/unit:8>>], case do_write(Reader2, Data) of {ok, _Reader3} -> ok; {error, Reason} -> {error, {NameInArchive, Reason}} end. add_opts_time(undefined, Now) -> Now; add_opts_time(Time, _Now) -> Time. add_directory(Reader, DirName, NameInArchive, Info, Opts) -> case file:list_dir(DirName) of {ok, []} -> add_verbose(Opts, "a ~ts~n", [NameInArchive]), Header = fileinfo_to_header(NameInArchive, Info, false), add_header(Reader, Header, Opts); {ok, Files} -> add_verbose(Opts, "a ~ts~n", [NameInArchive]), try add_files(Reader, Files, DirName, NameInArchive, Opts) of ok -> ok; {error, _} = Err -> Err catch throw:{error, {_Name, _Reason}} = Err -> Err; throw:{error, Reason} -> {error, {DirName, Reason}} end; {error, Reason} -> {error, {DirName, Reason}} end. add_files(_Reader, [], _Dir, _DirInArchive, _Opts) -> ok; add_files(Reader, [Name|Rest], Dir, DirInArchive, #add_opts{read_info=Info}=Opts) -> FullName = filename:join(Dir, Name), NameInArchive = filename:join(DirInArchive, Name), Res = case Info(FullName) of {error, Reason} -> {error, {FullName, Reason}}; {ok, #file_info{type=directory}=Fi} -> add_directory(Reader, FullName, NameInArchive, Fi, Opts); {ok, #file_info{type=symlink}=Fi} -> add_verbose(Opts, "a ~ts~n", [NameInArchive]), {ok, Linkname} = file:read_link(FullName), Header = fileinfo_to_header(NameInArchive, Fi, Linkname), add_header(Reader, Header, Opts); {ok, #file_info{type=regular}=Fi} -> add_verbose(Opts, "a ~ts~n", [NameInArchive]), Header = fileinfo_to_header(NameInArchive, Fi, false), {ok, Reader2} = add_header(Reader, Header, Opts), FileSize = Header#tar_header.size, {ok, FileSize, Reader3} = do_copy(Reader2, FullName, Opts), Padding = skip_padding(FileSize), Pad = <<0:Padding/unit:8>>, do_write(Reader3, Pad); {ok, #file_info{}=Fi} -> add_verbose(Opts, "a ~ts~n", [NameInArchive]), Header = fileinfo_to_header(NameInArchive, Fi, false), add_header(Reader, Header, Opts) end, case Res of ok -> add_files(Reader, Rest, Dir, DirInArchive, Opts); {ok, ReaderNext} -> add_files(ReaderNext, Rest, Dir, DirInArchive, Opts); {error, _} = Err -> Err end. format_string(String, Size) when length(String) > Size -> throw({error, {write_string, field_too_long}}); format_string(String, Size) -> Ascii = to_ascii(String), if byte_size(Ascii) < Size -> [Ascii, 0]; true -> Ascii end. format_octal(Octal) -> iolist_to_binary(io_lib:fwrite("~.8B", [Octal])). add_header(#reader{}=Reader, #tar_header{}=Header, Opts) -> {ok, Iodata} = build_header(Header, Opts), do_write(Reader, Iodata). write_to_block(Block, IoData, Start) when is_list(IoData) -> write_to_block(Block, iolist_to_binary(IoData), Start); write_to_block(Block, Bin, Start) when is_binary(Bin) -> Size = byte_size(Bin), <> = Block, <>. build_header(#tar_header{}=Header, Opts) -> #tar_header{ name=Name, mode=Mode, uid=Uid, gid=Gid, size=Size, typeflag=Type, linkname=Linkname, uname=Uname, gname=Gname, devmajor=Devmaj, devminor=Devmin } = Header, Mtime = Header#tar_header.mtime, Block0 = ?ZERO_BLOCK, {Block1, Pax0} = write_string(Block0, ?V7_NAME, ?V7_NAME_LEN, Name, ?PAX_PATH, #{}), Block2 = write_octal(Block1, ?V7_MODE, ?V7_MODE_LEN, Mode), {Block3, Pax1} = write_numeric(Block2, ?V7_UID, ?V7_UID_LEN, Uid, ?PAX_UID, Pax0), {Block4, Pax2} = write_numeric(Block3, ?V7_GID, ?V7_GID_LEN, Gid, ?PAX_GID, Pax1), {Block5, Pax3} = write_numeric(Block4, ?V7_SIZE, ?V7_SIZE_LEN, Size, ?PAX_SIZE, Pax2), {Block6, Pax4} = write_numeric(Block5, ?V7_MTIME, ?V7_MTIME_LEN, Mtime, ?PAX_NONE, Pax3), {Block7, Pax5} = write_string(Block6, ?V7_TYPE, ?V7_TYPE_LEN, <>, ?PAX_NONE, Pax4), {Block8, Pax6} = write_string(Block7, ?V7_LINKNAME, ?V7_LINKNAME_LEN, Linkname, ?PAX_LINKPATH, Pax5), {Block9, Pax7} = write_string(Block8, ?USTAR_UNAME, ?USTAR_UNAME_LEN, Uname, ?PAX_UNAME, Pax6), {Block10, Pax8} = write_string(Block9, ?USTAR_GNAME, ?USTAR_GNAME_LEN, Gname, ?PAX_GNAME, Pax7), {Block11, Pax9} = write_numeric(Block10, ?USTAR_DEVMAJ, ?USTAR_DEVMAJ_LEN, Devmaj, ?PAX_NONE, Pax8), {Block12, Pax10} = write_numeric(Block11, ?USTAR_DEVMIN, ?USTAR_DEVMIN_LEN, Devmin, ?PAX_NONE, Pax9), {Block13, Pax11} = set_path(Block12, Pax10), PaxEntry = case maps:size(Pax11) of 0 -> []; _ -> build_pax_entry(Header, Pax11, Opts) end, Block14 = set_format(Block13, ?FORMAT_USTAR), Block15 = set_checksum(Block14), {ok, [PaxEntry, Block15]}. set_path(Block0, Pax) -> %% only use ustar header when name is too long case maps:get(?PAX_PATH, Pax, nil) of nil -> {Block0, Pax}; PaxPath -> case split_ustar_path(PaxPath) of {ok, UstarName, UstarPrefix} -> {Block1, _} = write_string(Block0, ?V7_NAME, ?V7_NAME_LEN, UstarName, ?PAX_NONE, #{}), {Block2, _} = write_string(Block1, ?USTAR_PREFIX, ?USTAR_PREFIX_LEN, UstarPrefix, ?PAX_NONE, #{}), {Block2, maps:remove(?PAX_PATH, Pax)}; false -> {Block0, Pax} end end. set_format(Block0, Format) when Format =:= ?FORMAT_USTAR; Format =:= ?FORMAT_PAX -> Block1 = write_to_block(Block0, ?MAGIC_USTAR, ?USTAR_MAGIC), write_to_block(Block1, ?VERSION_USTAR, ?USTAR_VERSION); set_format(_Block, Format) -> throw({error, {invalid_format, Format}}). set_checksum(Block) -> Checksum = compute_checksum(Block), write_octal(Block, ?V7_CHKSUM, ?V7_CHKSUM_LEN, Checksum). build_pax_entry(Header, PaxAttrs, Opts) -> Path = Header#tar_header.name, Filename = filename:basename(Path), Dir = filename:dirname(Path), Path2 = filename:join([Dir, "PaxHeaders.0", Filename]), AsciiPath = to_ascii(Path2), Path3 = if byte_size(AsciiPath) > ?V7_NAME_LEN -> binary_part(AsciiPath, 0, ?V7_NAME_LEN - 1); true -> AsciiPath end, Keys = maps:keys(PaxAttrs), SortedKeys = lists:sort(Keys), PaxFile = build_pax_file(SortedKeys, PaxAttrs), Size = byte_size(PaxFile), Padding = (?BLOCK_SIZE - (byte_size(PaxFile) rem ?BLOCK_SIZE)) rem ?BLOCK_SIZE, Pad = <<0:Padding/unit:8>>, PaxHeader = #tar_header{ name=unicode:characters_to_list(Path3), size=Size, mtime=Header#tar_header.mtime, atime=Header#tar_header.atime, ctime=Header#tar_header.ctime, typeflag=?TYPE_X_HEADER }, {ok, PaxHeaderData} = build_header(PaxHeader, Opts), [PaxHeaderData, PaxFile, Pad]. build_pax_file(Keys, PaxAttrs) -> build_pax_file(Keys, PaxAttrs, []). build_pax_file([], _, Acc) -> unicode:characters_to_binary(Acc); build_pax_file([K|Rest], Attrs, Acc) -> V = maps:get(K, Attrs), Size = sizeof(K) + sizeof(V) + 3, Size2 = sizeof(Size) + Size, Key = to_string(K), Value = to_string(V), Record = unicode:characters_to_binary(io_lib:format("~B ~ts=~ts\n", [Size2, Key, Value])), if byte_size(Record) =/= Size2 -> Size3 = byte_size(Record), Record2 = io_lib:format("~B ~ts=~ts\n", [Size3, Key, Value]), build_pax_file(Rest, Attrs, [Acc, Record2]); true -> build_pax_file(Rest, Attrs, [Acc, Record]) end. sizeof(Bin) when is_binary(Bin) -> byte_size(Bin); sizeof(List) when is_list(List) -> length(List); sizeof(N) when is_integer(N) -> byte_size(integer_to_binary(N)); sizeof(N) when is_float(N) -> byte_size(float_to_binary(N)). to_string(Bin) when is_binary(Bin) -> unicode:characters_to_list(Bin); to_string(List) when is_list(List) -> List; to_string(N) when is_integer(N) -> integer_to_list(N); to_string(N) when is_float(N) -> float_to_list(N). split_ustar_path(Path) -> Len = length(Path), NotAscii = not is_ascii(Path), if Len =< ?V7_NAME_LEN; NotAscii -> false; true -> PathBin = binary:list_to_bin(Path), case filename:split(PathBin) of [Part] when byte_size(Part) >= ?V7_NAME_LEN -> false; Parts -> case lists:last(Parts) of Name when byte_size(Name) >= ?V7_NAME_LEN -> false; Name -> Parts2 = lists:sublist(Parts, length(Parts) - 1), join_split_ustar_path(Parts2, {ok, Name, nil}) end end end. join_split_ustar_path([], Acc) -> Acc; join_split_ustar_path([Part|_], {ok, _, nil}) when byte_size(Part) > ?USTAR_PREFIX_LEN -> false; join_split_ustar_path([Part|_], {ok, _Name, Acc}) when (byte_size(Part)+byte_size(Acc)) > ?USTAR_PREFIX_LEN -> false; join_split_ustar_path([Part|Rest], {ok, Name, nil}) -> join_split_ustar_path(Rest, {ok, Name, Part}); join_split_ustar_path([Part|Rest], {ok, Name, Acc}) -> join_split_ustar_path(Rest, {ok, Name, <>}). write_octal(Block, Pos, Size, X) -> Octal = zero_pad(format_octal(X), Size-1), if byte_size(Octal) < Size -> write_to_block(Block, Octal, Pos); true -> throw({error, {write_failed, octal_field_too_long}}) end. write_string(Block, Pos, Size, Str, PaxAttr, Pax0) -> NotAscii = not is_ascii(Str), if PaxAttr =/= ?PAX_NONE andalso (length(Str) > Size orelse NotAscii) -> Pax1 = maps:put(PaxAttr, Str, Pax0), {Block, Pax1}; true -> Formatted = format_string(Str, Size), {write_to_block(Block, Formatted, Pos), Pax0} end. write_numeric(Block, Pos, Size, X, PaxAttr, Pax0) -> %% attempt octal Octal = zero_pad(format_octal(X), Size-1), if byte_size(Octal) < Size -> {write_to_block(Block, [Octal, 0], Pos), Pax0}; PaxAttr =/= ?PAX_NONE -> Pax1 = maps:put(PaxAttr, X, Pax0), {Block, Pax1}; true -> throw({error, {write_failed, numeric_field_too_long}}) end. zero_pad(Str, Size) when byte_size(Str) >= Size -> Str; zero_pad(Str, Size) -> Padding = Size - byte_size(Str), Pad = binary:copy(<<$0>>, Padding), <>. %%%================================================================ %% Functions for creating or modifying tar archives read_block(Reader) -> case do_read(Reader, ?BLOCK_SIZE) of eof -> throw({error, eof}); %% Two zero blocks mark the end of the archive {ok, ?ZERO_BLOCK, Reader1} -> case do_read(Reader1, ?BLOCK_SIZE) of eof -> % This is technically a malformed end-of-archive marker, % as two ZERO_BLOCKs are expected as the marker, % but if we've already made it this far, we should just ignore it eof; {ok, ?ZERO_BLOCK, _Reader2} -> eof; {ok, _Block, _Reader2} -> throw({error, invalid_end_of_archive}); {error,_} = Err -> throw(Err) end; {ok, Block, Reader1} when is_binary(Block) -> {ok, Block, Reader1}; {error, _} = Err -> throw(Err) end. get_header(#reader{}=Reader) -> case read_block(Reader) of eof -> eof; {ok, Block, Reader1} -> convert_header(Block, Reader1) end. %% Converts the tar header to a record. to_v7(Bin) when is_binary(Bin), byte_size(Bin) =:= ?BLOCK_SIZE -> #header_v7{ name=binary_part(Bin, ?V7_NAME, ?V7_NAME_LEN), mode=binary_part(Bin, ?V7_MODE, ?V7_MODE_LEN), uid=binary_part(Bin, ?V7_UID, ?V7_UID_LEN), gid=binary_part(Bin, ?V7_GID, ?V7_GID_LEN), size=binary_part(Bin, ?V7_SIZE, ?V7_SIZE_LEN), mtime=binary_part(Bin, ?V7_MTIME, ?V7_MTIME_LEN), checksum=binary_part(Bin, ?V7_CHKSUM, ?V7_CHKSUM_LEN), typeflag=binary:at(Bin, ?V7_TYPE), linkname=binary_part(Bin, ?V7_LINKNAME, ?V7_LINKNAME_LEN) }; to_v7(_) -> {error, header_block_too_small}. to_gnu(#header_v7{}=V7, Bin) when is_binary(Bin), byte_size(Bin) =:= ?BLOCK_SIZE -> #header_gnu{ header_v7=V7, magic=binary_part(Bin, ?GNU_MAGIC, ?GNU_MAGIC_LEN), version=binary_part(Bin, ?GNU_VERSION, ?GNU_VERSION_LEN), uname=binary_part(Bin, 265, 32), gname=binary_part(Bin, 297, 32), devmajor=binary_part(Bin, 329, 8), devminor=binary_part(Bin, 337, 8), atime=binary_part(Bin, 345, 12), ctime=binary_part(Bin, 357, 12), sparse=to_sparse_array(binary_part(Bin, 386, 24*4+1)), real_size=binary_part(Bin, 483, 12) }. to_star(#header_v7{}=V7, Bin) when is_binary(Bin), byte_size(Bin) =:= ?BLOCK_SIZE -> #header_star{ header_v7=V7, magic=binary_part(Bin, ?USTAR_MAGIC, ?USTAR_MAGIC_LEN), version=binary_part(Bin, ?USTAR_VERSION, ?USTAR_VERSION_LEN), uname=binary_part(Bin, ?USTAR_UNAME, ?USTAR_UNAME_LEN), gname=binary_part(Bin, ?USTAR_GNAME, ?USTAR_GNAME_LEN), devmajor=binary_part(Bin, ?USTAR_DEVMAJ, ?USTAR_DEVMAJ_LEN), devminor=binary_part(Bin, ?USTAR_DEVMIN, ?USTAR_DEVMIN_LEN), prefix=binary_part(Bin, 345, 131), atime=binary_part(Bin, 476, 12), ctime=binary_part(Bin, 488, 12), trailer=binary_part(Bin, ?STAR_TRAILER, ?STAR_TRAILER_LEN) }. to_ustar(#header_v7{}=V7, Bin) when is_binary(Bin), byte_size(Bin) =:= ?BLOCK_SIZE -> #header_ustar{ header_v7=V7, magic=binary_part(Bin, ?USTAR_MAGIC, ?USTAR_MAGIC_LEN), version=binary_part(Bin, ?USTAR_VERSION, ?USTAR_VERSION_LEN), uname=binary_part(Bin, ?USTAR_UNAME, ?USTAR_UNAME_LEN), gname=binary_part(Bin, ?USTAR_GNAME, ?USTAR_GNAME_LEN), devmajor=binary_part(Bin, ?USTAR_DEVMAJ, ?USTAR_DEVMAJ_LEN), devminor=binary_part(Bin, ?USTAR_DEVMIN, ?USTAR_DEVMIN_LEN), prefix=binary_part(Bin, 345, 155) }. to_sparse_array(Bin) when is_binary(Bin) -> MaxEntries = byte_size(Bin) div 24, IsExtended = 1 =:= binary:at(Bin, 24*MaxEntries), Entries = parse_sparse_entries(Bin, MaxEntries-1, []), #sparse_array{ entries=Entries, max_entries=MaxEntries, is_extended=IsExtended }. parse_sparse_entries(<<>>, _, Acc) -> Acc; parse_sparse_entries(_, -1, Acc) -> Acc; parse_sparse_entries(Bin, N, Acc) -> case to_sparse_entry(binary_part(Bin, N*24, 24)) of nil -> parse_sparse_entries(Bin, N-1, Acc); Entry = #sparse_entry{} -> parse_sparse_entries(Bin, N-1, [Entry|Acc]) end. -define(EMPTY_ENTRY, <<0,0,0,0,0,0,0,0,0,0,0,0>>). to_sparse_entry(Bin) when is_binary(Bin), byte_size(Bin) =:= 24 -> OffsetBin = binary_part(Bin, 0, 12), NumBytesBin = binary_part(Bin, 12, 12), case {OffsetBin, NumBytesBin} of {?EMPTY_ENTRY, ?EMPTY_ENTRY} -> nil; _ -> #sparse_entry{ offset=parse_numeric(OffsetBin), num_bytes=parse_numeric(NumBytesBin)} end. -spec get_format(binary()) -> {ok, pos_integer(), header_v7()} | ?FORMAT_UNKNOWN | {error, term()}. get_format(Bin) when is_binary(Bin), byte_size(Bin) =:= ?BLOCK_SIZE -> do_get_format(to_v7(Bin), Bin). do_get_format({error, _} = Err, _Bin) -> Err; do_get_format(#header_v7{}=V7, Bin) when is_binary(Bin), byte_size(Bin) =:= ?BLOCK_SIZE -> Checksum = parse_octal(V7#header_v7.checksum), IsBadChecksum = case compute_checksum(Bin) of Checksum -> false; _ -> compute_signed_checksum(Bin) =/= Checksum end, case IsBadChecksum of true -> ?FORMAT_UNKNOWN; false -> %% guess magic Ustar = to_ustar(V7, Bin), Star = to_star(V7, Bin), Magic = Ustar#header_ustar.magic, Version = Ustar#header_ustar.version, Trailer = Star#header_star.trailer, Format = if Magic =:= ?MAGIC_USTAR, Trailer =:= ?TRAILER_STAR -> ?FORMAT_STAR; Magic =:= ?MAGIC_USTAR -> ?FORMAT_USTAR; Magic =:= ?MAGIC_GNU, Version =:= ?VERSION_GNU -> ?FORMAT_GNU; true -> ?FORMAT_V7 end, {ok, Format, V7} end. unpack_format(Format, #header_v7{}=V7, Bin, Reader) when is_binary(Bin), byte_size(Bin) =:= ?BLOCK_SIZE -> Mtime = parse_numeric(V7#header_v7.mtime), Header0 = #tar_header{ name=parse_string(V7#header_v7.name), mode=parse_numeric(V7#header_v7.mode), uid=parse_numeric(V7#header_v7.uid), gid=parse_numeric(V7#header_v7.gid), size=parse_numeric(V7#header_v7.size), mtime=Mtime, atime=Mtime, ctime=Mtime, typeflag=V7#header_v7.typeflag, linkname=parse_string(V7#header_v7.linkname) }, Typeflag = Header0#tar_header.typeflag, Header1 = if Format > ?FORMAT_V7 -> unpack_modern(Format, V7, Bin, Header0); true -> Name = Header0#tar_header.name, Header0#tar_header{name=safe_join_path("", Name)} end, HeaderOnly = is_header_only_type(Typeflag), Header2 = if HeaderOnly -> Header1#tar_header{size=0}; true -> Header1 end, if Typeflag =:= ?TYPE_GNU_SPARSE -> Gnu = to_gnu(V7, Bin), RealSize = parse_numeric(Gnu#header_gnu.real_size), {Sparsemap, Reader2} = parse_sparse_map(Gnu, Reader), Header3 = Header2#tar_header{size=RealSize}, {Header3, new_sparse_file_reader(Reader2, Sparsemap, RealSize)}; true -> FileReader = #reg_file_reader{ handle=Reader, num_bytes=Header2#tar_header.size, size=Header2#tar_header.size, pos = 0 }, {Header2, FileReader} end. unpack_modern(Format, #header_v7{}=V7, Bin, #tar_header{}=Header0) when is_binary(Bin) -> Typeflag = Header0#tar_header.typeflag, Ustar = to_ustar(V7, Bin), H0 = Header0#tar_header{ uname=parse_string(Ustar#header_ustar.uname), gname=parse_string(Ustar#header_ustar.gname)}, H1 = if Typeflag =:= ?TYPE_CHAR orelse Typeflag =:= ?TYPE_BLOCK -> Ma = parse_numeric(Ustar#header_ustar.devmajor), Mi = parse_numeric(Ustar#header_ustar.devminor), H0#tar_header{ devmajor=Ma, devminor=Mi }; true -> H0 end, {Prefix, H2} = case Format of ?FORMAT_USTAR -> {parse_string(Ustar#header_ustar.prefix), H1}; ?FORMAT_STAR -> Star = to_star(V7, Bin), Prefix0 = parse_string(Star#header_star.prefix), Atime0 = Star#header_star.atime, Atime = parse_numeric(Atime0), Ctime0 = Star#header_star.ctime, Ctime = parse_numeric(Ctime0), {Prefix0, H1#tar_header{ atime=Atime, ctime=Ctime }}; _ -> {"", H1} end, Name = H2#tar_header.name, H2#tar_header{name=safe_join_path(Prefix, Name)}. safe_join_path([], Name) -> filename:join([Name]); safe_join_path(Prefix, []) -> filename:join([Prefix]); safe_join_path(Prefix, Name) -> filename:join(Prefix, Name). new_sparse_file_reader(Reader, Sparsemap, RealSize) -> true = validate_sparse_entries(Sparsemap, RealSize), #sparse_file_reader{ handle = Reader, num_bytes = RealSize, pos = 0, size = RealSize, sparse_map = Sparsemap}. validate_sparse_entries(Entries, RealSize) -> validate_sparse_entries(Entries, RealSize, 0, 0). validate_sparse_entries([], _RealSize, _I, _LastOffset) -> true; validate_sparse_entries([#sparse_entry{}=Entry|Rest], RealSize, I, LastOffset) -> Offset = Entry#sparse_entry.offset, NumBytes = Entry#sparse_entry.num_bytes, if Offset > ?MAX_INT64-NumBytes -> throw({error, {invalid_sparse_map_entry, offset_too_large}}); Offset+NumBytes > RealSize -> throw({error, {invalid_sparse_map_entry, offset_too_large}}); I > 0 andalso LastOffset > Offset -> throw({error, {invalid_sparse_map_entry, overlapping_offsets}}); true -> ok end, validate_sparse_entries(Rest, RealSize, I+1, Offset+NumBytes). -spec parse_sparse_map(header_gnu(), descriptor_type()) -> {[sparse_entry()], descriptor_type()}. parse_sparse_map(#header_gnu{sparse=Sparse}, Reader) when Sparse#sparse_array.is_extended -> parse_sparse_map(Sparse, Reader, []); parse_sparse_map(#header_gnu{sparse=Sparse}, Reader) -> {Sparse#sparse_array.entries, Reader}. parse_sparse_map(#sparse_array{is_extended=true,entries=Entries}, Reader, Acc) -> case read_block(Reader) of eof -> throw({error, eof}); {ok, Block, Reader2} -> Sparse2 = to_sparse_array(Block), parse_sparse_map(Sparse2, Reader2, Entries++Acc) end; parse_sparse_map(#sparse_array{entries=Entries}, Reader, Acc) -> Sorted = lists:sort(fun (#sparse_entry{offset=A},#sparse_entry{offset=B}) -> A =< B end, Entries++Acc), {Sorted, Reader}. %% Defined by taking the sum of the unsigned byte values of the %% entire header record, treating the checksum bytes to as ASCII spaces compute_checksum(<>) -> C0 = checksum(H1) + (byte_size(H2) * $\s), C1 = checksum(Rest), C0 + C1. compute_signed_checksum(<>) -> C0 = signed_checksum(H1) + (byte_size(H2) * $\s), C1 = signed_checksum(Rest), C0 + C1. %% Returns the checksum of a binary. checksum(Bin) -> checksum(Bin, 0). checksum(<>, Sum) -> checksum(Rest, Sum+A+B+C+D); checksum(<>, Sum) -> checksum(Rest, Sum+A); checksum(<<>>, Sum) -> Sum. signed_checksum(Bin) -> signed_checksum(Bin, 0). signed_checksum(<>, Sum) -> signed_checksum(Rest, Sum+A); signed_checksum(<<>>, Sum) -> Sum. -spec parse_numeric(binary()) -> non_neg_integer(). parse_numeric(<<>>) -> 0; parse_numeric(<> = Bin) -> %% check for base-256 format first %% if the bit is set, then all following bits constitute a two's %% complement encoded number in big-endian byte order if First band 16#80 =/= 0 -> %% Handling negative numbers relies on the following identity: %% -a-1 == ^a %% If the number is negative, we use an inversion mask to invert %% the data bytes and treat the value as an unsigned number Inv = if First band 16#40 =/= 0 -> 16#00; true -> 16#FF end, Bytes = binary:bin_to_list(Bin), Reducer = fun (C, {I, X}) -> C1 = C bxor Inv, C2 = if I =:= 0 -> C1 band 16#7F; true -> C1 end, if (X bsr 56) > 0 -> throw({error,integer_overflow}); true -> {I+1, (X bsl 8) bor C2} end end, {_, N} = lists:foldl(Reducer, {0,0}, Bytes), if (N bsr 63) > 0 -> throw({error, integer_overflow}); true -> if Inv =:= 16#FF -> -1 bxor N; true -> N end end; true -> %% normal case is an octal number parse_octal(Bin) end. parse_octal(<>) -> %% skip leading/trailing zero bytes and spaces do_parse_octal(Bin, 0). do_parse_octal(<<$\s, Rest/binary>>, Acc) -> do_parse_octal(Rest, Acc); do_parse_octal(<<0, Rest/binary>>, Acc) -> do_parse_octal(Rest, Acc); do_parse_octal(<>, Acc) -> Digit = C - $0, case Digit band 7 of Digit -> do_parse_octal(Rest, Acc bsl 3 bor Digit); _ -> throw({error, invalid_tar_checksum}) end; do_parse_octal(<<>>, Acc) -> Acc. parse_string(Bin) when is_binary(Bin) -> N = strlen(Bin, 0), <> = Bin, case unicode:characters_to_list(Prefix) of Str when is_list(Str) -> Str; {incomplete, _Str, _Rest} -> binary_to_list(Bin); {error, _Str, _Rest} -> throw({error, {bad_header, invalid_string}}) end. strlen(<<>>, N) -> N; strlen(<<0, _/binary>>, N) -> N; strlen(<<_, Rest/binary>>, N) -> strlen(Rest, N + 1). convert_header(Bin, #reader{pos=Pos}=Reader) when byte_size(Bin) =:= ?BLOCK_SIZE, (Pos rem ?BLOCK_SIZE) =:= 0 -> case get_format(Bin) of ?FORMAT_UNKNOWN -> throw({error, bad_header}); {ok, Format, V7} -> unpack_format(Format, V7, Bin, Reader); {error, Reason} -> throw({error, {bad_header, Reason}}) end; convert_header(Bin, #reader{pos=Pos}) when byte_size(Bin) =:= ?BLOCK_SIZE -> throw({error, misaligned_read, Pos}); convert_header(Bin, _Reader) when byte_size(Bin) =:= 0 -> eof; convert_header(_Bin, _Reader) -> throw({error, eof}). %% Creates a partially-populated header record based %% on the provided file_info record. If the file is %% a symlink, then `link` is used as the link target. %% If the file is a directory, a slash is appended to the name. fileinfo_to_header(Name, #file_info{}=Fi, Link) when is_list(Name) -> BaseHeader = #tar_header{name=Name, mtime=Fi#file_info.mtime, atime=Fi#file_info.atime, ctime=Fi#file_info.ctime, mode=Fi#file_info.mode, uid=Fi#file_info.uid, gid=Fi#file_info.gid, typeflag=?TYPE_REGULAR}, do_fileinfo_to_header(BaseHeader, Fi, Link). do_fileinfo_to_header(Header, #file_info{size=Size,type=regular}, _Link) -> Header#tar_header{size=Size,typeflag=?TYPE_REGULAR}; do_fileinfo_to_header(#tar_header{name=Name}=Header, #file_info{type=directory}, _Link) -> Header#tar_header{name=Name++"/",typeflag=?TYPE_DIR}; do_fileinfo_to_header(Header, #file_info{type=symlink}, Link) -> Header#tar_header{typeflag=?TYPE_SYMLINK,linkname=Link}; do_fileinfo_to_header(Header, #file_info{type=device,mode=Mode}=Fi, _Link) when (Mode band ?S_IFMT) =:= ?S_IFCHR -> Header#tar_header{typeflag=?TYPE_CHAR, devmajor=Fi#file_info.major_device, devminor=Fi#file_info.minor_device}; do_fileinfo_to_header(Header, #file_info{type=device,mode=Mode}=Fi, _Link) when (Mode band ?S_IFMT) =:= ?S_IFBLK -> Header#tar_header{typeflag=?TYPE_BLOCK, devmajor=Fi#file_info.major_device, devminor=Fi#file_info.minor_device}; do_fileinfo_to_header(Header, #file_info{type=other,mode=Mode}, _Link) when (Mode band ?S_IFMT) =:= ?S_FIFO -> Header#tar_header{typeflag=?TYPE_FIFO}; do_fileinfo_to_header(Header, Fi, _Link) -> {error, {invalid_file_type, Header#tar_header.name, Fi}}. is_ascii(Str) when is_list(Str) -> not lists:any(fun (Char) -> Char >= 16#80 end, Str); is_ascii(Bin) when is_binary(Bin) -> is_ascii1(Bin). is_ascii1(<<>>) -> true; is_ascii1(<>) when C >= 16#80 -> false; is_ascii1(<<_, Rest/binary>>) -> is_ascii1(Rest). to_ascii(Str) when is_list(Str) -> case is_ascii(Str) of true -> unicode:characters_to_binary(Str); false -> Chars = lists:filter(fun (Char) -> Char < 16#80 end, Str), unicode:characters_to_binary(Chars) end; to_ascii(Bin) when is_binary(Bin) -> to_ascii(Bin, <<>>). to_ascii(<<>>, Acc) -> Acc; to_ascii(<>, Acc) when C < 16#80 -> to_ascii(Rest, <>); to_ascii(<<_, Rest/binary>>, Acc) -> to_ascii(Rest, Acc). is_header_only_type(?TYPE_SYMLINK) -> true; is_header_only_type(?TYPE_LINK) -> true; is_header_only_type(?TYPE_DIR) -> true; is_header_only_type(_) -> false. foldl_read(#reader{access=read}=Reader, Fun, Accu, #read_opts{}=Opts) when is_function(Fun,4) -> case foldl_read0(Reader, Fun, Accu, Opts) of {ok, Result, _Reader2} -> Result; {error, _} = Err -> Err end; foldl_read(#reader{access=Access}, _Fun, _Accu, _Opts) -> {error, {read_mode_expected, Access}}; foldl_read(TarName, Fun, Accu, #read_opts{}=Opts) when is_function(Fun,4) -> try open(TarName, [read|Opts#read_opts.open_mode]) of {ok, #reader{access=read}=Reader} -> try foldl_read(Reader, Fun, Accu, Opts) after _ = close(Reader) end; {error, _} = Err -> Err catch throw:Err -> Err end. foldl_read0(Reader, Fun, Accu, Opts) -> try foldl_read1(Fun, Accu, Reader, Opts, #{}) of {ok,_,_} = Ok -> Ok catch throw:{error, {Reason, Format, Args}} -> read_verbose(Opts, Format, Args), {error, Reason}; throw:Err -> Err end. foldl_read1(Fun, Accu0, Reader0, Opts, ExtraHeaders) -> {ok, Reader1} = skip_unread(Reader0), case get_header(Reader1) of eof -> Fun(eof, Reader1, Opts, Accu0); {Header, Reader2} -> case Header#tar_header.typeflag of ?TYPE_X_HEADER -> {ExtraHeaders2, Reader3} = parse_pax(Reader2), ExtraHeaders3 = maps:merge(ExtraHeaders, ExtraHeaders2), foldl_read1(Fun, Accu0, Reader3, Opts, ExtraHeaders3); ?TYPE_GNU_LONGNAME -> {RealName, Reader3} = get_real_name(Reader2), ExtraHeaders2 = maps:put(?PAX_PATH, parse_string(RealName), ExtraHeaders), foldl_read1(Fun, Accu0, Reader3, Opts, ExtraHeaders2); ?TYPE_GNU_LONGLINK -> {RealName, Reader3} = get_real_name(Reader2), ExtraHeaders2 = maps:put(?PAX_LINKPATH, parse_string(RealName), ExtraHeaders), foldl_read1(Fun, Accu0, Reader3, Opts, ExtraHeaders2); _ -> Header1 = merge_pax(Header, ExtraHeaders), {ok, NewAccu, Reader3} = Fun(Header1, Reader2, Opts, Accu0), foldl_read1(Fun, NewAccu, Reader3, Opts, #{}) end end. %% Applies all known PAX attributes to the current tar header -spec merge_pax(tar_header(), #{binary() => binary()}) -> tar_header(). merge_pax(Header, ExtraHeaders) when is_map(ExtraHeaders) -> do_merge_pax(Header, maps:to_list(ExtraHeaders)). do_merge_pax(Header, []) -> Header; do_merge_pax(Header, [{?PAX_PATH, Path}|Rest]) -> do_merge_pax(Header#tar_header{name=unicode:characters_to_list(Path)}, Rest); do_merge_pax(Header, [{?PAX_LINKPATH, LinkPath}|Rest]) -> do_merge_pax(Header#tar_header{linkname=unicode:characters_to_list(LinkPath)}, Rest); do_merge_pax(Header, [{?PAX_GNAME, Gname}|Rest]) -> do_merge_pax(Header#tar_header{gname=unicode:characters_to_list(Gname)}, Rest); do_merge_pax(Header, [{?PAX_UNAME, Uname}|Rest]) -> do_merge_pax(Header#tar_header{uname=unicode:characters_to_list(Uname)}, Rest); do_merge_pax(Header, [{?PAX_UID, Uid}|Rest]) -> Uid2 = binary_to_integer(Uid), do_merge_pax(Header#tar_header{uid=Uid2}, Rest); do_merge_pax(Header, [{?PAX_GID, Gid}|Rest]) -> Gid2 = binary_to_integer(Gid), do_merge_pax(Header#tar_header{gid=Gid2}, Rest); do_merge_pax(Header, [{?PAX_ATIME, Atime}|Rest]) -> Atime2 = parse_pax_time(Atime), do_merge_pax(Header#tar_header{atime=Atime2}, Rest); do_merge_pax(Header, [{?PAX_MTIME, Mtime}|Rest]) -> Mtime2 = parse_pax_time(Mtime), do_merge_pax(Header#tar_header{mtime=Mtime2}, Rest); do_merge_pax(Header, [{?PAX_CTIME, Ctime}|Rest]) -> Ctime2 = parse_pax_time(Ctime), do_merge_pax(Header#tar_header{ctime=Ctime2}, Rest); do_merge_pax(Header, [{?PAX_SIZE, Size}|Rest]) -> Size2 = binary_to_integer(Size), do_merge_pax(Header#tar_header{size=Size2}, Rest); do_merge_pax(Header, [{<>, _Value}|Rest]) -> do_merge_pax(Header, Rest); do_merge_pax(Header, [_Ignore|Rest]) -> do_merge_pax(Header, Rest). %% Returns the time since UNIX epoch as a datetime -spec parse_pax_time(binary()) -> tar_time(). parse_pax_time(Bin) when is_binary(Bin) -> TotalNano = case binary:split(Bin, [<<$.>>]) of [SecondsStr, NanoStr0] -> Seconds = binary_to_integer(SecondsStr), if byte_size(NanoStr0) < ?MAX_NANO_INT_SIZE -> %% right pad PaddingN = ?MAX_NANO_INT_SIZE-byte_size(NanoStr0), Padding = binary:copy(<<$0>>, PaddingN), NanoStr1 = <>, Nano = binary_to_integer(NanoStr1), (Seconds*?BILLION)+Nano; byte_size(NanoStr0) > ?MAX_NANO_INT_SIZE -> %% right truncate NanoStr1 = binary_part(NanoStr0, 0, ?MAX_NANO_INT_SIZE), Nano = binary_to_integer(NanoStr1), (Seconds*?BILLION)+Nano; true -> (Seconds*?BILLION)+binary_to_integer(NanoStr0) end; [SecondsStr] -> binary_to_integer(SecondsStr)*?BILLION end, %% truncate to microseconds Micro = TotalNano div 1000, Mega = Micro div 1000000000000, Secs = Micro div 1000000 - (Mega*1000000), Secs. %% Given a regular file reader, reads the whole file and %% parses all extended attributes it contains. parse_pax(#reg_file_reader{handle=Handle,num_bytes=0}) -> {#{}, Handle}; parse_pax(#reg_file_reader{handle=Handle0,num_bytes=NumBytes}) -> case do_read(Handle0, NumBytes) of {ok, Bytes, Handle1} -> do_parse_pax(Handle1, Bytes, #{}); {error, _} = Err -> throw(Err) end. do_parse_pax(Reader, <<>>, Headers) -> {Headers, Reader}; do_parse_pax(Reader, Bin, Headers) -> {Key, Value, Residual} = parse_pax_record(Bin), NewHeaders = maps:put(Key, Value, Headers), do_parse_pax(Reader, Residual, NewHeaders). %% Parse an extended attribute parse_pax_record(Bin) when is_binary(Bin) -> case binary:split(Bin, [<<$\n>>]) of [Record, Residual] -> case binary:split(Record, [<<$\s>>], [trim_all]) of [_Len, Record1] -> case binary:split(Record1, [<<$=>>], [trim_all]) of [AttrName, AttrValue] -> {AttrName, AttrValue, Residual}; _Other -> throw({error, malformed_pax_record}) end; _Other -> throw({error, malformed_pax_record}) end; _Other -> throw({error, malformed_pax_record}) end. get_real_name(#reg_file_reader{handle=Handle,num_bytes=0}) -> {"", Handle}; get_real_name(#reg_file_reader{handle=Handle0,num_bytes=NumBytes}) -> case do_read(Handle0, NumBytes) of {ok, RealName, Handle1} -> {RealName, Handle1}; {error, _} = Err -> throw(Err) end; get_real_name(#sparse_file_reader{num_bytes=NumBytes}=Reader0) -> case do_read(Reader0, NumBytes) of {ok, RealName, Reader1} -> {RealName, Reader1}; {error, _} = Err -> throw(Err) end. %% Skip the remaining bytes for the current file entry skip_file(#reg_file_reader{handle=Handle0,pos=Pos,size=Size}=Reader) -> Padding = skip_padding(Size), AbsPos = Handle0#reader.pos + (Size-Pos) + Padding, case do_position(Handle0, AbsPos) of {ok, _, Handle1} -> Reader#reg_file_reader{handle=Handle1,num_bytes=0,pos=Size}; Err -> throw(Err) end; skip_file(#sparse_file_reader{pos=Pos,size=Size}=Reader) -> case do_read(Reader, Size-Pos) of {ok, _, Reader2} -> Reader2; Err -> throw(Err) end. skip_padding(0) -> 0; skip_padding(Size) when (Size rem ?BLOCK_SIZE) =:= 0 -> 0; skip_padding(Size) when Size =< ?BLOCK_SIZE -> ?BLOCK_SIZE - Size; skip_padding(Size) -> ?BLOCK_SIZE - (Size rem ?BLOCK_SIZE). skip_unread(#reader{pos=Pos}=Reader0) when (Pos rem ?BLOCK_SIZE) > 0 -> Padding = skip_padding(Pos + ?BLOCK_SIZE), AbsPos = Pos + Padding, case do_position(Reader0, AbsPos) of {ok, _, Reader1} -> {ok, Reader1}; Err -> throw(Err) end; skip_unread(#reader{}=Reader) -> {ok, Reader}; skip_unread(#reg_file_reader{handle=Handle,num_bytes=0}) -> skip_unread(Handle); skip_unread(#reg_file_reader{}=Reader) -> #reg_file_reader{handle=Handle} = skip_file(Reader), {ok, Handle}; skip_unread(#sparse_file_reader{handle=Handle,num_bytes=0}) -> skip_unread(Handle); skip_unread(#sparse_file_reader{}=Reader) -> #sparse_file_reader{handle=Handle} = skip_file(Reader), {ok, Handle}. write_extracted_element(#tar_header{name=Name,typeflag=Type}, Bin, #read_opts{output=memory}=Opts) -> case typeflag(Type) of regular -> read_verbose(Opts, "x ~ts~n", [Name]), {ok, {Name, Bin}}; _ -> ok end; write_extracted_element(#tar_header{name=Name0}=Header, Bin, Opts) -> Name1 = make_safe_path(Name0, Opts), Created = case typeflag(Header#tar_header.typeflag) of regular -> create_regular(Name1, Name0, Bin, Opts); directory -> read_verbose(Opts, "x ~ts~n", [Name0]), create_extracted_dir(Name1, Opts); symlink -> read_verbose(Opts, "x ~ts~n", [Name0]), LinkName = safe_link_name(Header, Opts), create_symlink(Name1, LinkName, Opts); Device when Device =:= char orelse Device =:= block -> %% char/block devices will be created as empty files %% and then have their major/minor device set later create_regular(Name1, Name0, <<>>, Opts); fifo -> %% fifo devices will be created as empty files create_regular(Name1, Name0, <<>>, Opts); Other -> % Ignore. read_verbose(Opts, "x ~ts - unsupported type ~p~n", [Name0, Other]), not_written end, case Created of ok -> set_extracted_file_info(Name1, Header); not_written -> ok end. make_safe_path([$/|Path], Opts) -> make_safe_path(Path, Opts); make_safe_path(Path0, #read_opts{cwd=Cwd}) -> case filelib:safe_relative_path(Path0, Cwd) of unsafe -> throw({error,{Path0,unsafe_path}}); Path -> filename:absname(Path, Cwd) end. safe_link_name(#tar_header{name=Name,linkname=Path0},#read_opts{cwd=Cwd} ) -> ParentDir = filename:dirname(Name), ResolvedTarget = filename:join(ParentDir, Path0), case filelib:safe_relative_path(ResolvedTarget, Cwd) of unsafe -> throw({error,{Path0,unsafe_symlink}}); _Path -> Path0 end. create_regular(Name, NameInArchive, Bin, Opts) -> case write_extracted_file(Name, Bin, Opts) of not_written -> read_verbose(Opts, "x ~ts - exists, not created~n", [NameInArchive]), not_written; Ok -> read_verbose(Opts, "x ~ts~n", [NameInArchive]), Ok end. create_extracted_dir(Name, _Opts) -> case file:make_dir(Name) of ok -> ok; {error,enotsup} -> not_written; {error,eexist} -> not_written; {error,enoent} -> make_dirs(Name, dir); {error,Reason} -> throw({error, Reason}) end. create_symlink(Name, Linkname, Opts) -> case file:make_symlink(Linkname, Name) of ok -> ok; {error,enoent} -> ok = make_dirs(Name, file), create_symlink(Name, Linkname, Opts); {error,eexist} -> not_written; {error,enotsup} -> read_verbose(Opts, "x ~ts - symbolic links not supported~n", [Name]), not_written; {error,Reason} -> throw({error, Reason}) end. write_extracted_file(Name, Bin, Opts) -> Write = case Opts#read_opts.keep_old_files of true -> case file:read_file_info(Name) of {ok, _} -> false; _ -> true end; false -> true end, case Write of true -> write_file(Name, Bin); false -> not_written end. write_file(Name, Bin) -> case file:write_file(Name, Bin) of ok -> ok; {error,enoent} -> case make_dirs(Name, file) of ok -> write_file(Name, Bin); {error,Reason} -> throw({error, Reason}) end; {error,Reason} -> throw({error, Reason}) end. set_extracted_file_info(_, #tar_header{typeflag = ?TYPE_SYMLINK}) -> ok; set_extracted_file_info(_, #tar_header{typeflag = ?TYPE_LINK}) -> ok; set_extracted_file_info(Name, #tar_header{typeflag = ?TYPE_CHAR}=Header) -> set_device_info(Name, Header); set_extracted_file_info(Name, #tar_header{typeflag = ?TYPE_BLOCK}=Header) -> set_device_info(Name, Header); set_extracted_file_info(Name, #tar_header{mtime=Mtime,mode=Mode}) -> Info = #file_info{mode=Mode, mtime=Mtime}, file:write_file_info(Name, Info, [{time, posix}]). set_device_info(Name, #tar_header{}=Header) -> Mtime = Header#tar_header.mtime, Mode = Header#tar_header.mode, Devmajor = Header#tar_header.devmajor, Devminor = Header#tar_header.devminor, Info = #file_info{ mode=Mode, mtime=Mtime, major_device=Devmajor, minor_device=Devminor }, file:write_file_info(Name, Info). %% Makes all directories leading up to the file. make_dirs(Name, file) -> filelib:ensure_dir(Name); make_dirs(Name, dir) -> filelib:ensure_dir(filename:join(Name,"*")). %% Prints the message on if the verbose option is given (for reading). read_verbose(#read_opts{verbose=true}, Format, Args) -> io:format(Format, Args); read_verbose(_, _, _) -> ok. %% Prints the message on if the verbose option is given. add_verbose(#add_opts{verbose=true}, Format, Args) -> io:format(Format, Args); add_verbose(_, _, _) -> ok. %%%%%%%%%%%%%%%%%% %% I/O primitives %%%%%%%%%%%%%%%%%% do_write(#reader{handle=Handle,func=Fun}=Reader0, Data) when is_function(Fun,2) -> case Fun(write,{Handle,Data}) of ok -> {ok, Pos, Reader1} = do_position(Reader0, {cur,0}), {ok, Reader1#reader{pos=Pos}}; {error, _} = Err -> Err end. do_copy(#reader{func=Fun}=Reader, Source, #add_opts{chunk_size=ChunkSize}) when is_function(Fun, 2) -> case file:open(Source, [read, binary]) of {ok, SourceFd} -> case copy_chunked(Reader, SourceFd, ChunkSize, 0) of {ok, _Copied, _Reader2} = Ok-> _ = file:close(SourceFd), Ok; Err -> _ = file:close(SourceFd), throw(Err) end; Err -> throw(Err) end. copy_chunked(#reader{}=Reader, Source, ChunkSize, Copied) -> case file:read(Source, ChunkSize) of {ok, Bin} -> {ok, Reader2} = do_write(Reader, Bin), copy_chunked(Reader2, Source, ChunkSize, Copied+byte_size(Bin)); eof -> {ok, Copied, Reader}; Other -> Other end. do_position(#reader{handle=Handle,func=Fun}=Reader, Pos) when is_function(Fun,2)-> case Fun(position, {Handle,Pos}) of {ok, NewPos} -> %% since Pos may not always be an absolute seek, %% make sure we update the reader with the new absolute position {ok, AbsPos} = Fun(position, {Handle, {cur, 0}}), {ok, NewPos, Reader#reader{pos=AbsPos}}; Other -> Other end. do_read(#reg_file_reader{handle=Handle,pos=Pos,size=Size}=Reader, Len) -> NumBytes = Size - Pos, ActualLen = if NumBytes - Len < 0 -> NumBytes; true -> Len end, case do_read(Handle, ActualLen) of {ok, Bin, Handle2} -> NewPos = Pos + ActualLen, NumBytes2 = Size - NewPos, Reader1 = Reader#reg_file_reader{ handle=Handle2, pos=NewPos, num_bytes=NumBytes2}, {ok, Bin, Reader1}; Other -> Other end; do_read(#sparse_file_reader{}=Reader, Len) -> do_sparse_read(Reader, Len); do_read(#reader{pos=Pos,handle=Handle,func=Fun}=Reader, Len) when is_function(Fun,2)-> %% Always convert to binary internally case Fun(read2,{Handle,Len}) of {ok, List} when is_list(List) -> Bin = list_to_binary(List), NewPos = Pos+byte_size(Bin), {ok, Bin, Reader#reader{pos=NewPos}}; {ok, Bin} when is_binary(Bin) -> NewPos = Pos+byte_size(Bin), {ok, Bin, Reader#reader{pos=NewPos}}; Other -> Other end. do_sparse_read(Reader, Len) -> do_sparse_read(Reader, Len, <<>>). do_sparse_read(#sparse_file_reader{sparse_map=[#sparse_entry{num_bytes=0}|Entries] }=Reader0, Len, Acc) -> %% skip all empty fragments Reader1 = Reader0#sparse_file_reader{sparse_map=Entries}, do_sparse_read(Reader1, Len, Acc); do_sparse_read(#sparse_file_reader{sparse_map=[], pos=Pos,size=Size}=Reader0, Len, Acc) when Pos < Size -> %% if there are no more fragments, it is possible that there is one last sparse hole %% this behaviour matches the BSD tar utility %% however, GNU tar stops returning data even if we haven't reached the end {ok, Bin, Reader1} = read_sparse_hole(Reader0, Size, Len), do_sparse_read(Reader1, Len-byte_size(Bin), <>); do_sparse_read(#sparse_file_reader{sparse_map=[]}=Reader, _Len, Acc) -> {ok, Acc, Reader}; do_sparse_read(#sparse_file_reader{}=Reader, 0, Acc) -> {ok, Acc, Reader}; do_sparse_read(#sparse_file_reader{sparse_map=[#sparse_entry{offset=Offset}|_], pos=Pos}=Reader0, Len, Acc) when Pos < Offset -> {ok, Bin, Reader1} = read_sparse_hole(Reader0, Offset, Offset-Pos), do_sparse_read(Reader1, Len-byte_size(Bin), <>); do_sparse_read(#sparse_file_reader{sparse_map=[Entry|Entries], pos=Pos}=Reader0, Len, Acc) -> %% we're in a data fragment, so read from it %% end offset of fragment EndPos = Entry#sparse_entry.offset + Entry#sparse_entry.num_bytes, %% bytes left in fragment NumBytes = EndPos - Pos, ActualLen = if Len > NumBytes -> NumBytes; true -> Len end, case do_read(Reader0#sparse_file_reader.handle, ActualLen) of {ok, Bin, Handle} -> BytesRead = byte_size(Bin), ActualEndPos = Pos+BytesRead, Reader1 = if ActualEndPos =:= EndPos -> Reader0#sparse_file_reader{sparse_map=Entries}; true -> Reader0 end, Size = Reader1#sparse_file_reader.size, NumBytes2 = Size - ActualEndPos, Reader2 = Reader1#sparse_file_reader{ handle=Handle, pos=ActualEndPos, num_bytes=NumBytes2}, do_sparse_read(Reader2, Len-byte_size(Bin), <>); Other -> Other end. %% Reads a sparse hole ending at Offset read_sparse_hole(#sparse_file_reader{pos=Pos}=Reader, Offset, Len) -> N = Offset - Pos, N2 = if N > Len -> Len; true -> N end, Bin = <<0:N2/unit:8>>, NumBytes = Reader#sparse_file_reader.size - (Pos+N2), {ok, Bin, Reader#sparse_file_reader{ num_bytes=NumBytes, pos=Pos+N2}}. -spec do_close(tar_descriptor()) -> ok | {error, term()}. do_close(#reader{handle=Handle,func=Fun}) when is_function(Fun,2) -> Fun(close,Handle). %%%%%%%%%%%%%%%%%% %% Option parsing %%%%%%%%%%%%%%%%%% extract_opts(List) -> extract_opts(List, default_options()). table_opts(List) -> read_opts(List, default_options()). default_options() -> {ok, Cwd} = file:get_cwd(), #read_opts{cwd=Cwd}. extract_opts([keep_old_files|Rest], Opts) -> extract_opts(Rest, Opts#read_opts{keep_old_files=true}); extract_opts([{cwd, Cwd}|Rest], Opts) -> extract_opts(Rest, Opts#read_opts{cwd=Cwd}); extract_opts([{files, Files}|Rest], Opts) -> Set = ordsets:from_list(Files), extract_opts(Rest, Opts#read_opts{files=Set}); extract_opts([memory|Rest], Opts) -> extract_opts(Rest, Opts#read_opts{output=memory}); extract_opts([compressed|Rest], Opts=#read_opts{open_mode=OpenMode}) -> extract_opts(Rest, Opts#read_opts{open_mode=[compressed|OpenMode]}); extract_opts([cooked|Rest], Opts=#read_opts{open_mode=OpenMode}) -> extract_opts(Rest, Opts#read_opts{open_mode=[cooked|OpenMode]}); extract_opts([verbose|Rest], Opts) -> extract_opts(Rest, Opts#read_opts{verbose=true}); extract_opts([{chunks,N}|Rest], Opts) -> extract_opts(Rest, Opts#read_opts{chunk_size=N}); extract_opts([{max_size,N}|Rest], Opts) -> extract_opts(Rest, Opts#read_opts{max_size=N}); extract_opts([Other|Rest], Opts) -> extract_opts(Rest, read_opts([Other], Opts)); extract_opts([], Opts) -> Opts. read_opts([compressed|Rest], Opts=#read_opts{open_mode=OpenMode}) -> read_opts(Rest, Opts#read_opts{open_mode=[compressed|OpenMode]}); read_opts([cooked|Rest], Opts=#read_opts{open_mode=OpenMode}) -> read_opts(Rest, Opts#read_opts{open_mode=[cooked|OpenMode]}); read_opts([verbose|Rest], Opts) -> read_opts(Rest, Opts#read_opts{verbose=true}); read_opts([_|Rest], Opts) -> read_opts(Rest, Opts); read_opts([], Opts) -> Opts. hex-2.4.2/src/mix_hex_erl_tar.hrl000066400000000000000000000405171517471540100167660ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% This file is a copy of erl_tar.hrl from OTP with the following modifications: %% 1. Added chunk_size field to #read_opts{} for streaming extraction to disk %% 2. Added {chunks, pos_integer()} to extract_opt() type %% 3. Default chunk_size to 65536 in #add_opts{} instead of 0 %% 4. Added max_size field to #read_opts{} for zip bomb protection %% 5. Added {max_size, pos_integer() | infinity} to extract_opt() type %% %% OTP commit: 013041bd68c2547848e88963739edea7f0a1a90f %% %% %CopyrightBegin% %% %% SPDX-License-Identifier: Apache-2.0 %% %% Copyright Ericsson AB 1997-2025. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% Options used when adding files to a tar archive. -record(add_opts, { read_info, %% Fun to use for read file/link info. chunk_size = 65536, %% Chunk size for reading files. verbose = false, %% Verbose on/off. atime = undefined, mtime = undefined, ctime = undefined, mode = 8#100644, uid = 0, gid = 0}). -type add_opts() :: #add_opts{}. %% Options used when reading a tar archive. -record(read_opts, { cwd :: string(), %% Current working directory. keep_old_files = false :: boolean(), %% Owerwrite or not. files = all, %% Set of files to extract (or all) output = file :: 'file' | 'memory', open_mode = [], %% Open mode options. verbose = false :: boolean(), %% Verbose on/off. chunk_size = 65536, %% Chunk size for streaming to disk. max_size = infinity :: pos_integer() | 'infinity'}). -type read_opts() :: #read_opts{}. -type add_opt() :: dereference | verbose | {chunks, pos_integer()} | {atime, non_neg_integer()} | {mtime, non_neg_integer()} | {ctime, non_neg_integer()} | {mode, non_neg_integer()} | {uid, non_neg_integer()} | {gid, non_neg_integer()}. -type name_in_archive() :: string(). -type extract_opt() :: {cwd, string()} | {files, [name_in_archive()]} | {chunks, pos_integer()} | {max_size, pos_integer() | infinity} | compressed | cooked | memory | keep_old_files | verbose. -type create_opt() :: compressed | cooked | dereference | verbose. -type filelist() :: [file:filename() | {name_in_archive(), file:filename_all()}]. -type tar_time() :: non_neg_integer(). %% The tar header, once fully parsed. -record(tar_header, { name = "" :: name_in_archive(), %% name of header file entry mode = 8#100644 :: non_neg_integer(), %% permission and mode bits uid = 0 :: non_neg_integer(), %% user id of owner gid = 0 :: non_neg_integer(), %% group id of owner size = 0 :: non_neg_integer(), %% length in bytes mtime :: tar_time(), %% modified time typeflag :: char(), %% type of header entry linkname = "" :: name_in_archive(), %% target name of link uname = "" :: string(), %% user name of owner gname = "" :: string(), %% group name of owner devmajor = 0 :: non_neg_integer(), %% major number of character or block device devminor = 0 :: non_neg_integer(), %% minor number of character or block device atime :: tar_time(), %% access time ctime :: tar_time() %% status change time }). -type tar_header() :: #tar_header{}. %% Metadata for a sparse file fragment -record(sparse_entry, { offset = 0 :: non_neg_integer(), num_bytes = 0 :: non_neg_integer()}). -type sparse_entry() :: #sparse_entry{}. %% Contains metadata about fragments of a sparse file -record(sparse_array, { entries = [] :: [sparse_entry()], is_extended = false :: boolean(), max_entries = 0 :: non_neg_integer()}). -type sparse_array() :: #sparse_array{}. %% A subset of tar header fields common to all tar implementations -record(header_v7, { name :: binary(), mode :: binary(), %% octal uid :: binary(), %% integer gid :: binary(), %% integer size :: binary(), %% integer mtime :: binary(), %% integer checksum :: binary(), %% integer typeflag :: byte(), %% char linkname :: binary()}). -type header_v7() :: #header_v7{}. %% The set of fields specific to GNU tar formatted archives -record(header_gnu, { header_v7 :: header_v7(), magic :: binary(), version :: binary(), uname :: binary(), gname :: binary(), devmajor :: binary(), %% integer devminor :: binary(), %% integer atime :: binary(), %% integer ctime :: binary(), %% integer sparse :: sparse_array(), real_size :: binary()}). %% integer -type header_gnu() :: #header_gnu{}. %% The set of fields specific to STAR-formatted archives -record(header_star, { header_v7 :: header_v7(), magic :: binary(), version :: binary(), uname :: binary(), gname :: binary(), devmajor :: binary(), %% integer devminor :: binary(), %% integer prefix :: binary(), atime :: binary(), %% integer ctime :: binary(), %% integer trailer :: binary()}). -type header_star() :: #header_star{}. %% The set of fields specific to USTAR-formatted archives -record(header_ustar, { header_v7 :: header_v7(), magic :: binary(), version :: binary(), uname :: binary(), gname :: binary(), devmajor :: binary(), %% integer devminor :: binary(), %% integer prefix :: binary()}). -type header_ustar() :: #header_ustar{}. -type header_fields() :: header_v7() | header_gnu() | header_star() | header_ustar(). %% The overall tar reader, it holds the low-level file handle, %% its access, position, and the I/O primitives wrapper. -record(reader, { handle :: user_data(), access :: read | write | ram, pos = 0 :: non_neg_integer(), func :: file_op() }). -opaque tar_descriptor() :: #reader{}. -export_type([tar_descriptor/0]). %% A reader for a regular file within the tar archive, %% It tracks its current state relative to that file. -record(reg_file_reader, { handle :: tar_descriptor(), num_bytes = 0, pos = 0, size = 0 }). -type reg_file_reader() :: #reg_file_reader{}. %% A reader for a sparse file within the tar archive, %% It tracks its current state relative to that file. -record(sparse_file_reader, { handle :: tar_descriptor(), num_bytes = 0, %% bytes remaining pos = 0, %% pos size = 0, %% total size of file sparse_map = #sparse_array{} }). -type sparse_file_reader() :: #sparse_file_reader{}. %% Types for the readers -type descriptor_type() :: tar_descriptor() | reg_file_reader() | sparse_file_reader(). -type user_data() :: term(). %% Type for the I/O primitive wrapper function -type file_op() :: fun((write | close | read2 | position, {user_data(), iodata()} | user_data() | {user_data(), non_neg_integer()} | {user_data(), non_neg_integer()}) -> ok | eof | {ok, string() | binary()} | {ok, non_neg_integer()} | {error, term()}). %% These constants (except S_IFMT) are %% used to determine what type of device %% a file is. Namely, `S_IFMT band file_info.mode` %% will equal one of these contants, and tells us %% which type it is. The stdlib file_info record %% does not differentiate between device types, and %% will not allow us to differentiate between sockets %% and named pipes. These constants are pulled from libc. -define(S_IFMT, 61440). -define(S_IFSOCK, 49152). %% socket -define(S_FIFO, 4096). %% fifo/named pipe -define(S_IFBLK, 24576). %% block device -define(S_IFCHR, 8192). %% character device %% Typeflag constants for the tar header -define(TYPE_REGULAR, $0). %% regular file -define(TYPE_REGULAR_A, 0). %% regular file -define(TYPE_LINK, $1). %% hard link -define(TYPE_SYMLINK, $2). %% symbolic link -define(TYPE_CHAR, $3). %% character device node -define(TYPE_BLOCK, $4). %% block device node -define(TYPE_DIR, $5). %% directory -define(TYPE_FIFO, $6). %% fifo node -define(TYPE_CONT, $7). %% reserved -define(TYPE_X_HEADER, $x). %% extended header -define(TYPE_X_GLOBAL_HEADER, $g). %% global extended header -define(TYPE_GNU_LONGNAME, $L). %% next file has a long name -define(TYPE_GNU_LONGLINK, $K). %% next file symlinks to a file with a long name -define(TYPE_GNU_SPARSE, $S). %% sparse file %% Mode constants from tar spec -define(MODE_ISUID, 4000). %% set uid -define(MODE_ISGID, 2000). %% set gid -define(MODE_ISVTX, 1000). %% save text (sticky bit) -define(MODE_ISDIR, 40000). %% directory -define(MODE_ISFIFO, 10000). %% fifo -define(MODE_ISREG, 100000). %% regular file -define(MODE_ISLNK, 120000). %% symbolic link -define(MODE_ISBLK, 60000). %% block special file -define(MODE_ISCHR, 20000). %% character special file -define(MODE_ISSOCK, 140000). %% socket %% Keywords for PAX extended header -define(PAX_ATIME, <<"atime">>). -define(PAX_CHARSET, <<"charset">>). -define(PAX_COMMENT, <<"comment">>). -define(PAX_CTIME, <<"ctime">>). %% ctime is not a valid pax header -define(PAX_GID, <<"gid">>). -define(PAX_GNAME, <<"gname">>). -define(PAX_LINKPATH, <<"linkpath">>). -define(PAX_MTIME, <<"mtime">>). -define(PAX_PATH, <<"path">>). -define(PAX_SIZE, <<"size">>). -define(PAX_UID, <<"uid">>). -define(PAX_UNAME, <<"uname">>). -define(PAX_XATTR, <<"SCHILY.xattr.">>). -define(PAX_XATTR_STR, "SCHILY.xattr."). -define(PAX_NONE, <<"">>). %% Tar format constants %% Unknown format -define(FORMAT_UNKNOWN, 0). %% The format of the original Unix V7 tar tool prior to standardization -define(FORMAT_V7, 1). %% The old and new GNU formats, incompatible with USTAR. %% This covers the old GNU sparse extension, but it does %% not cover the GNU sparse extensions using PAX headers, %% versions 0.0, 0.1, and 1.0; these fall under the PAX format. -define(FORMAT_GNU, 2). %% Schily's tar format, which is incompatible with USTAR. %% This does not cover STAR extensions to the PAX format; these %% fall under the PAX format. -define(FORMAT_STAR, 3). %% USTAR is the former standardization of tar defined in POSIX.1-1988, %% it is incompatible with the GNU and STAR formats. -define(FORMAT_USTAR, 4). %% PAX is the latest standardization of tar defined in POSIX.1-2001. %% This is an extension of USTAR and is "backwards compatible" with it. %% %% Some newer formats add their own extensions to PAX, such as GNU sparse %% files and SCHILY extended attributes. Since they are backwards compatible %% with PAX, they will be labelled as "PAX". -define(FORMAT_PAX, 5). %% Magic constants -define(MAGIC_GNU, <<"ustar ">>). -define(VERSION_GNU, <<" \x00">>). -define(MAGIC_USTAR, <<"ustar\x00">>). -define(VERSION_USTAR, <<"00">>). -define(TRAILER_STAR, <<"tar\x00">>). %% Size constants -define(BLOCK_SIZE, 512). %% size of each block in a tar stream -define(NAME_SIZE, 100). %% max length of the name field in USTAR format -define(PREFIX_SIZE, 155). %% max length of the prefix field in USTAR format %% Maximum size of a nanosecond value as an integer -define(MAX_NANO_INT_SIZE, 9). %% Maximum size of a 64-bit signed integer -define(MAX_INT64, (1 bsl 63 - 1)). -define(PAX_GNU_SPARSE_NUMBLOCKS, <<"GNU.sparse.numblocks">>). -define(PAX_GNU_SPARSE_OFFSET, <<"GNU.sparse.offset">>). -define(PAX_GNU_SPARSE_NUMBYTES, <<"GNU.sparse.numbytes">>). -define(PAX_GNU_SPARSE_MAP, <<"GNU.sparse.map">>). -define(PAX_GNU_SPARSE_NAME, <<"GNU.sparse.name">>). -define(PAX_GNU_SPARSE_MAJOR, <<"GNU.sparse.major">>). -define(PAX_GNU_SPARSE_MINOR, <<"GNU.sparse.minor">>). -define(PAX_GNU_SPARSE_SIZE, <<"GNU.sparse.size">>). -define(PAX_GNU_SPARSE_REALSIZE, <<"GNU.sparse.realsize">>). -define(V7_NAME, 0). -define(V7_NAME_LEN, 100). -define(V7_MODE, 100). -define(V7_MODE_LEN, 8). -define(V7_UID, 108). -define(V7_UID_LEN, 8). -define(V7_GID, 116). -define(V7_GID_LEN, 8). -define(V7_SIZE, 124). -define(V7_SIZE_LEN, 12). -define(V7_MTIME, 136). -define(V7_MTIME_LEN, 12). -define(V7_CHKSUM, 148). -define(V7_CHKSUM_LEN, 8). -define(V7_TYPE, 156). -define(V7_TYPE_LEN, 1). -define(V7_LINKNAME, 157). -define(V7_LINKNAME_LEN, 100). -define(STAR_TRAILER, 508). -define(STAR_TRAILER_LEN, 4). -define(USTAR_MAGIC, 257). -define(USTAR_MAGIC_LEN, 6). -define(USTAR_VERSION, 263). -define(USTAR_VERSION_LEN, 2). -define(USTAR_UNAME, 265). -define(USTAR_UNAME_LEN, 32). -define(USTAR_GNAME, 297). -define(USTAR_GNAME_LEN, 32). -define(USTAR_DEVMAJ, 329). -define(USTAR_DEVMAJ_LEN, 8). -define(USTAR_DEVMIN, 337). -define(USTAR_DEVMIN_LEN, 8). -define(USTAR_PREFIX, 345). -define(USTAR_PREFIX_LEN, 155). -define(GNU_MAGIC, 257). -define(GNU_MAGIC_LEN, 6). -define(GNU_VERSION, 263). -define(GNU_VERSION_LEN, 2). %% ?BLOCK_SIZE of zero-bytes. %% Two of these in a row mark the end of an archive. -define(ZERO_BLOCK, <<0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0>>). -define(BILLION, 1000000000). -define(EPOCH, {{1970,1,1}, {0,0,0}}). hex-2.4.2/src/mix_hex_http.erl000066400000000000000000000061431517471540100163070ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @doc %% HTTP contract. -module(mix_hex_http). -export([request/5, request_to_file/6]). -ifdef(TEST). -export([user_agent/1]). -endif. -include_lib("mix_hex_core.hrl"). -type method() :: get | post | put | patch | delete. -type status() :: non_neg_integer(). -export_type([status/0]). -type headers() :: #{binary() => binary()}. -export_type([headers/0]). -type body() :: {ContentType :: binary(), Body :: binary()} | undefined. -export_type([body/0]). -type adapter_config() :: map(). -export_type([adapter_config/0]). -callback request(method(), URI :: binary(), headers(), body(), adapter_config()) -> {ok, {status(), headers(), binary()}} | {error, term()}. -callback request_to_file( method(), URI :: binary(), headers(), body(), file:name_all(), adapter_config() ) -> {ok, {status(), headers()}} | {error, term()}. -spec request(mix_hex_core:config(), method(), URI :: binary(), headers(), body()) -> {ok, {status(), headers(), binary()}} | {error, term()}. request(Config, Method, URI, Headers, Body) when is_binary(URI) and is_map(Headers) -> {Adapter, AdapterConfig} = adapter(Config), UserAgentFragment = maps:get(http_user_agent_fragment, Config), Headers2 = put_new(<<"user-agent">>, user_agent(UserAgentFragment), Headers), Adapter:request(Method, URI, Headers2, Body, AdapterConfig). -spec request_to_file( mix_hex_core:config(), method(), URI :: binary(), headers(), body(), file:name_all() ) -> {ok, {status(), headers()}} | {error, term()}. request_to_file(Config, Method, URI, Headers, Body, Filename) when is_binary(URI) and is_map(Headers) -> {Adapter, AdapterConfig} = adapter(Config), UserAgentFragment = maps:get(http_user_agent_fragment, Config), Headers2 = put_new(<<"user-agent">>, user_agent(UserAgentFragment), Headers), Adapter:request_to_file(Method, URI, Headers2, Body, Filename, AdapterConfig). %% @private user_agent(UserAgentFragment) -> OTPRelease = erlang:system_info(otp_release), ERTSVersion = erlang:system_info(version), OTPString = " (OTP/" ++ OTPRelease ++ ") (erts/" ++ ERTSVersion ++ ")", iolist_to_binary(["hex_core/", ?HEX_CORE_VERSION, " ", UserAgentFragment, OTPString]). %%==================================================================== %% Internal functions %%==================================================================== %% @private adapter(Config) -> case maps:get(http_adapter, Config, {mix_hex_http_httpc, #{}}) of {Adapter, AdapterConfig} -> {Adapter, AdapterConfig}; %% TODO: remove in v0.9 Adapter when is_atom(Adapter) -> AdapterConfig = maps:get(http_adapter_config, Config, #{}), io:format( "[mix_hex_http] setting #{http_adapter => Module, http_adapter_config => Map} " "is deprecated in favour of #{http_adapter => {Module, Map}}~n" ), {Adapter, AdapterConfig} end. %% @private put_new(Key, Value, Map) -> case maps:find(Key, Map) of {ok, _} -> Map; error -> maps:put(Key, Value, Map) end. hex-2.4.2/src/mix_hex_http_httpc.erl000066400000000000000000000101041517471540100175010ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @doc %% httpc-based implementation of {@link mix_hex_http} contract. %% %% Configuration keys: %% %% * `profile' - the name of the profile, defaults to `default'. See %% {@link httpc:set_options/2} for more information on setting %% options on profiles. %% %% * `http_options' - a list of HTTP options, defaults to `[]'. See %% {@link httpc:request/5} for a list of available HTTP options. -module(mix_hex_http_httpc). -behaviour(mix_hex_http). -export([request/5, request_to_file/6]). %%==================================================================== %% API functions %%==================================================================== request(Method, URI, ReqHeaders, Body, AdapterConfig) when is_binary(URI) -> Profile = maps:get(profile, AdapterConfig, default), HTTPOptions = http_options(URI, AdapterConfig), Request = build_request(URI, ReqHeaders, Body), case httpc:request(Method, Request, HTTPOptions, [{body_format, binary}], Profile) of {ok, {{_, StatusCode, _}, RespHeaders, RespBody}} -> RespHeaders2 = load_headers(RespHeaders), {ok, {StatusCode, RespHeaders2, RespBody}}; {error, Reason} -> {error, Reason} end. request_to_file(Method, URI, ReqHeaders, Body, Filename, AdapterConfig) when is_binary(URI) -> Profile = maps:get(profile, AdapterConfig, default), HTTPOptions = http_options(URI, AdapterConfig), Request = build_request(URI, ReqHeaders, Body), case httpc:request( Method, Request, HTTPOptions, [{stream, unicode:characters_to_list(Filename)}], Profile ) of {ok, saved_to_file} -> {ok, {200, #{}}}; {ok, {{_, StatusCode, _}, RespHeaders, _RespBody}} -> RespHeaders2 = load_headers(RespHeaders), {ok, {StatusCode, RespHeaders2}}; {error, Reason} -> {error, Reason} end. %%==================================================================== %% Internal functions %%==================================================================== %% @private http_options(URI, AdapterConfig) -> HTTPOptions0 = maps:get(http_options, AdapterConfig, []), HTTPS = case URI of <<"https", _/binary>> -> true; _ -> false end, SSLOpts0 = proplists:get_value(ssl, HTTPOptions0), if HTTPS == true andalso SSLOpts0 == undefined -> try [ {ssl, [ {verify, verify_peer}, {cacerts, public_key:cacerts_get()}, {depth, 3}, {customize_hostname_check, [ {match_fun, public_key:pkix_verify_hostname_match_fun(https)} ]} ]} | HTTPOptions0 ] catch _:_ -> io:format( "[mix_hex_http_httpc] using default ssl options which are insecure.~n" "Configure your adapter with: " "{mix_hex_http_httpc, #{http_options => [{ssl, SslOpts}]}}~n" "or upgrade Erlang/OTP to OTP-25 or later.~n" ), HTTPOptions0 end; true -> HTTPOptions0 end. %% @private build_request(URI, ReqHeaders, Body) -> build_request2(binary_to_list(URI), dump_headers(ReqHeaders), Body). %% @private build_request2(URI, ReqHeaders, undefined) -> {URI, ReqHeaders}; build_request2(URI, ReqHeaders, {ContentType, Body}) -> {URI, ReqHeaders, ContentType, Body}. %% @private dump_headers(Map) -> maps:fold( fun(K, V, Acc) -> [{binary_to_list(K), binary_to_list(V)} | Acc] end, [], Map ). %% @private load_headers(List) -> lists:foldl( fun({K, V}, Acc) -> maps:put(list_to_binary(K), list_to_binary(V), Acc) end, #{}, List ). hex-2.4.2/src/mix_hex_licenses.erl000066400000000000000000000543771517471540100171510ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @doc %% Hex Licenses. %% File generated by https://github.com/supersimple/spdx. Do not edit manually. -module(mix_hex_licenses). -export([valid/1]). valid(<<"0BSD">>) -> true; valid(<<"3D-Slicer-1.0">>) -> true; valid(<<"AAL">>) -> true; valid(<<"ADSL">>) -> true; valid(<<"AFL-1.1">>) -> true; valid(<<"AFL-1.2">>) -> true; valid(<<"AFL-2.0">>) -> true; valid(<<"AFL-2.1">>) -> true; valid(<<"AFL-3.0">>) -> true; valid(<<"AGPL-1.0">>) -> true; valid(<<"AGPL-1.0-only">>) -> true; valid(<<"AGPL-1.0-or-later">>) -> true; valid(<<"AGPL-3.0">>) -> true; valid(<<"AGPL-3.0-only">>) -> true; valid(<<"AGPL-3.0-or-later">>) -> true; valid(<<"AMD-newlib">>) -> true; valid(<<"AMDPLPA">>) -> true; valid(<<"AML">>) -> true; valid(<<"AML-glslang">>) -> true; valid(<<"AMPAS">>) -> true; valid(<<"ANTLR-PD">>) -> true; valid(<<"ANTLR-PD-fallback">>) -> true; valid(<<"APAFML">>) -> true; valid(<<"APL-1.0">>) -> true; valid(<<"APSL-1.0">>) -> true; valid(<<"APSL-1.1">>) -> true; valid(<<"APSL-1.2">>) -> true; valid(<<"APSL-2.0">>) -> true; valid(<<"ASWF-Digital-Assets-1.0">>) -> true; valid(<<"ASWF-Digital-Assets-1.1">>) -> true; valid(<<"Abstyles">>) -> true; valid(<<"AdaCore-doc">>) -> true; valid(<<"Adobe-2006">>) -> true; valid(<<"Adobe-Display-PostScript">>) -> true; valid(<<"Adobe-Glyph">>) -> true; valid(<<"Adobe-Utopia">>) -> true; valid(<<"Afmparse">>) -> true; valid(<<"Aladdin">>) -> true; valid(<<"Apache-1.0">>) -> true; valid(<<"Apache-1.1">>) -> true; valid(<<"Apache-2.0">>) -> true; valid(<<"App-s2p">>) -> true; valid(<<"Arphic-1999">>) -> true; valid(<<"Artistic-1.0">>) -> true; valid(<<"Artistic-1.0-Perl">>) -> true; valid(<<"Artistic-1.0-cl8">>) -> true; valid(<<"Artistic-2.0">>) -> true; valid(<<"BSD-1-Clause">>) -> true; valid(<<"BSD-2-Clause">>) -> true; valid(<<"BSD-2-Clause-Darwin">>) -> true; valid(<<"BSD-2-Clause-FreeBSD">>) -> true; valid(<<"BSD-2-Clause-NetBSD">>) -> true; valid(<<"BSD-2-Clause-Patent">>) -> true; valid(<<"BSD-2-Clause-Views">>) -> true; valid(<<"BSD-2-Clause-first-lines">>) -> true; valid(<<"BSD-3-Clause">>) -> true; valid(<<"BSD-3-Clause-Attribution">>) -> true; valid(<<"BSD-3-Clause-Clear">>) -> true; valid(<<"BSD-3-Clause-HP">>) -> true; valid(<<"BSD-3-Clause-LBNL">>) -> true; valid(<<"BSD-3-Clause-Modification">>) -> true; valid(<<"BSD-3-Clause-No-Military-License">>) -> true; valid(<<"BSD-3-Clause-No-Nuclear-License">>) -> true; valid(<<"BSD-3-Clause-No-Nuclear-License-2014">>) -> true; valid(<<"BSD-3-Clause-No-Nuclear-Warranty">>) -> true; valid(<<"BSD-3-Clause-Open-MPI">>) -> true; valid(<<"BSD-3-Clause-Sun">>) -> true; valid(<<"BSD-3-Clause-acpica">>) -> true; valid(<<"BSD-3-Clause-flex">>) -> true; valid(<<"BSD-4-Clause">>) -> true; valid(<<"BSD-4-Clause-Shortened">>) -> true; valid(<<"BSD-4-Clause-UC">>) -> true; valid(<<"BSD-4.3RENO">>) -> true; valid(<<"BSD-4.3TAHOE">>) -> true; valid(<<"BSD-Advertising-Acknowledgement">>) -> true; valid(<<"BSD-Attribution-HPND-disclaimer">>) -> true; valid(<<"BSD-Inferno-Nettverk">>) -> true; valid(<<"BSD-Protection">>) -> true; valid(<<"BSD-Source-Code">>) -> true; valid(<<"BSD-Source-beginning-file">>) -> true; valid(<<"BSD-Systemics">>) -> true; valid(<<"BSD-Systemics-W3Works">>) -> true; valid(<<"BSL-1.0">>) -> true; valid(<<"BUSL-1.1">>) -> true; valid(<<"Baekmuk">>) -> true; valid(<<"Bahyph">>) -> true; valid(<<"Barr">>) -> true; valid(<<"Beerware">>) -> true; valid(<<"BitTorrent-1.0">>) -> true; valid(<<"BitTorrent-1.1">>) -> true; valid(<<"Bitstream-Charter">>) -> true; valid(<<"Bitstream-Vera">>) -> true; valid(<<"BlueOak-1.0.0">>) -> true; valid(<<"Boehm-GC">>) -> true; valid(<<"Borceux">>) -> true; valid(<<"Brian-Gladman-2-Clause">>) -> true; valid(<<"Brian-Gladman-3-Clause">>) -> true; valid(<<"C-UDA-1.0">>) -> true; valid(<<"CAL-1.0">>) -> true; valid(<<"CAL-1.0-Combined-Work-Exception">>) -> true; valid(<<"CATOSL-1.1">>) -> true; valid(<<"CC-BY-1.0">>) -> true; valid(<<"CC-BY-2.0">>) -> true; valid(<<"CC-BY-2.5">>) -> true; valid(<<"CC-BY-2.5-AU">>) -> true; valid(<<"CC-BY-3.0">>) -> true; valid(<<"CC-BY-3.0-AT">>) -> true; valid(<<"CC-BY-3.0-AU">>) -> true; valid(<<"CC-BY-3.0-DE">>) -> true; valid(<<"CC-BY-3.0-IGO">>) -> true; valid(<<"CC-BY-3.0-NL">>) -> true; valid(<<"CC-BY-3.0-US">>) -> true; valid(<<"CC-BY-4.0">>) -> true; valid(<<"CC-BY-NC-1.0">>) -> true; valid(<<"CC-BY-NC-2.0">>) -> true; valid(<<"CC-BY-NC-2.5">>) -> true; valid(<<"CC-BY-NC-3.0">>) -> true; valid(<<"CC-BY-NC-3.0-DE">>) -> true; valid(<<"CC-BY-NC-4.0">>) -> true; valid(<<"CC-BY-NC-ND-1.0">>) -> true; valid(<<"CC-BY-NC-ND-2.0">>) -> true; valid(<<"CC-BY-NC-ND-2.5">>) -> true; valid(<<"CC-BY-NC-ND-3.0">>) -> true; valid(<<"CC-BY-NC-ND-3.0-DE">>) -> true; valid(<<"CC-BY-NC-ND-3.0-IGO">>) -> true; valid(<<"CC-BY-NC-ND-4.0">>) -> true; valid(<<"CC-BY-NC-SA-1.0">>) -> true; valid(<<"CC-BY-NC-SA-2.0">>) -> true; valid(<<"CC-BY-NC-SA-2.0-DE">>) -> true; valid(<<"CC-BY-NC-SA-2.0-FR">>) -> true; valid(<<"CC-BY-NC-SA-2.0-UK">>) -> true; valid(<<"CC-BY-NC-SA-2.5">>) -> true; valid(<<"CC-BY-NC-SA-3.0">>) -> true; valid(<<"CC-BY-NC-SA-3.0-DE">>) -> true; valid(<<"CC-BY-NC-SA-3.0-IGO">>) -> true; valid(<<"CC-BY-NC-SA-4.0">>) -> true; valid(<<"CC-BY-ND-1.0">>) -> true; valid(<<"CC-BY-ND-2.0">>) -> true; valid(<<"CC-BY-ND-2.5">>) -> true; valid(<<"CC-BY-ND-3.0">>) -> true; valid(<<"CC-BY-ND-3.0-DE">>) -> true; valid(<<"CC-BY-ND-4.0">>) -> true; valid(<<"CC-BY-SA-1.0">>) -> true; valid(<<"CC-BY-SA-2.0">>) -> true; valid(<<"CC-BY-SA-2.0-UK">>) -> true; valid(<<"CC-BY-SA-2.1-JP">>) -> true; valid(<<"CC-BY-SA-2.5">>) -> true; valid(<<"CC-BY-SA-3.0">>) -> true; valid(<<"CC-BY-SA-3.0-AT">>) -> true; valid(<<"CC-BY-SA-3.0-DE">>) -> true; valid(<<"CC-BY-SA-3.0-IGO">>) -> true; valid(<<"CC-BY-SA-4.0">>) -> true; valid(<<"CC-PDDC">>) -> true; valid(<<"CC0-1.0">>) -> true; valid(<<"CDDL-1.0">>) -> true; valid(<<"CDDL-1.1">>) -> true; valid(<<"CDL-1.0">>) -> true; valid(<<"CDLA-Permissive-1.0">>) -> true; valid(<<"CDLA-Permissive-2.0">>) -> true; valid(<<"CDLA-Sharing-1.0">>) -> true; valid(<<"CECILL-1.0">>) -> true; valid(<<"CECILL-1.1">>) -> true; valid(<<"CECILL-2.0">>) -> true; valid(<<"CECILL-2.1">>) -> true; valid(<<"CECILL-B">>) -> true; valid(<<"CECILL-C">>) -> true; valid(<<"CERN-OHL-1.1">>) -> true; valid(<<"CERN-OHL-1.2">>) -> true; valid(<<"CERN-OHL-P-2.0">>) -> true; valid(<<"CERN-OHL-S-2.0">>) -> true; valid(<<"CERN-OHL-W-2.0">>) -> true; valid(<<"CFITSIO">>) -> true; valid(<<"CMU-Mach">>) -> true; valid(<<"CMU-Mach-nodoc">>) -> true; valid(<<"CNRI-Jython">>) -> true; valid(<<"CNRI-Python">>) -> true; valid(<<"CNRI-Python-GPL-Compatible">>) -> true; valid(<<"COIL-1.0">>) -> true; valid(<<"CPAL-1.0">>) -> true; valid(<<"CPL-1.0">>) -> true; valid(<<"CPOL-1.02">>) -> true; valid(<<"CUA-OPL-1.0">>) -> true; valid(<<"Caldera">>) -> true; valid(<<"Caldera-no-preamble">>) -> true; valid(<<"Catharon">>) -> true; valid(<<"ClArtistic">>) -> true; valid(<<"Clips">>) -> true; valid(<<"Community-Spec-1.0">>) -> true; valid(<<"Condor-1.1">>) -> true; valid(<<"Cornell-Lossless-JPEG">>) -> true; valid(<<"Cronyx">>) -> true; valid(<<"Crossword">>) -> true; valid(<<"CrystalStacker">>) -> true; valid(<<"Cube">>) -> true; valid(<<"D-FSL-1.0">>) -> true; valid(<<"DEC-3-Clause">>) -> true; valid(<<"DL-DE-BY-2.0">>) -> true; valid(<<"DL-DE-ZERO-2.0">>) -> true; valid(<<"DOC">>) -> true; valid(<<"DRL-1.0">>) -> true; valid(<<"DRL-1.1">>) -> true; valid(<<"DSDP">>) -> true; valid(<<"Dotseqn">>) -> true; valid(<<"ECL-1.0">>) -> true; valid(<<"ECL-2.0">>) -> true; valid(<<"EFL-1.0">>) -> true; valid(<<"EFL-2.0">>) -> true; valid(<<"EPICS">>) -> true; valid(<<"EPL-1.0">>) -> true; valid(<<"EPL-2.0">>) -> true; valid(<<"EUDatagrid">>) -> true; valid(<<"EUPL-1.0">>) -> true; valid(<<"EUPL-1.1">>) -> true; valid(<<"EUPL-1.2">>) -> true; valid(<<"Elastic-2.0">>) -> true; valid(<<"Entessa">>) -> true; valid(<<"ErlPL-1.1">>) -> true; valid(<<"Eurosym">>) -> true; valid(<<"FBM">>) -> true; valid(<<"FDK-AAC">>) -> true; valid(<<"FSFAP">>) -> true; valid(<<"FSFAP-no-warranty-disclaimer">>) -> true; valid(<<"FSFUL">>) -> true; valid(<<"FSFULLR">>) -> true; valid(<<"FSFULLRWD">>) -> true; valid(<<"FTL">>) -> true; valid(<<"Fair">>) -> true; valid(<<"Ferguson-Twofish">>) -> true; valid(<<"Frameworx-1.0">>) -> true; valid(<<"FreeBSD-DOC">>) -> true; valid(<<"FreeImage">>) -> true; valid(<<"Furuseth">>) -> true; valid(<<"GCR-docs">>) -> true; valid(<<"GD">>) -> true; valid(<<"GFDL-1.1">>) -> true; valid(<<"GFDL-1.1-invariants-only">>) -> true; valid(<<"GFDL-1.1-invariants-or-later">>) -> true; valid(<<"GFDL-1.1-no-invariants-only">>) -> true; valid(<<"GFDL-1.1-no-invariants-or-later">>) -> true; valid(<<"GFDL-1.1-only">>) -> true; valid(<<"GFDL-1.1-or-later">>) -> true; valid(<<"GFDL-1.2">>) -> true; valid(<<"GFDL-1.2-invariants-only">>) -> true; valid(<<"GFDL-1.2-invariants-or-later">>) -> true; valid(<<"GFDL-1.2-no-invariants-only">>) -> true; valid(<<"GFDL-1.2-no-invariants-or-later">>) -> true; valid(<<"GFDL-1.2-only">>) -> true; valid(<<"GFDL-1.2-or-later">>) -> true; valid(<<"GFDL-1.3">>) -> true; valid(<<"GFDL-1.3-invariants-only">>) -> true; valid(<<"GFDL-1.3-invariants-or-later">>) -> true; valid(<<"GFDL-1.3-no-invariants-only">>) -> true; valid(<<"GFDL-1.3-no-invariants-or-later">>) -> true; valid(<<"GFDL-1.3-only">>) -> true; valid(<<"GFDL-1.3-or-later">>) -> true; valid(<<"GL2PS">>) -> true; valid(<<"GLWTPL">>) -> true; valid(<<"GPL-1.0">>) -> true; valid(<<"GPL-1.0+">>) -> true; valid(<<"GPL-1.0-only">>) -> true; valid(<<"GPL-1.0-or-later">>) -> true; valid(<<"GPL-2.0">>) -> true; valid(<<"GPL-2.0+">>) -> true; valid(<<"GPL-2.0-only">>) -> true; valid(<<"GPL-2.0-or-later">>) -> true; valid(<<"GPL-2.0-with-GCC-exception">>) -> true; valid(<<"GPL-2.0-with-autoconf-exception">>) -> true; valid(<<"GPL-2.0-with-bison-exception">>) -> true; valid(<<"GPL-2.0-with-classpath-exception">>) -> true; valid(<<"GPL-2.0-with-font-exception">>) -> true; valid(<<"GPL-3.0">>) -> true; valid(<<"GPL-3.0+">>) -> true; valid(<<"GPL-3.0-only">>) -> true; valid(<<"GPL-3.0-or-later">>) -> true; valid(<<"GPL-3.0-with-GCC-exception">>) -> true; valid(<<"GPL-3.0-with-autoconf-exception">>) -> true; valid(<<"Giftware">>) -> true; valid(<<"Glide">>) -> true; valid(<<"Glulxe">>) -> true; valid(<<"Graphics-Gems">>) -> true; valid(<<"Gutmann">>) -> true; valid(<<"HP-1986">>) -> true; valid(<<"HP-1989">>) -> true; valid(<<"HPND">>) -> true; valid(<<"HPND-DEC">>) -> true; valid(<<"HPND-Fenneberg-Livingston">>) -> true; valid(<<"HPND-INRIA-IMAG">>) -> true; valid(<<"HPND-Intel">>) -> true; valid(<<"HPND-Kevlin-Henney">>) -> true; valid(<<"HPND-MIT-disclaimer">>) -> true; valid(<<"HPND-Markus-Kuhn">>) -> true; valid(<<"HPND-Pbmplus">>) -> true; valid(<<"HPND-UC">>) -> true; valid(<<"HPND-UC-export-US">>) -> true; valid(<<"HPND-doc">>) -> true; valid(<<"HPND-doc-sell">>) -> true; valid(<<"HPND-export-US">>) -> true; valid(<<"HPND-export-US-acknowledgement">>) -> true; valid(<<"HPND-export-US-modify">>) -> true; valid(<<"HPND-export2-US">>) -> true; valid(<<"HPND-merchantability-variant">>) -> true; valid(<<"HPND-sell-MIT-disclaimer-xserver">>) -> true; valid(<<"HPND-sell-regexpr">>) -> true; valid(<<"HPND-sell-variant">>) -> true; valid(<<"HPND-sell-variant-MIT-disclaimer">>) -> true; valid(<<"HPND-sell-variant-MIT-disclaimer-rev">>) -> true; valid(<<"HTMLTIDY">>) -> true; valid(<<"HaskellReport">>) -> true; valid(<<"Hippocratic-2.1">>) -> true; valid(<<"IBM-pibs">>) -> true; valid(<<"ICU">>) -> true; valid(<<"IEC-Code-Components-EULA">>) -> true; valid(<<"IJG">>) -> true; valid(<<"IJG-short">>) -> true; valid(<<"IPA">>) -> true; valid(<<"IPL-1.0">>) -> true; valid(<<"ISC">>) -> true; valid(<<"ISC-Veillard">>) -> true; valid(<<"ImageMagick">>) -> true; valid(<<"Imlib2">>) -> true; valid(<<"Info-ZIP">>) -> true; valid(<<"Inner-Net-2.0">>) -> true; valid(<<"Intel">>) -> true; valid(<<"Intel-ACPI">>) -> true; valid(<<"Interbase-1.0">>) -> true; valid(<<"JPL-image">>) -> true; valid(<<"JPNIC">>) -> true; valid(<<"JSON">>) -> true; valid(<<"Jam">>) -> true; valid(<<"JasPer-2.0">>) -> true; valid(<<"Kastrup">>) -> true; valid(<<"Kazlib">>) -> true; valid(<<"Knuth-CTAN">>) -> true; valid(<<"LAL-1.2">>) -> true; valid(<<"LAL-1.3">>) -> true; valid(<<"LGPL-2.0">>) -> true; valid(<<"LGPL-2.0+">>) -> true; valid(<<"LGPL-2.0-only">>) -> true; valid(<<"LGPL-2.0-or-later">>) -> true; valid(<<"LGPL-2.1">>) -> true; valid(<<"LGPL-2.1+">>) -> true; valid(<<"LGPL-2.1-only">>) -> true; valid(<<"LGPL-2.1-or-later">>) -> true; valid(<<"LGPL-3.0">>) -> true; valid(<<"LGPL-3.0+">>) -> true; valid(<<"LGPL-3.0-only">>) -> true; valid(<<"LGPL-3.0-or-later">>) -> true; valid(<<"LGPLLR">>) -> true; valid(<<"LOOP">>) -> true; valid(<<"LPD-document">>) -> true; valid(<<"LPL-1.0">>) -> true; valid(<<"LPL-1.02">>) -> true; valid(<<"LPPL-1.0">>) -> true; valid(<<"LPPL-1.1">>) -> true; valid(<<"LPPL-1.2">>) -> true; valid(<<"LPPL-1.3a">>) -> true; valid(<<"LPPL-1.3c">>) -> true; valid(<<"LZMA-SDK-9.11-to-9.20">>) -> true; valid(<<"LZMA-SDK-9.22">>) -> true; valid(<<"Latex2e">>) -> true; valid(<<"Latex2e-translated-notice">>) -> true; valid(<<"Leptonica">>) -> true; valid(<<"LiLiQ-P-1.1">>) -> true; valid(<<"LiLiQ-R-1.1">>) -> true; valid(<<"LiLiQ-Rplus-1.1">>) -> true; valid(<<"Libpng">>) -> true; valid(<<"Linux-OpenIB">>) -> true; valid(<<"Linux-man-pages-1-para">>) -> true; valid(<<"Linux-man-pages-copyleft">>) -> true; valid(<<"Linux-man-pages-copyleft-2-para">>) -> true; valid(<<"Linux-man-pages-copyleft-var">>) -> true; valid(<<"Lucida-Bitmap-Fonts">>) -> true; valid(<<"MIT">>) -> true; valid(<<"MIT-0">>) -> true; valid(<<"MIT-CMU">>) -> true; valid(<<"MIT-Festival">>) -> true; valid(<<"MIT-Khronos-old">>) -> true; valid(<<"MIT-Modern-Variant">>) -> true; valid(<<"MIT-Wu">>) -> true; valid(<<"MIT-advertising">>) -> true; valid(<<"MIT-enna">>) -> true; valid(<<"MIT-feh">>) -> true; valid(<<"MIT-open-group">>) -> true; valid(<<"MIT-testregex">>) -> true; valid(<<"MITNFA">>) -> true; valid(<<"MMIXware">>) -> true; valid(<<"MPEG-SSG">>) -> true; valid(<<"MPL-1.0">>) -> true; valid(<<"MPL-1.1">>) -> true; valid(<<"MPL-2.0">>) -> true; valid(<<"MPL-2.0-no-copyleft-exception">>) -> true; valid(<<"MS-LPL">>) -> true; valid(<<"MS-PL">>) -> true; valid(<<"MS-RL">>) -> true; valid(<<"MTLL">>) -> true; valid(<<"Mackerras-3-Clause">>) -> true; valid(<<"Mackerras-3-Clause-acknowledgment">>) -> true; valid(<<"MakeIndex">>) -> true; valid(<<"Martin-Birgmeier">>) -> true; valid(<<"McPhee-slideshow">>) -> true; valid(<<"Minpack">>) -> true; valid(<<"MirOS">>) -> true; valid(<<"Motosoto">>) -> true; valid(<<"MulanPSL-1.0">>) -> true; valid(<<"MulanPSL-2.0">>) -> true; valid(<<"Multics">>) -> true; valid(<<"Mup">>) -> true; valid(<<"NAIST-2003">>) -> true; valid(<<"NASA-1.3">>) -> true; valid(<<"NBPL-1.0">>) -> true; valid(<<"NCBI-PD">>) -> true; valid(<<"NCGL-UK-2.0">>) -> true; valid(<<"NCL">>) -> true; valid(<<"NCSA">>) -> true; valid(<<"NGPL">>) -> true; valid(<<"NICTA-1.0">>) -> true; valid(<<"NIST-PD">>) -> true; valid(<<"NIST-PD-fallback">>) -> true; valid(<<"NIST-Software">>) -> true; valid(<<"NLOD-1.0">>) -> true; valid(<<"NLOD-2.0">>) -> true; valid(<<"NLPL">>) -> true; valid(<<"NOSL">>) -> true; valid(<<"NPL-1.0">>) -> true; valid(<<"NPL-1.1">>) -> true; valid(<<"NPOSL-3.0">>) -> true; valid(<<"NRL">>) -> true; valid(<<"NTP">>) -> true; valid(<<"NTP-0">>) -> true; valid(<<"Naumen">>) -> true; valid(<<"Net-SNMP">>) -> true; valid(<<"NetCDF">>) -> true; valid(<<"Newsletr">>) -> true; valid(<<"Nokia">>) -> true; valid(<<"Noweb">>) -> true; valid(<<"Nunit">>) -> true; valid(<<"O-UDA-1.0">>) -> true; valid(<<"OAR">>) -> true; valid(<<"OCCT-PL">>) -> true; valid(<<"OCLC-2.0">>) -> true; valid(<<"ODC-By-1.0">>) -> true; valid(<<"ODbL-1.0">>) -> true; valid(<<"OFFIS">>) -> true; valid(<<"OFL-1.0">>) -> true; valid(<<"OFL-1.0-RFN">>) -> true; valid(<<"OFL-1.0-no-RFN">>) -> true; valid(<<"OFL-1.1">>) -> true; valid(<<"OFL-1.1-RFN">>) -> true; valid(<<"OFL-1.1-no-RFN">>) -> true; valid(<<"OGC-1.0">>) -> true; valid(<<"OGDL-Taiwan-1.0">>) -> true; valid(<<"OGL-Canada-2.0">>) -> true; valid(<<"OGL-UK-1.0">>) -> true; valid(<<"OGL-UK-2.0">>) -> true; valid(<<"OGL-UK-3.0">>) -> true; valid(<<"OGTSL">>) -> true; valid(<<"OLDAP-1.1">>) -> true; valid(<<"OLDAP-1.2">>) -> true; valid(<<"OLDAP-1.3">>) -> true; valid(<<"OLDAP-1.4">>) -> true; valid(<<"OLDAP-2.0">>) -> true; valid(<<"OLDAP-2.0.1">>) -> true; valid(<<"OLDAP-2.1">>) -> true; valid(<<"OLDAP-2.2">>) -> true; valid(<<"OLDAP-2.2.1">>) -> true; valid(<<"OLDAP-2.2.2">>) -> true; valid(<<"OLDAP-2.3">>) -> true; valid(<<"OLDAP-2.4">>) -> true; valid(<<"OLDAP-2.5">>) -> true; valid(<<"OLDAP-2.6">>) -> true; valid(<<"OLDAP-2.7">>) -> true; valid(<<"OLDAP-2.8">>) -> true; valid(<<"OLFL-1.3">>) -> true; valid(<<"OML">>) -> true; valid(<<"OPL-1.0">>) -> true; valid(<<"OPL-UK-3.0">>) -> true; valid(<<"OPUBL-1.0">>) -> true; valid(<<"OSET-PL-2.1">>) -> true; valid(<<"OSL-1.0">>) -> true; valid(<<"OSL-1.1">>) -> true; valid(<<"OSL-2.0">>) -> true; valid(<<"OSL-2.1">>) -> true; valid(<<"OSL-3.0">>) -> true; valid(<<"OpenPBS-2.3">>) -> true; valid(<<"OpenSSL">>) -> true; valid(<<"OpenSSL-standalone">>) -> true; valid(<<"OpenVision">>) -> true; valid(<<"PADL">>) -> true; valid(<<"PDDL-1.0">>) -> true; valid(<<"PHP-3.0">>) -> true; valid(<<"PHP-3.01">>) -> true; valid(<<"PPL">>) -> true; valid(<<"PSF-2.0">>) -> true; valid(<<"Parity-6.0.0">>) -> true; valid(<<"Parity-7.0.0">>) -> true; valid(<<"Pixar">>) -> true; valid(<<"Plexus">>) -> true; valid(<<"PolyForm-Noncommercial-1.0.0">>) -> true; valid(<<"PolyForm-Small-Business-1.0.0">>) -> true; valid(<<"PostgreSQL">>) -> true; valid(<<"Python-2.0">>) -> true; valid(<<"Python-2.0.1">>) -> true; valid(<<"QPL-1.0">>) -> true; valid(<<"QPL-1.0-INRIA-2004">>) -> true; valid(<<"Qhull">>) -> true; valid(<<"RHeCos-1.1">>) -> true; valid(<<"RPL-1.1">>) -> true; valid(<<"RPL-1.5">>) -> true; valid(<<"RPSL-1.0">>) -> true; valid(<<"RSA-MD">>) -> true; valid(<<"RSCPL">>) -> true; valid(<<"Rdisc">>) -> true; valid(<<"Ruby">>) -> true; valid(<<"SAX-PD">>) -> true; valid(<<"SAX-PD-2.0">>) -> true; valid(<<"SCEA">>) -> true; valid(<<"SGI-B-1.0">>) -> true; valid(<<"SGI-B-1.1">>) -> true; valid(<<"SGI-B-2.0">>) -> true; valid(<<"SGI-OpenGL">>) -> true; valid(<<"SGP4">>) -> true; valid(<<"SHL-0.5">>) -> true; valid(<<"SHL-0.51">>) -> true; valid(<<"SISSL">>) -> true; valid(<<"SISSL-1.2">>) -> true; valid(<<"SL">>) -> true; valid(<<"SMLNJ">>) -> true; valid(<<"SMPPL">>) -> true; valid(<<"SNIA">>) -> true; valid(<<"SPL-1.0">>) -> true; valid(<<"SSH-OpenSSH">>) -> true; valid(<<"SSH-short">>) -> true; valid(<<"SSLeay-standalone">>) -> true; valid(<<"SSPL-1.0">>) -> true; valid(<<"SWL">>) -> true; valid(<<"Saxpath">>) -> true; valid(<<"SchemeReport">>) -> true; valid(<<"Sendmail">>) -> true; valid(<<"Sendmail-8.23">>) -> true; valid(<<"SimPL-2.0">>) -> true; valid(<<"Sleepycat">>) -> true; valid(<<"Soundex">>) -> true; valid(<<"Spencer-86">>) -> true; valid(<<"Spencer-94">>) -> true; valid(<<"Spencer-99">>) -> true; valid(<<"StandardML-NJ">>) -> true; valid(<<"SugarCRM-1.1.3">>) -> true; valid(<<"Sun-PPP">>) -> true; valid(<<"Sun-PPP-2000">>) -> true; valid(<<"SunPro">>) -> true; valid(<<"Symlinks">>) -> true; valid(<<"TAPR-OHL-1.0">>) -> true; valid(<<"TCL">>) -> true; valid(<<"TCP-wrappers">>) -> true; valid(<<"TGPPL-1.0">>) -> true; valid(<<"TMate">>) -> true; valid(<<"TORQUE-1.1">>) -> true; valid(<<"TOSL">>) -> true; valid(<<"TPDL">>) -> true; valid(<<"TPL-1.0">>) -> true; valid(<<"TTWL">>) -> true; valid(<<"TTYP0">>) -> true; valid(<<"TU-Berlin-1.0">>) -> true; valid(<<"TU-Berlin-2.0">>) -> true; valid(<<"TermReadKey">>) -> true; valid(<<"UCAR">>) -> true; valid(<<"UCL-1.0">>) -> true; valid(<<"UMich-Merit">>) -> true; valid(<<"UPL-1.0">>) -> true; valid(<<"URT-RLE">>) -> true; valid(<<"Unicode-3.0">>) -> true; valid(<<"Unicode-DFS-2015">>) -> true; valid(<<"Unicode-DFS-2016">>) -> true; valid(<<"Unicode-TOU">>) -> true; valid(<<"UnixCrypt">>) -> true; valid(<<"Unlicense">>) -> true; valid(<<"VOSTROM">>) -> true; valid(<<"VSL-1.0">>) -> true; valid(<<"Vim">>) -> true; valid(<<"W3C">>) -> true; valid(<<"W3C-19980720">>) -> true; valid(<<"W3C-20150513">>) -> true; valid(<<"WTFPL">>) -> true; valid(<<"Watcom-1.0">>) -> true; valid(<<"Widget-Workshop">>) -> true; valid(<<"Wsuipa">>) -> true; valid(<<"X11">>) -> true; valid(<<"X11-distribute-modifications-variant">>) -> true; valid(<<"XFree86-1.1">>) -> true; valid(<<"XSkat">>) -> true; valid(<<"Xdebug-1.03">>) -> true; valid(<<"Xerox">>) -> true; valid(<<"Xfig">>) -> true; valid(<<"Xnet">>) -> true; valid(<<"YPL-1.0">>) -> true; valid(<<"YPL-1.1">>) -> true; valid(<<"ZPL-1.1">>) -> true; valid(<<"ZPL-2.0">>) -> true; valid(<<"ZPL-2.1">>) -> true; valid(<<"Zed">>) -> true; valid(<<"Zeeff">>) -> true; valid(<<"Zend-2.0">>) -> true; valid(<<"Zimbra-1.3">>) -> true; valid(<<"Zimbra-1.4">>) -> true; valid(<<"Zlib">>) -> true; valid(<<"any-OSI">>) -> true; valid(<<"bcrypt-Solar-Designer">>) -> true; valid(<<"blessing">>) -> true; valid(<<"bzip2-1.0.5">>) -> true; valid(<<"bzip2-1.0.6">>) -> true; valid(<<"check-cvs">>) -> true; valid(<<"checkmk">>) -> true; valid(<<"copyleft-next-0.3.0">>) -> true; valid(<<"copyleft-next-0.3.1">>) -> true; valid(<<"curl">>) -> true; valid(<<"cve-tou">>) -> true; valid(<<"diffmark">>) -> true; valid(<<"dtoa">>) -> true; valid(<<"dvipdfm">>) -> true; valid(<<"eCos-2.0">>) -> true; valid(<<"eGenix">>) -> true; valid(<<"etalab-2.0">>) -> true; valid(<<"fwlw">>) -> true; valid(<<"gSOAP-1.3b">>) -> true; valid(<<"gnuplot">>) -> true; valid(<<"gtkbook">>) -> true; valid(<<"hdparm">>) -> true; valid(<<"iMatix">>) -> true; valid(<<"libpng-2.0">>) -> true; valid(<<"libselinux-1.0">>) -> true; valid(<<"libtiff">>) -> true; valid(<<"libutil-David-Nugent">>) -> true; valid(<<"lsof">>) -> true; valid(<<"magaz">>) -> true; valid(<<"mailprio">>) -> true; valid(<<"metamail">>) -> true; valid(<<"mpi-permissive">>) -> true; valid(<<"mpich2">>) -> true; valid(<<"mplus">>) -> true; valid(<<"pkgconf">>) -> true; valid(<<"pnmstitch">>) -> true; valid(<<"psfrag">>) -> true; valid(<<"psutils">>) -> true; valid(<<"python-ldap">>) -> true; valid(<<"radvd">>) -> true; valid(<<"snprintf">>) -> true; valid(<<"softSurfer">>) -> true; valid(<<"ssh-keyscan">>) -> true; valid(<<"swrule">>) -> true; valid(<<"threeparttable">>) -> true; valid(<<"ulem">>) -> true; valid(<<"w3m">>) -> true; valid(<<"wxWindows">>) -> true; valid(<<"xinetd">>) -> true; valid(<<"xkeyboard-config-Zinoviev">>) -> true; valid(<<"xlock">>) -> true; valid(<<"xpp">>) -> true; valid(<<"xzoom">>) -> true; valid(<<"zlib-acknowledgement">>) -> true; valid(_) -> false. hex-2.4.2/src/mix_hex_pb_names.erl000066400000000000000000001152141517471540100171140ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% -*- coding: utf-8 -*- %% % this file is @generated %% @private %% Automatically generated, do not edit %% Generated by gpb_compile version 4.21.1 %% Version source: file -module(mix_hex_pb_names). -export([encode_msg/2, encode_msg/3]). -export([decode_msg/2, decode_msg/3]). -export([merge_msgs/3, merge_msgs/4]). -export([verify_msg/2, verify_msg/3]). -export([get_msg_defs/0]). -export([get_msg_names/0]). -export([get_group_names/0]). -export([get_msg_or_group_names/0]). -export([get_enum_names/0]). -export([find_msg_def/1, fetch_msg_def/1]). -export([find_enum_def/1, fetch_enum_def/1]). -export([enum_symbol_by_value/2, enum_value_by_symbol/2]). -export([get_service_names/0]). -export([get_service_def/1]). -export([get_rpc_names/1]). -export([find_rpc_def/2, fetch_rpc_def/2]). -export([fqbin_to_service_name/1]). -export([service_name_to_fqbin/1]). -export([fqbins_to_service_and_rpc_name/2]). -export([service_and_rpc_name_to_fqbins/2]). -export([fqbin_to_msg_name/1]). -export([msg_name_to_fqbin/1]). -export([fqbin_to_enum_name/1]). -export([enum_name_to_fqbin/1]). -export([get_package_name/0]). -export([uses_packages/0]). -export([source_basename/0]). -export([get_all_source_basenames/0]). -export([get_all_proto_names/0]). -export([get_msg_containment/1]). -export([get_pkg_containment/1]). -export([get_service_containment/1]). -export([get_rpc_containment/1]). -export([get_enum_containment/1]). -export([get_proto_by_msg_name_as_fqbin/1]). -export([get_proto_by_service_name_as_fqbin/1]). -export([get_proto_by_enum_name_as_fqbin/1]). -export([get_protos_by_pkg_name_as_fqbin/1]). -export([gpb_version_as_string/0, gpb_version_as_list/0]). -export([gpb_version_source/0]). %% enumerated types -export_type([]). %% message types -type 'Names'() :: #{packages => ['Package'()], % = 1, repeated repository => unicode:chardata() % = 2, required }. -type 'Package'() :: #{name => unicode:chardata(), % = 1, required updated_at => 'Timestamp'() % = 3, optional }. -type 'Timestamp'() :: #{seconds => integer(), % = 1, required, 64 bits nanos => integer() % = 2, required, 32 bits }. -export_type(['Names'/0, 'Package'/0, 'Timestamp'/0]). -type '$msg_name'() :: 'Names' | 'Package' | 'Timestamp'. -type '$msg'() :: 'Names'() | 'Package'() | 'Timestamp'(). -export_type(['$msg_name'/0, '$msg'/0]). -if(?OTP_RELEASE >= 24). -dialyzer({no_underspecs, encode_msg/2}). -endif. -spec encode_msg('$msg'(), '$msg_name'()) -> binary(). encode_msg(Msg, MsgName) when is_atom(MsgName) -> encode_msg(Msg, MsgName, []). -if(?OTP_RELEASE >= 24). -dialyzer({no_underspecs, encode_msg/3}). -endif. -spec encode_msg('$msg'(), '$msg_name'(), list()) -> binary(). encode_msg(Msg, MsgName, Opts) -> verify_msg(Msg, MsgName, Opts), TrUserData = proplists:get_value(user_data, Opts), case MsgName of 'Names' -> encode_msg_Names(id(Msg, TrUserData), TrUserData); 'Package' -> encode_msg_Package(id(Msg, TrUserData), TrUserData); 'Timestamp' -> encode_msg_Timestamp(id(Msg, TrUserData), TrUserData) end. encode_msg_Names(Msg, TrUserData) -> encode_msg_Names(Msg, <<>>, TrUserData). encode_msg_Names(#{repository := F2} = M, Bin, TrUserData) -> B1 = case M of #{packages := F1} -> TrF1 = id(F1, TrUserData), if TrF1 == [] -> Bin; true -> e_field_Names_packages(TrF1, Bin, TrUserData) end; _ -> Bin end, begin TrF2 = id(F2, TrUserData), e_type_string(TrF2, <>, TrUserData) end. encode_msg_Package(Msg, TrUserData) -> encode_msg_Package(Msg, <<>>, TrUserData). encode_msg_Package(#{name := F1} = M, Bin, TrUserData) -> B1 = begin TrF1 = id(F1, TrUserData), e_type_string(TrF1, <>, TrUserData) end, case M of #{updated_at := F2} -> begin TrF2 = id(F2, TrUserData), e_mfield_Package_updated_at(TrF2, <>, TrUserData) end; _ -> B1 end. encode_msg_Timestamp(Msg, TrUserData) -> encode_msg_Timestamp(Msg, <<>>, TrUserData). encode_msg_Timestamp(#{seconds := F1, nanos := F2}, Bin, TrUserData) -> B1 = begin TrF1 = id(F1, TrUserData), e_type_int64(TrF1, <>, TrUserData) end, begin TrF2 = id(F2, TrUserData), e_type_int32(TrF2, <>, TrUserData) end. e_mfield_Names_packages(Msg, Bin, TrUserData) -> SubBin = encode_msg_Package(Msg, <<>>, TrUserData), Bin2 = e_varint(byte_size(SubBin), Bin), <>. e_field_Names_packages([Elem | Rest], Bin, TrUserData) -> Bin2 = <>, Bin3 = e_mfield_Names_packages(id(Elem, TrUserData), Bin2, TrUserData), e_field_Names_packages(Rest, Bin3, TrUserData); e_field_Names_packages([], Bin, _TrUserData) -> Bin. e_mfield_Package_updated_at(Msg, Bin, TrUserData) -> SubBin = encode_msg_Timestamp(Msg, <<>>, TrUserData), Bin2 = e_varint(byte_size(SubBin), Bin), <>. -compile({nowarn_unused_function,e_type_sint/3}). e_type_sint(Value, Bin, _TrUserData) when Value >= 0 -> e_varint(Value * 2, Bin); e_type_sint(Value, Bin, _TrUserData) -> e_varint(Value * -2 - 1, Bin). -compile({nowarn_unused_function,e_type_int32/3}). e_type_int32(Value, Bin, _TrUserData) when 0 =< Value, Value =< 127 -> <>; e_type_int32(Value, Bin, _TrUserData) -> <> = <>, e_varint(N, Bin). -compile({nowarn_unused_function,e_type_int64/3}). e_type_int64(Value, Bin, _TrUserData) when 0 =< Value, Value =< 127 -> <>; e_type_int64(Value, Bin, _TrUserData) -> <> = <>, e_varint(N, Bin). -compile({nowarn_unused_function,e_type_bool/3}). e_type_bool(true, Bin, _TrUserData) -> <>; e_type_bool(false, Bin, _TrUserData) -> <>; e_type_bool(1, Bin, _TrUserData) -> <>; e_type_bool(0, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_string/3}). e_type_string(S, Bin, _TrUserData) -> Utf8 = unicode:characters_to_binary(S), Bin2 = e_varint(byte_size(Utf8), Bin), <>. -compile({nowarn_unused_function,e_type_bytes/3}). e_type_bytes(Bytes, Bin, _TrUserData) when is_binary(Bytes) -> Bin2 = e_varint(byte_size(Bytes), Bin), <>; e_type_bytes(Bytes, Bin, _TrUserData) when is_list(Bytes) -> BytesBin = iolist_to_binary(Bytes), Bin2 = e_varint(byte_size(BytesBin), Bin), <>. -compile({nowarn_unused_function,e_type_fixed32/3}). e_type_fixed32(Value, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_sfixed32/3}). e_type_sfixed32(Value, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_fixed64/3}). e_type_fixed64(Value, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_sfixed64/3}). e_type_sfixed64(Value, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_float/3}). e_type_float(V, Bin, _) when is_number(V) -> <>; e_type_float(infinity, Bin, _) -> <>; e_type_float('-infinity', Bin, _) -> <>; e_type_float(nan, Bin, _) -> <>. -compile({nowarn_unused_function,e_type_double/3}). e_type_double(V, Bin, _) when is_number(V) -> <>; e_type_double(infinity, Bin, _) -> <>; e_type_double('-infinity', Bin, _) -> <>; e_type_double(nan, Bin, _) -> <>. -compile({nowarn_unused_function,e_unknown_elems/2}). e_unknown_elems([Elem | Rest], Bin) -> BinR = case Elem of {varint, FNum, N} -> BinF = e_varint(FNum bsl 3, Bin), e_varint(N, BinF); {length_delimited, FNum, Data} -> BinF = e_varint(FNum bsl 3 bor 2, Bin), BinL = e_varint(byte_size(Data), BinF), <>; {group, FNum, GroupFields} -> Bin1 = e_varint(FNum bsl 3 bor 3, Bin), Bin2 = e_unknown_elems(GroupFields, Bin1), e_varint(FNum bsl 3 bor 4, Bin2); {fixed32, FNum, V} -> BinF = e_varint(FNum bsl 3 bor 5, Bin), <>; {fixed64, FNum, V} -> BinF = e_varint(FNum bsl 3 bor 1, Bin), <> end, e_unknown_elems(Rest, BinR); e_unknown_elems([], Bin) -> Bin. -compile({nowarn_unused_function,e_varint/3}). e_varint(N, Bin, _TrUserData) -> e_varint(N, Bin). -compile({nowarn_unused_function,e_varint/2}). e_varint(N, Bin) when N =< 127 -> <>; e_varint(N, Bin) -> Bin2 = <>, e_varint(N bsr 7, Bin2). decode_msg(Bin, MsgName) when is_binary(Bin) -> decode_msg(Bin, MsgName, []). decode_msg(Bin, MsgName, Opts) when is_binary(Bin) -> TrUserData = proplists:get_value(user_data, Opts), decode_msg_1_catch(Bin, MsgName, TrUserData). -ifdef('OTP_RELEASE'). decode_msg_1_catch(Bin, MsgName, TrUserData) -> try decode_msg_2_doit(MsgName, Bin, TrUserData) catch error:{gpb_error,_}=Reason:StackTrace -> erlang:raise(error, Reason, StackTrace); Class:Reason:StackTrace -> error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}}) end. -else. decode_msg_1_catch(Bin, MsgName, TrUserData) -> try decode_msg_2_doit(MsgName, Bin, TrUserData) catch error:{gpb_error,_}=Reason -> erlang:raise(error, Reason, erlang:get_stacktrace()); Class:Reason -> StackTrace = erlang:get_stacktrace(), error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}}) end. -endif. decode_msg_2_doit('Names', Bin, TrUserData) -> id(decode_msg_Names(Bin, TrUserData), TrUserData); decode_msg_2_doit('Package', Bin, TrUserData) -> id(decode_msg_Package(Bin, TrUserData), TrUserData); decode_msg_2_doit('Timestamp', Bin, TrUserData) -> id(decode_msg_Timestamp(Bin, TrUserData), TrUserData). decode_msg_Names(Bin, TrUserData) -> dfp_read_field_def_Names(Bin, 0, 0, 0, id([], TrUserData), id('$undef', TrUserData), TrUserData). dfp_read_field_def_Names(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> d_field_Names_packages(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); dfp_read_field_def_Names(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> d_field_Names_repository(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); dfp_read_field_def_Names(<<>>, 0, 0, _, R1, F@_2, TrUserData) -> S1 = #{repository => F@_2}, if R1 == '$undef' -> S1; true -> S1#{packages => lists_reverse(R1, TrUserData)} end; dfp_read_field_def_Names(Other, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dg_read_field_def_Names(Other, Z1, Z2, F, F@_1, F@_2, TrUserData). dg_read_field_def_Names(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 32 - 7 -> dg_read_field_def_Names(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); dg_read_field_def_Names(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, TrUserData) -> Key = X bsl N + Acc, case Key of 10 -> d_field_Names_packages(Rest, 0, 0, 0, F@_1, F@_2, TrUserData); 18 -> d_field_Names_repository(Rest, 0, 0, 0, F@_1, F@_2, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_Names(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 1 -> skip_64_Names(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 2 -> skip_length_delimited_Names(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 3 -> skip_group_Names(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 5 -> skip_32_Names(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData) end end; dg_read_field_def_Names(<<>>, 0, 0, _, R1, F@_2, TrUserData) -> S1 = #{repository => F@_2}, if R1 == '$undef' -> S1; true -> S1#{packages => lists_reverse(R1, TrUserData)} end. d_field_Names_packages(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> d_field_Names_packages(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); d_field_Names_packages(<<0:1, X:7, Rest/binary>>, N, Acc, F, Prev, F@_2, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, {id(decode_msg_Package(Bs, TrUserData), TrUserData), Rest2} end, dfp_read_field_def_Names(RestF, 0, 0, F, cons(NewFValue, Prev, TrUserData), F@_2, TrUserData). d_field_Names_repository(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> d_field_Names_repository(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); d_field_Names_repository(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_Names(RestF, 0, 0, F, F@_1, NewFValue, TrUserData). skip_varint_Names(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> skip_varint_Names(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); skip_varint_Names(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Names(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). skip_length_delimited_Names(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> skip_length_delimited_Names(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); skip_length_delimited_Names(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) -> Length = X bsl N + Acc, <<_:Length/binary, Rest2/binary>> = Rest, dfp_read_field_def_Names(Rest2, 0, 0, F, F@_1, F@_2, TrUserData). skip_group_Names(Bin, _, Z2, FNum, F@_1, F@_2, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_Names(Rest, 0, Z2, FNum, F@_1, F@_2, TrUserData). skip_32_Names(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Names(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). skip_64_Names(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Names(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). decode_msg_Package(Bin, TrUserData) -> dfp_read_field_def_Package(Bin, 0, 0, 0, id('$undef', TrUserData), id('$undef', TrUserData), TrUserData). dfp_read_field_def_Package(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> d_field_Package_name(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); dfp_read_field_def_Package(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> d_field_Package_updated_at(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); dfp_read_field_def_Package(<<>>, 0, 0, _, F@_1, F@_2, _) -> S1 = #{name => F@_1}, if F@_2 == '$undef' -> S1; true -> S1#{updated_at => F@_2} end; dfp_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dg_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, TrUserData). dg_read_field_def_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 32 - 7 -> dg_read_field_def_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); dg_read_field_def_Package(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, TrUserData) -> Key = X bsl N + Acc, case Key of 10 -> d_field_Package_name(Rest, 0, 0, 0, F@_1, F@_2, TrUserData); 26 -> d_field_Package_updated_at(Rest, 0, 0, 0, F@_1, F@_2, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 1 -> skip_64_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 2 -> skip_length_delimited_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 3 -> skip_group_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 5 -> skip_32_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData) end end; dg_read_field_def_Package(<<>>, 0, 0, _, F@_1, F@_2, _) -> S1 = #{name => F@_1}, if F@_2 == '$undef' -> S1; true -> S1#{updated_at => F@_2} end. d_field_Package_name(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> d_field_Package_name(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); d_field_Package_name(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_Package(RestF, 0, 0, F, NewFValue, F@_2, TrUserData). d_field_Package_updated_at(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> d_field_Package_updated_at(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); d_field_Package_updated_at(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, Prev, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, {id(decode_msg_Timestamp(Bs, TrUserData), TrUserData), Rest2} end, dfp_read_field_def_Package(RestF, 0, 0, F, F@_1, if Prev == '$undef' -> NewFValue; true -> merge_msg_Timestamp(Prev, NewFValue, TrUserData) end, TrUserData). skip_varint_Package(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> skip_varint_Package(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); skip_varint_Package(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). skip_length_delimited_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> skip_length_delimited_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); skip_length_delimited_Package(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) -> Length = X bsl N + Acc, <<_:Length/binary, Rest2/binary>> = Rest, dfp_read_field_def_Package(Rest2, 0, 0, F, F@_1, F@_2, TrUserData). skip_group_Package(Bin, _, Z2, FNum, F@_1, F@_2, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_Package(Rest, 0, Z2, FNum, F@_1, F@_2, TrUserData). skip_32_Package(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). skip_64_Package(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). decode_msg_Timestamp(Bin, TrUserData) -> dfp_read_field_def_Timestamp(Bin, 0, 0, 0, id('$undef', TrUserData), id('$undef', TrUserData), TrUserData). dfp_read_field_def_Timestamp(<<8, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> d_field_Timestamp_seconds(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); dfp_read_field_def_Timestamp(<<16, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> d_field_Timestamp_nanos(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); dfp_read_field_def_Timestamp(<<>>, 0, 0, _, F@_1, F@_2, _) -> #{seconds => F@_1, nanos => F@_2}; dfp_read_field_def_Timestamp(Other, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dg_read_field_def_Timestamp(Other, Z1, Z2, F, F@_1, F@_2, TrUserData). dg_read_field_def_Timestamp(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 32 - 7 -> dg_read_field_def_Timestamp(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); dg_read_field_def_Timestamp(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, TrUserData) -> Key = X bsl N + Acc, case Key of 8 -> d_field_Timestamp_seconds(Rest, 0, 0, 0, F@_1, F@_2, TrUserData); 16 -> d_field_Timestamp_nanos(Rest, 0, 0, 0, F@_1, F@_2, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_Timestamp(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 1 -> skip_64_Timestamp(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 2 -> skip_length_delimited_Timestamp(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 3 -> skip_group_Timestamp(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 5 -> skip_32_Timestamp(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData) end end; dg_read_field_def_Timestamp(<<>>, 0, 0, _, F@_1, F@_2, _) -> #{seconds => F@_1, nanos => F@_2}. d_field_Timestamp_seconds(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> d_field_Timestamp_seconds(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); d_field_Timestamp_seconds(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, TrUserData) -> {NewFValue, RestF} = {begin <> = <<(X bsl N + Acc):64/unsigned-native>>, id(Res, TrUserData) end, Rest}, dfp_read_field_def_Timestamp(RestF, 0, 0, F, NewFValue, F@_2, TrUserData). d_field_Timestamp_nanos(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> d_field_Timestamp_nanos(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); d_field_Timestamp_nanos(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, TrUserData) -> {NewFValue, RestF} = {begin <> = <<(X bsl N + Acc):32/unsigned-native>>, id(Res, TrUserData) end, Rest}, dfp_read_field_def_Timestamp(RestF, 0, 0, F, F@_1, NewFValue, TrUserData). skip_varint_Timestamp(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> skip_varint_Timestamp(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); skip_varint_Timestamp(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Timestamp(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). skip_length_delimited_Timestamp(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> skip_length_delimited_Timestamp(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); skip_length_delimited_Timestamp(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) -> Length = X bsl N + Acc, <<_:Length/binary, Rest2/binary>> = Rest, dfp_read_field_def_Timestamp(Rest2, 0, 0, F, F@_1, F@_2, TrUserData). skip_group_Timestamp(Bin, _, Z2, FNum, F@_1, F@_2, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_Timestamp(Rest, 0, Z2, FNum, F@_1, F@_2, TrUserData). skip_32_Timestamp(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Timestamp(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). skip_64_Timestamp(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Timestamp(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). read_group(Bin, FieldNum) -> {NumBytes, EndTagLen} = read_gr_b(Bin, 0, 0, 0, 0, FieldNum), <> = Bin, {Group, Rest}. %% Like skipping over fields, but record the total length, %% Each field is <(FieldNum bsl 3) bor FieldType> ++ %% Record the length because varints may be non-optimally encoded. %% %% Groups can be nested, but assume the same FieldNum cannot be nested %% because group field numbers are shared with the rest of the fields %% numbers. Thus we can search just for an group-end with the same %% field number. %% %% (The only time the same group field number could occur would %% be in a nested sub message, but then it would be inside a %% length-delimited entry, which we skip-read by length.) read_gr_b(<<1:1, X:7, Tl/binary>>, N, Acc, NumBytes, TagLen, FieldNum) when N < (32-7) -> read_gr_b(Tl, N+7, X bsl N + Acc, NumBytes, TagLen+1, FieldNum); read_gr_b(<<0:1, X:7, Tl/binary>>, N, Acc, NumBytes, TagLen, FieldNum) -> Key = X bsl N + Acc, TagLen1 = TagLen + 1, case {Key bsr 3, Key band 7} of {FieldNum, 4} -> % 4 = group_end {NumBytes, TagLen1}; {_, 0} -> % 0 = varint read_gr_vi(Tl, 0, NumBytes + TagLen1, FieldNum); {_, 1} -> % 1 = bits64 <<_:64, Tl2/binary>> = Tl, read_gr_b(Tl2, 0, 0, NumBytes + TagLen1 + 8, 0, FieldNum); {_, 2} -> % 2 = length_delimited read_gr_ld(Tl, 0, 0, NumBytes + TagLen1, FieldNum); {_, 3} -> % 3 = group_start read_gr_b(Tl, 0, 0, NumBytes + TagLen1, 0, FieldNum); {_, 4} -> % 4 = group_end read_gr_b(Tl, 0, 0, NumBytes + TagLen1, 0, FieldNum); {_, 5} -> % 5 = bits32 <<_:32, Tl2/binary>> = Tl, read_gr_b(Tl2, 0, 0, NumBytes + TagLen1 + 4, 0, FieldNum) end. read_gr_vi(<<1:1, _:7, Tl/binary>>, N, NumBytes, FieldNum) when N < (64-7) -> read_gr_vi(Tl, N+7, NumBytes+1, FieldNum); read_gr_vi(<<0:1, _:7, Tl/binary>>, _, NumBytes, FieldNum) -> read_gr_b(Tl, 0, 0, NumBytes+1, 0, FieldNum). read_gr_ld(<<1:1, X:7, Tl/binary>>, N, Acc, NumBytes, FieldNum) when N < (64-7) -> read_gr_ld(Tl, N+7, X bsl N + Acc, NumBytes+1, FieldNum); read_gr_ld(<<0:1, X:7, Tl/binary>>, N, Acc, NumBytes, FieldNum) -> Len = X bsl N + Acc, NumBytes1 = NumBytes + 1, <<_:Len/binary, Tl2/binary>> = Tl, read_gr_b(Tl2, 0, 0, NumBytes1 + Len, 0, FieldNum). merge_msgs(Prev, New, MsgName) when is_atom(MsgName) -> merge_msgs(Prev, New, MsgName, []). merge_msgs(Prev, New, MsgName, Opts) -> TrUserData = proplists:get_value(user_data, Opts), case MsgName of 'Names' -> merge_msg_Names(Prev, New, TrUserData); 'Package' -> merge_msg_Package(Prev, New, TrUserData); 'Timestamp' -> merge_msg_Timestamp(Prev, New, TrUserData) end. -compile({nowarn_unused_function,merge_msg_Names/3}). merge_msg_Names(#{} = PMsg, #{repository := NFrepository} = NMsg, TrUserData) -> S1 = #{repository => NFrepository}, case {PMsg, NMsg} of {#{packages := PFpackages}, #{packages := NFpackages}} -> S1#{packages => 'erlang_++'(PFpackages, NFpackages, TrUserData)}; {_, #{packages := NFpackages}} -> S1#{packages => NFpackages}; {#{packages := PFpackages}, _} -> S1#{packages => PFpackages}; {_, _} -> S1 end. -compile({nowarn_unused_function,merge_msg_Package/3}). merge_msg_Package(#{} = PMsg, #{name := NFname} = NMsg, TrUserData) -> S1 = #{name => NFname}, case {PMsg, NMsg} of {#{updated_at := PFupdated_at}, #{updated_at := NFupdated_at}} -> S1#{updated_at => merge_msg_Timestamp(PFupdated_at, NFupdated_at, TrUserData)}; {_, #{updated_at := NFupdated_at}} -> S1#{updated_at => NFupdated_at}; {#{updated_at := PFupdated_at}, _} -> S1#{updated_at => PFupdated_at}; {_, _} -> S1 end. -compile({nowarn_unused_function,merge_msg_Timestamp/3}). merge_msg_Timestamp(#{}, #{seconds := NFseconds, nanos := NFnanos}, _) -> #{seconds => NFseconds, nanos => NFnanos}. verify_msg(Msg, MsgName) when is_atom(MsgName) -> verify_msg(Msg, MsgName, []). verify_msg(Msg, MsgName, Opts) -> TrUserData = proplists:get_value(user_data, Opts), case MsgName of 'Names' -> v_msg_Names(Msg, [MsgName], TrUserData); 'Package' -> v_msg_Package(Msg, [MsgName], TrUserData); 'Timestamp' -> v_msg_Timestamp(Msg, [MsgName], TrUserData); _ -> mk_type_error(not_a_known_message, Msg, []) end. -compile({nowarn_unused_function,v_msg_Names/3}). -dialyzer({nowarn_function,v_msg_Names/3}). v_msg_Names(#{repository := F2} = M, Path, TrUserData) -> case M of #{packages := F1} -> if is_list(F1) -> _ = [v_submsg_Package(Elem, [packages | Path], TrUserData) || Elem <- F1], ok; true -> mk_type_error({invalid_list_of, {msg, 'Package'}}, F1, [packages | Path]) end; _ -> ok end, v_type_string(F2, [repository | Path], TrUserData), lists:foreach(fun (packages) -> ok; (repository) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), ok; v_msg_Names(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [repository] -- maps:keys(M), 'Names'}, M, Path); v_msg_Names(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'Names'}, X, Path). -compile({nowarn_unused_function,v_submsg_Package/3}). -dialyzer({nowarn_function,v_submsg_Package/3}). v_submsg_Package(Msg, Path, TrUserData) -> v_msg_Package(Msg, Path, TrUserData). -compile({nowarn_unused_function,v_msg_Package/3}). -dialyzer({nowarn_function,v_msg_Package/3}). v_msg_Package(#{name := F1} = M, Path, TrUserData) -> v_type_string(F1, [name | Path], TrUserData), case M of #{updated_at := F2} -> v_submsg_Timestamp(F2, [updated_at | Path], TrUserData); _ -> ok end, lists:foreach(fun (name) -> ok; (updated_at) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), ok; v_msg_Package(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [name] -- maps:keys(M), 'Package'}, M, Path); v_msg_Package(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'Package'}, X, Path). -compile({nowarn_unused_function,v_submsg_Timestamp/3}). -dialyzer({nowarn_function,v_submsg_Timestamp/3}). v_submsg_Timestamp(Msg, Path, TrUserData) -> v_msg_Timestamp(Msg, Path, TrUserData). -compile({nowarn_unused_function,v_msg_Timestamp/3}). -dialyzer({nowarn_function,v_msg_Timestamp/3}). v_msg_Timestamp(#{seconds := F1, nanos := F2} = M, Path, TrUserData) -> v_type_int64(F1, [seconds | Path], TrUserData), v_type_int32(F2, [nanos | Path], TrUserData), lists:foreach(fun (seconds) -> ok; (nanos) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), ok; v_msg_Timestamp(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [seconds, nanos] -- maps:keys(M), 'Timestamp'}, M, Path); v_msg_Timestamp(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'Timestamp'}, X, Path). -compile({nowarn_unused_function,v_type_int32/3}). -dialyzer({nowarn_function,v_type_int32/3}). v_type_int32(N, _Path, _TrUserData) when is_integer(N), -2147483648 =< N, N =< 2147483647 -> ok; v_type_int32(N, Path, _TrUserData) when is_integer(N) -> mk_type_error({value_out_of_range, int32, signed, 32}, N, Path); v_type_int32(X, Path, _TrUserData) -> mk_type_error({bad_integer, int32, signed, 32}, X, Path). -compile({nowarn_unused_function,v_type_int64/3}). -dialyzer({nowarn_function,v_type_int64/3}). v_type_int64(N, _Path, _TrUserData) when is_integer(N), -9223372036854775808 =< N, N =< 9223372036854775807 -> ok; v_type_int64(N, Path, _TrUserData) when is_integer(N) -> mk_type_error({value_out_of_range, int64, signed, 64}, N, Path); v_type_int64(X, Path, _TrUserData) -> mk_type_error({bad_integer, int64, signed, 64}, X, Path). -compile({nowarn_unused_function,v_type_string/3}). -dialyzer({nowarn_function,v_type_string/3}). v_type_string(S, Path, _TrUserData) when is_list(S); is_binary(S) -> try unicode:characters_to_binary(S) of B when is_binary(B) -> ok; {error, _, _} -> mk_type_error(bad_unicode_string, S, Path) catch error:badarg -> mk_type_error(bad_unicode_string, S, Path) end; v_type_string(X, Path, _TrUserData) -> mk_type_error(bad_unicode_string, X, Path). -compile({nowarn_unused_function,mk_type_error/3}). -spec mk_type_error(_, _, list()) -> no_return(). mk_type_error(Error, ValueSeen, Path) -> Path2 = prettify_path(Path), erlang:error({gpb_type_error, {Error, [{value, ValueSeen}, {path, Path2}]}}). -compile({nowarn_unused_function,prettify_path/1}). -dialyzer({nowarn_function,prettify_path/1}). prettify_path([]) -> top_level; prettify_path(PathR) -> lists:append(lists:join(".", lists:map(fun atom_to_list/1, lists:reverse(PathR)))). -compile({nowarn_unused_function,id/2}). -compile({inline,id/2}). id(X, _TrUserData) -> X. -compile({nowarn_unused_function,v_ok/3}). -compile({inline,v_ok/3}). v_ok(_Value, _Path, _TrUserData) -> ok. -compile({nowarn_unused_function,m_overwrite/3}). -compile({inline,m_overwrite/3}). m_overwrite(_Prev, New, _TrUserData) -> New. -compile({nowarn_unused_function,cons/3}). -compile({inline,cons/3}). cons(Elem, Acc, _TrUserData) -> [Elem | Acc]. -compile({nowarn_unused_function,lists_reverse/2}). -compile({inline,lists_reverse/2}). 'lists_reverse'(L, _TrUserData) -> lists:reverse(L). -compile({nowarn_unused_function,'erlang_++'/3}). -compile({inline,'erlang_++'/3}). 'erlang_++'(A, B, _TrUserData) -> A ++ B. get_msg_defs() -> [{{msg, 'Names'}, [#{name => packages, fnum => 1, rnum => 2, type => {msg, 'Package'}, occurrence => repeated, opts => []}, #{name => repository, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}]}, {{msg, 'Package'}, [#{name => name, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => updated_at, fnum => 3, rnum => 3, type => {msg, 'Timestamp'}, occurrence => optional, opts => []}]}, {{msg, 'Timestamp'}, [#{name => seconds, fnum => 1, rnum => 2, type => int64, occurrence => required, opts => []}, #{name => nanos, fnum => 2, rnum => 3, type => int32, occurrence => required, opts => []}]}]. get_msg_names() -> ['Names', 'Package', 'Timestamp']. get_group_names() -> []. get_msg_or_group_names() -> ['Names', 'Package', 'Timestamp']. get_enum_names() -> []. fetch_msg_def(MsgName) -> case find_msg_def(MsgName) of Fs when is_list(Fs) -> Fs; error -> erlang:error({no_such_msg, MsgName}) end. -spec fetch_enum_def(_) -> no_return(). fetch_enum_def(EnumName) -> erlang:error({no_such_enum, EnumName}). find_msg_def('Names') -> [#{name => packages, fnum => 1, rnum => 2, type => {msg, 'Package'}, occurrence => repeated, opts => []}, #{name => repository, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}]; find_msg_def('Package') -> [#{name => name, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => updated_at, fnum => 3, rnum => 3, type => {msg, 'Timestamp'}, occurrence => optional, opts => []}]; find_msg_def('Timestamp') -> [#{name => seconds, fnum => 1, rnum => 2, type => int64, occurrence => required, opts => []}, #{name => nanos, fnum => 2, rnum => 3, type => int32, occurrence => required, opts => []}]; find_msg_def(_) -> error. find_enum_def(_) -> error. -spec enum_symbol_by_value(_, _) -> no_return(). enum_symbol_by_value(E, V) -> erlang:error({no_enum_defs, E, V}). -spec enum_value_by_symbol(_, _) -> no_return(). enum_value_by_symbol(E, V) -> erlang:error({no_enum_defs, E, V}). get_service_names() -> []. get_service_def(_) -> error. get_rpc_names(_) -> error. find_rpc_def(_, _) -> error. -spec fetch_rpc_def(_, _) -> no_return(). fetch_rpc_def(ServiceName, RpcName) -> erlang:error({no_such_rpc, ServiceName, RpcName}). %% Convert a a fully qualified (ie with package name) service name %% as a binary to a service name as an atom. -spec fqbin_to_service_name(_) -> no_return(). fqbin_to_service_name(X) -> error({gpb_error, {badservice, X}}). %% Convert a service name as an atom to a fully qualified %% (ie with package name) name as a binary. -spec service_name_to_fqbin(_) -> no_return(). service_name_to_fqbin(X) -> error({gpb_error, {badservice, X}}). %% Convert a a fully qualified (ie with package name) service name %% and an rpc name, both as binaries to a service name and an rpc %% name, as atoms. -spec fqbins_to_service_and_rpc_name(_, _) -> no_return(). fqbins_to_service_and_rpc_name(S, R) -> error({gpb_error, {badservice_or_rpc, {S, R}}}). %% Convert a service name and an rpc name, both as atoms, %% to a fully qualified (ie with package name) service name and %% an rpc name as binaries. -spec service_and_rpc_name_to_fqbins(_, _) -> no_return(). service_and_rpc_name_to_fqbins(S, R) -> error({gpb_error, {badservice_or_rpc, {S, R}}}). fqbin_to_msg_name(<<"Names">>) -> 'Names'; fqbin_to_msg_name(<<"Package">>) -> 'Package'; fqbin_to_msg_name(<<"Timestamp">>) -> 'Timestamp'; fqbin_to_msg_name(E) -> error({gpb_error, {badmsg, E}}). msg_name_to_fqbin('Names') -> <<"Names">>; msg_name_to_fqbin('Package') -> <<"Package">>; msg_name_to_fqbin('Timestamp') -> <<"Timestamp">>; msg_name_to_fqbin(E) -> error({gpb_error, {badmsg, E}}). -spec fqbin_to_enum_name(_) -> no_return(). fqbin_to_enum_name(E) -> error({gpb_error, {badenum, E}}). -spec enum_name_to_fqbin(_) -> no_return(). enum_name_to_fqbin(E) -> error({gpb_error, {badenum, E}}). get_package_name() -> undefined. %% Whether or not the message names %% are prepended with package name or not. uses_packages() -> false. source_basename() -> "mix_hex_pb_names.proto". %% Retrieve all proto file names, also imported ones. %% The order is top-down. The first element is always the main %% source file. The files are returned with extension, %% see get_all_proto_names/0 for a version that returns %% the basenames sans extension get_all_source_basenames() -> ["mix_hex_pb_names.proto"]. %% Retrieve all proto file names, also imported ones. %% The order is top-down. The first element is always the main %% source file. The files are returned sans .proto extension, %% to make it easier to use them with the various get_xyz_containment %% functions. get_all_proto_names() -> ["mix_hex_pb_names"]. get_msg_containment("mix_hex_pb_names") -> ['Names', 'Package', 'Timestamp']; get_msg_containment(P) -> error({gpb_error, {badproto, P}}). get_pkg_containment("mix_hex_pb_names") -> undefined; get_pkg_containment(P) -> error({gpb_error, {badproto, P}}). get_service_containment("mix_hex_pb_names") -> []; get_service_containment(P) -> error({gpb_error, {badproto, P}}). get_rpc_containment("mix_hex_pb_names") -> []; get_rpc_containment(P) -> error({gpb_error, {badproto, P}}). get_enum_containment("mix_hex_pb_names") -> []; get_enum_containment(P) -> error({gpb_error, {badproto, P}}). get_proto_by_msg_name_as_fqbin(<<"Timestamp">>) -> "mix_hex_pb_names"; get_proto_by_msg_name_as_fqbin(<<"Names">>) -> "mix_hex_pb_names"; get_proto_by_msg_name_as_fqbin(<<"Package">>) -> "mix_hex_pb_names"; get_proto_by_msg_name_as_fqbin(E) -> error({gpb_error, {badmsg, E}}). -spec get_proto_by_service_name_as_fqbin(_) -> no_return(). get_proto_by_service_name_as_fqbin(E) -> error({gpb_error, {badservice, E}}). -spec get_proto_by_enum_name_as_fqbin(_) -> no_return(). get_proto_by_enum_name_as_fqbin(E) -> error({gpb_error, {badenum, E}}). -spec get_protos_by_pkg_name_as_fqbin(_) -> no_return(). get_protos_by_pkg_name_as_fqbin(E) -> error({gpb_error, {badpkg, E}}). gpb_version_as_string() -> "4.21.1". gpb_version_as_list() -> [4,21,1]. gpb_version_source() -> "file". hex-2.4.2/src/mix_hex_pb_package.erl000066400000000000000000002005451517471540100174060ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% -*- coding: utf-8 -*- %% % this file is @generated %% @private %% Automatically generated, do not edit %% Generated by gpb_compile version 4.21.1 %% Version source: file -module(mix_hex_pb_package). -export([encode_msg/2, encode_msg/3]). -export([decode_msg/2, decode_msg/3]). -export([merge_msgs/3, merge_msgs/4]). -export([verify_msg/2, verify_msg/3]). -export([get_msg_defs/0]). -export([get_msg_names/0]). -export([get_group_names/0]). -export([get_msg_or_group_names/0]). -export([get_enum_names/0]). -export([find_msg_def/1, fetch_msg_def/1]). -export([find_enum_def/1, fetch_enum_def/1]). -export([enum_symbol_by_value/2, enum_value_by_symbol/2]). -export([enum_symbol_by_value_RetirementReason/1, enum_value_by_symbol_RetirementReason/1]). -export([get_service_names/0]). -export([get_service_def/1]). -export([get_rpc_names/1]). -export([find_rpc_def/2, fetch_rpc_def/2]). -export([fqbin_to_service_name/1]). -export([service_name_to_fqbin/1]). -export([fqbins_to_service_and_rpc_name/2]). -export([service_and_rpc_name_to_fqbins/2]). -export([fqbin_to_msg_name/1]). -export([msg_name_to_fqbin/1]). -export([fqbin_to_enum_name/1]). -export([enum_name_to_fqbin/1]). -export([get_package_name/0]). -export([uses_packages/0]). -export([source_basename/0]). -export([get_all_source_basenames/0]). -export([get_all_proto_names/0]). -export([get_msg_containment/1]). -export([get_pkg_containment/1]). -export([get_service_containment/1]). -export([get_rpc_containment/1]). -export([get_enum_containment/1]). -export([get_proto_by_msg_name_as_fqbin/1]). -export([get_proto_by_service_name_as_fqbin/1]). -export([get_proto_by_enum_name_as_fqbin/1]). -export([get_protos_by_pkg_name_as_fqbin/1]). -export([gpb_version_as_string/0, gpb_version_as_list/0]). -export([gpb_version_source/0]). %% enumerated types -type 'RetirementReason'() :: 'RETIRED_OTHER' | 'RETIRED_INVALID' | 'RETIRED_SECURITY' | 'RETIRED_DEPRECATED' | 'RETIRED_RENAMED'. -export_type(['RetirementReason'/0]). %% message types -type 'Package'() :: #{releases => ['Release'()], % = 1, repeated name => unicode:chardata(), % = 2, required repository => unicode:chardata() % = 3, required }. -type 'Release'() :: #{version => unicode:chardata(), % = 1, required inner_checksum => iodata(), % = 2, required dependencies => ['Dependency'()], % = 3, repeated retired => 'RetirementStatus'(), % = 4, optional outer_checksum => iodata() % = 5, optional }. -type 'RetirementStatus'() :: #{reason => 'RETIRED_OTHER' | 'RETIRED_INVALID' | 'RETIRED_SECURITY' | 'RETIRED_DEPRECATED' | 'RETIRED_RENAMED' | integer(), % = 1, required, enum RetirementReason message => unicode:chardata() % = 2, optional }. -type 'Dependency'() :: #{package => unicode:chardata(), % = 1, required requirement => unicode:chardata(), % = 2, required optional => boolean() | 0 | 1, % = 3, optional app => unicode:chardata(), % = 4, optional repository => unicode:chardata() % = 5, optional }. -export_type(['Package'/0, 'Release'/0, 'RetirementStatus'/0, 'Dependency'/0]). -type '$msg_name'() :: 'Package' | 'Release' | 'RetirementStatus' | 'Dependency'. -type '$msg'() :: 'Package'() | 'Release'() | 'RetirementStatus'() | 'Dependency'(). -export_type(['$msg_name'/0, '$msg'/0]). -if(?OTP_RELEASE >= 24). -dialyzer({no_underspecs, encode_msg/2}). -endif. -spec encode_msg('$msg'(), '$msg_name'()) -> binary(). encode_msg(Msg, MsgName) when is_atom(MsgName) -> encode_msg(Msg, MsgName, []). -if(?OTP_RELEASE >= 24). -dialyzer({no_underspecs, encode_msg/3}). -endif. -spec encode_msg('$msg'(), '$msg_name'(), list()) -> binary(). encode_msg(Msg, MsgName, Opts) -> verify_msg(Msg, MsgName, Opts), TrUserData = proplists:get_value(user_data, Opts), case MsgName of 'Package' -> encode_msg_Package(id(Msg, TrUserData), TrUserData); 'Release' -> encode_msg_Release(id(Msg, TrUserData), TrUserData); 'RetirementStatus' -> encode_msg_RetirementStatus(id(Msg, TrUserData), TrUserData); 'Dependency' -> encode_msg_Dependency(id(Msg, TrUserData), TrUserData) end. encode_msg_Package(Msg, TrUserData) -> encode_msg_Package(Msg, <<>>, TrUserData). encode_msg_Package(#{name := F2, repository := F3} = M, Bin, TrUserData) -> B1 = case M of #{releases := F1} -> TrF1 = id(F1, TrUserData), if TrF1 == [] -> Bin; true -> e_field_Package_releases(TrF1, Bin, TrUserData) end; _ -> Bin end, B2 = begin TrF2 = id(F2, TrUserData), e_type_string(TrF2, <>, TrUserData) end, begin TrF3 = id(F3, TrUserData), e_type_string(TrF3, <>, TrUserData) end. encode_msg_Release(Msg, TrUserData) -> encode_msg_Release(Msg, <<>>, TrUserData). encode_msg_Release(#{version := F1, inner_checksum := F2} = M, Bin, TrUserData) -> B1 = begin TrF1 = id(F1, TrUserData), e_type_string(TrF1, <>, TrUserData) end, B2 = begin TrF2 = id(F2, TrUserData), e_type_bytes(TrF2, <>, TrUserData) end, B3 = case M of #{dependencies := F3} -> TrF3 = id(F3, TrUserData), if TrF3 == [] -> B2; true -> e_field_Release_dependencies(TrF3, B2, TrUserData) end; _ -> B2 end, B4 = case M of #{retired := F4} -> begin TrF4 = id(F4, TrUserData), e_mfield_Release_retired(TrF4, <>, TrUserData) end; _ -> B3 end, case M of #{outer_checksum := F5} -> begin TrF5 = id(F5, TrUserData), e_type_bytes(TrF5, <>, TrUserData) end; _ -> B4 end. encode_msg_RetirementStatus(Msg, TrUserData) -> encode_msg_RetirementStatus(Msg, <<>>, TrUserData). encode_msg_RetirementStatus(#{reason := F1} = M, Bin, TrUserData) -> B1 = begin TrF1 = id(F1, TrUserData), e_enum_RetirementReason(TrF1, <>, TrUserData) end, case M of #{message := F2} -> begin TrF2 = id(F2, TrUserData), e_type_string(TrF2, <>, TrUserData) end; _ -> B1 end. encode_msg_Dependency(Msg, TrUserData) -> encode_msg_Dependency(Msg, <<>>, TrUserData). encode_msg_Dependency(#{package := F1, requirement := F2} = M, Bin, TrUserData) -> B1 = begin TrF1 = id(F1, TrUserData), e_type_string(TrF1, <>, TrUserData) end, B2 = begin TrF2 = id(F2, TrUserData), e_type_string(TrF2, <>, TrUserData) end, B3 = case M of #{optional := F3} -> begin TrF3 = id(F3, TrUserData), e_type_bool(TrF3, <>, TrUserData) end; _ -> B2 end, B4 = case M of #{app := F4} -> begin TrF4 = id(F4, TrUserData), e_type_string(TrF4, <>, TrUserData) end; _ -> B3 end, case M of #{repository := F5} -> begin TrF5 = id(F5, TrUserData), e_type_string(TrF5, <>, TrUserData) end; _ -> B4 end. e_mfield_Package_releases(Msg, Bin, TrUserData) -> SubBin = encode_msg_Release(Msg, <<>>, TrUserData), Bin2 = e_varint(byte_size(SubBin), Bin), <>. e_field_Package_releases([Elem | Rest], Bin, TrUserData) -> Bin2 = <>, Bin3 = e_mfield_Package_releases(id(Elem, TrUserData), Bin2, TrUserData), e_field_Package_releases(Rest, Bin3, TrUserData); e_field_Package_releases([], Bin, _TrUserData) -> Bin. e_mfield_Release_dependencies(Msg, Bin, TrUserData) -> SubBin = encode_msg_Dependency(Msg, <<>>, TrUserData), Bin2 = e_varint(byte_size(SubBin), Bin), <>. e_field_Release_dependencies([Elem | Rest], Bin, TrUserData) -> Bin2 = <>, Bin3 = e_mfield_Release_dependencies(id(Elem, TrUserData), Bin2, TrUserData), e_field_Release_dependencies(Rest, Bin3, TrUserData); e_field_Release_dependencies([], Bin, _TrUserData) -> Bin. e_mfield_Release_retired(Msg, Bin, TrUserData) -> SubBin = encode_msg_RetirementStatus(Msg, <<>>, TrUserData), Bin2 = e_varint(byte_size(SubBin), Bin), <>. e_enum_RetirementReason('RETIRED_OTHER', Bin, _TrUserData) -> <>; e_enum_RetirementReason('RETIRED_INVALID', Bin, _TrUserData) -> <>; e_enum_RetirementReason('RETIRED_SECURITY', Bin, _TrUserData) -> <>; e_enum_RetirementReason('RETIRED_DEPRECATED', Bin, _TrUserData) -> <>; e_enum_RetirementReason('RETIRED_RENAMED', Bin, _TrUserData) -> <>; e_enum_RetirementReason(V, Bin, _TrUserData) -> e_varint(V, Bin). -compile({nowarn_unused_function,e_type_sint/3}). e_type_sint(Value, Bin, _TrUserData) when Value >= 0 -> e_varint(Value * 2, Bin); e_type_sint(Value, Bin, _TrUserData) -> e_varint(Value * -2 - 1, Bin). -compile({nowarn_unused_function,e_type_int32/3}). e_type_int32(Value, Bin, _TrUserData) when 0 =< Value, Value =< 127 -> <>; e_type_int32(Value, Bin, _TrUserData) -> <> = <>, e_varint(N, Bin). -compile({nowarn_unused_function,e_type_int64/3}). e_type_int64(Value, Bin, _TrUserData) when 0 =< Value, Value =< 127 -> <>; e_type_int64(Value, Bin, _TrUserData) -> <> = <>, e_varint(N, Bin). -compile({nowarn_unused_function,e_type_bool/3}). e_type_bool(true, Bin, _TrUserData) -> <>; e_type_bool(false, Bin, _TrUserData) -> <>; e_type_bool(1, Bin, _TrUserData) -> <>; e_type_bool(0, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_string/3}). e_type_string(S, Bin, _TrUserData) -> Utf8 = unicode:characters_to_binary(S), Bin2 = e_varint(byte_size(Utf8), Bin), <>. -compile({nowarn_unused_function,e_type_bytes/3}). e_type_bytes(Bytes, Bin, _TrUserData) when is_binary(Bytes) -> Bin2 = e_varint(byte_size(Bytes), Bin), <>; e_type_bytes(Bytes, Bin, _TrUserData) when is_list(Bytes) -> BytesBin = iolist_to_binary(Bytes), Bin2 = e_varint(byte_size(BytesBin), Bin), <>. -compile({nowarn_unused_function,e_type_fixed32/3}). e_type_fixed32(Value, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_sfixed32/3}). e_type_sfixed32(Value, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_fixed64/3}). e_type_fixed64(Value, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_sfixed64/3}). e_type_sfixed64(Value, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_float/3}). e_type_float(V, Bin, _) when is_number(V) -> <>; e_type_float(infinity, Bin, _) -> <>; e_type_float('-infinity', Bin, _) -> <>; e_type_float(nan, Bin, _) -> <>. -compile({nowarn_unused_function,e_type_double/3}). e_type_double(V, Bin, _) when is_number(V) -> <>; e_type_double(infinity, Bin, _) -> <>; e_type_double('-infinity', Bin, _) -> <>; e_type_double(nan, Bin, _) -> <>. -compile({nowarn_unused_function,e_unknown_elems/2}). e_unknown_elems([Elem | Rest], Bin) -> BinR = case Elem of {varint, FNum, N} -> BinF = e_varint(FNum bsl 3, Bin), e_varint(N, BinF); {length_delimited, FNum, Data} -> BinF = e_varint(FNum bsl 3 bor 2, Bin), BinL = e_varint(byte_size(Data), BinF), <>; {group, FNum, GroupFields} -> Bin1 = e_varint(FNum bsl 3 bor 3, Bin), Bin2 = e_unknown_elems(GroupFields, Bin1), e_varint(FNum bsl 3 bor 4, Bin2); {fixed32, FNum, V} -> BinF = e_varint(FNum bsl 3 bor 5, Bin), <>; {fixed64, FNum, V} -> BinF = e_varint(FNum bsl 3 bor 1, Bin), <> end, e_unknown_elems(Rest, BinR); e_unknown_elems([], Bin) -> Bin. -compile({nowarn_unused_function,e_varint/3}). e_varint(N, Bin, _TrUserData) -> e_varint(N, Bin). -compile({nowarn_unused_function,e_varint/2}). e_varint(N, Bin) when N =< 127 -> <>; e_varint(N, Bin) -> Bin2 = <>, e_varint(N bsr 7, Bin2). decode_msg(Bin, MsgName) when is_binary(Bin) -> decode_msg(Bin, MsgName, []). decode_msg(Bin, MsgName, Opts) when is_binary(Bin) -> TrUserData = proplists:get_value(user_data, Opts), decode_msg_1_catch(Bin, MsgName, TrUserData). -ifdef('OTP_RELEASE'). decode_msg_1_catch(Bin, MsgName, TrUserData) -> try decode_msg_2_doit(MsgName, Bin, TrUserData) catch error:{gpb_error,_}=Reason:StackTrace -> erlang:raise(error, Reason, StackTrace); Class:Reason:StackTrace -> error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}}) end. -else. decode_msg_1_catch(Bin, MsgName, TrUserData) -> try decode_msg_2_doit(MsgName, Bin, TrUserData) catch error:{gpb_error,_}=Reason -> erlang:raise(error, Reason, erlang:get_stacktrace()); Class:Reason -> StackTrace = erlang:get_stacktrace(), error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}}) end. -endif. decode_msg_2_doit('Package', Bin, TrUserData) -> id(decode_msg_Package(Bin, TrUserData), TrUserData); decode_msg_2_doit('Release', Bin, TrUserData) -> id(decode_msg_Release(Bin, TrUserData), TrUserData); decode_msg_2_doit('RetirementStatus', Bin, TrUserData) -> id(decode_msg_RetirementStatus(Bin, TrUserData), TrUserData); decode_msg_2_doit('Dependency', Bin, TrUserData) -> id(decode_msg_Dependency(Bin, TrUserData), TrUserData). decode_msg_Package(Bin, TrUserData) -> dfp_read_field_def_Package(Bin, 0, 0, 0, id([], TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), TrUserData). dfp_read_field_def_Package(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_field_Package_releases(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); dfp_read_field_def_Package(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_field_Package_name(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); dfp_read_field_def_Package(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_field_Package_repository(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); dfp_read_field_def_Package(<<>>, 0, 0, _, R1, F@_2, F@_3, TrUserData) -> S1 = #{name => F@_2, repository => F@_3}, if R1 == '$undef' -> S1; true -> S1#{releases => lists_reverse(R1, TrUserData)} end; dfp_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dg_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). dg_read_field_def_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 32 - 7 -> dg_read_field_def_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); dg_read_field_def_Package(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, TrUserData) -> Key = X bsl N + Acc, case Key of 10 -> d_field_Package_releases(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); 18 -> d_field_Package_name(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); 26 -> d_field_Package_repository(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); 1 -> skip_64_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); 2 -> skip_length_delimited_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); 3 -> skip_group_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); 5 -> skip_32_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData) end end; dg_read_field_def_Package(<<>>, 0, 0, _, R1, F@_2, F@_3, TrUserData) -> S1 = #{name => F@_2, repository => F@_3}, if R1 == '$undef' -> S1; true -> S1#{releases => lists_reverse(R1, TrUserData)} end. d_field_Package_releases(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_field_Package_releases(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); d_field_Package_releases(<<0:1, X:7, Rest/binary>>, N, Acc, F, Prev, F@_2, F@_3, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, {id(decode_msg_Release(Bs, TrUserData), TrUserData), Rest2} end, dfp_read_field_def_Package(RestF, 0, 0, F, cons(NewFValue, Prev, TrUserData), F@_2, F@_3, TrUserData). d_field_Package_name(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_field_Package_name(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); d_field_Package_name(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, F@_3, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_Package(RestF, 0, 0, F, F@_1, NewFValue, F@_3, TrUserData). d_field_Package_repository(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_field_Package_repository(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); d_field_Package_repository(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, _, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_Package(RestF, 0, 0, F, F@_1, F@_2, NewFValue, TrUserData). skip_varint_Package(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> skip_varint_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); skip_varint_Package(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). skip_length_delimited_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> skip_length_delimited_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); skip_length_delimited_Package(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) -> Length = X bsl N + Acc, <<_:Length/binary, Rest2/binary>> = Rest, dfp_read_field_def_Package(Rest2, 0, 0, F, F@_1, F@_2, F@_3, TrUserData). skip_group_Package(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_Package(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, TrUserData). skip_32_Package(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). skip_64_Package(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). decode_msg_Release(Bin, TrUserData) -> dfp_read_field_def_Release(Bin, 0, 0, 0, id('$undef', TrUserData), id('$undef', TrUserData), id([], TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), TrUserData). dfp_read_field_def_Release(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Release_version(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); dfp_read_field_def_Release(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Release_inner_checksum(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); dfp_read_field_def_Release(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Release_dependencies(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); dfp_read_field_def_Release(<<34, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Release_retired(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); dfp_read_field_def_Release(<<42, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Release_outer_checksum(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); dfp_read_field_def_Release(<<>>, 0, 0, _, F@_1, F@_2, R1, F@_4, F@_5, TrUserData) -> S1 = #{version => F@_1, inner_checksum => F@_2}, S2 = if R1 == '$undef' -> S1; true -> S1#{dependencies => lists_reverse(R1, TrUserData)} end, S3 = if F@_4 == '$undef' -> S2; true -> S2#{retired => F@_4} end, if F@_5 == '$undef' -> S3; true -> S3#{outer_checksum => F@_5} end; dfp_read_field_def_Release(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> dg_read_field_def_Release(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). dg_read_field_def_Release(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 32 - 7 -> dg_read_field_def_Release(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); dg_read_field_def_Release(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> Key = X bsl N + Acc, case Key of 10 -> d_field_Release_version(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 18 -> d_field_Release_inner_checksum(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 26 -> d_field_Release_dependencies(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 34 -> d_field_Release_retired(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 42 -> d_field_Release_outer_checksum(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 1 -> skip_64_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 2 -> skip_length_delimited_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 3 -> skip_group_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 5 -> skip_32_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) end end; dg_read_field_def_Release(<<>>, 0, 0, _, F@_1, F@_2, R1, F@_4, F@_5, TrUserData) -> S1 = #{version => F@_1, inner_checksum => F@_2}, S2 = if R1 == '$undef' -> S1; true -> S1#{dependencies => lists_reverse(R1, TrUserData)} end, S3 = if F@_4 == '$undef' -> S2; true -> S2#{retired => F@_4} end, if F@_5 == '$undef' -> S3; true -> S3#{outer_checksum => F@_5} end. d_field_Release_version(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 57 -> d_field_Release_version(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); d_field_Release_version(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, F@_3, F@_4, F@_5, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_Release(RestF, 0, 0, F, NewFValue, F@_2, F@_3, F@_4, F@_5, TrUserData). d_field_Release_inner_checksum(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 57 -> d_field_Release_inner_checksum(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); d_field_Release_inner_checksum(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, F@_3, F@_4, F@_5, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_Release(RestF, 0, 0, F, F@_1, NewFValue, F@_3, F@_4, F@_5, TrUserData). d_field_Release_dependencies(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 57 -> d_field_Release_dependencies(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); d_field_Release_dependencies(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, Prev, F@_4, F@_5, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, {id(decode_msg_Dependency(Bs, TrUserData), TrUserData), Rest2} end, dfp_read_field_def_Release(RestF, 0, 0, F, F@_1, F@_2, cons(NewFValue, Prev, TrUserData), F@_4, F@_5, TrUserData). d_field_Release_retired(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 57 -> d_field_Release_retired(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); d_field_Release_retired(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, Prev, F@_5, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, {id(decode_msg_RetirementStatus(Bs, TrUserData), TrUserData), Rest2} end, dfp_read_field_def_Release(RestF, 0, 0, F, F@_1, F@_2, F@_3, if Prev == '$undef' -> NewFValue; true -> merge_msg_RetirementStatus(Prev, NewFValue, TrUserData) end, F@_5, TrUserData). d_field_Release_outer_checksum(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 57 -> d_field_Release_outer_checksum(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); d_field_Release_outer_checksum(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, _, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_Release(RestF, 0, 0, F, F@_1, F@_2, F@_3, F@_4, NewFValue, TrUserData). skip_varint_Release(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> skip_varint_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); skip_varint_Release(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> dfp_read_field_def_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). skip_length_delimited_Release(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 57 -> skip_length_delimited_Release(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); skip_length_delimited_Release(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> Length = X bsl N + Acc, <<_:Length/binary, Rest2/binary>> = Rest, dfp_read_field_def_Release(Rest2, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). skip_group_Release(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_Release(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). skip_32_Release(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> dfp_read_field_def_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). skip_64_Release(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> dfp_read_field_def_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). decode_msg_RetirementStatus(Bin, TrUserData) -> dfp_read_field_def_RetirementStatus(Bin, 0, 0, 0, id('$undef', TrUserData), id('$undef', TrUserData), TrUserData). dfp_read_field_def_RetirementStatus(<<8, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> d_field_RetirementStatus_reason(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); dfp_read_field_def_RetirementStatus(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> d_field_RetirementStatus_message(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); dfp_read_field_def_RetirementStatus(<<>>, 0, 0, _, F@_1, F@_2, _) -> S1 = #{reason => F@_1}, if F@_2 == '$undef' -> S1; true -> S1#{message => F@_2} end; dfp_read_field_def_RetirementStatus(Other, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dg_read_field_def_RetirementStatus(Other, Z1, Z2, F, F@_1, F@_2, TrUserData). dg_read_field_def_RetirementStatus(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 32 - 7 -> dg_read_field_def_RetirementStatus(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); dg_read_field_def_RetirementStatus(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, TrUserData) -> Key = X bsl N + Acc, case Key of 8 -> d_field_RetirementStatus_reason(Rest, 0, 0, 0, F@_1, F@_2, TrUserData); 18 -> d_field_RetirementStatus_message(Rest, 0, 0, 0, F@_1, F@_2, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_RetirementStatus(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 1 -> skip_64_RetirementStatus(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 2 -> skip_length_delimited_RetirementStatus(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 3 -> skip_group_RetirementStatus(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 5 -> skip_32_RetirementStatus(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData) end end; dg_read_field_def_RetirementStatus(<<>>, 0, 0, _, F@_1, F@_2, _) -> S1 = #{reason => F@_1}, if F@_2 == '$undef' -> S1; true -> S1#{message => F@_2} end. d_field_RetirementStatus_reason(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> d_field_RetirementStatus_reason(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); d_field_RetirementStatus_reason(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, TrUserData) -> {NewFValue, RestF} = {id(d_enum_RetirementReason(begin <> = <<(X bsl N + Acc):32/unsigned-native>>, id(Res, TrUserData) end), TrUserData), Rest}, dfp_read_field_def_RetirementStatus(RestF, 0, 0, F, NewFValue, F@_2, TrUserData). d_field_RetirementStatus_message(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> d_field_RetirementStatus_message(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); d_field_RetirementStatus_message(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_RetirementStatus(RestF, 0, 0, F, F@_1, NewFValue, TrUserData). skip_varint_RetirementStatus(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> skip_varint_RetirementStatus(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); skip_varint_RetirementStatus(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_RetirementStatus(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). skip_length_delimited_RetirementStatus(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> skip_length_delimited_RetirementStatus(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); skip_length_delimited_RetirementStatus(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) -> Length = X bsl N + Acc, <<_:Length/binary, Rest2/binary>> = Rest, dfp_read_field_def_RetirementStatus(Rest2, 0, 0, F, F@_1, F@_2, TrUserData). skip_group_RetirementStatus(Bin, _, Z2, FNum, F@_1, F@_2, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_RetirementStatus(Rest, 0, Z2, FNum, F@_1, F@_2, TrUserData). skip_32_RetirementStatus(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_RetirementStatus(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). skip_64_RetirementStatus(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_RetirementStatus(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). decode_msg_Dependency(Bin, TrUserData) -> dfp_read_field_def_Dependency(Bin, 0, 0, 0, id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), TrUserData). dfp_read_field_def_Dependency(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Dependency_package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); dfp_read_field_def_Dependency(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Dependency_requirement(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); dfp_read_field_def_Dependency(<<24, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Dependency_optional(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); dfp_read_field_def_Dependency(<<34, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Dependency_app(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); dfp_read_field_def_Dependency(<<42, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Dependency_repository(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); dfp_read_field_def_Dependency(<<>>, 0, 0, _, F@_1, F@_2, F@_3, F@_4, F@_5, _) -> S1 = #{package => F@_1, requirement => F@_2}, S2 = if F@_3 == '$undef' -> S1; true -> S1#{optional => F@_3} end, S3 = if F@_4 == '$undef' -> S2; true -> S2#{app => F@_4} end, if F@_5 == '$undef' -> S3; true -> S3#{repository => F@_5} end; dfp_read_field_def_Dependency(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> dg_read_field_def_Dependency(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). dg_read_field_def_Dependency(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 32 - 7 -> dg_read_field_def_Dependency(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); dg_read_field_def_Dependency(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> Key = X bsl N + Acc, case Key of 10 -> d_field_Dependency_package(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 18 -> d_field_Dependency_requirement(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 24 -> d_field_Dependency_optional(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 34 -> d_field_Dependency_app(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 42 -> d_field_Dependency_repository(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_Dependency(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 1 -> skip_64_Dependency(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 2 -> skip_length_delimited_Dependency(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 3 -> skip_group_Dependency(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 5 -> skip_32_Dependency(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) end end; dg_read_field_def_Dependency(<<>>, 0, 0, _, F@_1, F@_2, F@_3, F@_4, F@_5, _) -> S1 = #{package => F@_1, requirement => F@_2}, S2 = if F@_3 == '$undef' -> S1; true -> S1#{optional => F@_3} end, S3 = if F@_4 == '$undef' -> S2; true -> S2#{app => F@_4} end, if F@_5 == '$undef' -> S3; true -> S3#{repository => F@_5} end. d_field_Dependency_package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 57 -> d_field_Dependency_package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); d_field_Dependency_package(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, F@_3, F@_4, F@_5, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_Dependency(RestF, 0, 0, F, NewFValue, F@_2, F@_3, F@_4, F@_5, TrUserData). d_field_Dependency_requirement(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 57 -> d_field_Dependency_requirement(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); d_field_Dependency_requirement(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, F@_3, F@_4, F@_5, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_Dependency(RestF, 0, 0, F, F@_1, NewFValue, F@_3, F@_4, F@_5, TrUserData). d_field_Dependency_optional(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 57 -> d_field_Dependency_optional(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); d_field_Dependency_optional(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, _, F@_4, F@_5, TrUserData) -> {NewFValue, RestF} = {id(X bsl N + Acc =/= 0, TrUserData), Rest}, dfp_read_field_def_Dependency(RestF, 0, 0, F, F@_1, F@_2, NewFValue, F@_4, F@_5, TrUserData). d_field_Dependency_app(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 57 -> d_field_Dependency_app(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); d_field_Dependency_app(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, _, F@_5, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_Dependency(RestF, 0, 0, F, F@_1, F@_2, F@_3, NewFValue, F@_5, TrUserData). d_field_Dependency_repository(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 57 -> d_field_Dependency_repository(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); d_field_Dependency_repository(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, _, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_Dependency(RestF, 0, 0, F, F@_1, F@_2, F@_3, F@_4, NewFValue, TrUserData). skip_varint_Dependency(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> skip_varint_Dependency(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); skip_varint_Dependency(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> dfp_read_field_def_Dependency(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). skip_length_delimited_Dependency(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) when N < 57 -> skip_length_delimited_Dependency(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); skip_length_delimited_Dependency(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> Length = X bsl N + Acc, <<_:Length/binary, Rest2/binary>> = Rest, dfp_read_field_def_Dependency(Rest2, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). skip_group_Dependency(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_Dependency(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). skip_32_Dependency(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> dfp_read_field_def_Dependency(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). skip_64_Dependency(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> dfp_read_field_def_Dependency(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). d_enum_RetirementReason(0) -> 'RETIRED_OTHER'; d_enum_RetirementReason(1) -> 'RETIRED_INVALID'; d_enum_RetirementReason(2) -> 'RETIRED_SECURITY'; d_enum_RetirementReason(3) -> 'RETIRED_DEPRECATED'; d_enum_RetirementReason(4) -> 'RETIRED_RENAMED'; d_enum_RetirementReason(V) -> V. read_group(Bin, FieldNum) -> {NumBytes, EndTagLen} = read_gr_b(Bin, 0, 0, 0, 0, FieldNum), <> = Bin, {Group, Rest}. %% Like skipping over fields, but record the total length, %% Each field is <(FieldNum bsl 3) bor FieldType> ++ %% Record the length because varints may be non-optimally encoded. %% %% Groups can be nested, but assume the same FieldNum cannot be nested %% because group field numbers are shared with the rest of the fields %% numbers. Thus we can search just for an group-end with the same %% field number. %% %% (The only time the same group field number could occur would %% be in a nested sub message, but then it would be inside a %% length-delimited entry, which we skip-read by length.) read_gr_b(<<1:1, X:7, Tl/binary>>, N, Acc, NumBytes, TagLen, FieldNum) when N < (32-7) -> read_gr_b(Tl, N+7, X bsl N + Acc, NumBytes, TagLen+1, FieldNum); read_gr_b(<<0:1, X:7, Tl/binary>>, N, Acc, NumBytes, TagLen, FieldNum) -> Key = X bsl N + Acc, TagLen1 = TagLen + 1, case {Key bsr 3, Key band 7} of {FieldNum, 4} -> % 4 = group_end {NumBytes, TagLen1}; {_, 0} -> % 0 = varint read_gr_vi(Tl, 0, NumBytes + TagLen1, FieldNum); {_, 1} -> % 1 = bits64 <<_:64, Tl2/binary>> = Tl, read_gr_b(Tl2, 0, 0, NumBytes + TagLen1 + 8, 0, FieldNum); {_, 2} -> % 2 = length_delimited read_gr_ld(Tl, 0, 0, NumBytes + TagLen1, FieldNum); {_, 3} -> % 3 = group_start read_gr_b(Tl, 0, 0, NumBytes + TagLen1, 0, FieldNum); {_, 4} -> % 4 = group_end read_gr_b(Tl, 0, 0, NumBytes + TagLen1, 0, FieldNum); {_, 5} -> % 5 = bits32 <<_:32, Tl2/binary>> = Tl, read_gr_b(Tl2, 0, 0, NumBytes + TagLen1 + 4, 0, FieldNum) end. read_gr_vi(<<1:1, _:7, Tl/binary>>, N, NumBytes, FieldNum) when N < (64-7) -> read_gr_vi(Tl, N+7, NumBytes+1, FieldNum); read_gr_vi(<<0:1, _:7, Tl/binary>>, _, NumBytes, FieldNum) -> read_gr_b(Tl, 0, 0, NumBytes+1, 0, FieldNum). read_gr_ld(<<1:1, X:7, Tl/binary>>, N, Acc, NumBytes, FieldNum) when N < (64-7) -> read_gr_ld(Tl, N+7, X bsl N + Acc, NumBytes+1, FieldNum); read_gr_ld(<<0:1, X:7, Tl/binary>>, N, Acc, NumBytes, FieldNum) -> Len = X bsl N + Acc, NumBytes1 = NumBytes + 1, <<_:Len/binary, Tl2/binary>> = Tl, read_gr_b(Tl2, 0, 0, NumBytes1 + Len, 0, FieldNum). merge_msgs(Prev, New, MsgName) when is_atom(MsgName) -> merge_msgs(Prev, New, MsgName, []). merge_msgs(Prev, New, MsgName, Opts) -> TrUserData = proplists:get_value(user_data, Opts), case MsgName of 'Package' -> merge_msg_Package(Prev, New, TrUserData); 'Release' -> merge_msg_Release(Prev, New, TrUserData); 'RetirementStatus' -> merge_msg_RetirementStatus(Prev, New, TrUserData); 'Dependency' -> merge_msg_Dependency(Prev, New, TrUserData) end. -compile({nowarn_unused_function,merge_msg_Package/3}). merge_msg_Package(#{} = PMsg, #{name := NFname, repository := NFrepository} = NMsg, TrUserData) -> S1 = #{name => NFname, repository => NFrepository}, case {PMsg, NMsg} of {#{releases := PFreleases}, #{releases := NFreleases}} -> S1#{releases => 'erlang_++'(PFreleases, NFreleases, TrUserData)}; {_, #{releases := NFreleases}} -> S1#{releases => NFreleases}; {#{releases := PFreleases}, _} -> S1#{releases => PFreleases}; {_, _} -> S1 end. -compile({nowarn_unused_function,merge_msg_Release/3}). merge_msg_Release(#{} = PMsg, #{version := NFversion, inner_checksum := NFinner_checksum} = NMsg, TrUserData) -> S1 = #{version => NFversion, inner_checksum => NFinner_checksum}, S2 = case {PMsg, NMsg} of {#{dependencies := PFdependencies}, #{dependencies := NFdependencies}} -> S1#{dependencies => 'erlang_++'(PFdependencies, NFdependencies, TrUserData)}; {_, #{dependencies := NFdependencies}} -> S1#{dependencies => NFdependencies}; {#{dependencies := PFdependencies}, _} -> S1#{dependencies => PFdependencies}; {_, _} -> S1 end, S3 = case {PMsg, NMsg} of {#{retired := PFretired}, #{retired := NFretired}} -> S2#{retired => merge_msg_RetirementStatus(PFretired, NFretired, TrUserData)}; {_, #{retired := NFretired}} -> S2#{retired => NFretired}; {#{retired := PFretired}, _} -> S2#{retired => PFretired}; {_, _} -> S2 end, case {PMsg, NMsg} of {_, #{outer_checksum := NFouter_checksum}} -> S3#{outer_checksum => NFouter_checksum}; {#{outer_checksum := PFouter_checksum}, _} -> S3#{outer_checksum => PFouter_checksum}; _ -> S3 end. -compile({nowarn_unused_function,merge_msg_RetirementStatus/3}). merge_msg_RetirementStatus(#{} = PMsg, #{reason := NFreason} = NMsg, _) -> S1 = #{reason => NFreason}, case {PMsg, NMsg} of {_, #{message := NFmessage}} -> S1#{message => NFmessage}; {#{message := PFmessage}, _} -> S1#{message => PFmessage}; _ -> S1 end. -compile({nowarn_unused_function,merge_msg_Dependency/3}). merge_msg_Dependency(#{} = PMsg, #{package := NFpackage, requirement := NFrequirement} = NMsg, _) -> S1 = #{package => NFpackage, requirement => NFrequirement}, S2 = case {PMsg, NMsg} of {_, #{optional := NFoptional}} -> S1#{optional => NFoptional}; {#{optional := PFoptional}, _} -> S1#{optional => PFoptional}; _ -> S1 end, S3 = case {PMsg, NMsg} of {_, #{app := NFapp}} -> S2#{app => NFapp}; {#{app := PFapp}, _} -> S2#{app => PFapp}; _ -> S2 end, case {PMsg, NMsg} of {_, #{repository := NFrepository}} -> S3#{repository => NFrepository}; {#{repository := PFrepository}, _} -> S3#{repository => PFrepository}; _ -> S3 end. verify_msg(Msg, MsgName) when is_atom(MsgName) -> verify_msg(Msg, MsgName, []). verify_msg(Msg, MsgName, Opts) -> TrUserData = proplists:get_value(user_data, Opts), case MsgName of 'Package' -> v_msg_Package(Msg, [MsgName], TrUserData); 'Release' -> v_msg_Release(Msg, [MsgName], TrUserData); 'RetirementStatus' -> v_msg_RetirementStatus(Msg, [MsgName], TrUserData); 'Dependency' -> v_msg_Dependency(Msg, [MsgName], TrUserData); _ -> mk_type_error(not_a_known_message, Msg, []) end. -compile({nowarn_unused_function,v_msg_Package/3}). -dialyzer({nowarn_function,v_msg_Package/3}). v_msg_Package(#{name := F2, repository := F3} = M, Path, TrUserData) -> case M of #{releases := F1} -> if is_list(F1) -> _ = [v_submsg_Release(Elem, [releases | Path], TrUserData) || Elem <- F1], ok; true -> mk_type_error({invalid_list_of, {msg, 'Release'}}, F1, [releases | Path]) end; _ -> ok end, v_type_string(F2, [name | Path], TrUserData), v_type_string(F3, [repository | Path], TrUserData), lists:foreach(fun (releases) -> ok; (name) -> ok; (repository) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), ok; v_msg_Package(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [name, repository] -- maps:keys(M), 'Package'}, M, Path); v_msg_Package(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'Package'}, X, Path). -compile({nowarn_unused_function,v_submsg_Release/3}). -dialyzer({nowarn_function,v_submsg_Release/3}). v_submsg_Release(Msg, Path, TrUserData) -> v_msg_Release(Msg, Path, TrUserData). -compile({nowarn_unused_function,v_msg_Release/3}). -dialyzer({nowarn_function,v_msg_Release/3}). v_msg_Release(#{version := F1, inner_checksum := F2} = M, Path, TrUserData) -> v_type_string(F1, [version | Path], TrUserData), v_type_bytes(F2, [inner_checksum | Path], TrUserData), case M of #{dependencies := F3} -> if is_list(F3) -> _ = [v_submsg_Dependency(Elem, [dependencies | Path], TrUserData) || Elem <- F3], ok; true -> mk_type_error({invalid_list_of, {msg, 'Dependency'}}, F3, [dependencies | Path]) end; _ -> ok end, case M of #{retired := F4} -> v_submsg_RetirementStatus(F4, [retired | Path], TrUserData); _ -> ok end, case M of #{outer_checksum := F5} -> v_type_bytes(F5, [outer_checksum | Path], TrUserData); _ -> ok end, lists:foreach(fun (version) -> ok; (inner_checksum) -> ok; (dependencies) -> ok; (retired) -> ok; (outer_checksum) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), ok; v_msg_Release(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [version, inner_checksum] -- maps:keys(M), 'Release'}, M, Path); v_msg_Release(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'Release'}, X, Path). -compile({nowarn_unused_function,v_submsg_RetirementStatus/3}). -dialyzer({nowarn_function,v_submsg_RetirementStatus/3}). v_submsg_RetirementStatus(Msg, Path, TrUserData) -> v_msg_RetirementStatus(Msg, Path, TrUserData). -compile({nowarn_unused_function,v_msg_RetirementStatus/3}). -dialyzer({nowarn_function,v_msg_RetirementStatus/3}). v_msg_RetirementStatus(#{reason := F1} = M, Path, TrUserData) -> v_enum_RetirementReason(F1, [reason | Path], TrUserData), case M of #{message := F2} -> v_type_string(F2, [message | Path], TrUserData); _ -> ok end, lists:foreach(fun (reason) -> ok; (message) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), ok; v_msg_RetirementStatus(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [reason] -- maps:keys(M), 'RetirementStatus'}, M, Path); v_msg_RetirementStatus(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'RetirementStatus'}, X, Path). -compile({nowarn_unused_function,v_submsg_Dependency/3}). -dialyzer({nowarn_function,v_submsg_Dependency/3}). v_submsg_Dependency(Msg, Path, TrUserData) -> v_msg_Dependency(Msg, Path, TrUserData). -compile({nowarn_unused_function,v_msg_Dependency/3}). -dialyzer({nowarn_function,v_msg_Dependency/3}). v_msg_Dependency(#{package := F1, requirement := F2} = M, Path, TrUserData) -> v_type_string(F1, [package | Path], TrUserData), v_type_string(F2, [requirement | Path], TrUserData), case M of #{optional := F3} -> v_type_bool(F3, [optional | Path], TrUserData); _ -> ok end, case M of #{app := F4} -> v_type_string(F4, [app | Path], TrUserData); _ -> ok end, case M of #{repository := F5} -> v_type_string(F5, [repository | Path], TrUserData); _ -> ok end, lists:foreach(fun (package) -> ok; (requirement) -> ok; (optional) -> ok; (app) -> ok; (repository) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), ok; v_msg_Dependency(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [package, requirement] -- maps:keys(M), 'Dependency'}, M, Path); v_msg_Dependency(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'Dependency'}, X, Path). -compile({nowarn_unused_function,v_enum_RetirementReason/3}). -dialyzer({nowarn_function,v_enum_RetirementReason/3}). v_enum_RetirementReason('RETIRED_OTHER', _Path, _TrUserData) -> ok; v_enum_RetirementReason('RETIRED_INVALID', _Path, _TrUserData) -> ok; v_enum_RetirementReason('RETIRED_SECURITY', _Path, _TrUserData) -> ok; v_enum_RetirementReason('RETIRED_DEPRECATED', _Path, _TrUserData) -> ok; v_enum_RetirementReason('RETIRED_RENAMED', _Path, _TrUserData) -> ok; v_enum_RetirementReason(V, _Path, _TrUserData) when -2147483648 =< V, V =< 2147483647, is_integer(V) -> ok; v_enum_RetirementReason(X, Path, _TrUserData) -> mk_type_error({invalid_enum, 'RetirementReason'}, X, Path). -compile({nowarn_unused_function,v_type_bool/3}). -dialyzer({nowarn_function,v_type_bool/3}). v_type_bool(false, _Path, _TrUserData) -> ok; v_type_bool(true, _Path, _TrUserData) -> ok; v_type_bool(0, _Path, _TrUserData) -> ok; v_type_bool(1, _Path, _TrUserData) -> ok; v_type_bool(X, Path, _TrUserData) -> mk_type_error(bad_boolean_value, X, Path). -compile({nowarn_unused_function,v_type_string/3}). -dialyzer({nowarn_function,v_type_string/3}). v_type_string(S, Path, _TrUserData) when is_list(S); is_binary(S) -> try unicode:characters_to_binary(S) of B when is_binary(B) -> ok; {error, _, _} -> mk_type_error(bad_unicode_string, S, Path) catch error:badarg -> mk_type_error(bad_unicode_string, S, Path) end; v_type_string(X, Path, _TrUserData) -> mk_type_error(bad_unicode_string, X, Path). -compile({nowarn_unused_function,v_type_bytes/3}). -dialyzer({nowarn_function,v_type_bytes/3}). v_type_bytes(B, _Path, _TrUserData) when is_binary(B) -> ok; v_type_bytes(B, _Path, _TrUserData) when is_list(B) -> ok; v_type_bytes(X, Path, _TrUserData) -> mk_type_error(bad_binary_value, X, Path). -compile({nowarn_unused_function,mk_type_error/3}). -spec mk_type_error(_, _, list()) -> no_return(). mk_type_error(Error, ValueSeen, Path) -> Path2 = prettify_path(Path), erlang:error({gpb_type_error, {Error, [{value, ValueSeen}, {path, Path2}]}}). -compile({nowarn_unused_function,prettify_path/1}). -dialyzer({nowarn_function,prettify_path/1}). prettify_path([]) -> top_level; prettify_path(PathR) -> lists:append(lists:join(".", lists:map(fun atom_to_list/1, lists:reverse(PathR)))). -compile({nowarn_unused_function,id/2}). -compile({inline,id/2}). id(X, _TrUserData) -> X. -compile({nowarn_unused_function,v_ok/3}). -compile({inline,v_ok/3}). v_ok(_Value, _Path, _TrUserData) -> ok. -compile({nowarn_unused_function,m_overwrite/3}). -compile({inline,m_overwrite/3}). m_overwrite(_Prev, New, _TrUserData) -> New. -compile({nowarn_unused_function,cons/3}). -compile({inline,cons/3}). cons(Elem, Acc, _TrUserData) -> [Elem | Acc]. -compile({nowarn_unused_function,lists_reverse/2}). -compile({inline,lists_reverse/2}). 'lists_reverse'(L, _TrUserData) -> lists:reverse(L). -compile({nowarn_unused_function,'erlang_++'/3}). -compile({inline,'erlang_++'/3}). 'erlang_++'(A, B, _TrUserData) -> A ++ B. get_msg_defs() -> [{{enum, 'RetirementReason'}, [{'RETIRED_OTHER', 0}, {'RETIRED_INVALID', 1}, {'RETIRED_SECURITY', 2}, {'RETIRED_DEPRECATED', 3}, {'RETIRED_RENAMED', 4}]}, {{msg, 'Package'}, [#{name => releases, fnum => 1, rnum => 2, type => {msg, 'Release'}, occurrence => repeated, opts => []}, #{name => name, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}, #{name => repository, fnum => 3, rnum => 4, type => string, occurrence => required, opts => []}]}, {{msg, 'Release'}, [#{name => version, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => inner_checksum, fnum => 2, rnum => 3, type => bytes, occurrence => required, opts => []}, #{name => dependencies, fnum => 3, rnum => 4, type => {msg, 'Dependency'}, occurrence => repeated, opts => []}, #{name => retired, fnum => 4, rnum => 5, type => {msg, 'RetirementStatus'}, occurrence => optional, opts => []}, #{name => outer_checksum, fnum => 5, rnum => 6, type => bytes, occurrence => optional, opts => []}]}, {{msg, 'RetirementStatus'}, [#{name => reason, fnum => 1, rnum => 2, type => {enum, 'RetirementReason'}, occurrence => required, opts => []}, #{name => message, fnum => 2, rnum => 3, type => string, occurrence => optional, opts => []}]}, {{msg, 'Dependency'}, [#{name => package, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => requirement, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}, #{name => optional, fnum => 3, rnum => 4, type => bool, occurrence => optional, opts => []}, #{name => app, fnum => 4, rnum => 5, type => string, occurrence => optional, opts => []}, #{name => repository, fnum => 5, rnum => 6, type => string, occurrence => optional, opts => []}]}]. get_msg_names() -> ['Package', 'Release', 'RetirementStatus', 'Dependency']. get_group_names() -> []. get_msg_or_group_names() -> ['Package', 'Release', 'RetirementStatus', 'Dependency']. get_enum_names() -> ['RetirementReason']. fetch_msg_def(MsgName) -> case find_msg_def(MsgName) of Fs when is_list(Fs) -> Fs; error -> erlang:error({no_such_msg, MsgName}) end. fetch_enum_def(EnumName) -> case find_enum_def(EnumName) of Es when is_list(Es) -> Es; error -> erlang:error({no_such_enum, EnumName}) end. find_msg_def('Package') -> [#{name => releases, fnum => 1, rnum => 2, type => {msg, 'Release'}, occurrence => repeated, opts => []}, #{name => name, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}, #{name => repository, fnum => 3, rnum => 4, type => string, occurrence => required, opts => []}]; find_msg_def('Release') -> [#{name => version, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => inner_checksum, fnum => 2, rnum => 3, type => bytes, occurrence => required, opts => []}, #{name => dependencies, fnum => 3, rnum => 4, type => {msg, 'Dependency'}, occurrence => repeated, opts => []}, #{name => retired, fnum => 4, rnum => 5, type => {msg, 'RetirementStatus'}, occurrence => optional, opts => []}, #{name => outer_checksum, fnum => 5, rnum => 6, type => bytes, occurrence => optional, opts => []}]; find_msg_def('RetirementStatus') -> [#{name => reason, fnum => 1, rnum => 2, type => {enum, 'RetirementReason'}, occurrence => required, opts => []}, #{name => message, fnum => 2, rnum => 3, type => string, occurrence => optional, opts => []}]; find_msg_def('Dependency') -> [#{name => package, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => requirement, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}, #{name => optional, fnum => 3, rnum => 4, type => bool, occurrence => optional, opts => []}, #{name => app, fnum => 4, rnum => 5, type => string, occurrence => optional, opts => []}, #{name => repository, fnum => 5, rnum => 6, type => string, occurrence => optional, opts => []}]; find_msg_def(_) -> error. find_enum_def('RetirementReason') -> [{'RETIRED_OTHER', 0}, {'RETIRED_INVALID', 1}, {'RETIRED_SECURITY', 2}, {'RETIRED_DEPRECATED', 3}, {'RETIRED_RENAMED', 4}]; find_enum_def(_) -> error. enum_symbol_by_value('RetirementReason', Value) -> enum_symbol_by_value_RetirementReason(Value). enum_value_by_symbol('RetirementReason', Sym) -> enum_value_by_symbol_RetirementReason(Sym). enum_symbol_by_value_RetirementReason(0) -> 'RETIRED_OTHER'; enum_symbol_by_value_RetirementReason(1) -> 'RETIRED_INVALID'; enum_symbol_by_value_RetirementReason(2) -> 'RETIRED_SECURITY'; enum_symbol_by_value_RetirementReason(3) -> 'RETIRED_DEPRECATED'; enum_symbol_by_value_RetirementReason(4) -> 'RETIRED_RENAMED'. enum_value_by_symbol_RetirementReason('RETIRED_OTHER') -> 0; enum_value_by_symbol_RetirementReason('RETIRED_INVALID') -> 1; enum_value_by_symbol_RetirementReason('RETIRED_SECURITY') -> 2; enum_value_by_symbol_RetirementReason('RETIRED_DEPRECATED') -> 3; enum_value_by_symbol_RetirementReason('RETIRED_RENAMED') -> 4. get_service_names() -> []. get_service_def(_) -> error. get_rpc_names(_) -> error. find_rpc_def(_, _) -> error. -spec fetch_rpc_def(_, _) -> no_return(). fetch_rpc_def(ServiceName, RpcName) -> erlang:error({no_such_rpc, ServiceName, RpcName}). %% Convert a a fully qualified (ie with package name) service name %% as a binary to a service name as an atom. -spec fqbin_to_service_name(_) -> no_return(). fqbin_to_service_name(X) -> error({gpb_error, {badservice, X}}). %% Convert a service name as an atom to a fully qualified %% (ie with package name) name as a binary. -spec service_name_to_fqbin(_) -> no_return(). service_name_to_fqbin(X) -> error({gpb_error, {badservice, X}}). %% Convert a a fully qualified (ie with package name) service name %% and an rpc name, both as binaries to a service name and an rpc %% name, as atoms. -spec fqbins_to_service_and_rpc_name(_, _) -> no_return(). fqbins_to_service_and_rpc_name(S, R) -> error({gpb_error, {badservice_or_rpc, {S, R}}}). %% Convert a service name and an rpc name, both as atoms, %% to a fully qualified (ie with package name) service name and %% an rpc name as binaries. -spec service_and_rpc_name_to_fqbins(_, _) -> no_return(). service_and_rpc_name_to_fqbins(S, R) -> error({gpb_error, {badservice_or_rpc, {S, R}}}). fqbin_to_msg_name(<<"Package">>) -> 'Package'; fqbin_to_msg_name(<<"Release">>) -> 'Release'; fqbin_to_msg_name(<<"RetirementStatus">>) -> 'RetirementStatus'; fqbin_to_msg_name(<<"Dependency">>) -> 'Dependency'; fqbin_to_msg_name(E) -> error({gpb_error, {badmsg, E}}). msg_name_to_fqbin('Package') -> <<"Package">>; msg_name_to_fqbin('Release') -> <<"Release">>; msg_name_to_fqbin('RetirementStatus') -> <<"RetirementStatus">>; msg_name_to_fqbin('Dependency') -> <<"Dependency">>; msg_name_to_fqbin(E) -> error({gpb_error, {badmsg, E}}). fqbin_to_enum_name(<<"RetirementReason">>) -> 'RetirementReason'; fqbin_to_enum_name(E) -> error({gpb_error, {badenum, E}}). enum_name_to_fqbin('RetirementReason') -> <<"RetirementReason">>; enum_name_to_fqbin(E) -> error({gpb_error, {badenum, E}}). get_package_name() -> undefined. %% Whether or not the message names %% are prepended with package name or not. uses_packages() -> false. source_basename() -> "mix_hex_pb_package.proto". %% Retrieve all proto file names, also imported ones. %% The order is top-down. The first element is always the main %% source file. The files are returned with extension, %% see get_all_proto_names/0 for a version that returns %% the basenames sans extension get_all_source_basenames() -> ["mix_hex_pb_package.proto"]. %% Retrieve all proto file names, also imported ones. %% The order is top-down. The first element is always the main %% source file. The files are returned sans .proto extension, %% to make it easier to use them with the various get_xyz_containment %% functions. get_all_proto_names() -> ["mix_hex_pb_package"]. get_msg_containment("mix_hex_pb_package") -> ['Dependency', 'Package', 'Release', 'RetirementStatus']; get_msg_containment(P) -> error({gpb_error, {badproto, P}}). get_pkg_containment("mix_hex_pb_package") -> undefined; get_pkg_containment(P) -> error({gpb_error, {badproto, P}}). get_service_containment("mix_hex_pb_package") -> []; get_service_containment(P) -> error({gpb_error, {badproto, P}}). get_rpc_containment("mix_hex_pb_package") -> []; get_rpc_containment(P) -> error({gpb_error, {badproto, P}}). get_enum_containment("mix_hex_pb_package") -> ['RetirementReason']; get_enum_containment(P) -> error({gpb_error, {badproto, P}}). get_proto_by_msg_name_as_fqbin(<<"RetirementStatus">>) -> "mix_hex_pb_package"; get_proto_by_msg_name_as_fqbin(<<"Release">>) -> "mix_hex_pb_package"; get_proto_by_msg_name_as_fqbin(<<"Package">>) -> "mix_hex_pb_package"; get_proto_by_msg_name_as_fqbin(<<"Dependency">>) -> "mix_hex_pb_package"; get_proto_by_msg_name_as_fqbin(E) -> error({gpb_error, {badmsg, E}}). -spec get_proto_by_service_name_as_fqbin(_) -> no_return(). get_proto_by_service_name_as_fqbin(E) -> error({gpb_error, {badservice, E}}). get_proto_by_enum_name_as_fqbin(<<"RetirementReason">>) -> "mix_hex_pb_package"; get_proto_by_enum_name_as_fqbin(E) -> error({gpb_error, {badenum, E}}). -spec get_protos_by_pkg_name_as_fqbin(_) -> no_return(). get_protos_by_pkg_name_as_fqbin(E) -> error({gpb_error, {badpkg, E}}). gpb_version_as_string() -> "4.21.1". gpb_version_as_list() -> [4,21,1]. gpb_version_source() -> "file". hex-2.4.2/src/mix_hex_pb_signed.erl000066400000000000000000000551421517471540100172650ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% -*- coding: utf-8 -*- %% % this file is @generated %% @private %% Automatically generated, do not edit %% Generated by gpb_compile version 4.21.1 %% Version source: file -module(mix_hex_pb_signed). -export([encode_msg/2, encode_msg/3]). -export([decode_msg/2, decode_msg/3]). -export([merge_msgs/3, merge_msgs/4]). -export([verify_msg/2, verify_msg/3]). -export([get_msg_defs/0]). -export([get_msg_names/0]). -export([get_group_names/0]). -export([get_msg_or_group_names/0]). -export([get_enum_names/0]). -export([find_msg_def/1, fetch_msg_def/1]). -export([find_enum_def/1, fetch_enum_def/1]). -export([enum_symbol_by_value/2, enum_value_by_symbol/2]). -export([get_service_names/0]). -export([get_service_def/1]). -export([get_rpc_names/1]). -export([find_rpc_def/2, fetch_rpc_def/2]). -export([fqbin_to_service_name/1]). -export([service_name_to_fqbin/1]). -export([fqbins_to_service_and_rpc_name/2]). -export([service_and_rpc_name_to_fqbins/2]). -export([fqbin_to_msg_name/1]). -export([msg_name_to_fqbin/1]). -export([fqbin_to_enum_name/1]). -export([enum_name_to_fqbin/1]). -export([get_package_name/0]). -export([uses_packages/0]). -export([source_basename/0]). -export([get_all_source_basenames/0]). -export([get_all_proto_names/0]). -export([get_msg_containment/1]). -export([get_pkg_containment/1]). -export([get_service_containment/1]). -export([get_rpc_containment/1]). -export([get_enum_containment/1]). -export([get_proto_by_msg_name_as_fqbin/1]). -export([get_proto_by_service_name_as_fqbin/1]). -export([get_proto_by_enum_name_as_fqbin/1]). -export([get_protos_by_pkg_name_as_fqbin/1]). -export([gpb_version_as_string/0, gpb_version_as_list/0]). -export([gpb_version_source/0]). %% enumerated types -export_type([]). %% message types -type 'Signed'() :: #{payload => iodata(), % = 1, required signature => iodata() % = 2, optional }. -export_type(['Signed'/0]). -type '$msg_name'() :: 'Signed'. -type '$msg'() :: 'Signed'(). -export_type(['$msg_name'/0, '$msg'/0]). -if(?OTP_RELEASE >= 24). -dialyzer({no_underspecs, encode_msg/2}). -endif. -spec encode_msg('$msg'(), '$msg_name'()) -> binary(). encode_msg(Msg, MsgName) when is_atom(MsgName) -> encode_msg(Msg, MsgName, []). -if(?OTP_RELEASE >= 24). -dialyzer({no_underspecs, encode_msg/3}). -endif. -spec encode_msg('$msg'(), '$msg_name'(), list()) -> binary(). encode_msg(Msg, MsgName, Opts) -> verify_msg(Msg, MsgName, Opts), TrUserData = proplists:get_value(user_data, Opts), case MsgName of 'Signed' -> encode_msg_Signed(id(Msg, TrUserData), TrUserData) end. encode_msg_Signed(Msg, TrUserData) -> encode_msg_Signed(Msg, <<>>, TrUserData). encode_msg_Signed(#{payload := F1} = M, Bin, TrUserData) -> B1 = begin TrF1 = id(F1, TrUserData), e_type_bytes(TrF1, <>, TrUserData) end, case M of #{signature := F2} -> begin TrF2 = id(F2, TrUserData), e_type_bytes(TrF2, <>, TrUserData) end; _ -> B1 end. -compile({nowarn_unused_function,e_type_sint/3}). e_type_sint(Value, Bin, _TrUserData) when Value >= 0 -> e_varint(Value * 2, Bin); e_type_sint(Value, Bin, _TrUserData) -> e_varint(Value * -2 - 1, Bin). -compile({nowarn_unused_function,e_type_int32/3}). e_type_int32(Value, Bin, _TrUserData) when 0 =< Value, Value =< 127 -> <>; e_type_int32(Value, Bin, _TrUserData) -> <> = <>, e_varint(N, Bin). -compile({nowarn_unused_function,e_type_int64/3}). e_type_int64(Value, Bin, _TrUserData) when 0 =< Value, Value =< 127 -> <>; e_type_int64(Value, Bin, _TrUserData) -> <> = <>, e_varint(N, Bin). -compile({nowarn_unused_function,e_type_bool/3}). e_type_bool(true, Bin, _TrUserData) -> <>; e_type_bool(false, Bin, _TrUserData) -> <>; e_type_bool(1, Bin, _TrUserData) -> <>; e_type_bool(0, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_string/3}). e_type_string(S, Bin, _TrUserData) -> Utf8 = unicode:characters_to_binary(S), Bin2 = e_varint(byte_size(Utf8), Bin), <>. -compile({nowarn_unused_function,e_type_bytes/3}). e_type_bytes(Bytes, Bin, _TrUserData) when is_binary(Bytes) -> Bin2 = e_varint(byte_size(Bytes), Bin), <>; e_type_bytes(Bytes, Bin, _TrUserData) when is_list(Bytes) -> BytesBin = iolist_to_binary(Bytes), Bin2 = e_varint(byte_size(BytesBin), Bin), <>. -compile({nowarn_unused_function,e_type_fixed32/3}). e_type_fixed32(Value, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_sfixed32/3}). e_type_sfixed32(Value, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_fixed64/3}). e_type_fixed64(Value, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_sfixed64/3}). e_type_sfixed64(Value, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_float/3}). e_type_float(V, Bin, _) when is_number(V) -> <>; e_type_float(infinity, Bin, _) -> <>; e_type_float('-infinity', Bin, _) -> <>; e_type_float(nan, Bin, _) -> <>. -compile({nowarn_unused_function,e_type_double/3}). e_type_double(V, Bin, _) when is_number(V) -> <>; e_type_double(infinity, Bin, _) -> <>; e_type_double('-infinity', Bin, _) -> <>; e_type_double(nan, Bin, _) -> <>. -compile({nowarn_unused_function,e_unknown_elems/2}). e_unknown_elems([Elem | Rest], Bin) -> BinR = case Elem of {varint, FNum, N} -> BinF = e_varint(FNum bsl 3, Bin), e_varint(N, BinF); {length_delimited, FNum, Data} -> BinF = e_varint(FNum bsl 3 bor 2, Bin), BinL = e_varint(byte_size(Data), BinF), <>; {group, FNum, GroupFields} -> Bin1 = e_varint(FNum bsl 3 bor 3, Bin), Bin2 = e_unknown_elems(GroupFields, Bin1), e_varint(FNum bsl 3 bor 4, Bin2); {fixed32, FNum, V} -> BinF = e_varint(FNum bsl 3 bor 5, Bin), <>; {fixed64, FNum, V} -> BinF = e_varint(FNum bsl 3 bor 1, Bin), <> end, e_unknown_elems(Rest, BinR); e_unknown_elems([], Bin) -> Bin. -compile({nowarn_unused_function,e_varint/3}). e_varint(N, Bin, _TrUserData) -> e_varint(N, Bin). -compile({nowarn_unused_function,e_varint/2}). e_varint(N, Bin) when N =< 127 -> <>; e_varint(N, Bin) -> Bin2 = <>, e_varint(N bsr 7, Bin2). decode_msg(Bin, MsgName) when is_binary(Bin) -> decode_msg(Bin, MsgName, []). decode_msg(Bin, MsgName, Opts) when is_binary(Bin) -> TrUserData = proplists:get_value(user_data, Opts), decode_msg_1_catch(Bin, MsgName, TrUserData). -ifdef('OTP_RELEASE'). decode_msg_1_catch(Bin, MsgName, TrUserData) -> try decode_msg_2_doit(MsgName, Bin, TrUserData) catch error:{gpb_error,_}=Reason:StackTrace -> erlang:raise(error, Reason, StackTrace); Class:Reason:StackTrace -> error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}}) end. -else. decode_msg_1_catch(Bin, MsgName, TrUserData) -> try decode_msg_2_doit(MsgName, Bin, TrUserData) catch error:{gpb_error,_}=Reason -> erlang:raise(error, Reason, erlang:get_stacktrace()); Class:Reason -> StackTrace = erlang:get_stacktrace(), error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}}) end. -endif. decode_msg_2_doit('Signed', Bin, TrUserData) -> id(decode_msg_Signed(Bin, TrUserData), TrUserData). decode_msg_Signed(Bin, TrUserData) -> dfp_read_field_def_Signed(Bin, 0, 0, 0, id('$undef', TrUserData), id('$undef', TrUserData), TrUserData). dfp_read_field_def_Signed(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> d_field_Signed_payload(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); dfp_read_field_def_Signed(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> d_field_Signed_signature(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); dfp_read_field_def_Signed(<<>>, 0, 0, _, F@_1, F@_2, _) -> S1 = #{payload => F@_1}, if F@_2 == '$undef' -> S1; true -> S1#{signature => F@_2} end; dfp_read_field_def_Signed(Other, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dg_read_field_def_Signed(Other, Z1, Z2, F, F@_1, F@_2, TrUserData). dg_read_field_def_Signed(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 32 - 7 -> dg_read_field_def_Signed(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); dg_read_field_def_Signed(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, TrUserData) -> Key = X bsl N + Acc, case Key of 10 -> d_field_Signed_payload(Rest, 0, 0, 0, F@_1, F@_2, TrUserData); 18 -> d_field_Signed_signature(Rest, 0, 0, 0, F@_1, F@_2, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_Signed(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 1 -> skip_64_Signed(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 2 -> skip_length_delimited_Signed(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 3 -> skip_group_Signed(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 5 -> skip_32_Signed(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData) end end; dg_read_field_def_Signed(<<>>, 0, 0, _, F@_1, F@_2, _) -> S1 = #{payload => F@_1}, if F@_2 == '$undef' -> S1; true -> S1#{signature => F@_2} end. d_field_Signed_payload(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> d_field_Signed_payload(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); d_field_Signed_payload(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_Signed(RestF, 0, 0, F, NewFValue, F@_2, TrUserData). d_field_Signed_signature(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> d_field_Signed_signature(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); d_field_Signed_signature(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_Signed(RestF, 0, 0, F, F@_1, NewFValue, TrUserData). skip_varint_Signed(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> skip_varint_Signed(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); skip_varint_Signed(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Signed(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). skip_length_delimited_Signed(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> skip_length_delimited_Signed(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); skip_length_delimited_Signed(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) -> Length = X bsl N + Acc, <<_:Length/binary, Rest2/binary>> = Rest, dfp_read_field_def_Signed(Rest2, 0, 0, F, F@_1, F@_2, TrUserData). skip_group_Signed(Bin, _, Z2, FNum, F@_1, F@_2, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_Signed(Rest, 0, Z2, FNum, F@_1, F@_2, TrUserData). skip_32_Signed(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Signed(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). skip_64_Signed(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Signed(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). read_group(Bin, FieldNum) -> {NumBytes, EndTagLen} = read_gr_b(Bin, 0, 0, 0, 0, FieldNum), <> = Bin, {Group, Rest}. %% Like skipping over fields, but record the total length, %% Each field is <(FieldNum bsl 3) bor FieldType> ++ %% Record the length because varints may be non-optimally encoded. %% %% Groups can be nested, but assume the same FieldNum cannot be nested %% because group field numbers are shared with the rest of the fields %% numbers. Thus we can search just for an group-end with the same %% field number. %% %% (The only time the same group field number could occur would %% be in a nested sub message, but then it would be inside a %% length-delimited entry, which we skip-read by length.) read_gr_b(<<1:1, X:7, Tl/binary>>, N, Acc, NumBytes, TagLen, FieldNum) when N < (32-7) -> read_gr_b(Tl, N+7, X bsl N + Acc, NumBytes, TagLen+1, FieldNum); read_gr_b(<<0:1, X:7, Tl/binary>>, N, Acc, NumBytes, TagLen, FieldNum) -> Key = X bsl N + Acc, TagLen1 = TagLen + 1, case {Key bsr 3, Key band 7} of {FieldNum, 4} -> % 4 = group_end {NumBytes, TagLen1}; {_, 0} -> % 0 = varint read_gr_vi(Tl, 0, NumBytes + TagLen1, FieldNum); {_, 1} -> % 1 = bits64 <<_:64, Tl2/binary>> = Tl, read_gr_b(Tl2, 0, 0, NumBytes + TagLen1 + 8, 0, FieldNum); {_, 2} -> % 2 = length_delimited read_gr_ld(Tl, 0, 0, NumBytes + TagLen1, FieldNum); {_, 3} -> % 3 = group_start read_gr_b(Tl, 0, 0, NumBytes + TagLen1, 0, FieldNum); {_, 4} -> % 4 = group_end read_gr_b(Tl, 0, 0, NumBytes + TagLen1, 0, FieldNum); {_, 5} -> % 5 = bits32 <<_:32, Tl2/binary>> = Tl, read_gr_b(Tl2, 0, 0, NumBytes + TagLen1 + 4, 0, FieldNum) end. read_gr_vi(<<1:1, _:7, Tl/binary>>, N, NumBytes, FieldNum) when N < (64-7) -> read_gr_vi(Tl, N+7, NumBytes+1, FieldNum); read_gr_vi(<<0:1, _:7, Tl/binary>>, _, NumBytes, FieldNum) -> read_gr_b(Tl, 0, 0, NumBytes+1, 0, FieldNum). read_gr_ld(<<1:1, X:7, Tl/binary>>, N, Acc, NumBytes, FieldNum) when N < (64-7) -> read_gr_ld(Tl, N+7, X bsl N + Acc, NumBytes+1, FieldNum); read_gr_ld(<<0:1, X:7, Tl/binary>>, N, Acc, NumBytes, FieldNum) -> Len = X bsl N + Acc, NumBytes1 = NumBytes + 1, <<_:Len/binary, Tl2/binary>> = Tl, read_gr_b(Tl2, 0, 0, NumBytes1 + Len, 0, FieldNum). merge_msgs(Prev, New, MsgName) when is_atom(MsgName) -> merge_msgs(Prev, New, MsgName, []). merge_msgs(Prev, New, MsgName, Opts) -> TrUserData = proplists:get_value(user_data, Opts), case MsgName of 'Signed' -> merge_msg_Signed(Prev, New, TrUserData) end. -compile({nowarn_unused_function,merge_msg_Signed/3}). merge_msg_Signed(#{} = PMsg, #{payload := NFpayload} = NMsg, _) -> S1 = #{payload => NFpayload}, case {PMsg, NMsg} of {_, #{signature := NFsignature}} -> S1#{signature => NFsignature}; {#{signature := PFsignature}, _} -> S1#{signature => PFsignature}; _ -> S1 end. verify_msg(Msg, MsgName) when is_atom(MsgName) -> verify_msg(Msg, MsgName, []). verify_msg(Msg, MsgName, Opts) -> TrUserData = proplists:get_value(user_data, Opts), case MsgName of 'Signed' -> v_msg_Signed(Msg, [MsgName], TrUserData); _ -> mk_type_error(not_a_known_message, Msg, []) end. -compile({nowarn_unused_function,v_msg_Signed/3}). -dialyzer({nowarn_function,v_msg_Signed/3}). v_msg_Signed(#{payload := F1} = M, Path, TrUserData) -> v_type_bytes(F1, [payload | Path], TrUserData), case M of #{signature := F2} -> v_type_bytes(F2, [signature | Path], TrUserData); _ -> ok end, lists:foreach(fun (payload) -> ok; (signature) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), ok; v_msg_Signed(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [payload] -- maps:keys(M), 'Signed'}, M, Path); v_msg_Signed(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'Signed'}, X, Path). -compile({nowarn_unused_function,v_type_bytes/3}). -dialyzer({nowarn_function,v_type_bytes/3}). v_type_bytes(B, _Path, _TrUserData) when is_binary(B) -> ok; v_type_bytes(B, _Path, _TrUserData) when is_list(B) -> ok; v_type_bytes(X, Path, _TrUserData) -> mk_type_error(bad_binary_value, X, Path). -compile({nowarn_unused_function,mk_type_error/3}). -spec mk_type_error(_, _, list()) -> no_return(). mk_type_error(Error, ValueSeen, Path) -> Path2 = prettify_path(Path), erlang:error({gpb_type_error, {Error, [{value, ValueSeen}, {path, Path2}]}}). -compile({nowarn_unused_function,prettify_path/1}). -dialyzer({nowarn_function,prettify_path/1}). prettify_path([]) -> top_level; prettify_path(PathR) -> lists:append(lists:join(".", lists:map(fun atom_to_list/1, lists:reverse(PathR)))). -compile({nowarn_unused_function,id/2}). -compile({inline,id/2}). id(X, _TrUserData) -> X. -compile({nowarn_unused_function,v_ok/3}). -compile({inline,v_ok/3}). v_ok(_Value, _Path, _TrUserData) -> ok. -compile({nowarn_unused_function,m_overwrite/3}). -compile({inline,m_overwrite/3}). m_overwrite(_Prev, New, _TrUserData) -> New. -compile({nowarn_unused_function,cons/3}). -compile({inline,cons/3}). cons(Elem, Acc, _TrUserData) -> [Elem | Acc]. -compile({nowarn_unused_function,lists_reverse/2}). -compile({inline,lists_reverse/2}). 'lists_reverse'(L, _TrUserData) -> lists:reverse(L). -compile({nowarn_unused_function,'erlang_++'/3}). -compile({inline,'erlang_++'/3}). 'erlang_++'(A, B, _TrUserData) -> A ++ B. get_msg_defs() -> [{{msg, 'Signed'}, [#{name => payload, fnum => 1, rnum => 2, type => bytes, occurrence => required, opts => []}, #{name => signature, fnum => 2, rnum => 3, type => bytes, occurrence => optional, opts => []}]}]. get_msg_names() -> ['Signed']. get_group_names() -> []. get_msg_or_group_names() -> ['Signed']. get_enum_names() -> []. fetch_msg_def(MsgName) -> case find_msg_def(MsgName) of Fs when is_list(Fs) -> Fs; error -> erlang:error({no_such_msg, MsgName}) end. -spec fetch_enum_def(_) -> no_return(). fetch_enum_def(EnumName) -> erlang:error({no_such_enum, EnumName}). find_msg_def('Signed') -> [#{name => payload, fnum => 1, rnum => 2, type => bytes, occurrence => required, opts => []}, #{name => signature, fnum => 2, rnum => 3, type => bytes, occurrence => optional, opts => []}]; find_msg_def(_) -> error. find_enum_def(_) -> error. -spec enum_symbol_by_value(_, _) -> no_return(). enum_symbol_by_value(E, V) -> erlang:error({no_enum_defs, E, V}). -spec enum_value_by_symbol(_, _) -> no_return(). enum_value_by_symbol(E, V) -> erlang:error({no_enum_defs, E, V}). get_service_names() -> []. get_service_def(_) -> error. get_rpc_names(_) -> error. find_rpc_def(_, _) -> error. -spec fetch_rpc_def(_, _) -> no_return(). fetch_rpc_def(ServiceName, RpcName) -> erlang:error({no_such_rpc, ServiceName, RpcName}). %% Convert a a fully qualified (ie with package name) service name %% as a binary to a service name as an atom. -spec fqbin_to_service_name(_) -> no_return(). fqbin_to_service_name(X) -> error({gpb_error, {badservice, X}}). %% Convert a service name as an atom to a fully qualified %% (ie with package name) name as a binary. -spec service_name_to_fqbin(_) -> no_return(). service_name_to_fqbin(X) -> error({gpb_error, {badservice, X}}). %% Convert a a fully qualified (ie with package name) service name %% and an rpc name, both as binaries to a service name and an rpc %% name, as atoms. -spec fqbins_to_service_and_rpc_name(_, _) -> no_return(). fqbins_to_service_and_rpc_name(S, R) -> error({gpb_error, {badservice_or_rpc, {S, R}}}). %% Convert a service name and an rpc name, both as atoms, %% to a fully qualified (ie with package name) service name and %% an rpc name as binaries. -spec service_and_rpc_name_to_fqbins(_, _) -> no_return(). service_and_rpc_name_to_fqbins(S, R) -> error({gpb_error, {badservice_or_rpc, {S, R}}}). fqbin_to_msg_name(<<"Signed">>) -> 'Signed'; fqbin_to_msg_name(E) -> error({gpb_error, {badmsg, E}}). msg_name_to_fqbin('Signed') -> <<"Signed">>; msg_name_to_fqbin(E) -> error({gpb_error, {badmsg, E}}). -spec fqbin_to_enum_name(_) -> no_return(). fqbin_to_enum_name(E) -> error({gpb_error, {badenum, E}}). -spec enum_name_to_fqbin(_) -> no_return(). enum_name_to_fqbin(E) -> error({gpb_error, {badenum, E}}). get_package_name() -> undefined. %% Whether or not the message names %% are prepended with package name or not. uses_packages() -> false. source_basename() -> "mix_hex_pb_signed.proto". %% Retrieve all proto file names, also imported ones. %% The order is top-down. The first element is always the main %% source file. The files are returned with extension, %% see get_all_proto_names/0 for a version that returns %% the basenames sans extension get_all_source_basenames() -> ["mix_hex_pb_signed.proto"]. %% Retrieve all proto file names, also imported ones. %% The order is top-down. The first element is always the main %% source file. The files are returned sans .proto extension, %% to make it easier to use them with the various get_xyz_containment %% functions. get_all_proto_names() -> ["mix_hex_pb_signed"]. get_msg_containment("mix_hex_pb_signed") -> ['Signed']; get_msg_containment(P) -> error({gpb_error, {badproto, P}}). get_pkg_containment("mix_hex_pb_signed") -> undefined; get_pkg_containment(P) -> error({gpb_error, {badproto, P}}). get_service_containment("mix_hex_pb_signed") -> []; get_service_containment(P) -> error({gpb_error, {badproto, P}}). get_rpc_containment("mix_hex_pb_signed") -> []; get_rpc_containment(P) -> error({gpb_error, {badproto, P}}). get_enum_containment("mix_hex_pb_signed") -> []; get_enum_containment(P) -> error({gpb_error, {badproto, P}}). get_proto_by_msg_name_as_fqbin(<<"Signed">>) -> "mix_hex_pb_signed"; get_proto_by_msg_name_as_fqbin(E) -> error({gpb_error, {badmsg, E}}). -spec get_proto_by_service_name_as_fqbin(_) -> no_return(). get_proto_by_service_name_as_fqbin(E) -> error({gpb_error, {badservice, E}}). -spec get_proto_by_enum_name_as_fqbin(_) -> no_return(). get_proto_by_enum_name_as_fqbin(E) -> error({gpb_error, {badenum, E}}). -spec get_protos_by_pkg_name_as_fqbin(_) -> no_return(). get_protos_by_pkg_name_as_fqbin(E) -> error({gpb_error, {badpkg, E}}). gpb_version_as_string() -> "4.21.1". gpb_version_as_list() -> [4,21,1]. gpb_version_source() -> "file". hex-2.4.2/src/mix_hex_pb_versions.erl000066400000000000000000001100031517471540100176500ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% -*- coding: utf-8 -*- %% % this file is @generated %% @private %% Automatically generated, do not edit %% Generated by gpb_compile version 4.21.1 %% Version source: file -module(mix_hex_pb_versions). -export([encode_msg/2, encode_msg/3]). -export([decode_msg/2, decode_msg/3]). -export([merge_msgs/3, merge_msgs/4]). -export([verify_msg/2, verify_msg/3]). -export([get_msg_defs/0]). -export([get_msg_names/0]). -export([get_group_names/0]). -export([get_msg_or_group_names/0]). -export([get_enum_names/0]). -export([find_msg_def/1, fetch_msg_def/1]). -export([find_enum_def/1, fetch_enum_def/1]). -export([enum_symbol_by_value/2, enum_value_by_symbol/2]). -export([get_service_names/0]). -export([get_service_def/1]). -export([get_rpc_names/1]). -export([find_rpc_def/2, fetch_rpc_def/2]). -export([fqbin_to_service_name/1]). -export([service_name_to_fqbin/1]). -export([fqbins_to_service_and_rpc_name/2]). -export([service_and_rpc_name_to_fqbins/2]). -export([fqbin_to_msg_name/1]). -export([msg_name_to_fqbin/1]). -export([fqbin_to_enum_name/1]). -export([enum_name_to_fqbin/1]). -export([get_package_name/0]). -export([uses_packages/0]). -export([source_basename/0]). -export([get_all_source_basenames/0]). -export([get_all_proto_names/0]). -export([get_msg_containment/1]). -export([get_pkg_containment/1]). -export([get_service_containment/1]). -export([get_rpc_containment/1]). -export([get_enum_containment/1]). -export([get_proto_by_msg_name_as_fqbin/1]). -export([get_proto_by_service_name_as_fqbin/1]). -export([get_proto_by_enum_name_as_fqbin/1]). -export([get_protos_by_pkg_name_as_fqbin/1]). -export([gpb_version_as_string/0, gpb_version_as_list/0]). -export([gpb_version_source/0]). %% enumerated types -export_type([]). %% message types -type 'Versions'() :: #{packages => ['Package'()], % = 1, repeated repository => unicode:chardata() % = 2, required }. -type 'Package'() :: #{name => unicode:chardata(), % = 1, required versions => [unicode:chardata()], % = 2, repeated retired => [integer()] % = 3, repeated, 32 bits }. -export_type(['Versions'/0, 'Package'/0]). -type '$msg_name'() :: 'Versions' | 'Package'. -type '$msg'() :: 'Versions'() | 'Package'(). -export_type(['$msg_name'/0, '$msg'/0]). -if(?OTP_RELEASE >= 24). -dialyzer({no_underspecs, encode_msg/2}). -endif. -spec encode_msg('$msg'(), '$msg_name'()) -> binary(). encode_msg(Msg, MsgName) when is_atom(MsgName) -> encode_msg(Msg, MsgName, []). -if(?OTP_RELEASE >= 24). -dialyzer({no_underspecs, encode_msg/3}). -endif. -spec encode_msg('$msg'(), '$msg_name'(), list()) -> binary(). encode_msg(Msg, MsgName, Opts) -> verify_msg(Msg, MsgName, Opts), TrUserData = proplists:get_value(user_data, Opts), case MsgName of 'Versions' -> encode_msg_Versions(id(Msg, TrUserData), TrUserData); 'Package' -> encode_msg_Package(id(Msg, TrUserData), TrUserData) end. encode_msg_Versions(Msg, TrUserData) -> encode_msg_Versions(Msg, <<>>, TrUserData). encode_msg_Versions(#{repository := F2} = M, Bin, TrUserData) -> B1 = case M of #{packages := F1} -> TrF1 = id(F1, TrUserData), if TrF1 == [] -> Bin; true -> e_field_Versions_packages(TrF1, Bin, TrUserData) end; _ -> Bin end, begin TrF2 = id(F2, TrUserData), e_type_string(TrF2, <>, TrUserData) end. encode_msg_Package(Msg, TrUserData) -> encode_msg_Package(Msg, <<>>, TrUserData). encode_msg_Package(#{name := F1} = M, Bin, TrUserData) -> B1 = begin TrF1 = id(F1, TrUserData), e_type_string(TrF1, <>, TrUserData) end, B2 = case M of #{versions := F2} -> TrF2 = id(F2, TrUserData), if TrF2 == [] -> B1; true -> e_field_Package_versions(TrF2, B1, TrUserData) end; _ -> B1 end, case M of #{retired := F3} -> TrF3 = id(F3, TrUserData), if TrF3 == [] -> B2; true -> e_field_Package_retired(TrF3, B2, TrUserData) end; _ -> B2 end. e_mfield_Versions_packages(Msg, Bin, TrUserData) -> SubBin = encode_msg_Package(Msg, <<>>, TrUserData), Bin2 = e_varint(byte_size(SubBin), Bin), <>. e_field_Versions_packages([Elem | Rest], Bin, TrUserData) -> Bin2 = <>, Bin3 = e_mfield_Versions_packages(id(Elem, TrUserData), Bin2, TrUserData), e_field_Versions_packages(Rest, Bin3, TrUserData); e_field_Versions_packages([], Bin, _TrUserData) -> Bin. e_field_Package_versions([Elem | Rest], Bin, TrUserData) -> Bin2 = <>, Bin3 = e_type_string(id(Elem, TrUserData), Bin2, TrUserData), e_field_Package_versions(Rest, Bin3, TrUserData); e_field_Package_versions([], Bin, _TrUserData) -> Bin. e_field_Package_retired(Elems, Bin, TrUserData) when Elems =/= [] -> SubBin = e_pfield_Package_retired(Elems, <<>>, TrUserData), Bin2 = <>, Bin3 = e_varint(byte_size(SubBin), Bin2), <>; e_field_Package_retired([], Bin, _TrUserData) -> Bin. e_pfield_Package_retired([Value | Rest], Bin, TrUserData) -> Bin2 = e_type_int32(id(Value, TrUserData), Bin, TrUserData), e_pfield_Package_retired(Rest, Bin2, TrUserData); e_pfield_Package_retired([], Bin, _TrUserData) -> Bin. -compile({nowarn_unused_function,e_type_sint/3}). e_type_sint(Value, Bin, _TrUserData) when Value >= 0 -> e_varint(Value * 2, Bin); e_type_sint(Value, Bin, _TrUserData) -> e_varint(Value * -2 - 1, Bin). -compile({nowarn_unused_function,e_type_int32/3}). e_type_int32(Value, Bin, _TrUserData) when 0 =< Value, Value =< 127 -> <>; e_type_int32(Value, Bin, _TrUserData) -> <> = <>, e_varint(N, Bin). -compile({nowarn_unused_function,e_type_int64/3}). e_type_int64(Value, Bin, _TrUserData) when 0 =< Value, Value =< 127 -> <>; e_type_int64(Value, Bin, _TrUserData) -> <> = <>, e_varint(N, Bin). -compile({nowarn_unused_function,e_type_bool/3}). e_type_bool(true, Bin, _TrUserData) -> <>; e_type_bool(false, Bin, _TrUserData) -> <>; e_type_bool(1, Bin, _TrUserData) -> <>; e_type_bool(0, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_string/3}). e_type_string(S, Bin, _TrUserData) -> Utf8 = unicode:characters_to_binary(S), Bin2 = e_varint(byte_size(Utf8), Bin), <>. -compile({nowarn_unused_function,e_type_bytes/3}). e_type_bytes(Bytes, Bin, _TrUserData) when is_binary(Bytes) -> Bin2 = e_varint(byte_size(Bytes), Bin), <>; e_type_bytes(Bytes, Bin, _TrUserData) when is_list(Bytes) -> BytesBin = iolist_to_binary(Bytes), Bin2 = e_varint(byte_size(BytesBin), Bin), <>. -compile({nowarn_unused_function,e_type_fixed32/3}). e_type_fixed32(Value, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_sfixed32/3}). e_type_sfixed32(Value, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_fixed64/3}). e_type_fixed64(Value, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_sfixed64/3}). e_type_sfixed64(Value, Bin, _TrUserData) -> <>. -compile({nowarn_unused_function,e_type_float/3}). e_type_float(V, Bin, _) when is_number(V) -> <>; e_type_float(infinity, Bin, _) -> <>; e_type_float('-infinity', Bin, _) -> <>; e_type_float(nan, Bin, _) -> <>. -compile({nowarn_unused_function,e_type_double/3}). e_type_double(V, Bin, _) when is_number(V) -> <>; e_type_double(infinity, Bin, _) -> <>; e_type_double('-infinity', Bin, _) -> <>; e_type_double(nan, Bin, _) -> <>. -compile({nowarn_unused_function,e_unknown_elems/2}). e_unknown_elems([Elem | Rest], Bin) -> BinR = case Elem of {varint, FNum, N} -> BinF = e_varint(FNum bsl 3, Bin), e_varint(N, BinF); {length_delimited, FNum, Data} -> BinF = e_varint(FNum bsl 3 bor 2, Bin), BinL = e_varint(byte_size(Data), BinF), <>; {group, FNum, GroupFields} -> Bin1 = e_varint(FNum bsl 3 bor 3, Bin), Bin2 = e_unknown_elems(GroupFields, Bin1), e_varint(FNum bsl 3 bor 4, Bin2); {fixed32, FNum, V} -> BinF = e_varint(FNum bsl 3 bor 5, Bin), <>; {fixed64, FNum, V} -> BinF = e_varint(FNum bsl 3 bor 1, Bin), <> end, e_unknown_elems(Rest, BinR); e_unknown_elems([], Bin) -> Bin. -compile({nowarn_unused_function,e_varint/3}). e_varint(N, Bin, _TrUserData) -> e_varint(N, Bin). -compile({nowarn_unused_function,e_varint/2}). e_varint(N, Bin) when N =< 127 -> <>; e_varint(N, Bin) -> Bin2 = <>, e_varint(N bsr 7, Bin2). decode_msg(Bin, MsgName) when is_binary(Bin) -> decode_msg(Bin, MsgName, []). decode_msg(Bin, MsgName, Opts) when is_binary(Bin) -> TrUserData = proplists:get_value(user_data, Opts), decode_msg_1_catch(Bin, MsgName, TrUserData). -ifdef('OTP_RELEASE'). decode_msg_1_catch(Bin, MsgName, TrUserData) -> try decode_msg_2_doit(MsgName, Bin, TrUserData) catch error:{gpb_error,_}=Reason:StackTrace -> erlang:raise(error, Reason, StackTrace); Class:Reason:StackTrace -> error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}}) end. -else. decode_msg_1_catch(Bin, MsgName, TrUserData) -> try decode_msg_2_doit(MsgName, Bin, TrUserData) catch error:{gpb_error,_}=Reason -> erlang:raise(error, Reason, erlang:get_stacktrace()); Class:Reason -> StackTrace = erlang:get_stacktrace(), error({gpb_error,{decoding_failure, {Bin, MsgName, {Class, Reason, StackTrace}}}}) end. -endif. decode_msg_2_doit('Versions', Bin, TrUserData) -> id(decode_msg_Versions(Bin, TrUserData), TrUserData); decode_msg_2_doit('Package', Bin, TrUserData) -> id(decode_msg_Package(Bin, TrUserData), TrUserData). decode_msg_Versions(Bin, TrUserData) -> dfp_read_field_def_Versions(Bin, 0, 0, 0, id([], TrUserData), id('$undef', TrUserData), TrUserData). dfp_read_field_def_Versions(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> d_field_Versions_packages(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); dfp_read_field_def_Versions(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> d_field_Versions_repository(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); dfp_read_field_def_Versions(<<>>, 0, 0, _, R1, F@_2, TrUserData) -> S1 = #{repository => F@_2}, if R1 == '$undef' -> S1; true -> S1#{packages => lists_reverse(R1, TrUserData)} end; dfp_read_field_def_Versions(Other, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dg_read_field_def_Versions(Other, Z1, Z2, F, F@_1, F@_2, TrUserData). dg_read_field_def_Versions(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 32 - 7 -> dg_read_field_def_Versions(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); dg_read_field_def_Versions(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, TrUserData) -> Key = X bsl N + Acc, case Key of 10 -> d_field_Versions_packages(Rest, 0, 0, 0, F@_1, F@_2, TrUserData); 18 -> d_field_Versions_repository(Rest, 0, 0, 0, F@_1, F@_2, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_Versions(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 1 -> skip_64_Versions(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 2 -> skip_length_delimited_Versions(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 3 -> skip_group_Versions(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 5 -> skip_32_Versions(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData) end end; dg_read_field_def_Versions(<<>>, 0, 0, _, R1, F@_2, TrUserData) -> S1 = #{repository => F@_2}, if R1 == '$undef' -> S1; true -> S1#{packages => lists_reverse(R1, TrUserData)} end. d_field_Versions_packages(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> d_field_Versions_packages(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); d_field_Versions_packages(<<0:1, X:7, Rest/binary>>, N, Acc, F, Prev, F@_2, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, {id(decode_msg_Package(Bs, TrUserData), TrUserData), Rest2} end, dfp_read_field_def_Versions(RestF, 0, 0, F, cons(NewFValue, Prev, TrUserData), F@_2, TrUserData). d_field_Versions_repository(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> d_field_Versions_repository(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); d_field_Versions_repository(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_Versions(RestF, 0, 0, F, F@_1, NewFValue, TrUserData). skip_varint_Versions(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> skip_varint_Versions(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); skip_varint_Versions(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Versions(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). skip_length_delimited_Versions(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> skip_length_delimited_Versions(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); skip_length_delimited_Versions(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) -> Length = X bsl N + Acc, <<_:Length/binary, Rest2/binary>> = Rest, dfp_read_field_def_Versions(Rest2, 0, 0, F, F@_1, F@_2, TrUserData). skip_group_Versions(Bin, _, Z2, FNum, F@_1, F@_2, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_Versions(Rest, 0, Z2, FNum, F@_1, F@_2, TrUserData). skip_32_Versions(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Versions(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). skip_64_Versions(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Versions(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). decode_msg_Package(Bin, TrUserData) -> dfp_read_field_def_Package(Bin, 0, 0, 0, id('$undef', TrUserData), id([], TrUserData), id([], TrUserData), TrUserData). dfp_read_field_def_Package(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_field_Package_name(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); dfp_read_field_def_Package(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_field_Package_versions(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); dfp_read_field_def_Package(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_pfield_Package_retired(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); dfp_read_field_def_Package(<<24, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_field_Package_retired(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); dfp_read_field_def_Package(<<>>, 0, 0, _, F@_1, R1, R2, TrUserData) -> #{name => F@_1, versions => lists_reverse(R1, TrUserData), retired => lists_reverse(R2, TrUserData)}; dfp_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dg_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). dg_read_field_def_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 32 - 7 -> dg_read_field_def_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); dg_read_field_def_Package(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, TrUserData) -> Key = X bsl N + Acc, case Key of 10 -> d_field_Package_name(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); 18 -> d_field_Package_versions(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); 26 -> d_pfield_Package_retired(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); 24 -> d_field_Package_retired(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); 1 -> skip_64_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); 2 -> skip_length_delimited_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); 3 -> skip_group_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); 5 -> skip_32_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData) end end; dg_read_field_def_Package(<<>>, 0, 0, _, F@_1, R1, R2, TrUserData) -> #{name => F@_1, versions => lists_reverse(R1, TrUserData), retired => lists_reverse(R2, TrUserData)}. d_field_Package_name(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_field_Package_name(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); d_field_Package_name(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, F@_3, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_Package(RestF, 0, 0, F, NewFValue, F@_2, F@_3, TrUserData). d_field_Package_versions(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_field_Package_versions(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); d_field_Package_versions(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, Prev, F@_3, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_Package(RestF, 0, 0, F, F@_1, cons(NewFValue, Prev, TrUserData), F@_3, TrUserData). d_field_Package_retired(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_field_Package_retired(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); d_field_Package_retired(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, Prev, TrUserData) -> {NewFValue, RestF} = {begin <> = <<(X bsl N + Acc):32/unsigned-native>>, id(Res, TrUserData) end, Rest}, dfp_read_field_def_Package(RestF, 0, 0, F, F@_1, F@_2, cons(NewFValue, Prev, TrUserData), TrUserData). d_pfield_Package_retired(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_pfield_Package_retired(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); d_pfield_Package_retired(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, E, TrUserData) -> Len = X bsl N + Acc, <> = Rest, NewSeq = d_packed_field_Package_retired(PackedBytes, 0, 0, F, E, TrUserData), dfp_read_field_def_Package(Rest2, 0, 0, F, F@_1, F@_2, NewSeq, TrUserData). d_packed_field_Package_retired(<<1:1, X:7, Rest/binary>>, N, Acc, F, AccSeq, TrUserData) when N < 57 -> d_packed_field_Package_retired(Rest, N + 7, X bsl N + Acc, F, AccSeq, TrUserData); d_packed_field_Package_retired(<<0:1, X:7, Rest/binary>>, N, Acc, F, AccSeq, TrUserData) -> {NewFValue, RestF} = {begin <> = <<(X bsl N + Acc):32/unsigned-native>>, id(Res, TrUserData) end, Rest}, d_packed_field_Package_retired(RestF, 0, 0, F, [NewFValue | AccSeq], TrUserData); d_packed_field_Package_retired(<<>>, 0, 0, _, AccSeq, _) -> AccSeq. skip_varint_Package(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> skip_varint_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); skip_varint_Package(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). skip_length_delimited_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> skip_length_delimited_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); skip_length_delimited_Package(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) -> Length = X bsl N + Acc, <<_:Length/binary, Rest2/binary>> = Rest, dfp_read_field_def_Package(Rest2, 0, 0, F, F@_1, F@_2, F@_3, TrUserData). skip_group_Package(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_Package(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, TrUserData). skip_32_Package(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). skip_64_Package(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). read_group(Bin, FieldNum) -> {NumBytes, EndTagLen} = read_gr_b(Bin, 0, 0, 0, 0, FieldNum), <> = Bin, {Group, Rest}. %% Like skipping over fields, but record the total length, %% Each field is <(FieldNum bsl 3) bor FieldType> ++ %% Record the length because varints may be non-optimally encoded. %% %% Groups can be nested, but assume the same FieldNum cannot be nested %% because group field numbers are shared with the rest of the fields %% numbers. Thus we can search just for an group-end with the same %% field number. %% %% (The only time the same group field number could occur would %% be in a nested sub message, but then it would be inside a %% length-delimited entry, which we skip-read by length.) read_gr_b(<<1:1, X:7, Tl/binary>>, N, Acc, NumBytes, TagLen, FieldNum) when N < (32-7) -> read_gr_b(Tl, N+7, X bsl N + Acc, NumBytes, TagLen+1, FieldNum); read_gr_b(<<0:1, X:7, Tl/binary>>, N, Acc, NumBytes, TagLen, FieldNum) -> Key = X bsl N + Acc, TagLen1 = TagLen + 1, case {Key bsr 3, Key band 7} of {FieldNum, 4} -> % 4 = group_end {NumBytes, TagLen1}; {_, 0} -> % 0 = varint read_gr_vi(Tl, 0, NumBytes + TagLen1, FieldNum); {_, 1} -> % 1 = bits64 <<_:64, Tl2/binary>> = Tl, read_gr_b(Tl2, 0, 0, NumBytes + TagLen1 + 8, 0, FieldNum); {_, 2} -> % 2 = length_delimited read_gr_ld(Tl, 0, 0, NumBytes + TagLen1, FieldNum); {_, 3} -> % 3 = group_start read_gr_b(Tl, 0, 0, NumBytes + TagLen1, 0, FieldNum); {_, 4} -> % 4 = group_end read_gr_b(Tl, 0, 0, NumBytes + TagLen1, 0, FieldNum); {_, 5} -> % 5 = bits32 <<_:32, Tl2/binary>> = Tl, read_gr_b(Tl2, 0, 0, NumBytes + TagLen1 + 4, 0, FieldNum) end. read_gr_vi(<<1:1, _:7, Tl/binary>>, N, NumBytes, FieldNum) when N < (64-7) -> read_gr_vi(Tl, N+7, NumBytes+1, FieldNum); read_gr_vi(<<0:1, _:7, Tl/binary>>, _, NumBytes, FieldNum) -> read_gr_b(Tl, 0, 0, NumBytes+1, 0, FieldNum). read_gr_ld(<<1:1, X:7, Tl/binary>>, N, Acc, NumBytes, FieldNum) when N < (64-7) -> read_gr_ld(Tl, N+7, X bsl N + Acc, NumBytes+1, FieldNum); read_gr_ld(<<0:1, X:7, Tl/binary>>, N, Acc, NumBytes, FieldNum) -> Len = X bsl N + Acc, NumBytes1 = NumBytes + 1, <<_:Len/binary, Tl2/binary>> = Tl, read_gr_b(Tl2, 0, 0, NumBytes1 + Len, 0, FieldNum). merge_msgs(Prev, New, MsgName) when is_atom(MsgName) -> merge_msgs(Prev, New, MsgName, []). merge_msgs(Prev, New, MsgName, Opts) -> TrUserData = proplists:get_value(user_data, Opts), case MsgName of 'Versions' -> merge_msg_Versions(Prev, New, TrUserData); 'Package' -> merge_msg_Package(Prev, New, TrUserData) end. -compile({nowarn_unused_function,merge_msg_Versions/3}). merge_msg_Versions(#{} = PMsg, #{repository := NFrepository} = NMsg, TrUserData) -> S1 = #{repository => NFrepository}, case {PMsg, NMsg} of {#{packages := PFpackages}, #{packages := NFpackages}} -> S1#{packages => 'erlang_++'(PFpackages, NFpackages, TrUserData)}; {_, #{packages := NFpackages}} -> S1#{packages => NFpackages}; {#{packages := PFpackages}, _} -> S1#{packages => PFpackages}; {_, _} -> S1 end. -compile({nowarn_unused_function,merge_msg_Package/3}). merge_msg_Package(#{} = PMsg, #{name := NFname} = NMsg, TrUserData) -> S1 = #{name => NFname}, S2 = case {PMsg, NMsg} of {#{versions := PFversions}, #{versions := NFversions}} -> S1#{versions => 'erlang_++'(PFversions, NFversions, TrUserData)}; {_, #{versions := NFversions}} -> S1#{versions => NFversions}; {#{versions := PFversions}, _} -> S1#{versions => PFversions}; {_, _} -> S1 end, case {PMsg, NMsg} of {#{retired := PFretired}, #{retired := NFretired}} -> S2#{retired => 'erlang_++'(PFretired, NFretired, TrUserData)}; {_, #{retired := NFretired}} -> S2#{retired => NFretired}; {#{retired := PFretired}, _} -> S2#{retired => PFretired}; {_, _} -> S2 end. verify_msg(Msg, MsgName) when is_atom(MsgName) -> verify_msg(Msg, MsgName, []). verify_msg(Msg, MsgName, Opts) -> TrUserData = proplists:get_value(user_data, Opts), case MsgName of 'Versions' -> v_msg_Versions(Msg, [MsgName], TrUserData); 'Package' -> v_msg_Package(Msg, [MsgName], TrUserData); _ -> mk_type_error(not_a_known_message, Msg, []) end. -compile({nowarn_unused_function,v_msg_Versions/3}). -dialyzer({nowarn_function,v_msg_Versions/3}). v_msg_Versions(#{repository := F2} = M, Path, TrUserData) -> case M of #{packages := F1} -> if is_list(F1) -> _ = [v_submsg_Package(Elem, [packages | Path], TrUserData) || Elem <- F1], ok; true -> mk_type_error({invalid_list_of, {msg, 'Package'}}, F1, [packages | Path]) end; _ -> ok end, v_type_string(F2, [repository | Path], TrUserData), lists:foreach(fun (packages) -> ok; (repository) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), ok; v_msg_Versions(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [repository] -- maps:keys(M), 'Versions'}, M, Path); v_msg_Versions(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'Versions'}, X, Path). -compile({nowarn_unused_function,v_submsg_Package/3}). -dialyzer({nowarn_function,v_submsg_Package/3}). v_submsg_Package(Msg, Path, TrUserData) -> v_msg_Package(Msg, Path, TrUserData). -compile({nowarn_unused_function,v_msg_Package/3}). -dialyzer({nowarn_function,v_msg_Package/3}). v_msg_Package(#{name := F1} = M, Path, TrUserData) -> v_type_string(F1, [name | Path], TrUserData), case M of #{versions := F2} -> if is_list(F2) -> _ = [v_type_string(Elem, [versions | Path], TrUserData) || Elem <- F2], ok; true -> mk_type_error({invalid_list_of, string}, F2, [versions | Path]) end; _ -> ok end, case M of #{retired := F3} -> if is_list(F3) -> _ = [v_type_int32(Elem, [retired | Path], TrUserData) || Elem <- F3], ok; true -> mk_type_error({invalid_list_of, int32}, F3, [retired | Path]) end; _ -> ok end, lists:foreach(fun (name) -> ok; (versions) -> ok; (retired) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), ok; v_msg_Package(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [name] -- maps:keys(M), 'Package'}, M, Path); v_msg_Package(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'Package'}, X, Path). -compile({nowarn_unused_function,v_type_int32/3}). -dialyzer({nowarn_function,v_type_int32/3}). v_type_int32(N, _Path, _TrUserData) when is_integer(N), -2147483648 =< N, N =< 2147483647 -> ok; v_type_int32(N, Path, _TrUserData) when is_integer(N) -> mk_type_error({value_out_of_range, int32, signed, 32}, N, Path); v_type_int32(X, Path, _TrUserData) -> mk_type_error({bad_integer, int32, signed, 32}, X, Path). -compile({nowarn_unused_function,v_type_string/3}). -dialyzer({nowarn_function,v_type_string/3}). v_type_string(S, Path, _TrUserData) when is_list(S); is_binary(S) -> try unicode:characters_to_binary(S) of B when is_binary(B) -> ok; {error, _, _} -> mk_type_error(bad_unicode_string, S, Path) catch error:badarg -> mk_type_error(bad_unicode_string, S, Path) end; v_type_string(X, Path, _TrUserData) -> mk_type_error(bad_unicode_string, X, Path). -compile({nowarn_unused_function,mk_type_error/3}). -spec mk_type_error(_, _, list()) -> no_return(). mk_type_error(Error, ValueSeen, Path) -> Path2 = prettify_path(Path), erlang:error({gpb_type_error, {Error, [{value, ValueSeen}, {path, Path2}]}}). -compile({nowarn_unused_function,prettify_path/1}). -dialyzer({nowarn_function,prettify_path/1}). prettify_path([]) -> top_level; prettify_path(PathR) -> lists:append(lists:join(".", lists:map(fun atom_to_list/1, lists:reverse(PathR)))). -compile({nowarn_unused_function,id/2}). -compile({inline,id/2}). id(X, _TrUserData) -> X. -compile({nowarn_unused_function,v_ok/3}). -compile({inline,v_ok/3}). v_ok(_Value, _Path, _TrUserData) -> ok. -compile({nowarn_unused_function,m_overwrite/3}). -compile({inline,m_overwrite/3}). m_overwrite(_Prev, New, _TrUserData) -> New. -compile({nowarn_unused_function,cons/3}). -compile({inline,cons/3}). cons(Elem, Acc, _TrUserData) -> [Elem | Acc]. -compile({nowarn_unused_function,lists_reverse/2}). -compile({inline,lists_reverse/2}). 'lists_reverse'(L, _TrUserData) -> lists:reverse(L). -compile({nowarn_unused_function,'erlang_++'/3}). -compile({inline,'erlang_++'/3}). 'erlang_++'(A, B, _TrUserData) -> A ++ B. get_msg_defs() -> [{{msg, 'Versions'}, [#{name => packages, fnum => 1, rnum => 2, type => {msg, 'Package'}, occurrence => repeated, opts => []}, #{name => repository, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}]}, {{msg, 'Package'}, [#{name => name, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => versions, fnum => 2, rnum => 3, type => string, occurrence => repeated, opts => []}, #{name => retired, fnum => 3, rnum => 4, type => int32, occurrence => repeated, opts => [packed]}]}]. get_msg_names() -> ['Versions', 'Package']. get_group_names() -> []. get_msg_or_group_names() -> ['Versions', 'Package']. get_enum_names() -> []. fetch_msg_def(MsgName) -> case find_msg_def(MsgName) of Fs when is_list(Fs) -> Fs; error -> erlang:error({no_such_msg, MsgName}) end. -spec fetch_enum_def(_) -> no_return(). fetch_enum_def(EnumName) -> erlang:error({no_such_enum, EnumName}). find_msg_def('Versions') -> [#{name => packages, fnum => 1, rnum => 2, type => {msg, 'Package'}, occurrence => repeated, opts => []}, #{name => repository, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}]; find_msg_def('Package') -> [#{name => name, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => versions, fnum => 2, rnum => 3, type => string, occurrence => repeated, opts => []}, #{name => retired, fnum => 3, rnum => 4, type => int32, occurrence => repeated, opts => [packed]}]; find_msg_def(_) -> error. find_enum_def(_) -> error. -spec enum_symbol_by_value(_, _) -> no_return(). enum_symbol_by_value(E, V) -> erlang:error({no_enum_defs, E, V}). -spec enum_value_by_symbol(_, _) -> no_return(). enum_value_by_symbol(E, V) -> erlang:error({no_enum_defs, E, V}). get_service_names() -> []. get_service_def(_) -> error. get_rpc_names(_) -> error. find_rpc_def(_, _) -> error. -spec fetch_rpc_def(_, _) -> no_return(). fetch_rpc_def(ServiceName, RpcName) -> erlang:error({no_such_rpc, ServiceName, RpcName}). %% Convert a a fully qualified (ie with package name) service name %% as a binary to a service name as an atom. -spec fqbin_to_service_name(_) -> no_return(). fqbin_to_service_name(X) -> error({gpb_error, {badservice, X}}). %% Convert a service name as an atom to a fully qualified %% (ie with package name) name as a binary. -spec service_name_to_fqbin(_) -> no_return(). service_name_to_fqbin(X) -> error({gpb_error, {badservice, X}}). %% Convert a a fully qualified (ie with package name) service name %% and an rpc name, both as binaries to a service name and an rpc %% name, as atoms. -spec fqbins_to_service_and_rpc_name(_, _) -> no_return(). fqbins_to_service_and_rpc_name(S, R) -> error({gpb_error, {badservice_or_rpc, {S, R}}}). %% Convert a service name and an rpc name, both as atoms, %% to a fully qualified (ie with package name) service name and %% an rpc name as binaries. -spec service_and_rpc_name_to_fqbins(_, _) -> no_return(). service_and_rpc_name_to_fqbins(S, R) -> error({gpb_error, {badservice_or_rpc, {S, R}}}). fqbin_to_msg_name(<<"Versions">>) -> 'Versions'; fqbin_to_msg_name(<<"Package">>) -> 'Package'; fqbin_to_msg_name(E) -> error({gpb_error, {badmsg, E}}). msg_name_to_fqbin('Versions') -> <<"Versions">>; msg_name_to_fqbin('Package') -> <<"Package">>; msg_name_to_fqbin(E) -> error({gpb_error, {badmsg, E}}). -spec fqbin_to_enum_name(_) -> no_return(). fqbin_to_enum_name(E) -> error({gpb_error, {badenum, E}}). -spec enum_name_to_fqbin(_) -> no_return(). enum_name_to_fqbin(E) -> error({gpb_error, {badenum, E}}). get_package_name() -> undefined. %% Whether or not the message names %% are prepended with package name or not. uses_packages() -> false. source_basename() -> "mix_hex_pb_versions.proto". %% Retrieve all proto file names, also imported ones. %% The order is top-down. The first element is always the main %% source file. The files are returned with extension, %% see get_all_proto_names/0 for a version that returns %% the basenames sans extension get_all_source_basenames() -> ["mix_hex_pb_versions.proto"]. %% Retrieve all proto file names, also imported ones. %% The order is top-down. The first element is always the main %% source file. The files are returned sans .proto extension, %% to make it easier to use them with the various get_xyz_containment %% functions. get_all_proto_names() -> ["mix_hex_pb_versions"]. get_msg_containment("mix_hex_pb_versions") -> ['Package', 'Versions']; get_msg_containment(P) -> error({gpb_error, {badproto, P}}). get_pkg_containment("mix_hex_pb_versions") -> undefined; get_pkg_containment(P) -> error({gpb_error, {badproto, P}}). get_service_containment("mix_hex_pb_versions") -> []; get_service_containment(P) -> error({gpb_error, {badproto, P}}). get_rpc_containment("mix_hex_pb_versions") -> []; get_rpc_containment(P) -> error({gpb_error, {badproto, P}}). get_enum_containment("mix_hex_pb_versions") -> []; get_enum_containment(P) -> error({gpb_error, {badproto, P}}). get_proto_by_msg_name_as_fqbin(<<"Versions">>) -> "mix_hex_pb_versions"; get_proto_by_msg_name_as_fqbin(<<"Package">>) -> "mix_hex_pb_versions"; get_proto_by_msg_name_as_fqbin(E) -> error({gpb_error, {badmsg, E}}). -spec get_proto_by_service_name_as_fqbin(_) -> no_return(). get_proto_by_service_name_as_fqbin(E) -> error({gpb_error, {badservice, E}}). -spec get_proto_by_enum_name_as_fqbin(_) -> no_return(). get_proto_by_enum_name_as_fqbin(E) -> error({gpb_error, {badenum, E}}). -spec get_protos_by_pkg_name_as_fqbin(_) -> no_return(). get_protos_by_pkg_name_as_fqbin(E) -> error({gpb_error, {badpkg, E}}). gpb_version_as_string() -> "4.21.1". gpb_version_as_list() -> [4,21,1]. gpb_version_source() -> "file". hex-2.4.2/src/mix_hex_registry.erl000066400000000000000000000120111517471540100171670ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @doc %% Functions for encoding and decoding Hex registries. -module(mix_hex_registry). -export([ encode_names/1, decode_names/2, build_names/2, unpack_names/3, encode_versions/1, decode_versions/2, build_versions/2, unpack_versions/3, encode_package/1, decode_package/3, build_package/2, unpack_package/4, sign_protobuf/2, decode_signed/1, decode_and_verify_signed/2, sign/2, verify/3 ]). -include_lib("public_key/include/public_key.hrl"). -type private_key() :: #'RSAPrivateKey'{} | binary(). -type public_key() :: #'RSAPublicKey'{} | binary(). %%==================================================================== %% API functions %%==================================================================== %% @doc %% Builds names resource. build_names(Names, PrivateKey) -> Payload = encode_names(Names), zlib:gzip(sign_protobuf(Payload, PrivateKey)). %% @doc %% Unpacks names resource. unpack_names(Payload, Repository, PublicKey) -> case decode_and_verify_signed(zlib:gunzip(Payload), PublicKey) of {ok, Names} -> decode_names(Names, Repository); Other -> Other end. %% @private encode_names(Names) -> mix_hex_pb_names:encode_msg(Names, 'Names'). %% @private decode_names(Payload, no_verify) -> {ok, mix_hex_pb_names:decode_msg(Payload, 'Names')}; decode_names(Payload, Repository) -> case mix_hex_pb_names:decode_msg(Payload, 'Names') of #{repository := Repository, packages := _Packages} = Result -> {ok, Result}; _ -> {error, bad_repo_name} end. %% @doc %% Builds versions resource. build_versions(Versions, PrivateKey) -> Payload = encode_versions(Versions), zlib:gzip(sign_protobuf(Payload, PrivateKey)). %% @doc %% Unpacks versions resource. unpack_versions(Payload, Repository, PublicKey) -> case decode_and_verify_signed(zlib:gunzip(Payload), PublicKey) of {ok, Versions} -> decode_versions(Versions, Repository); Other -> Other end. %% @private encode_versions(Versions) -> mix_hex_pb_versions:encode_msg(Versions, 'Versions'). %% @private decode_versions(Payload, no_verify) -> {ok, mix_hex_pb_versions:decode_msg(Payload, 'Versions')}; decode_versions(Payload, Repository) -> case mix_hex_pb_versions:decode_msg(Payload, 'Versions') of #{repository := Repository, packages := _Packages} = Result -> {ok, Result}; _ -> {error, bad_repo_name} end. %% @doc %% Builds package resource. build_package(Package, PrivateKey) -> Payload = encode_package(Package), zlib:gzip(sign_protobuf(Payload, PrivateKey)). %% @doc %% Unpacks package resource. unpack_package(Payload, Repository, Name, PublicKey) -> case decode_and_verify_signed(zlib:gunzip(Payload), PublicKey) of {ok, Package} -> decode_package(Package, Repository, Name); Other -> Other end. %% @private encode_package(Package) -> mix_hex_pb_package:encode_msg(Package, 'Package'). %% @private decode_package(Payload, no_verify, no_verify) -> {ok, mix_hex_pb_package:decode_msg(Payload, 'Package')}; decode_package(Payload, Repository, Package) -> case mix_hex_pb_package:decode_msg(Payload, 'Package') of #{repository := Repository, name := Package, releases := _Releases} = Result -> {ok, Result}; _ -> {error, bad_repo_name} end. %% @private sign_protobuf(Payload, PrivateKey) -> Signature = sign(Payload, PrivateKey), mix_hex_pb_signed:encode_msg(#{payload => Payload, signature => Signature}, 'Signed'). %% @private decode_signed(Signed) -> mix_hex_pb_signed:decode_msg(Signed, 'Signed'). %% @private -spec decode_and_verify_signed(binary(), public_key()) -> {ok, binary()} | {error, term()}. decode_and_verify_signed(Signed, PublicKey) -> #{payload := Payload, signature := Signature} = decode_signed(Signed), case verify(Payload, Signature, PublicKey) of true -> {ok, Payload}; false -> {error, bad_signature}; {error, Reason} -> {error, Reason} end. %% @private -spec sign(binary(), private_key()) -> binary(). sign(Binary, PrivateKey) -> {ok, RSAPrivateKey} = key(PrivateKey), public_key:sign(Binary, sha512, RSAPrivateKey). %% @private -spec verify(binary(), binary(), public_key()) -> boolean() | {error, term()}. verify(Binary, Signature, PublicKey) -> case key(PublicKey) of {ok, RSAPublicKey} -> public_key:verify(Binary, sha512, Signature, RSAPublicKey); {error, Reason} -> {error, Reason} end. %%==================================================================== %% Internal functions %%==================================================================== %% @private key(#'RSAPublicKey'{} = Key) -> {ok, Key}; key(#'RSAPrivateKey'{} = Key) -> {ok, Key}; key(Binary) when is_binary(Binary) -> case public_key:pem_decode(Binary) of [Entry | _] -> {ok, public_key:pem_entry_decode(Entry)}; _ -> {error, bad_key} end. hex-2.4.2/src/mix_hex_repo.erl000066400000000000000000000215351517471540100162770ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @doc %% Repo API. -module(mix_hex_repo). -export([ get_names/1, get_versions/1, get_package/2, get_tarball/3, get_tarball_to_file/4, get_docs/3, get_docs_to_file/4, get_public_key/1, get_hex_installs/1 ]). %%==================================================================== %% API functions %%==================================================================== %% @doc %% Gets names resource from the repository. %% %% Examples: %% %% ``` %% > mix_hex_repo:get_names(mix_hex_core:default_config()). %% {ok,{200, ..., %% #{packages => [ %% #{name => <<"package1">>}, %% #{name => <<"package2">>}, %% ...]}}} %% ''' %% @end get_names(Config) when is_map(Config) -> Decoder = fun(Data) -> mix_hex_registry:decode_names(Data, verify_repo(Config)) end, get_protobuf(Config, <<"names">>, Decoder). %% @doc %% Gets versions resource from the repository. %% %% Examples: %% %% ``` %% > mix_hex_repo:get_versions(Config). %% {ok, {200, ..., %% #{packages => [ %% #{name => <<"package1">>, retired => [], %% versions => [<<"1.0.0">>]}, %% #{name => <<"package2">>, retired => [<<"0.5.0>>"], %% versions => [<<"0.5.0">>, <<"1.0.0">>]}, %% ...]}}} %% ''' %% @end get_versions(Config) when is_map(Config) -> Decoder = fun(Data) -> mix_hex_registry:decode_versions(Data, verify_repo(Config)) end, get_protobuf(Config, <<"versions">>, Decoder). %% @doc %% Gets package resource from the repository. %% %% Examples: %% %% ``` %% > mix_hex_repo:get_package(mix_hex_core:default_config(), <<"package1">>). %% {ok, {200, ..., %% #{name => <<"package1">>, %% releases => [ %% #{checksum => ..., version => <<"0.5.0">>, dependencies => []}, %% #{checksum => ..., version => <<"1.0.0">>, dependencies => [ %% #{package => <<"package2">>, optional => true, requirement => <<"~> 0.1">>} %% ]}, %% ]}}} %% ''' %% @end get_package(Config, Name) when is_binary(Name) and is_map(Config) -> Verify = maps:get(repo_verify_origin, Config, true), Decoder = fun(Data) -> case Verify of true -> mix_hex_registry:decode_package(Data, repo_name(Config), Name); false -> mix_hex_registry:decode_package(Data, no_verify, no_verify) end end, get_protobuf(Config, <<"packages/", Name/binary>>, Decoder). %% @doc %% Gets tarball from the repository. %% %% Examples: %% %% ``` %% > {ok, {200, _, Tarball}} = mix_hex_repo:get_tarball(mix_hex_core:default_config(), <<"package1">>, <<"1.0.0">>), %% > {ok, #{metadata := Metadata}} = mix_hex_tarball:unpack(Tarball, "/tmp/package"). %% ''' %% @end get_tarball(Config, Name, Version) -> ReqHeaders = make_headers(Config), case get(Config, tarball_url(Config, Name, Version), ReqHeaders) of {ok, {200, RespHeaders, Tarball}} -> {ok, {200, RespHeaders, Tarball}}; Other -> Other end. %% @doc %% Gets tarball from the repository and writes it to a file. %% %% Examples: %% %% ``` %% > {ok, {200, _}} = mix_hex_repo:get_tarball_to_file(mix_hex_core:default_config(), <<"package1">>, <<"1.0.0">>, "/tmp/package.tar"), %% > {ok, #{metadata := Metadata}} = mix_hex_tarball:unpack({file, "/tmp/package.tar"}, "/tmp/package"). %% ''' %% @end get_tarball_to_file(Config, Name, Version, Filename) -> ReqHeaders = make_headers(Config), case get_to_file(Config, tarball_url(Config, Name, Version), ReqHeaders, Filename) of {ok, {200, RespHeaders}} -> {ok, {200, RespHeaders}}; Other -> Other end. %% @doc %% Gets docs tarball from the repository. %% %% Examples: %% %% ``` %% > {ok, {200, _, Docs}} = mix_hex_repo:get_docs(mix_hex_core:default_config(), <<"package1">>, <<"1.0.0">>), %% > mix_hex_tarball:unpack_docs(Docs, "/tmp/docs") %% ok %% ''' get_docs(Config, Name, Version) -> ReqHeaders = make_headers(Config), case get(Config, docs_url(Config, Name, Version), ReqHeaders) of {ok, {200, RespHeaders, Docs}} -> {ok, {200, RespHeaders, Docs}}; Other -> Other end. %% @doc %% Gets docs tarball from the repository and writes it to a file. %% %% Examples: %% %% ``` %% > {ok, {200, _}} = mix_hex_repo:get_docs_to_file(mix_hex_core:default_config(), <<"package1">>, <<"1.0.0">>, "/tmp/docs.tar.gz"), %% > ok = mix_hex_tarball:unpack_docs({file, "/tmp/docs.tar.gz"}, "/tmp/docs"). %% ''' get_docs_to_file(Config, Name, Version, Filename) -> ReqHeaders = make_headers(Config), case get_to_file(Config, docs_url(Config, Name, Version), ReqHeaders, Filename) of {ok, {200, RespHeaders}} -> {ok, {200, RespHeaders}}; Other -> Other end. %% @doc %% Gets the public key from the repository. %% %% Examples: %% %% ``` %% > mix_hex_repo:get_public_key(mix_hex_core:default_config()) %% {ok, {200, _, PublicKey}} %% ''' get_public_key(Config) -> ReqHeaders = make_headers(Config), URI = build_url(Config, <<"public_key">>), case get(Config, URI, ReqHeaders) of {ok, {200, RespHeaders, PublicKey}} -> {ok, {200, RespHeaders, PublicKey}}; Other -> Other end. %% @doc %% Gets Hex installation versions CSV from repository. %% %% Examples: %% %% ``` %% > mix_hex_repo:get_hex_installs(mix_hex_core:default_config()). %% {ok, {200, ..., <<"1.0.0,abc123,1.13.0\n1.1.0,def456,1.14.0\n...">>}} %% ''' %% @end get_hex_installs(Config) -> ReqHeaders = make_headers(Config), URI = build_url(Config, <<"installs/hex-1.x.csv">>), case get(Config, URI, ReqHeaders) of {ok, {200, RespHeaders, CSV}} -> {ok, {200, RespHeaders, CSV}}; Other -> Other end. %%==================================================================== %% Internal functions %%==================================================================== %% @private get(Config, URI, Headers) -> mix_hex_http:request(Config, get, URI, Headers, undefined). %% @private get_to_file(Config, URI, Headers, Filename) -> mix_hex_http:request_to_file(Config, get, URI, Headers, undefined, Filename). %% @private get_protobuf(Config, Path, Decoder) -> PublicKey = maps:get(repo_public_key, Config), ReqHeaders = make_headers(Config), case get(Config, build_url(Config, Path), ReqHeaders) of {ok, {200, RespHeaders, Compressed}} -> Signed = zlib:gunzip(Compressed), case decode(Signed, PublicKey, Decoder, Config) of {ok, Decoded} -> {ok, {200, RespHeaders, Decoded}}; {error, _} = Error -> Error end; Other -> Other end. %% @private decode(Signed, PublicKey, Decoder, Config) -> Verify = maps:get(repo_verify, Config, true), case Verify of true -> case mix_hex_registry:decode_and_verify_signed(Signed, PublicKey) of {ok, Payload} -> Decoder(Payload); Other -> Other end; false -> #{payload := Payload} = mix_hex_registry:decode_signed(Signed), Decoder(Payload) end. %% @private verify_repo(Config) -> case maps:get(repo_verify_origin, Config, true) of true -> repo_name(Config); false -> no_verify end. %% @private repo_name(#{repo_organization := Name}) when is_binary(Name) -> Name; repo_name(#{repo_name := Name}) when is_binary(Name) -> Name. %% @private tarball_url(Config, Name, Version) -> Filename = tarball_filename(Name, Version), build_url(Config, <<"tarballs/", Filename/binary>>). %% @private docs_url(Config, Name, Version) -> Filename = docs_filename(Name, Version), build_url(Config, <<"docs/", Filename/binary>>). %% @private build_url(#{repo_url := URI, repo_organization := Org}, Path) when is_binary(Org) -> <>; build_url(#{repo_url := URI, repo_organization := undefined}, Path) -> <>; build_url(Config, Path) -> build_url(Config#{repo_organization => undefined}, Path). %% @private tarball_filename(Name, Version) -> <>. %% @private docs_filename(Name, Version) -> <>. %% @private make_headers(Config) -> maps:fold(fun set_header/3, #{}, Config). %% @private set_header(http_etag, ETag, Headers) when is_binary(ETag) -> maps:put(<<"if-none-match">>, ETag, Headers); set_header(repo_key, Token, Headers) when is_binary(Token) -> maps:put(<<"authorization">>, Token, Headers); set_header(api_otp, OTP, Headers) when is_binary(OTP) -> maps:put(<<"x-hex-otp">>, OTP, Headers); set_header(_, _, Headers) -> Headers. hex-2.4.2/src/mix_hex_safe_binary_to_term.erl000066400000000000000000000054451517471540100213470ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @hidden %% Safe deserialization of Erlang terms from binary. %% %% This module provides a restricted version of `binary_to_term/1' that: %% - Uses the `safe' option to prevent creation of new atoms (DoS protection) %% - Validates that the term contains no executable code (RCE protection) %% %% Inspired by Plug.Crypto's non_executable_binary_to_term: %% https://github.com/elixir-plug/plug_crypto/blob/c326c3c743b18cf5f4b12735d06dd90c72dcd779/lib/plug/crypto.ex -module(mix_hex_safe_binary_to_term). -export([safe_binary_to_term/1]). -type unsafe_term() :: function() | port(). -type error_reason() :: invalid_term | {unsafe_term, unsafe_term()}. -spec safe_binary_to_term(binary()) -> {ok, term()} | {error, error_reason()}. safe_binary_to_term(Binary) when is_binary(Binary) -> try binary_to_term(Binary, [safe]) of Term -> case validate_term(Term) of ok -> {ok, Term}; {error, _} = Error -> Error end catch error:badarg -> {error, invalid_term} end. -spec validate_term(term()) -> ok | {error, {unsafe_term, term()}}. validate_term(Term) when is_list(Term) -> validate_list(Term); validate_term(Term) when is_tuple(Term) -> validate_tuple(Term, tuple_size(Term)); validate_term(Term) when is_map(Term) -> validate_map(Term); validate_term(Term) when is_atom(Term); is_number(Term); is_bitstring(Term); is_pid(Term); is_reference(Term) -> ok; validate_term(Term) -> {error, {unsafe_term, Term}}. -spec validate_list(list()) -> ok | {error, {unsafe_term, term()}}. validate_list([]) -> ok; validate_list([H | T]) when is_list(T) -> case validate_term(H) of ok -> validate_list(T); Error -> Error end; validate_list([H | T]) -> %% Improper list case validate_term(H) of ok -> validate_term(T); Error -> Error end. -spec validate_tuple(tuple(), non_neg_integer()) -> ok | {error, {unsafe_term, term()}}. validate_tuple(_Tuple, 0) -> ok; validate_tuple(Tuple, N) -> case validate_term(element(N, Tuple)) of ok -> validate_tuple(Tuple, N - 1); Error -> Error end. -spec validate_map(map()) -> ok | {error, {unsafe_term, term()}}. validate_map(Map) -> try maps:fold( fun(Key, Value, ok) -> case validate_term(Key) of ok -> case validate_term(Value) of ok -> ok; Error -> throw(Error) end; Error -> throw(Error) end end, ok, Map ) catch throw:{error, _} = Error -> Error end. hex-2.4.2/src/mix_hex_tarball.erl000066400000000000000000000760741517471540100167630ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %% @doc %% Functions for creating and unpacking Hex tarballs. -module(mix_hex_tarball). -export([ create/2, create/3, create_docs/1, create_docs/2, unpack/2, unpack/3, unpack_docs/2, unpack_docs/3, format_checksum/1, format_error/1 ]). -ifdef(TEST). -export([do_decode_metadata/1, gzip/1, normalize_requirements/1]). -endif. -define(VERSION, <<"3">>). -define(HASH_CHUNK_SIZE, 65536). -define(MAX_VERSION_SIZE, 32). -define(MAX_CHECKSUM_SIZE, 128). -define(MAX_METADATA_SIZE, 128 * 1024). -define(BUILD_TOOL_FILES, [ {<<"mix.exs">>, <<"mix">>}, {<<"rebar.config">>, <<"rebar3">>}, {<<"rebar">>, <<"rebar3">>}, {<<"Makefile">>, <<"make">>}, {<<"Makefile.win">>, <<"make">>} ]). -include_lib("kernel/include/file.hrl"). -type checksum() :: binary(). -type contents() :: [{filename(), binary()}]. -type filename() :: string(). -type files() :: [{filename(), filename() | binary()}]. -type metadata() :: map(). -type tarball() :: binary(). %%==================================================================== %% API functions %%==================================================================== %% @doc %% Creates a package tarball. %% %% Returns the binary of the tarball the "inner checksum" and "outer checksum". %% The inner checksum is deprecated in favor of the outer checksum. %% %% Examples: %% %% ``` %% > Metadata = #{<<"name">> => <<"foo">>, <<"version">> => <<"1.0.0">>}, %% > Files = [{"src/foo.erl", <<"-module(foo).">>}], %% > mix_hex_tarball:create(Metadata, Files). %% {ok, #{tarball => <<86,69,...>>, %% outer_checksum => <<40,32,...>>, %% inner_checksum => <<178,12,...>>}} %% ''' %% @end -spec create(metadata(), files(), mix_hex_core:config()) -> {ok, #{ tarball => tarball(), outer_checksum => checksum(), inner_checksum => tarball() }} | {error, term()}. create(Metadata, Files, Config) -> #{ tarball_max_size := TarballMaxSize, tarball_max_uncompressed_size := TarballMaxUncompressedSize } = Config, MetadataBinary = encode_metadata(Metadata), case valid_size(MetadataBinary, ?MAX_METADATA_SIZE) of false -> {error, {tarball, {file_too_big, "metadata.config"}}}; true -> ContentsTarball = create_memory_tarball(Files), ContentsTarballCompressed = gzip(ContentsTarball), InnerChecksum = inner_checksum(?VERSION, MetadataBinary, ContentsTarballCompressed), InnerChecksumBase16 = encode_base16(InnerChecksum), OuterFiles = [ {"VERSION", ?VERSION}, {"CHECKSUM", InnerChecksumBase16}, {"metadata.config", MetadataBinary}, {"contents.tar.gz", ContentsTarballCompressed} ], case valid_size(ContentsTarball, TarballMaxUncompressedSize) of true -> Tarball = create_memory_tarball(OuterFiles), OuterChecksum = checksum(Tarball), case valid_size(Tarball, TarballMaxSize) of true -> {ok, #{ tarball => Tarball, outer_checksum => OuterChecksum, inner_checksum => InnerChecksum }}; false -> {error, {tarball, {too_big_compressed, TarballMaxSize}}} end; false -> {error, {tarball, {too_big_uncompressed, TarballMaxUncompressedSize}}} end end. -spec create(metadata(), files()) -> {ok, #{ tarball => tarball(), outer_checksum => checksum(), inner_checksum => tarball() }} | {error, term()}. create(Metadata, Files) -> create(Metadata, Files, mix_hex_core:default_config()). %% @doc %% Creates a docs tarball. %% %% Examples: %% %% ``` %% > Files = [{"doc/index.html", <<"Docs">>}], %% > mix_hex_tarball:create_docs(Files). %% {ok, <<86,69,...>>} %% ''' %% @end -spec create_docs(files(), mix_hex_core:config()) -> {ok, tarball()} | {error, term()}. create_docs(Files, Config) -> #{ docs_tarball_max_size := TarballMaxSize, docs_tarball_max_uncompressed_size := TarballMaxUncompressedSize } = Config, UncompressedTarball = create_memory_tarball(Files), case valid_size(UncompressedTarball, TarballMaxUncompressedSize) of true -> Tarball = gzip(UncompressedTarball), case valid_size(Tarball, TarballMaxSize) of true -> {ok, Tarball}; false -> {error, {tarball, {too_big_compressed, TarballMaxSize}}} end; false -> {error, {tarball, {too_big_uncompressed, TarballMaxUncompressedSize}}} end. -spec create_docs(files()) -> {ok, tarball()} | {error, term()}. create_docs(Files) -> create_docs(Files, mix_hex_core:default_config()). %% @doc %% Unpacks a package tarball. %% %% Remember to verify the outer tarball checksum against the registry checksum %% returned from `mix_hex_repo:get_package(Config, Package)'. %% %% The first argument is the tarball, either as a binary or `{file, Path}' %% to read from a file on disk. Using `{file, Path}' avoids loading the %% tarball into memory. %% %% The second argument controls the output: %% %% - `memory' - unpack contents into memory and return them %% - `none' - only extract metadata and checksums, skip contents %% - A path string - extract contents to the given directory %% %% Examples: %% %% ``` %% > mix_hex_tarball:unpack(Tarball, memory). %% {ok,#{outer_checksum => <<...>>, %% contents => [{"src/foo.erl",<<"-module(foo).">>}], %% metadata => #{<<"name">> => <<"foo">>, ...}}} %% %% > mix_hex_tarball:unpack(Tarball, none). %% {ok,#{outer_checksum => <<...>>, %% metadata => #{<<"name">> => <<"foo">>, ...}}} %% %% > mix_hex_tarball:unpack(Tarball, "path/to/unpack"). %% {ok,#{outer_checksum => <<...>>, %% metadata => #{<<"name">> => <<"foo">>, ...}}} %% ''' -spec unpack (tarball() | {file, filename()}, memory, mix_hex_core:config()) -> {ok, #{ outer_checksum => checksum(), inner_checksum => checksum(), metadata => metadata(), contents => contents() }} | {error, term()}; (tarball() | {file, filename()}, none, mix_hex_core:config()) -> {ok, #{ outer_checksum => checksum(), inner_checksum => checksum(), metadata => metadata() }} | {error, term()}; (tarball() | {file, filename()}, filename(), mix_hex_core:config()) -> {ok, #{ outer_checksum => checksum(), inner_checksum => checksum(), metadata => metadata() }} | {error, term()}. unpack(Input, memory, Config) -> case check_input_size(Input, Config) of true -> OuterChecksum = outer_checksum(Input), Source = tar_source(Input), case mix_hex_erl_tar:extract(Source, [memory]) of {ok, []} -> {error, {tarball, empty}}; {ok, FileList} -> case validate_outer_file_sizes(maps:from_list(FileList)) of {ok, Files} -> do_unpack(Files, OuterChecksum, memory, Config); {error, _} = Error -> Error end; {error, Reason} -> {error, {tarball, Reason}} end; false -> {error, {tarball, too_big}} end; unpack(Input, Output, Config) -> case check_input_size(Input, Config) of true -> OuterChecksum = outer_checksum(Input), Source = tar_source(Input), TmpDir = tmp_path(), ok = file:make_dir(TmpDir), try case mix_hex_erl_tar:extract(Source, [{cwd, TmpDir}]) of ok -> case read_outer_files(TmpDir) of {ok, Files} -> do_unpack(Files, OuterChecksum, Output, Config); {error, _} = Error -> Error end; {error, Reason} -> {error, {tarball, Reason}} end after remove_dir(TmpDir) end; false -> {error, {tarball, too_big}} end. %% @doc %% Unpacks a package tarball. %% %% @see unpack/3 -spec unpack (tarball() | {file, filename()}, memory) -> {ok, #{ outer_checksum => checksum(), inner_checksum => checksum(), metadata => metadata(), contents => contents() }} | {error, term()}; (tarball() | {file, filename()}, none) -> {ok, #{ outer_checksum => checksum(), inner_checksum => checksum(), metadata => metadata() }} | {error, term()}; (tarball() | {file, filename()}, filename()) -> {ok, #{ outer_checksum => checksum(), inner_checksum => checksum(), metadata => metadata() }} | {error, term()}. unpack(Tarball, Output) -> unpack(Tarball, Output, mix_hex_core:default_config()). %% @doc %% Unpacks a documentation tarball. %% %% The first argument is the tarball, either as a binary or `{file, Path}' %% to read from a file on disk. Using `{file, Path}' avoids loading the %% tarball into memory. %% %% Examples: %% %% ``` %% > mix_hex_tarball:unpack_docs(Tarball, memory). %% {ok, [{"index.html", <<"">>}, ...]} %% %% > mix_hex_tarball:unpack_docs(Tarball, "path/to/unpack"). %% ok %% ''' -spec unpack_docs (tarball() | {file, filename()}, memory, mix_hex_core:config()) -> {ok, contents()} | {error, term()}; (tarball() | {file, filename()}, filename(), mix_hex_core:config()) -> ok | {error, term()}. unpack_docs(Input, Output, Config) -> case check_docs_input_size(Input, Config) of true -> MaxSize = maps:get(docs_tarball_max_uncompressed_size, Config), unpack_tarball(tar_source(Input), Output, MaxSize); false -> {error, {tarball, too_big}} end. -spec unpack_docs (tarball() | {file, filename()}, memory) -> {ok, contents()} | {error, term()}; (tarball() | {file, filename()}, filename()) -> ok | {error, term()}. unpack_docs(Input, Output) -> unpack_docs(Input, Output, mix_hex_core:default_config()). %% @doc %% Returns base16-encoded representation of checksum. -spec format_checksum(checksum()) -> binary(). format_checksum(Checksum) -> encode_base16(Checksum). %% @doc %% Converts an error reason term to a human-readable error message string. -spec format_error(term()) -> string(). format_error({tarball, {file_too_big, Name}}) -> io_lib:format("file too big: ~s", [Name]); format_error({tarball, empty}) -> "empty tarball"; format_error({tarball, {too_big_uncompressed, Size}}) -> io_lib:format("package exceeds max uncompressed size ~w ~s", [format_byte_size(Size), "MB"]); format_error({tarball, {too_big_compressed, Size}}) -> io_lib:format("package exceeds max compressed size ~w ~s", [format_byte_size(Size), "MB"]); format_error({tarball, {missing_files, Files}}) -> io_lib:format("missing files: ~p", [Files]); format_error({tarball, {bad_version, Vsn}}) -> io_lib:format("unsupported version: ~p", [Vsn]); format_error({tarball, invalid_checksum}) -> "invalid tarball checksum"; format_error({tarball, Reason}) -> "tarball error, " ++ mix_hex_erl_tar:format_error(Reason); format_error({inner_tarball, Reason}) -> "inner tarball error, " ++ mix_hex_erl_tar:format_error(Reason); format_error({metadata, invalid_terms}) -> "error reading package metadata: invalid terms"; format_error({metadata, not_key_value}) -> "error reading package metadata: not in key-value format"; format_error({metadata, Reason}) -> "error reading package metadata" ++ mix_safe_erl_term:format_error(Reason); format_error({checksum_mismatch, ExpectedChecksum, ActualChecksum}) -> io_lib:format( "tarball checksum mismatch~n~n" ++ "Expected (base16-encoded): ~s~n" ++ "Actual (base16-encoded): ~s", [encode_base16(ExpectedChecksum), encode_base16(ActualChecksum)] ). format_byte_size(Size) -> Size / 1000000. %%==================================================================== %% Internal functions %%==================================================================== %% @private inner_checksum(Version, MetadataBinary, {path, ContentsPath}) -> HashState0 = crypto:hash_init(sha256), HashState1 = crypto:hash_update(HashState0, Version), HashState2 = crypto:hash_update(HashState1, MetadataBinary), HashState3 = stream_file_hash(HashState2, ContentsPath), crypto:hash_final(HashState3); inner_checksum(Version, MetadataBinary, ContentsBinary) -> Blob = <>, crypto:hash(sha256, Blob). %% @private checksum(ContentsBinary) when is_binary(ContentsBinary) -> crypto:hash(sha256, ContentsBinary). %% @private tar_source({file, Path}) -> Path; tar_source(Tarball) -> {binary, Tarball}. %% @private outer_checksum({file, Path}) -> file_checksum(Path); outer_checksum(Tarball) -> crypto:hash(sha256, Tarball). %% @private check_input_size({file, Path}, Config) -> valid_file_size(Path, maps:get(tarball_max_size, Config)); check_input_size(Tarball, Config) -> valid_size(Tarball, maps:get(tarball_max_size, Config)). %% @private check_docs_input_size({file, Path}, Config) -> valid_file_size(Path, maps:get(docs_tarball_max_size, Config)); check_docs_input_size(Tarball, Config) -> valid_size(Tarball, maps:get(docs_tarball_max_size, Config)). %% @private encode_metadata(Meta) -> Data = lists:map( fun(MetaPair) -> String = io_lib_pretty:print(binarify(MetaPair), [{encoding, utf8}]), unicode:characters_to_binary([String, ".\n"]) end, maps:to_list(Meta) ), iolist_to_binary(Data). %% @private do_unpack(Files, OuterChecksum, Output, Config) -> State = #{ inner_checksum => undefined, outer_checksum => OuterChecksum, contents => undefined, files => Files, metadata => undefined, output => Output, config => Config }, State1 = check_files(State), State2 = check_version(State1), State3 = check_inner_checksum(State2), State4 = decode_metadata(State3), finish_unpack(State4). %% @private finish_unpack({error, _} = Error) -> Error; finish_unpack(#{ metadata := Metadata, files := Files, inner_checksum := InnerChecksum, outer_checksum := OuterChecksum, output := Output, config := Config }) -> _ = maps:get("VERSION", Files), Contents = maps:get("contents.tar.gz", Files), MaxUncompressedSize = maps:get(tarball_max_uncompressed_size, Config), Result = #{ inner_checksum => InnerChecksum, outer_checksum => OuterChecksum, metadata => Metadata }, case Output of none -> {ok, Result}; memory -> case unpack_contents(Contents, memory, MaxUncompressedSize) of {ok, UnpackedContents} -> {ok, Result#{contents => UnpackedContents}}; {error, Reason} -> {error, {inner_tarball, Reason}} end; _ -> filelib:ensure_dir(filename:join(Output, "*")), case unpack_contents(Contents, Output, MaxUncompressedSize) of ok -> [ try_updating_mtime(filename:join(Output, P)) || P <- filelib:wildcard("**", Output) ], copy_metadata_config(Output, maps:get("metadata.config", Files)), {ok, Result}; {error, Reason} -> {error, {inner_tarball, Reason}} end end. %% @private unpack_contents(Contents, Output, MaxSize) -> Opts = case Output of memory -> [memory, compressed]; _ -> [{cwd, Output}, compressed] end, Source = case Contents of {path, ContentsPath} -> ContentsPath; ContentsBinary -> {binary, ContentsBinary} end, case mix_hex_erl_tar:extract(Source, [{max_size, MaxSize} | Opts]) of {error, too_big} -> {error, {too_big_uncompressed, MaxSize}}; Other -> Other end. %% @private copy_metadata_config(Output, MetadataBinary) -> ok = file:write_file(filename:join(Output, "hex_metadata.config"), MetadataBinary). %% @private check_files(#{files := Files} = State) -> RequiredFiles = ["VERSION", "CHECKSUM", "metadata.config", "contents.tar.gz"], case diff_keys(Files, RequiredFiles, []) of ok -> State; {error, {missing_keys, Keys}} -> {error, {tarball, {missing_files, Keys}}} end. %% @private check_version({error, _} = Error) -> Error; check_version(#{files := Files} = State) -> case maps:get("VERSION", Files) of <<"3">> -> State; Version -> {error, {tarball, {bad_version, Version}}} end. %% @private % Note: This checksum is deprecated check_inner_checksum({error, _} = Error) -> Error; check_inner_checksum(#{files := Files} = State) -> ChecksumBase16 = maps:get("CHECKSUM", Files), ExpectedChecksum = decode_base16(ChecksumBase16), Version = maps:get("VERSION", Files), MetadataBinary = maps:get("metadata.config", Files), Contents = maps:get("contents.tar.gz", Files), ActualChecksum = inner_checksum(Version, MetadataBinary, Contents), if byte_size(ExpectedChecksum) /= 32 -> {error, {tarball, invalid_inner_checksum}}; ExpectedChecksum == ActualChecksum -> maps:put(inner_checksum, ExpectedChecksum, State); true -> {error, {tarball, {inner_checksum_mismatch, ExpectedChecksum, ActualChecksum}}} end. %% @private decode_metadata({error, _} = Error) -> Error; decode_metadata(#{files := #{"metadata.config" := Binary}} = State) when is_binary(Binary) -> case do_decode_metadata(Binary) of #{} = Metadata -> maps:put(metadata, normalize_metadata(Metadata), State); Other -> Other end. %% @private do_decode_metadata(Binary) when is_binary(Binary) -> {ok, String} = characters_to_list(Binary), case mix_safe_erl_term:string(String) of {ok, Tokens, _Line} -> try Terms = mix_safe_erl_term:terms(Tokens), maps:from_list(Terms) catch error:function_clause -> {error, {metadata, invalid_terms}}; error:badarg -> {error, {metadata, not_key_value}} end; {error, {_Line, mix_safe_erl_term, Reason}, _Line2} -> {error, {metadata, Reason}} end. %% @private characters_to_list(Binary) -> case unicode:characters_to_list(Binary) of List when is_list(List) -> {ok, List}; {error, _, _} -> case unicode:characters_to_list(Binary, latin1) of List when is_list(List) -> {ok, List}; Other -> Other end end. %% @private normalize_metadata(Metadata1) -> Metadata2 = maybe_update_with(<<"requirements">>, fun normalize_requirements/1, Metadata1), Metadata3 = maybe_update_with(<<"links">>, fun try_into_map/1, Metadata2), Metadata4 = maybe_update_with(<<"extra">>, fun try_into_nested_map/1, Metadata3), guess_build_tools(Metadata4). %% @private normalize_requirements(Requirements) -> case is_list(Requirements) andalso (Requirements /= []) andalso is_list(hd(Requirements)) of true -> maps:from_list(lists:map(fun normalize_legacy_requirement/1, Requirements)); false -> try_into_map(fun normalize_normal_requirement/1, Requirements) end. %% @private normalize_normal_requirement({Name, Requirement}) -> {Name, try_into_map(Requirement)}. %% @private normalize_legacy_requirement(Requirement) -> Map = maps:from_list(Requirement), Name = maps:get(<<"name">>, Map), {Name, maps:without([<<"name">>], Map)}. %% @private guess_build_tools(#{<<"build_tools">> := BuildTools} = Metadata) when is_list(BuildTools) -> Metadata; guess_build_tools(#{<<"files">> := Filenames} = Metadata) -> BaseFiles = [ Filename || Filename <- Filenames, filename:dirname(binary_to_list(Filename)) == "." ], BuildTools = lists:usort([ Tool || {Filename, Tool} <- ?BUILD_TOOL_FILES, lists:member(Filename, BaseFiles) ]), Metadata#{<<"build_tools">> => BuildTools}; guess_build_tools(Metadata) -> Metadata. %%==================================================================== %% Tar Helpers %%==================================================================== %% @private unpack_tarball(Source, memory, MaxSize) -> case mix_hex_erl_tar:extract(Source, [memory, compressed, {max_size, MaxSize}]) of {error, too_big} -> {error, {tarball, {too_big_uncompressed, MaxSize}}}; Other -> Other end; unpack_tarball(Source, Output, MaxSize) -> filelib:ensure_dir(filename:join(Output, "*")), case mix_hex_erl_tar:extract(Source, [{cwd, Output}, compressed, {max_size, MaxSize}]) of ok -> [ try_updating_mtime(filename:join(Output, Path)) || Path <- filelib:wildcard("**", Output) ], ok; {error, too_big} -> {error, {tarball, {too_big_uncompressed, MaxSize}}}; Other -> Other end. %% @private %% let it silently fail for bad symlinks try_updating_mtime(Path) -> Time = calendar:universal_time(), _ = file:write_file_info(Path, #file_info{mtime = Time}, [{time, universal}]), ok. %% @private create_memory_tarball(Files) -> Path = tmp_path(), {ok, Tar} = mix_hex_erl_tar:open(Path, [write]), try add_files(Tar, Files) after ok = mix_hex_erl_tar:close(Tar) end, {ok, Tarball} = file:read_file(Path), ok = file:delete(Path), Tarball. %% @private tmp_path() -> "tmp_" ++ binary_to_list(encode_base16(crypto:strong_rand_bytes(32))). %% @private add_files(Tar, Files) when is_list(Files) -> lists:map(fun(File) -> add_file(Tar, File) end, Files). %% @private add_file(Tar, {Filename, Contents}) when is_list(Filename) and is_binary(Contents) -> ok = mix_hex_erl_tar:add(Tar, Contents, Filename, tar_opts()); add_file(Tar, Filename) when is_list(Filename) -> add_file(Tar, {Filename, Filename}); add_file(Tar, {Filename, AbsFilename}) when is_list(Filename), is_list(AbsFilename) -> {ok, FileInfo} = file:read_link_info(AbsFilename, []), case FileInfo#file_info.type of symlink -> ok = mix_hex_erl_tar:add(Tar, {Filename, AbsFilename}, tar_opts()); directory -> case file:list_dir(AbsFilename) of {ok, []} -> mix_hex_erl_tar:add(Tar, {Filename, AbsFilename}, tar_opts()); {ok, _} -> ok end; _ -> Mode = FileInfo#file_info.mode, {ok, Contents} = file:read_file(AbsFilename), ok = mix_hex_erl_tar:add(Tar, Contents, Filename, [{mode, Mode} | tar_opts()]) end. %% @private tar_opts() -> NixEpoch = calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}), Y2kEpoch = calendar:datetime_to_gregorian_seconds({{2000, 1, 1}, {0, 0, 0}}), Epoch = Y2kEpoch - NixEpoch, [{atime, Epoch}, {mtime, Epoch}, {ctime, Epoch}, {uid, 0}, {gid, 0}]. %% @private %% Reproducible gzip by not setting mtime and OS %% %% From https://tools.ietf.org/html/rfc1952 %% %% +---+---+---+---+---+---+---+---+---+---+ %% |ID1|ID2|CM |FLG| MTIME |XFL|OS | (more-->) %% +---+---+---+---+---+---+---+---+---+---+ %% %% +=======================+ %% |...compressed blocks...| (more-->) %% +=======================+ %% %% +---+---+---+---+---+---+---+---+ %% | CRC32 | ISIZE | %% +---+---+---+---+---+---+---+---+ gzip(Uncompressed) -> Compressed = gzip_no_header(Uncompressed), Header = <<31, 139, 8, 0, 0, 0, 0, 0, 0, 0>>, Crc = erlang:crc32(Uncompressed), Size = byte_size(Uncompressed), Trailer = <>, iolist_to_binary([Header, Compressed, Trailer]). %% @private gzip_no_header(Uncompressed) -> Zstream = zlib:open(), try zlib:deflateInit(Zstream, default, deflated, -15, 8, default), Compressed = zlib:deflate(Zstream, Uncompressed, finish), zlib:deflateEnd(Zstream), iolist_to_binary(Compressed) after zlib:close(Zstream) end. %%==================================================================== %% Helpers %%==================================================================== %% @private valid_size(Binary, infinity) when is_binary(Binary) -> true; valid_size(Binary, Limit) when is_binary(Binary) and is_integer(Limit) -> byte_size(Binary) =< Limit. %% @private valid_file_size(_Path, infinity) -> true; valid_file_size(Path, Limit) when is_integer(Limit) -> case file:read_file_info(Path) of {ok, #file_info{size = Size}} -> Size =< Limit; {error, _} -> false end. %% @private file_checksum(Path) -> {ok, Fd} = file:open(Path, [read, raw, binary]), try file_checksum_loop(Fd, crypto:hash_init(sha256)) after file:close(Fd) end. %% @private file_checksum_loop(Fd, HashState) -> case file:read(Fd, ?HASH_CHUNK_SIZE) of {ok, Data} -> file_checksum_loop(Fd, crypto:hash_update(HashState, Data)); eof -> crypto:hash_final(HashState) end. %% @private stream_file_hash(HashState, Path) -> {ok, Fd} = file:open(Path, [read, raw, binary]), try stream_file_hash_loop(Fd, HashState) after file:close(Fd) end. %% @private stream_file_hash_loop(Fd, HashState) -> case file:read(Fd, ?HASH_CHUNK_SIZE) of {ok, Data} -> stream_file_hash_loop(Fd, crypto:hash_update(HashState, Data)); eof -> HashState end. %% @private %% Reads outer tar files from a directory after extraction. %% Small files (VERSION, CHECKSUM, metadata.config) are read into memory. %% contents.tar.gz is referenced by path. read_outer_files(Dir) -> RequiredFiles = ["VERSION", "CHECKSUM", "metadata.config", "contents.tar.gz"], case read_outer_files(Dir, RequiredFiles, #{}) of {ok, Files} -> validate_outer_file_sizes(Files); {error, _} = Error -> Error end. read_outer_files(_Dir, [], Acc) -> {ok, Acc}; read_outer_files(Dir, ["contents.tar.gz" | Rest], Acc) -> Path = filename:join(Dir, "contents.tar.gz"), case filelib:is_regular(Path) of true -> read_outer_files(Dir, Rest, Acc#{"contents.tar.gz" => {path, Path}}); false -> {error, {tarball, {missing_files, ["contents.tar.gz"]}}} end; read_outer_files(Dir, [Name | Rest], Acc) -> Path = filename:join(Dir, Name), case file:read_file(Path) of {ok, Data} -> read_outer_files(Dir, Rest, Acc#{Name => Data}); {error, _} -> {error, {tarball, {missing_files, [Name]}}} end. %% @private validate_outer_file_sizes(Files) -> case byte_size(maps:get("VERSION", Files, <<>>)) > ?MAX_VERSION_SIZE of true -> {error, {tarball, {file_too_big, "VERSION"}}}; false -> case byte_size(maps:get("CHECKSUM", Files, <<>>)) > ?MAX_CHECKSUM_SIZE of true -> {error, {tarball, {file_too_big, "CHECKSUM"}}}; false -> case byte_size(maps:get("metadata.config", Files, <<>>)) > ?MAX_METADATA_SIZE of true -> {error, {tarball, {file_too_big, "metadata.config"}}}; false -> {ok, Files} end end end. %% @private remove_dir(Dir) -> case file:list_dir(Dir) of {ok, Entries} -> lists:foreach( fun(Entry) -> Path = filename:join(Dir, Entry), case filelib:is_dir(Path) of true -> remove_dir(Path); false -> file:delete(Path) end end, Entries ), file:del_dir(Dir); {error, _} -> ok end. %% @private binarify(Binary) when is_binary(Binary) -> Binary; binarify(Number) when is_number(Number) -> Number; binarify(Atom) when Atom == undefined orelse is_boolean(Atom) -> Atom; binarify(Atom) when is_atom(Atom) -> atom_to_binary(Atom, utf8); binarify(List) when is_list(List) -> [binarify(E) || E <- List]; binarify({Key, Value}) -> {binarify(Key), binarify(Value)}; binarify(Map) when is_map(Map) -> List = maps:to_list(Map), lists:map(fun({K, V}) -> binarify({K, V}) end, List). %% @private diff_keys(Map, RequiredKeys, OptionalKeys) -> Keys = maps:keys(Map), MissingKeys = RequiredKeys -- Keys, UnknownKeys = Keys -- (RequiredKeys ++ OptionalKeys), case {MissingKeys, UnknownKeys} of {[], []} -> ok; % Server should validate this but clients should not % {_, [_ | _]} -> % {error, {unknown_keys, UnknownKeys}}; _ -> {error, {missing_keys, MissingKeys}} end. %% @private maybe_update_with(Key, Fun, Map) -> case maps:find(Key, Map) of {ok, Value} -> maps:put(Key, Fun(Value), Map); error -> Map end. %% @private try_into_map(List) -> try_into_map(fun(X) -> X end, List). %% @private try_into_map(Fun, Input) -> case has_map_shape(Input) of true -> maps:from_list(lists:map(Fun, Input)); false -> Input end. %% @private try_into_nested_map(List) -> try_into_nested_map(fun(X) -> X end, List). %% @private try_into_nested_map(Fun, Input) -> case has_map_shape(Input) of true -> maps:from_list( lists:map( fun({Key, Value}) -> Fun({Key, try_into_nested_map(Fun, Value)}) end, Input ) ); false -> Input end. %% @private has_map_shape(Input) -> is_list(Input) andalso lists:all(fun(E) -> is_tuple(E) andalso (tuple_size(E) == 2) end, Input). %% @private encode_base16(Binary) -> <> = Binary, String = string:to_upper(lists:flatten(io_lib:format("~64.16.0b", [X]))), list_to_binary(String). %% Based on https://github.com/goj/base16/blob/master/src/base16.erl %% (C) 2012, Erlang Solutions Ltd. %% @private decode_base16(Base16) -> <<<<(unhex(H) bsl 4 + unhex(L))>> || <> <= Base16>>. %% @private unhex(D) when $0 =< D andalso D =< $9 -> D - $0; unhex(D) when $a =< D andalso D =< $f -> 10 + D - $a; unhex(D) when $A =< D andalso D =< $F -> 10 + D - $A. hex-2.4.2/src/mix_safe_erl_term.xrl000066400000000000000000000043011517471540100173100ustar00rootroot00000000000000%% Vendored from hex_core v0.15.0 (90f9f59), do not edit manually %%% Author : Robert Virding %%% Purpose : Token definitions for Erlang. Definitions. D = [0-9] U = [A-Z] L = [a-z] A = ({U}|{L}|{D}|_|@) WS = ([\000-\s]) Rules. {L}{A}* : tokenize_atom(TokenChars, TokenLine). '(\\\^.|\\.|[^'])*' : tokenize_atom(escape(unquote(TokenChars, TokenLen)), TokenLine). "(\\\^.|\\.|[^"])*" : {token, {string, TokenLine, escape(unquote(TokenChars, TokenLen))}}. {D}+ : {token, {integer, TokenLine, list_to_integer(TokenChars)}}. [\#\[\]}{,+-] : {token, {list_to_atom(TokenChars), TokenLine}}. (<<|>>|=>) : {token, {list_to_atom(TokenChars), TokenLine}}. \. : {token, {dot, TokenLine}}. / : {token, {'/', TokenLine}}. {WS}+ : skip_token. Erlang code. -export([terms/1]). terms(Tokens) -> terms(Tokens, []). terms([{dot, _} = H], Buffer) -> [buffer_to_term([H|Buffer])]; terms([{dot, _} = H|T], Buffer) -> [buffer_to_term([H|Buffer])|terms(T, [])]; terms([H|T], Buffer) -> terms(T, [H|Buffer]). buffer_to_term(Buffer) -> {ok, Term} = erl_parse:parse_term(lists:reverse(Buffer)), Term. unquote(TokenChars, TokenLen) -> lists:sublist(TokenChars, 2, TokenLen - 2). tokenize_atom(TokenChars, TokenLine) -> try list_to_existing_atom(TokenChars) of Atom -> {token, {atom, TokenLine, Atom}} catch error:badarg -> {error, "illegal atom " ++ TokenChars} end. escape([$\\|Cs]) -> do_escape(Cs); escape([C|Cs]) -> [C|escape(Cs)]; escape([]) -> []. do_escape([O1,O2,O3|S]) when O1 >= $0, O1 =< $7, O2 >= $0, O2 =< $7, O3 >= $0, O3 =< $7 -> [(O1*8 + O2)*8 + O3 - 73*$0|escape(S)]; do_escape([$^,C|Cs]) -> [C band 31|escape(Cs)]; do_escape([C|Cs]) when C >= $\000, C =< $\s -> escape(Cs); do_escape([C|Cs]) -> [escape_char(C)|escape(Cs)]. escape_char($n) -> $\n; %\n = LF escape_char($r) -> $\r; %\r = CR escape_char($t) -> $\t; %\t = TAB escape_char($v) -> $\v; %\v = VT escape_char($b) -> $\b; %\b = BS escape_char($f) -> $\f; %\f = FF escape_char($e) -> $\e; %\e = ESC escape_char($s) -> $\s; %\s = SPC escape_char($d) -> $\d; %\d = DEL escape_char(C) -> C. hex-2.4.2/test/000077500000000000000000000000001517471540100132675ustar00rootroot00000000000000hex-2.4.2/test/fixtures/000077500000000000000000000000001517471540100151405ustar00rootroot00000000000000hex-2.4.2/test/fixtures/certs/000077500000000000000000000000001517471540100162605ustar00rootroot00000000000000hex-2.4.2/test/fixtures/certs/amazon.der000066400000000000000000000024311517471540100202410ustar00rootroot0000000000000000N/* &O0  *H 01 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1;09U 2Terms of use at https://www.verisign.com/rpa (c)101/0-U&VeriSign Class 3 Secure Server CA - G30 150327000000Z 151027235959Z0j1 0 UUS10U Washington10USeattle10U Amazon.com, Inc.10Us3.amazonaws.com0"0  *H 0 ַx`iL17ue(spUWckBE䨔Շ# dP>./[G#0l=E1['ΝY_3eY1?sTVBv\Jԓ1ɈY˻E@M*U@7ۻ`yh9cB6OuFEx٪6@~ Nlǎa|ԶNaOx<{XHn!-%'* -ګnB$Mi0e0U0s3.amazonaws.com0 U00U0U%0++0eU ^0\0Z `HE60L0#+https://d.symcb.com/cps0%+0https://d.symcb.com/rpa0U#0 D\SD~ %cؾy0+U$0"0 http://sd.symcb.com/sd.crl0W+K0I0+0http://sd.symcd.com0&+0http://sd.symcb.com/sd.crt0  *H 0n}WM>K8ʔ ;] Sf|( B1\cEN4U}^ifugE&H@t&"@簞 `^%AV P1J4c#/w*_NLzX(sq &6l:6 ː]œ%Ql͙|{*xE6˺15mz=~s;,K(HE!v!6#:c0a0Un6sS,V8LNQ0U#0n6sS,V8LNQ0U00U0  *H  D(YG+'(A/=UpD >c,[wqb mҙJGЩ0-ӻy2fGf$'q>ũlzq]Zߋ1V5UK;f$TYn0u'MU;5 tcT)>Xe  ʩmqNKߤKK_r-'/p$ZCbS78TCf YT\p=?^rVyZ]z֋!,sozJ4i3XX?bg84s4l5yTfMC ["h] k;iy g\C-1f' ]^֪x:9k-5ͯmha*z,wFsZH y}jNhjA;4;zfyYgw`JO0g'ol֯u7nNة<t齕7"1/7FV[Rӧa&hex-2.4.2/test/fixtures/certs/ca.cert.pem000066400000000000000000000035431517471540100203070ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIFRjCCAy6gAwIBAgIJAOoztPT1GhWyMA0GCSqGSIb3DQEBCwUAMDAxCzAJBgNV BAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5kMQ8wDQYDVQQKDAZUZXN0Q0EwHhcNMTUw NzE2MDg1MTI1WhcNMzUwNzExMDg1MTI1WjAwMQswCQYDVQQGEwJHQjEQMA4GA1UE CAwHRW5nbGFuZDEPMA0GA1UECgwGVGVzdENBMIICIjANBgkqhkiG9w0BAQEFAAOC Ag8AMIICCgKCAgEA4MdTOopb6msWPEp3ijoDFqgMWB4Pft6uE/p0URIyo4eofpfo jVIYDQ8w26YnWe6KLQPAAGzC+JDNe6ulZsbeTCC1HmOjrogwGy9hotLOoIMcBnFZ FpfYeY3Q6mCpwzVsfTxRkPUfGF3RG/4HbeCKuA1D4r9uDVoj00asK6sOR+jHZ7Qx wPA8LZH8VpH5DgZonudBQrS4ufgdgLsNedwmRMHsoBvq1DxjsBvpZ1KHaR5ldaaq y9W/FYXdkB+TXETaHzjc4mc1tbN0V5EjM14JNTQSQFRr4Bmfy8z1DeJ8xEe98fGX p5ko5TXKUfq8vQ5GIAxxvq40x1LwfXLpe5f0scdet9oBWb1knRFbv3Lz4J70VNyE uXGWMMyRvnmKELzIV2XFSVeeLbNGixGpJFKmz6S7F7Ij2XCNx35NE6qafxbZKhdr Q2SJcvL1GI5KR+QMWbhrmBhHq2Lvu7vJczPZSbzBkfmDyFH5hW9A/oUBnvmBNX+S 5lo8tGYWotN1wJnZqVaaMO2Ji6mvpnadspRuISFKmy4Fq76lhzGM0bY+elgoc4iC cQ3bJjasbDq7qjb5nYQJ3cuQCOwDXfbFkyVRbNjNmXzMe5UqvuF49EU2AuroAMu6 MTVtep89fnOA6OmpEzsPutHBLOpLlCgXSEWQlLAeniF2BSE27SPqhh46s4sCAwEA AaNjMGEwHQYDVR0OBBYEFLNuNnOQH1OJj5osVvbzOEzaTp9RMB8GA1UdIwQYMBaA FLNuNnOQH1OJj5osVvbzOEzaTp9RMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDgROsoWRLPRysnmCjiQekvPVW3/XBE Cj6cY4As8VsHsHdxYg1tp4btxO3Sma+USkcbDtCpMMCtoi2GngXTu4Z5z+qKAjJm R5oI/6JmJP3mk8InCOZxEgSKPsWp+Gx6DsFxoNb7hY/wvV1a34sfEvaaEYAxhvre 1gZ/BlY1VQ5LO55mjyRUjFlutDB1J6xNrlU7NeCy+gt0YxasVLSqlinAPqlY1mUe 7McKFe/Bvwz/f/8fyqltcU7rS/X936SQ4gVLj+/kS4lfcpMcLScvpfwecNskWrcH gkNiqvtTwDc46B2FVLxDZoCZ9Q0PkFlU51wbHPrxcJf7PYYCP170cqxWE8B5Wl2l hR2letaL5iEst7XowHMa0W+MzHqDG0o0aR7bM39Y0lg/+qy4EmKP1P1npbMaOLXA j7M0c5QQBDSSbL6KNdt5jc9U2Wbp5rBNmvqXskMKklsiaF3DzuwNyN1rO83uaZ15 vyDGGWdcQ9stMZtmJ4AKXYOhXtaqeDo5a83L/KQtrBY1za9ttopovuya6uxhKq70 eqyP6yzXAqnIyPF3RnNa20ggeX2+auT38k4eaGpB8e0GGs2+AjQ7hXpmea1ZZ3eu YErITzCMZ5rvFb4n829s1q8dgXU3n5Vun0622Km5PB+uu9vcdOm9lTfSIjEvHzeO RqxWW1LTp2EmBQ== -----END CERTIFICATE----- hex-2.4.2/test/fixtures/certs/server.der000066400000000000000000000020261517471540100202620ustar00rootroot00000000000000000  *H  001 0 UGB10U England10 U TestCA0 150716085502Z 160725085502Z011 0 UGB10U England10U foo.com0\0  *H K0HA\<̀BzVUYj h1FZpĨh4"C=mV 00 U00 `HB@03 `HB &$OpenSSL Generated Server Certificate0UGt^z|.3WZp p?0`U#Y0Wn6sS,V8LNQ42001 0 UGB10U England10 U TestCA 30U0U% 0 +0  *H  02 vb'bٔWwՂ@n )'#_(-d>"_X;I f{4HOcRi8#@Bȭf4SĨ|o@ 'o|68;mh#dpmq fgN( wHCb6l`cITKb_.6?]r N/6` hB\9 J\. [tW4f0 ڏ8*Cf.F#R+5լ԰u կbN'A|J3`fGP_ )(3u\ABquV1G|Uܫd)P!][Մ 7Qt1rx ̼݋$GyݧE1c%:6C*zjP'/mҦ acMdkḱ+NԪv=F [ lznm9{޾F-P2gwI"KDEIRȃ"Y62hex-2.4.2/test/fixtures/certs/verisign_g3.der000066400000000000000000000027601517471540100212000ustar00rootroot0000000000000000Ԡnz μRԑ0  *H 01 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1:08U 1(c) 2006 VeriSign, Inc. - For authorized use only1E0CU9¬VeDOMo JԯNh.{44[I{bHtшxlSC&WX3;3 N$}dtJ4  K;c- ʶ=7H5)PxEcAO{qP(S,#= 3.0.0"}] end end hex-2.4.2/test/fixtures/ecto_override/000077500000000000000000000000001517471540100177715ustar00rootroot00000000000000hex-2.4.2/test/fixtures/ecto_override/mix.exs000066400000000000000000000003151517471540100213060ustar00rootroot00000000000000defmodule EctoOverride.Fixture.MixProject do use Mix.Project def project do [app: :ecto, version: "0.2.1", deps: deps()] end defp deps do [{:postgrex, "0.2.0", override: true}] end end hex-2.4.2/test/fixtures/ecto_sql_3_3_2/000077500000000000000000000000001517471540100176365ustar00rootroot00000000000000hex-2.4.2/test/fixtures/ecto_sql_3_3_2/mix.exs000066400000000000000000000003021517471540100211470ustar00rootroot00000000000000defmodule Ecto.SQL_3_3_2.Fixture.MixProject do use Mix.Project def project do [app: :ecto_sql, version: "3.3.2", deps: deps()] end defp deps do [{:ecto, "~> 3.3.1"}] end end hex-2.4.2/test/fixtures/ecto_sql_3_3_3/000077500000000000000000000000001517471540100176375ustar00rootroot00000000000000hex-2.4.2/test/fixtures/ecto_sql_3_3_3/mix.exs000066400000000000000000000003021517471540100211500ustar00rootroot00000000000000defmodule Ecto.SQL_3_3_2.Fixture.MixProject do use Mix.Project def project do [app: :ecto_sql, version: "3.3.3", deps: deps()] end defp deps do [{:ecto, "~> 3.3.2"}] end end hex-2.4.2/test/fixtures/ex_doc/000077500000000000000000000000001517471540100164015ustar00rootroot00000000000000hex-2.4.2/test/fixtures/ex_doc/mix.exs000066400000000000000000000001711517471540100177160ustar00rootroot00000000000000defmodule ExDoc.Fixture.MixProject do use Mix.Project def project do [app: :ex_doc, version: "0.0.1"] end end hex-2.4.2/test/fixtures/has_hex_dep/000077500000000000000000000000001517471540100174075ustar00rootroot00000000000000hex-2.4.2/test/fixtures/has_hex_dep/mix.exs000066400000000000000000000002761517471540100207320ustar00rootroot00000000000000defmodule HasHexDep.Fixture.MixProject do use Mix.Project def project do [app: :has_hex_dep, version: "0.0.1", deps: deps()] end defp deps do [{:ecto, "~> 0.1"}] end end hex-2.4.2/test/fixtures/override_with_path/000077500000000000000000000000001517471540100210265ustar00rootroot00000000000000hex-2.4.2/test/fixtures/override_with_path/mix.exs000066400000000000000000000003561517471540100223500ustar00rootroot00000000000000defmodule OverrideWithPath.Fixture.MixProject do use Mix.Project def project do [ app: :override_with_git, version: "0.1.0", deps: [{:postgrex, []}, {:ex_doc, path: "../ex_doc", override: true}] ] end end hex-2.4.2/test/fixtures/registries/000077500000000000000000000000001517471540100173205ustar00rootroot00000000000000hex-2.4.2/test/fixtures/registries/20200917.ets000066400000000000000000006727201517471540100207570ustar00rootroot00000000000000cXM bWLAhhdidZd nonode@nohost6bchddecentralized_countersdfalsehdread_concurrencydfalsehdwrite_concurrencydfalsehd compresseddfalsehdmemoryb24hdownerXd nonode@nohosthdheirdnonehdnamedElixir.Hex.Devhdsizebehdnoded nonode@nohosthd named_tabledfalsehdtypedsethdkeyposahd protectiond protectedhd major_versionahd minor_versionahd extended_infojbbWLAhhdinner_checksummhexpmmphoenix_paramsm0.2.2m 8n]ͨ րB0|z)-vgU탻MGbWLAhhd timestampmhexpmmphoenixm1.4.17hhba aha a9a0FbWLAhhd timestampmhexpmmphoenixm1.0.5hhba aha a9a0cbWLAhhddepsmhexpmmplugm1.1.1lhmhexpmmcowboymcowboym~> 1.0dtruej5bWLAhhdretiredmhexpmmphoenixm1.0.3dnil3bWLAhhdretiredmhexpmmplugm0.14.0dnilYbWLAhhdinner_checksummhexpmmplugm1.10.0m e)\TE&7䨃4vHuM6bWLAhhdretiredmhexpmmpostgrexm0.8.2dnilYbWLAhhdouter_checksummhexpmmplugm0.12.1m 4+`%`bȩ@L)^DF2bWLAhhdretiredmhexpmmmimem1.0.0dnilHbWLAhhd timestampmhexpmmpostgrexm0.13.3hhba aha a9a0ibWLAhhddepsmhexpmm phoenix_htmlm2.11.2lhmhexpmmplugmplugm~> 1.5dfalsejKbWLAhhddepsmhexpmmphoenixm1.2.3lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm6~> 1.4 or ~> 1.3.3 or ~> 1.2.4 or ~> 1.1.8 or ~> 1.0.5dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsejdbWLAhhdouter_checksummhexpmm phoenix_htmlm 2.0.0-devm 0WyʀZPlϠG!ȪHbWLAhhd timestampmhexpmmplugm 1.2.0-rc.0hhba aha a9a0[bWLAhhdinner_checksummhexpmmphoenixm0.1.0m Hyr M{=w-[bWLAhhdinner_checksummhexpmmphoenixm1.0.0m QyՉy ecoN*5bWLAhhdretiredmhexpmmphoenixm1.1.8dnilbWLAhhddepsmhexpmmphoenixm0.14.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsej9bWLAhhdretiredmhexpmm plug_cryptom1.1.2dnilbWLAhhddepsmhexpmmplugm1.9.0lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0dfalsehmhexpmm telemetrym telemetrym~> 0.4dtruejFbWLAhhd timestampmhexpmmdecimalm0.1.1hhba aha a9a0ebWLAhhddepsmhexpmmplugm0.6.0lhmhexpmmcowboymcowboym~> 1.0.0dtruej]bWLAhhdouter_checksummhexpmmpostgrexm0.15.1m A {ׇ lX3}dbY=[bWLAhhdinner_checksummhexpmmdecimalm1.4.0m eqjSӦEf(Ze.J3MbWLAhhd timestampmhexpmmphoenix_paramsm1.1.2hhba aha a9a0>bWLAhhdretiredmhexpmm phoenix_htmlm 2.0.0-devdnilbWLAhhddepsmhexpmmpostgrexm0.15.5lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 2.1dfalsehmhexpmmdecimalmdecimalm~> 1.5dfalsehmhexpmmjasonmjasonm~> 1.0dtruejbbWLAhhdouter_checksummhexpmmphoenix_paramsm1.1.1m Ym{DG:.oٙ=壈 `bWLAhhdouter_checksummhexpmm phoenix_htmlm2.0.1m Sf&]rJsu7 eUyB7~FbWLAhhd timestampmhexpmmphoenixm1.1.2hhba aha a9a04bWLAhhddepsmhexpmmphoenix_pubsubm1.1.1jXbWLAhhdouter_checksummhexpmmplugm0.4.1m Hd,dÎY7qrwܩ:LbWLAhhd timestampmhexpmm db_connectionm0.1.6hhba aha a9a05bWLAhhdretiredmhexpmmphoenixm1.4.4dnilDbWLAhhd timestampmhexpmmplugm0.11.3hhba aha a9a0\bWLAhhdinner_checksummhexpmmpostgrexm0.5.2m b_Do`7DND+@=T[bWLAhhdinner_checksummhexpmmphoenixm1.4.9m tm t3M=<ʱud5C4Ay.ftbWLAhhddepsmhexpmmplugm1.6.1lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4dtruehmhexpmmmimemmimem~> 1.0dfalsejYbWLAhhdouter_checksummhexpmmjasonm1.2.1m YW`Z`A@DqoiYbWLAhhdinner_checksummhexpmmplugm1.10.2m y4\}FTTŇd8\wZabWLAhhdinner_checksummhexpmm db_connectionm2.0.3m Co,gQČy99+rabWLAhhdouter_checksummhexpmm phoenix_htmlm2.12.0m y /DGP)%kmn^cbWLAhhddepsmhexpmmplugm1.1.5lhmhexpmmcowboymcowboym~> 1.0dtruejXbWLAhhdouter_checksummhexpmmplugm1.3.2m jhZ84 cM^~LbWLAhhd timestampmhexpmm phoenix_htmlm2.13.0hhba aha a9a0\bWLAhhdouter_checksummhexpmmphoenixm0.13.1m Z. MeC9fԮZB[bWLAhhdouter_checksummhexpmmphoenixm1.5.4m NQmއh.}^0\۳5QbWLAhhd timestampmhexpmm db_connectionm 1.0.0-rc.3hhba aha a9a0]bWLAhhdouter_checksummhexpmmpostgrexm0.15.5m %Z+'03(ڢaCabWLAhhdouter_checksummhexpmm db_connectionm0.1.8m ԋj<@kU- 1&B[[bWLAhhdinner_checksummhexpmmphoenixm1.2.3m ֧>8YwDXIH]bWLAhhdouter_checksummhexpmmpostgrexm0.14.2m 6ĬA'S6ΡGCեG,I7mv_bWLAhhdinner_checksummhexpmm plug_cowboym1.0.0m .*}4 tm3_EX0=h' nz 1.0.0dtruej\bWLAhhdouter_checksummhexpmmphoenixm0.2.10m eXNil ?-_0qA $Q"[bWLAhhdouter_checksummhexpmmphoenixm0.2.7m :=ќxלFaG5=Z;@vB9ϕ[bWLAhhdinner_checksummhexpmmphoenixm1.2.5m Z_"Y^#*evH#cbWLAhhddepsmhexpmmplugm1.0.2lhmhexpmmcowboymcowboym~> 1.0dtruej[bWLAhhdinner_checksummhexpmmdecimalm1.8.0m F. _ ţB~5HZes4.]bWLAhhdouter_checksummhexpmmpostgrexm0.15.3m G7bG< b0~QOs ,( `l\bWLAhhdinner_checksummhexpmmphoenixm0.13.0m FN6"K]m/6Mݛ%bbWLAhhdinner_checksummhexpmmphoenix_pubsubm2.0.0m vq{h읒H֍G9_GbWLAhhd timestampmhexpmmphoenixm0.16.1hhba aha a9a0bbWLAhhdinner_checksummhexpmmphoenix_pubsubm1.0.0m "FY'x@ʇa A ,W2bWLAhhdretiredmhexpmmplugm1.4.0dnilQbWLAhhd timestampmhexpmm db_connectionm 2.0.0-rc.0hhba aha a9a0KbWLAhhd timestampmhexpmm phoenix_htmlm2.4.0hhba aha a9a05bWLAhhdretiredmhexpmmphoenixm1.5.2dnilJbWLAhhd timestampmhexpmm plug_cowboym2.1.0hhba aha a9a05bWLAhhdretiredmhexpmmphoenixm1.3.0dnilFbWLAhhd timestampmhexpmmphoenixm1.5.4hhba aha a9a0[bWLAhhdinner_checksummhexpmmphoenixm1.5.1m eƒǐ an)4u]bWLAhhdouter_checksummhexpmmpostgrexm0.11.0m mD}$ֈ j*mJ~\Cg_CbWLAhhd timestampmhexpmmplugm0.4.3hhba aha a9a0"bWLAhhddepsmhexpmmphoenixm1.4.6lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruej[bWLAhhdinner_checksummhexpmmphoenixm0.6.1m 0Y 9cQ#hư(* q5bWLAhhdretiredmhexpmmphoenixm1.2.1dnilabWLAhhdouter_checksummhexpmm phoenix_htmlm2.11.2m /T06!lEZ n'`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.1.1m *Zbu6n4"0A(uTW70`bWLAhhdouter_checksummhexpmmphoenixm 1.3.0-rc.3m >tm~Ļsb`&x ew1bWLAhhddepsmhexpmm plug_cryptom1.1.0jbWLAhhddepsmhexpmmpostgrexm0.14.0lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 2.0dfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsehmhexpmmjasonmjasonm~> 1.0dtruejabWLAhhdinner_checksummhexpmm db_connectionm1.1.2m (e¤qN"`-v QYɫǨFbWLAhhd timestampmhexpmmdecimalm1.9.0hhba aha a9a0jbWLAhhddepsmhexpmmphoenixm1.4.16lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsejXbWLAhhdouter_checksummhexpmmmimem1.0.0m ~gG틓\Tzy,OLm}?\FbWLAhhd timestampmhexpmmphoenixm1.0.0hhba aha a9a0JbWLAhhd timestampmhexpmm plug_cowboym2.2.2hhba aha a9a0XbWLAhhdouter_checksummhexpmmplugm1.3.6m ?/ ߶? V.\od/uR-}XbWLAhhdinner_checksummhexpmmplugm1.1.9m r!gкgfk8邗#3(7$s5bWLAhhdretiredmhexpmmdecimalm1.3.0dnilQbWLAhhd timestampmhexpmm db_connectionm 1.0.0-rc.5hhba aha a9a0[bWLAhhdouter_checksummhexpmmphoenixm0.7.2m leuevoLGgұzo`bWLAhhdouter_checksummhexpmmphoenixm 1.5.0-rc.0m 7}ʋNEBt_y e%gf[bWLAhhdinner_checksummhexpmmphoenixm1.4.1m c(e|e|bLB#bWLAhhddepsmhexpmm db_connectionm0.2.0lhmhexpmmbackoffmbackoffm1.1.1dfalsehmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 0.7dtruej\bWLAhhdouter_checksummhexpmmpostgrexm0.8.0m (Ӂnz(i$Te&HHbWLAhhd timestampmhexpmmplugm 1.5.0-rc.0hhba aha a9a0lbWLAhhddepsmhexpmmpostgrexm0.5.4lhmhexpmmdecimalmdecimalm~> 0.2.3dfalsejbbWLAhhdinner_checksummhexpmmphoenix_paramsm0.4.1m YFG=1}7ӷtXHS@bWLAhhdretiredmhexpmm db_connectionm 1.0.0-rc.2dnil8bWLAhhdretiredmhexpmmjasonm 1.0.0-rc.3dnilCbWLAhhd timestampmhexpmmplugm1.3.2hhba aha a9a07bWLAhhdretiredmhexpmmpostgrexm0.13.1dnilLbWLAhhd timestampmhexpmm db_connectionm0.2.5hhba aha a9a0:bWLAhhdretiredmhexpmm phoenix_htmlm1.3.0dnilbWLAhhddepsmhexpmmphoenixm0.15.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm>= 0.13.1 and < 2.0.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejGbWLAhhd timestampmhexpmmpostgrexm0.8.1hhba aha a9a0=bWLAhhd timestampmhexpmmpostgrexhhba aha a9a04bWLAhhddepsmhexpmmphoenix_pubsubm1.1.0jbWLAhhddepsmhexpmmphoenixm0.2.10lhmhexpmmex_confmex_confm0.1.2dfalsehmhexpmminflexminflexm0.2.3dfalsehmhexpmmjazzmjazzm0.1.1dfalsehmhexpmmplugmplugm0.5.0dfalsejzbWLAhhdretiredmhexpmm plug_cowboym2.2.2tdmessagemBroken telemetry supportdreasondRETIRED_INVALIDJbWLAhhd timestampmhexpmm plug_cryptom1.1.2hhba aha a9a0abWLAhhdinner_checksummhexpmm db_connectionm0.2.0m JDM*sǰq̎j hKbWLAhhd timestampmhexpmm phoenix_htmlm2.0.0hhba aha a9a0\bWLAhhdouter_checksummhexpmmpostgrexm0.9.1m OEG4=zQsW0T%12HYZ\bWLAhhdinner_checksummhexpmmphoenixm0.10.0m ,~33gaTyV0A*IXUbWLAhhd registry_etagmhexpmm connectionm""cb8397ee262701f54669d854dd7531e6"&bWLAhd last_updatehhba aha a$aabWLAhhdouter_checksummhexpmm db_connectionm0.1.6m ZLVHcfċ /ن3KQlrbbWLAhhdouter_checksummhexpmmphoenix_paramsm0.4.1m Ue)2/Tyr;_,:bg$MIN`M/bWLAhhddepsmhexpmm telemetrym0.4.2jYbWLAhhdinner_checksummhexpmmplugm0.14.0m KN&ïF9VgD|Az[FbWLAhhd timestampmhexpmmphoenixm0.4.0hhba aha a9a0YbWLAhhdouter_checksummhexpmmplugm0.12.0m LeEo448W }L<NJQbWLAhhd timestampmhexpmm db_connectionm 1.0.0-rc.4hhba aha a9a02bWLAhhddepsmhexpmmdecimalm 1.9.0-rc.0j_bWLAhhdinner_checksummhexpmm plug_cowboym2.1.1m (ۥUbuwK3DbWLAhhd timestampmhexpmmplugm1.10.4hhba aha a9a09bWLAhhdretiredmhexpmm plug_cowboym2.0.1dnilFbWLAhhd timestampmhexpmmphoenixm0.1.0hhba aha a9a0lbWLAhhddepsmhexpmmpostgrexm0.5.5lhmhexpmmdecimalmdecimalm~> 0.2.3dfalsej3bWLAhhdretiredmhexpmmjasonm1.2.2dnilabWLAhhdinner_checksummhexpmm phoenix_htmlm2.11.1m wRnsd7RX͡2f&~jbWLAhhddepsmhexpmmpostgrexm0.8.0lhmhexpmmdecimalmdecimalm~> 1.0dfalsejDbWLAhhd timestampmhexpmmjasonm1.1.1hhba aha a9a0KbWLAhhd timestampmhexpmm phoenix_htmlm2.9.0hhba aha a9a0wbWLAhhddepsmhexpmm phoenix_htmlm1.3.0lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0dfalsejFbWLAhhd timestampmhexpmmphoenixm1.3.2hhba aha a9a0abWLAhhdouter_checksummhexpmm db_connectionm1.1.0m 4F7c@~ױ )KUa| bWLAhhddepsmhexpmmphoenixm0.4.1lhmhexpmmcowboymcowboym~> 1.0.0dtruehmhexpmmlinguistmlinguistm~> 0.1.2dfalsehmhexpmmplugmplugm0.7.0dfalsehmhexpmmpoisonmpoisonm~> 1.1.0dfalsej3bWLAhhdretiredmhexpmmplugm1.10.1dnil2bWLAhhdretiredmhexpmmplugm1.3.0dnildbWLAhhddepsmhexpmmplugm0.12.2lhmhexpmmcowboymcowboym~> 1.0dtruej2bWLAhhdretiredmhexpmmplugm1.8.2dnilbbWLAhhdouter_checksummhexpmmphoenix_paramsm0.2.2m FlXGִ]7LbWLAhhd timestampmhexpmm phoenix_htmlm2.10.5hhba aha a9a0]bWLAhhdouter_checksummhexpmmpostgrexm0.13.5m a3y6=p. &~ 1.0.1 or ~> 1.1 or ~> 2.1dtruehmhexpmmmimemmimem~> 1.0dfalsej2bWLAhhdretiredmhexpmmplugm0.4.3dnil;bWLAhhdretiredmhexpmm db_connectionm0.1.5dnilKbWLAhhd timestampmhexpmmdecimalm 1.9.0-rc.0hhba aha a9a0MbWLAhhd timestampmhexpmmphoenix_paramsm1.1.0hhba aha a9a0_bWLAhhdinner_checksummhexpmm plug_cowboym2.0.0m r+ 1.0dtruehmhexpmmpoisonmpoisonm~> 1.2dfalsejYbWLAhhdouter_checksummhexpmmjasonm1.0.1m QQjL Y= hv%W4 bbWLAhhdouter_checksummhexpmmphoenix_paramsm0.3.0m \߶E.F_A \<x)5bWLAhhdretiredmhexpmmphoenixm0.4.1dnilebWLAhhddepsmhexpmmplugm0.8.0lhmhexpmmcowboymcowboym~> 1.0.0dtruejDbWLAhhd timestampmhexpmmplugm1.10.1hhba aha a9a0HbWLAhhd timestampmhexpmmpostgrexm0.15.2hhba aha a9a04bWLAhhddepsmhexpmmphoenix_pubsubm2.0.0jYbWLAhhd registry_etagmhexpmmphoenix_paramsm""389651a1f33cf6831d08446192a18010"@y dƀ^bWLAhhdouter_checksummhexpmm connectionm1.0.4m JPɾ": qQ\ @&8%IbWLAhhd timestampmhexpmmjasonm 1.0.0-rc.2hhba aha a9a0bWLAhhddepsmhexpmmphoenixm0.16.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 0.14 or ~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsej_bWLAhhdouter_checksummhexpmm plug_cryptom1.1.2m k`[oԜ7Lj>j(;["ֵ[bWLAhhdouter_checksummhexpmmdecimalm1.1.1m ?6"/+ 1.0.2dfalsej[bWLAhhdinner_checksummhexpmmphoenixm1.0.2m ] 2EmUah⽶GzC2fbWLAhhddepsmhexpmmplugm1.6.3lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4dtruehmhexpmmmimemmimem~> 1.0dfalsejXbWLAhhdinner_checksummhexpmmplugm1.2.0m IkcJI׀:gΜ෹% Y cbWLAhhddepsmhexpmmplugm1.0.1lhmhexpmmcowboymcowboym~> 1.0dtruejXbWLAhhdouter_checksummhexpmmplugm0.8.0m s1eL>:cv6-+2bWLAhhdretiredmhexpmmplugm0.7.0dnilCbWLAhhd timestampmhexpmmplugm1.0.1hhba aha a9a0bbWLAhhdouter_checksummhexpmmpostgrexm 0.14.0-rc.0m lR#E/VG{(XU7]c-bWLAhhddepsmhexpmmdecimalm1.8.0jwbWLAhhddepsmhexpmm phoenix_htmlm 2.4.0-devlhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsej2bWLAhhdretiredmhexpmmplugm1.0.3dnil5bWLAhhdretiredmhexpmmphoenixm1.0.2dnil7bWLAhhdretiredmhexpmmpostgrexm0.10.0dnilabWLAhhdouter_checksummhexpmm phoenix_htmlm2.13.3m ӗ1TWm%u_U7%g[bWLAhhdinner_checksummhexpmmphoenixm0.7.1m }x3V%1,TY9 L~*;bWLAhhdretiredmhexpmm phoenix_htmlm2.13.0dnil5bWLAhhdretiredmhexpmmphoenixm1.1.1dnilXbWLAhhdouter_checksummhexpmmplugm0.6.0m ' ]R\cbWLAhhdinner_checksummhexpmm connectionm 1.0.0-rc.1m t$H' X*;֞bYyF'CbWLAhhd timestampmhexpmmplugm1.5.0hhba aha a9a0 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsej`bWLAhhdinner_checksummhexpmmphoenixm 1.2.0-rc.0m ?uF&ːIbJZ} |3ҋJbWLAhhd timestampmhexpmm plug_cryptom1.0.0hhba aha a9a0FbWLAhhd timestampmhexpmmdecimalm1.1.0hhba aha a9a0bWLAhhddepsmhexpmm db_connectionm1.1.0lhmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 1.0dtruejbWLAhhddepsmhexpmmphoenixm1.0.3lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejbbWLAhhdinner_checksummhexpmmpostgrexm 0.13.0-rc.0m cЛ $>Rir-.+f.)9;F,bWLAhhddepsmhexpmmphoenixm 1.3.0-rc.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.3.2 or ~> 1.4dfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0dfalsej6bWLAhhdretiredmhexpmmphoenixm0.13.1dnil5bWLAhhdretiredmhexpmmphoenixm1.0.5dnilfbWLAhhdouter_checksummhexpmm db_connectionm 2.0.0-rc.0m d<9 vl:0v.\ 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 1.0dtruejFbWLAhhd timestampmhexpmmphoenixm0.3.0hhba aha a9a0XbWLAhhdouter_checksummhexpmmplugm0.8.3m L(G/1hMKKzbWLAhhddepsmhexpmmphoenixm0.8.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 0.9.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejbWLAhhddepsmhexpmmplugm1.3.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsej5bWLAhhdretiredmhexpmmphoenixm0.2.4dnil`bWLAhhdinner_checksummhexpmm phoenix_htmlm1.1.0m UL-\^'u2˖ut(FbWLAhhd timestampmhexpmmphoenixm1.5.1hhba aha a9a0JbWLAhhd timestampmhexpmm plug_cowboym2.1.2hhba aha a9a0bWLAhhddepsmhexpmmphoenix_paramsm0.4.2lhmhexpmmdecimalmdecimalm>= 1.7.0dfalsehmhexpmmphoenixmphoenixm>= 1.3.3dfalsej5bWLAhhdretiredmhexpmmphoenixm1.0.6dnil2bWLAhhdretiredmhexpmmplugm1.4.4dnil]bWLAhhdouter_checksummhexpmmpostgrexm0.13.2m Y)'y6F"O'9 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruej5bWLAhhdretiredmhexpmmphoenixm0.6.1dnil5bWLAhhdretiredmhexpmmdecimalm0.1.2dnilCbWLAhhd timestampmhexpmmplugm1.6.0hhba aha a9a0_bWLAhhdouter_checksummhexpmm plug_cowboym2.1.1m O W[9DXm}rLs}@bWLAhhdretiredmhexpmm db_connectionm 2.0.0-rc.0dnilHbWLAhhd timestampmhexpmmpostgrexm0.13.5hhba aha a9a0ibWLAhhddepsmhexpmm phoenix_htmlm2.13.1lhmhexpmmplugmplugm~> 1.5dfalsej\bWLAhhdouter_checksummhexpmmpostgrexm0.5.5m =(s֚5J_һHx?nXԬ4! bWLAhhddepsmhexpmmpostgrexm0.15.0lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 2.1dfalsehmhexpmmdecimalmdecimalm~> 1.5dfalsehmhexpmmjasonmjasonm~> 1.0dtruej0bWLAhhddepsmhexpmm connectionm1.0.2jFbWLAhhd timestampmhexpmmphoenixm0.2.2hhba aha a9a0XbWLAhhdouter_checksummhexpmmplugm1.1.7m ֵePvEb}DزVA6bWLAhhdretiredmhexpmmpostgrexm0.8.1dnilbbWLAhhdinner_checksummhexpmmpostgrexm 0.14.0-rc.0m )Wec( y*F&N9HbWLAhhd timestampmhexpmm telemetrym0.1.0hhba aha a9a0MbWLAhhd timestampmhexpmmphoenix_pubsubm1.0.1hhba aha a9a0XbWLAhhdinner_checksummhexpmmmimem1.2.0m x+6 ӴQ@уֈ[ 4`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.8.0m V -*J= z%߻!KbWLAhhd timestampmhexpmm phoenix_htmlm2.6.1hhba aha a9a0]bWLAhhdouter_checksummhexpmm telemetrym0.4.0m |1p| 0-Ow?A`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.6.0m uXk_<Pׁ\}L͖{'[bWLAhhdinner_checksummhexpmmphoenixm0.7.2m HN!3rWDvQ b{wf;fbWLAhhddepsmhexpmmjasonm1.0.1lhmhexpmmdecimalmdecimalm~> 1.0dtruej5bWLAhhdretiredmhexpmmphoenixm1.3.2dnildbWLAhhdinner_checksummhexpmm phoenix_htmlm 2.0.0-devm 3{ۼ/"\4s9sHee[}(ebWLAhhddepsmhexpmmpostgrexm0.15.1lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 2.1dfalsehmhexpmmdecimalmdecimalm~> 1.5dfalsehmhexpmmjasonmjasonm~> 1.0dtruej`bWLAhhdouter_checksummhexpmmphoenixm 1.4.0-rc.2m &h (0"B}pe:8BibWLAhhddepsmhexpmmphoenixm1.4.8lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsej[bWLAhhdinner_checksummhexpmmphoenixm1.3.3m _9災Dj,y|XUfabWLAhhdouter_checksummhexpmm db_connectionm2.0.2m {YCbGv `bWLAhhdinner_checksummhexpmm phoenix_htmlm1.3.0m $lGT?iM*7Pq+_?2bWLAhhdretiredmhexpmmplugm1.2.5dnilbWLAhhddepsmhexpmm db_connectionm1.1.3lhmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 1.0dtruejfbWLAhhddepsmhexpmmjasonm1.1.0lhmhexpmmdecimalmdecimalm~> 1.0dtruej`bWLAhhdouter_checksummhexpmm phoenix_htmlm1.0.0m Eg^-"t"ޟR ʻu;ɒXw\(`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.2.0m tO$w^WGwLUcՌAKbWLAhhd timestampmhexpmm phoenix_htmlm2.5.0hhba aha a9a0\bWLAhhdouter_checksummhexpmmpostgrexm0.8.4m .X%(Xf$bWLAhhddepsmhexpmmplugm1.8.0lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0dfalsehmhexpmm telemetrym telemetrym~> 0.4dtruej[bWLAhhdouter_checksummhexpmmdecimalm0.1.2m Jޗ֊YJ AJ'm1bWLAhhddepsmhexpmm plug_cryptom1.1.2jYbWLAhhdinner_checksummhexpmmplugm0.12.2m Af+ ^@ hpܥ{$iz\bWLAhhdouter_checksummhexpmmphoenixm1.4.14m IX%G{eeq [v+XT[bWLAhhdouter_checksummhexpmmphoenixm1.4.7m S>ke5qN4'ՠ$]myFbWLAhhd timestampmhexpmmphoenixm0.2.4hhba aha a9a0MbWLAhhd timestampmhexpmmphoenix_paramsm0.1.1hhba aha a9a0bbWLAhhdinner_checksummhexpmmphoenix_paramsm0.1.1m Nl̒uJ8}]õrt,mbWLAhhddepsmhexpmmphoenixm1.5.1lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejbbWLAhhdouter_checksummhexpmmphoenix_paramsm0.1.2m 1R? ŲG)fqm!zbXbWLAhhdinner_checksummhexpmmplugm1.2.2m Ͻ!L?퍏K͛ibWLAhhddepsmhexpmm phoenix_htmlm2.14.0lhmhexpmmplugmplugm~> 1.5dfalsej/bWLAhhddepsmhexpmm telemetrym0.3.0jXbWLAhhdinner_checksummhexpmmplugm1.4.5m {sS&,^ ]$ '=[bWLAhhdinner_checksummhexpmmdecimalm0.2.5m @[x:`&'?ԀC{Xk4NêObWLAhhd timestampmhexpmm phoenix_htmlm 2.0.0-devhhba aha a9a0`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.9.2m MI?V`1= f[0g7iqKNobbWLAhhdouter_checksummhexpmmphoenix_paramsm1.1.2m ; 왐UWo*imv_bWLAhhdouter_checksummhexpmm plug_cowboym2.2.0m NrrlgzyGMR;(Ѯ\}[[bWLAhhdinner_checksummhexpmmphoenixm1.1.3m ZK`c`o^݆ p*<,azlFbWLAhhd timestampmhexpmmphoenixm1.4.2hhba aha a9a0YbWLAhhdouter_checksummhexpmmplugm0.13.0m +oĦ%W{US1o":gL_HbWLAhhd timestampmhexpmmpostgrexm0.13.1hhba aha a9a0XbWLAhhdouter_checksummhexpmmmimem1.1.0m 3 ګVh\ã8)'( _M7bWLAhhdretiredmhexpmmpostgrexm0.13.5dnilbbWLAhhdouter_checksummhexpmmphoenix_pubsubm0.1.0m ktp\5Ikz@}9N\ҡVb9)bWLAhhddepsmhexpmmphoenixm 1.4.0-rc.1lhmhexpmmcowboymcowboym~> 1.0 or ~> 2.5dtruehmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.6.4 or ~> 1.7dfalsejYbWLAhhdouter_checksummhexpmmplugm0.12.2m ncoŸ> iv,i7n|-GbWLAhhd timestampmhexpmmphoenixm0.17.0hhba aha a9a0abWLAhhdinner_checksummhexpmm phoenix_htmlm2.10.2m IclL^>5,uaB1?*[bWLAhhdinner_checksummhexpmmphoenixm1.4.8m -íIc$dQNzS5bWLAhhdretiredmhexpmmphoenixm1.1.9dnilFbWLAhhd timestampmhexpmmphoenixm0.7.0hhba aha a9a0abWLAhhdouter_checksummhexpmm db_connectionm0.1.0m rpOH<]+4o!HBr,M([bWLAhhdouter_checksummhexpmmdecimalm1.2.0m t"HD2HDM_dш[bWLAhhdinner_checksummhexpmmphoenixm0.6.0m wކM`p;y%N}퐉EwI XbWLAhhdinner_checksummhexpmmplugm1.7.1m er.t%O6N >XbWLAhhdinner_checksummhexpmmplugm1.2.3m P W8r^ @&sS3,Us[bWLAhhdinner_checksummhexpmmdecimalm0.2.3m r}$޿>4m\lZ$bWLAhhddepsmhexpmmplugm1.6.4lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4dtruehmhexpmmmimemmimem~> 1.0dfalsejbWLAhhddepsmhexpmm plug_cowboym2.2.0lhmhexpmmcowboymcowboym~> 2.7dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej5bWLAhhdretiredmhexpmmdecimalm1.8.1dnilCbWLAhhd timestampmhexpmmplugm1.4.3hhba aha a9a07bWLAhhdretiredmhexpmmplugm 1.5.0-rc.1dnil^bWLAhhdinner_checksummhexpmm connectionm1.0.1m X)Q:4B1ӟ,RBUYy첊V\ ibWLAhhddepsmhexpmm phoenix_htmlm2.10.1lhmhexpmmplugmplugm~> 1.0dfalsej_bWLAhhdinner_checksummhexpmm plug_cowboym2.1.0m Wh<:9Բ[NŎJnj' 0 ;bWLAhhdretiredmhexpmm db_connectionm2.2.0dnil]bWLAhhdinner_checksummhexpmmpostgrexm0.15.3m Xh|zbL͹~s񹀙ttj5bWLAhhdretiredmhexpmmdecimalm0.2.2dnil-bWLAhhddepsmhexpmmdecimalm1.0.1jMbWLAhhd timestampmhexpmmphoenix_paramsm1.1.1hhba aha a9a0bbWLAhhdinner_checksummhexpmmphoenix_paramsm1.0.1m S%*5`-a ,N6$kpabWLAhhdinner_checksummhexpmm phoenix_htmlm2.10.4m 2I1c| p?-MmYbWLAhhdinner_checksummhexpmmplugm0.13.0m /:.Gűw υIl4KYbWLAhhdinner_checksummhexpmmplugm0.10.0m D?K/Fr"E*ScˋdbWLAhhddepsmhexpmmplugm0.11.1lhmhexpmmcowboymcowboym~> 1.0dtruejbWLAhhddepsmhexpmmplugm1.7.0lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0dfalsejYbWLAhhdinner_checksummhexpmmjasonm1.1.0m 4/thՆS\[ WRO\bWLAhhdouter_checksummhexpmmphoenixm1.4.17m :]z=vR_n{7cԮFbWLAhhd timestampmhexpmmphoenixm1.3.0hhba aha a9a0bWLAhhddepsmhexpmmphoenix_paramsm1.0.3lhmhexpmmdecimalmdecimalm>= 1.7.0dfalsehmhexpmmphoenixmphoenixm>= 1.3.3dfalsejFbWLAhhd timestampmhexpmmphoenixm0.4.1hhba aha a9a0GbWLAhhd timestampmhexpmmphoenixm1.4.11hhba aha a9a0jbWLAhhddepsmhexpmmpostgrexm0.7.0lhmhexpmmdecimalmdecimalm~> 1.0dfalsejibWLAhhddepsmhexpmm phoenix_htmlm2.12.0lhmhexpmmplugmplugm~> 1.5dfalsejbWLAhhddepsmhexpmmplugm1.4.3lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsej`bWLAhhdinner_checksummhexpmm phoenix_htmlm1.2.0m Aگ^˸L^9sgSX9HXdbWLAhhdinner_checksummhexpmm phoenix_htmlm 2.4.0-devm 4 }hd2+6V;bWLAhhdretiredmhexpmm phoenix_htmlm2.10.1dnilXbWLAhhdouter_checksummhexpmmplugm1.7.1m ڥ ċl2 b|uUM^7bWLAhhdretiredmhexpmmplugm 1.4.0-rc.0dnilhbWLAhhddepsmhexpmm phoenix_htmlm2.8.0lhmhexpmmplugmplugm~> 1.0dfalsej6bWLAhhdretiredmhexpmmphoenixm0.11.0dnilLbWLAhhd timestampmhexpmm phoenix_htmlm2.13.2hhba aha a9a0XbWLAhhdouter_checksummhexpmmplugm1.0.4m ,翈QEfss0Lӛ[QwbWLAhhddepsmhexpmm db_connectionm2.0.4lhmhexpmm connectionm connectionm~> 1.0.2dfalsejLbWLAhhd timestampmhexpmm db_connectionm1.1.1hhba aha a9a0KbWLAhhd timestampmhexpmmphoenixm 1.3.0-rc.1hhba aha a9a0KbWLAhhd timestampmhexpmm phoenix_htmlm2.5.1hhba aha a9a0bWLAhhddepsmhexpmmplugm 1.4.0-rc.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsejMbWLAhhd timestampmhexpmmphoenix_pubsubm1.0.0hhba aha a9a0CbWLAhhd timestampmhexpmmplugm0.8.4hhba aha a9a0CbWLAhhd timestampmhexpmmplugm0.5.1hhba aha a9a0XbWLAhhdouter_checksummhexpmmplugm1.2.0m '8êBJm0ʭOh::bWLAhhdversionsmhexpmmjasonl m 1.0.0-rc.1m 1.0.0-rc.2m 1.0.0-rc.3m1.0.0m1.0.1m1.1.0m1.1.1m1.1.2m1.2.0m1.2.1m1.2.2j[bWLAhhdinner_checksummhexpmmphoenixm1.2.1m mŒ$ 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 1.1dfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsejabWLAhhdinner_checksummhexpmm db_connectionm0.2.5m >^(D4Uh*/R%qׁ ϕ_>7bWLAhhdretiredmhexpmmpostgrexm0.15.1dnilabWLAhhdouter_checksummhexpmm db_connectionm1.1.2m U^§Y2UŸfxCFchwk0f֧GbWLAhhd timestampmhexpmmphoenixm0.14.0hhba aha a9a05bWLAhhdretiredmhexpmmphoenixm1.4.7dnil[bWLAhhdouter_checksummhexpmmphoenixm1.4.5m kZJk9uhEA$sbWLAhhddepsmhexpmmplugm1.8.1lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0dfalsehmhexpmm telemetrym telemetrym~> 0.4dtruej6bWLAhhdretiredmhexpmmphoenixm1.4.15dnil-bWLAhhddepsmhexpmmdecimalm1.5.0j;bWLAhhdretiredmhexpmm db_connectionm0.2.4dnil8bWLAhhdretiredmhexpmmjasonm 1.0.0-rc.2dnil;bWLAhhd timestampmhexpmminflexhhbaaha a5abWLAhhddepsmhexpmmpostgrexm 1.0.0-rc.0lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 1.1dfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsejbWLAhhddepsmhexpmmphoenixm1.1.4lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsej[bWLAhhdouter_checksummhexpmmphoenixm0.2.1m +glXOptDt hT9ky[9{-6bWLAhhdretiredmhexpmmphoenixm1.4.14dnil7bWLAhhdretiredmhexpmmplugm 1.2.0-rc.0dnil6bWLAhhdretiredmhexpmmphoenixm0.16.0dnilabWLAhhdouter_checksummhexpmm db_connectionm2.2.0m k+ovnoj;bWLAhhdretiredmhexpmm db_connectionm2.2.2dnil[bWLAhhdinner_checksummhexpmmdecimalm2.0.0m LlWCG/By+VB֗7bWLAhhdretiredmhexpmmpostgrexm0.14.3dnilLbWLAhhd timestampmhexpmm db_connectionm2.0.4hhba aha a9a0-bWLAhhddepsmhexpmmdecimalm0.1.2jbWLAhhddepsmhexpmmplugm1.7.1lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0dfalsejXbWLAhhdinner_checksummhexpmmplugm1.0.1m N=XB.@9l t abWLAhhdinner_checksummhexpmm db_connectionm2.1.1m .Nnfx~ I((LjXbWLAhhdouter_checksummhexpmmplugm1.1.2m %XB=!UG^5EE*dT^bWLAhhdinner_checksummhexpmmjasonm 1.0.0-rc.3m RHr `D>PŕQOˣfƔ~XbWLAhhdouter_checksummhexpmmplugm1.2.5m >m?ªc`p\ѥLEuDbWLAhhd timestampmhexpmmplugm0.12.2hhba aha a9a0ibWLAhhddepsmhexpmmphoenixm1.4.7lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsejXbWLAhhdouter_checksummhexpmmmimem1.4.0m uB"?p#KjGO?jzOHbWLAhhd timestampmhexpmmpostgrexm0.15.4hhba aha a9a0:bWLAhhdretiredmhexpmm phoenix_htmlm1.2.1dnil2bWLAhhdretiredmhexpmmplugm1.0.1dnil2bWLAhhdretiredmhexpmmplugm0.8.3dnilabWLAhhdinner_checksummhexpmm db_connectionm2.0.4m ָ}+Sˬ2)cve=h8[bWLAhhdouter_checksummhexpmmphoenixm1.2.1m ;f`5 nz 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 1.0.0-beta.3dtruejMbWLAhhd timestampmhexpmmpostgrexm 0.13.0-rc.0hhba aha a9a0DbWLAhhd timestampmhexpmmjasonm1.2.1hhba aha a9a0MbWLAhhd timestampmhexpmmphoenix_pubsubm2.0.0hhba aha a9a0\bWLAhhdouter_checksummhexpmmphoenixm0.12.0m -V>dOva1MSdUC~68 HbWLAhhd timestampmhexpmmpostgrexm0.11.2hhba aha a9a0FbWLAhhd timestampmhexpmmdecimalm1.2.0hhba aha a9a0abWLAhhdinner_checksummhexpmm db_connectionm2.2.2m ;$[qbHI&0;]F Q%Θh;bWLAhhdretiredmhexpmm phoenix_htmlm2.13.3dnilMbWLAhhd timestampmhexpmmphoenix_paramsm0.3.0hhba aha a9a0FbWLAhhd timestampmhexpmmphoenixm1.4.3hhba aha a9a0LbWLAhhd timestampmhexpmm phoenix_htmlm2.10.3hhba aha a9a06bWLAhhdretiredmhexpmmphoenixm0.16.1dnilCbWLAhhd timestampmhexpmmplugm1.6.1hhba aha a9a0\bWLAhhdouter_checksummhexpmmpostgrexm0.8.1m #K4D}C{&o 2Ui,bWLAhhddepsmhexpmmphoenixm 1.3.0-rc.3lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.3.2 or ~> 1.4dfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0dfalsej[bWLAhhdouter_checksummhexpmmdecimalm1.4.0m vqN/q5 :=8K-zEFq#\bWLAhhdinner_checksummhexpmmphoenixm1.4.11m b÷lz>XPڧǦ K [bWLAhhdinner_checksummhexpmmphoenixm1.1.2m kl`ٛ D3x e&Qm4|}[bWLAhhdouter_checksummhexpmmphoenixm1.0.4m Y_:oTIj- {O5&foibWLAhhddepsmhexpmm db_connectionm0.1.0lhmhexpmmbackoffmbackoffm~> 1.0dfalsehmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dfalsehmhexpmmsbrokermsbrokerm~> 0.7dfalsejCbWLAhhd timestampmhexpmmmimem1.1.0hhba aha a9a0IbWLAhhd timestampmhexpmm connectionm1.0.1hhba aha a9a0DbWLAhhd timestampmhexpmmjasonm1.0.0hhba aha a9a0[bWLAhhdinner_checksummhexpmmphoenixm0.4.1m Fwbp1YGױg 8Z*bWLAhhddepsmhexpmmplugm0.4.4jdbWLAhhddepsmhexpmmplugm0.11.2lhmhexpmmcowboymcowboym~> 1.0dtruej 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsejbWLAhhddepsmhexpmmphoenixm1.0.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsej\bWLAhhdinner_checksummhexpmmpostgrexm0.5.4m <1=wX 1.0 or ~> 2.0dtruejYbWLAhhdinner_checksummhexpmmplugm1.10.4m AѢqSgd[գ}*?9\bWLAhhdinner_checksummhexpmmphoenixm0.2.11m -bboW8v[xhDbWLAhhd timestampmhexpmmplugm1.10.0hhba aha a9a0DbWLAhhd timestampmhexpmmjasonm1.0.1hhba aha a9a0abWLAhhdinner_checksummhexpmm phoenix_htmlm2.14.0m Ƽ(_Z=,XiB{eZ5bWLAhhdretiredmhexpmmphoenixm1.1.4dnil^bWLAhhdouter_checksummhexpmm connectionm1.0.3m nH +$}Z'|X3Oir;bWLAhhdretiredmhexpmm db_connectionm2.0.6dnil7bWLAhhdretiredmhexpmm telemetrym0.2.0dnilwbWLAhhddepsmhexpmm db_connectionm2.0.3lhmhexpmm connectionm connectionm~> 1.0.2dfalsejMbWLAhhd timestampmhexpmmphoenix_paramsm1.1.3hhba aha a9a0LbWLAhhd timestampmhexpmm db_connectionm1.0.0hhba aha a9a0:bWLAhhdretiredmhexpmm phoenix_htmlm2.0.0dnil'bWLAhhddepsmhexpmmphoenixm1.3.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4dfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0dfalsejLbWLAhhd timestampmhexpmm db_connectionm0.1.2hhba aha a9a0?bWLAhhd timestampmhexpmm connectionhhba aha a9a0[bWLAhhdinner_checksummhexpmmdecimalm0.1.2m iڠV;j\åiTYjFbWLAhhd timestampmhexpmmphoenixm0.5.0hhba aha a9a0MbWLAhhd timestampmhexpmmphoenix_paramsm0.1.0hhba aha a9a0abWLAhhdouter_checksummhexpmm db_connectionm2.1.1m Zr- L=3\)^KHIZGXbWLAhhdouter_checksummhexpmmplugm1.4.1m ʕ'PpL]MLH,kO8bWLAhhdretiredmhexpmm connectionm1.0.3dnil2bWLAhhdretiredmhexpmmplugm1.5.1dnilNbWLAhhd timestampmhexpmm connectionm 1.0.0-rc.1hhba aha a9a0;bWLAhhdretiredmhexpmm db_connectionm0.1.3dnil7bWLAhhdretiredmhexpmm telemetrym0.1.0dnil]bWLAhhdouter_checksummhexpmmplugm 1.5.0-rc.0m MfO3Ш<`v3_-[bWLAhhdinner_checksummhexpmmphoenixm1.1.9m Gւ52(t,[֛A}9ytY_DbWLAhhd timestampmhexpmmplugm0.11.1hhba aha a9a0;bWLAhhdretiredmhexpmm db_connectionm0.2.3dnilXbWLAhhdouter_checksummhexpmmplugm1.7.0m ~ƫjxXoDm* JbWLAhhd timestampmhexpmm plug_cowboym2.3.0hhba aha a9a0[bWLAhhdinner_checksummhexpmmphoenixm1.5.3m @NH4X? [b0;IGbWLAhhd timestampmhexpmmphoenixm0.17.1hhba aha a9a0XbWLAhhdouter_checksummhexpmmplugm1.3.3m C4t~ws/[4 };`bWLAhhdinner_checksummhexpmmdecimalm 2.0.0-rc.0m ĸK%5 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsej6bWLAhhdretiredmhexpmmpostgrexm0.9.0dnil;bWLAhhd timestampmhexpmmpoisonhhba aha a+a:bWLAhhdretiredmhexpmmphoenixm 1.2.0-rc.1dnilLbWLAhhd timestampmhexpmm db_connectionm2.0.5hhba aha a9a0CbWLAhhd timestampmhexpmmmimem1.2.0hhba aha a9a0bWLAhhddepsmhexpmmphoenixm0.7.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmpoisonmpoisonm~> 1.2dfalsejCbWLAhhd timestampmhexpmmplugm1.8.1hhba aha a9a0`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.2.0m ҩ7|˷D/!:P0ؠ 6bWLAhhdretiredmhexpmmphoenixm0.2.11dnilCbWLAhhd timestampmhexpmmplugm1.1.8hhba aha a9a0jbWLAhhddepsmhexpmmphoenixm1.4.10lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsejbWLAhhddepsmhexpmmpostgrexm 0.13.0-rc.0lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 1.1dfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsej[bWLAhhdouter_checksummhexpmmdecimalm0.2.5m .-h̆ lok$+FXnYGCbWLAhhd timestampmhexpmmplugm1.0.2hhba aha a9a0XbWLAhhdinner_checksummhexpmmplugm0.4.1m S?P>UjWz cc "[bWLAhhdinner_checksummhexpmmphoenixm0.2.4m Nr[+:Igʨ(4Y\ObWLAhhd registry_etagmhexpmmmimem""caa3030c95a211515c73c7c786aa1110"`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.9.3m Z!"C$/TOx0OKLC% XX*bWLAhhddepsmhexpmmmimem1.3.1jfbWLAhhdouter_checksummhexpmm db_connectionm 1.0.0-rc.2m 3ěV2igIhjl`l*>U&`bWLAhhdouter_checksummhexpmmphoenixm 1.3.0-rc.0m 5PN 1.0dtruejrbWLAhhddepsmhexpmmphoenix_paramsm0.1.1lhmhexpmmphoenixmphoenixm~> 1.3.3dfalsejkbWLAhhddepsmhexpmmjasonm 1.0.0-rc.3lhmhexpmmdecimalmdecimalm~> 1.0dtruej`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.1.2m {* 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0dfalsehmhexpmm telemetrym telemetrym~> 0.4dtruej:bWLAhhdretiredmhexpmm phoenix_htmlm2.1.1dnil[bWLAhhdinner_checksummhexpmmphoenixm1.4.6m 5 xj.HQxvlYbWLAhhddepsmhexpmm db_connectionm0.2.4lhmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 0.7dtruej\bWLAhhdinner_checksummhexpmmphoenixm1.4.16m ,fV|D8 3B7*Bn=:HCbWLAhhddepsmhexpmm db_connectionm0.1.7lhmhexpmmbackoffmbackoffm~> 1.0dfalsehmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 0.7dtruej9bWLAhhdretiredmhexpmm plug_cowboym1.0.0dnil]bWLAhhdinner_checksummhexpmmpostgrexm0.14.2m fY⍒C$6ʹ*y6I$.;xKbWLAhhd timestampmhexpmm phoenix_htmlm2.2.0hhba aha a9a0bWLAhhddepsmhexpmmpostgrexm0.12.2lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 1.0dfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsejCbWLAhhd timestampmhexpmmplugm1.3.1hhba aha a9a0rbWLAhhddepsmhexpmmphoenix_paramsm0.1.0lhmhexpmmphoenixmphoenixm~> 1.3.3dfalsej\bWLAhhdouter_checksummhexpmmphoenixm1.4.13m vZہbl{_QYb`tRv}bWLAhhddepsmhexpmm db_connectionm0.1.4lhmhexpmmbackoffmbackoffm~> 1.0dfalsehmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 0.7dtruejGbWLAhhd timestampmhexpmmphoenixm1.4.14hhba aha a9a0CbWLAhhd timestampmhexpmmplugm1.1.9hhba aha a9a0CbWLAhhd timestampmhexpmmplugm1.7.2hhba aha a9a0dbWLAhhddepsmhexpmmplugm0.11.0lhmhexpmmcowboymcowboym~> 1.0dtruej\bWLAhhdouter_checksummhexpmmpostgrexm0.8.2m Ac#=B0YO1uдfQ]L=ߨYbWLAhhdouter_checksummhexpmmplugm0.10.0m a{9 aH5;7KC]ϖ5"Qy!qJ[bWLAhhdinner_checksummhexpmmphoenixm0.3.0m |&QP?Xߛ*, b\^G>&݌^bWLAhhdinner_checksummhexpmmjasonm 1.0.0-rc.2m *]f?v7@.FV)FbWLAhhd timestampmhexpmmphoenixm0.2.0hhba aha a9a0wbWLAhhddepsmhexpmm db_connectionm2.1.1lhmhexpmm connectionm connectionm~> 1.0.2dfalsejXbWLAhhdinner_checksummhexpmmplugm1.1.3m ,Cp~imL`Z[bWLAhhdouter_checksummhexpmmphoenixm1.3.2m Cn [N24z9Or|Ex۶bbWLAhhdinner_checksummhexpmmphoenix_pubsubm1.1.2m Il0;.n[::75KM*Z>9bWLAhhd timestampmhexpmmmimehhba aha a9a0[bWLAhhdouter_checksummhexpmmdecimalm1.5.0m  &XUMu_]r?3ɻ9W⡭.YbWLAhhdouter_checksummhexpmmjasonm1.1.0m #aJc5o( ǫh_QЄR68lRbWLAhhd registry_etagmhexpmmphoenixm""584b1d4ed74a3b1ef299dc3546ce0746"_bWLAhhdinner_checksummhexpmm plug_cowboym2.2.0m 1{Qp"YrA z $bWLAhhddepsmhexpmmphoenixm1.0.5lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejbbWLAhhdouter_checksummhexpmmpostgrexm 0.13.0-rc.0m &[ضY H5, "_bWLAhhdinner_checksummhexpmm plug_cryptom1.1.0m HCՐbOH%oGU]ɇT.GbWLAhhd timestampmhexpmmpostgrexm0.4.2hhba aha a9a05bWLAhhdretiredmhexpmmphoenixm0.6.2dnilFbWLAhhd timestampmhexpmmdecimalm1.7.0hhba aha a9a0;bWLAhhdretiredmhexpmm db_connectionm2.0.1dnilFbWLAhhd timestampmhexpmmdecimalm1.8.0hhba aha a9a0bbWLAhhdouter_checksummhexpmmphoenix_pubsubm1.1.0m B.*ҞKna߄*cz;e,\bWLAhhdouter_checksummhexpmmphoenixm0.16.0m !,@X _F([bWLAhhdinner_checksummhexpmmphoenixm0.2.2m "r4|2_IijJPtT fBCbWLAhhd timestampmhexpmmphoenix_paramshhba aha a9a0;bWLAhhdretiredmhexpmm db_connectionm2.0.2dnilabWLAhhdinner_checksummhexpmm db_connectionm2.0.5m ݲga+}&MӐgBm\$I2:bWLAhhdretiredmhexpmm phoenix_htmlm2.6.0dnilCbWLAhhd timestampmhexpmmplugm1.4.2hhba aha a9a0cbWLAhhddepsmhexpmmplugm1.0.6lhmhexpmmcowboymcowboym~> 1.0dtruejbbWLAhhdouter_checksummhexpmmphoenix_pubsubm1.0.0m ,{?\MfVI$iqr`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.1.2m gKHuE?zDFx9 7to6bbWLAhhdouter_checksummhexpmmphoenix_pubsubm1.0.1m J/a?bsBqv˗ Ә[bWLAhhdouter_checksummhexpmmphoenixm0.2.6m xJlӐ,ϔMwYe`?fbWLAhhddepsmhexpmmjasonm1.0.0lhmhexpmmdecimalmdecimalm~> 1.0dtruej;bWLAhhdretiredmhexpmm db_connectionm2.2.1dnil3bWLAhhdretiredmhexpmmplugm0.13.1dnil`bWLAhhdinner_checksummhexpmmdecimalm 1.9.0-rc.0m cfja8{@<Lp}bbWLAhhdinner_checksummhexpmmphoenix_paramsm0.2.1m ftӓ~_2G0WY_ 9a KbWLAhhd timestampmhexpmm phoenix_htmlm1.1.0hhba aha a9a09bWLAhhdretiredmhexpmm plug_cowboym2.0.2dnilbWLAhhddepsmhexpmmplugm1.3.2lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsejbWLAhhddepsmhexpmm plug_cowboym2.2.1lhmhexpmmcowboymcowboym~> 2.7dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej[bWLAhhdinner_checksummhexpmmphoenixm0.5.0m tlAe-8æ%Z6@*:bWLAhhdretiredmhexpmmdecimalm 2.0.0-rc.0dnilXbWLAhhdinner_checksummhexpmmplugm0.4.4m ÃnOWs˧'S/M)WWbWLAhhddepsmhexpmmphoenixm1.2.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.1dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsejXbWLAhhdinner_checksummhexpmmplugm1.8.2m ڤ I9@wa\;Y t@'bWLAhhddepsmhexpmmpostgrexm 0.14.0-rc.0lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm ~> 2.0-rc.0dfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsehmhexpmmjasonmjasonm~> 1.0dtruejFbWLAhhd timestampmhexpmmphoenixm1.1.1hhba aha a9a05bWLAhhdretiredmhexpmmphoenixm1.3.4dnil[bWLAhhdouter_checksummhexpmmdecimalm1.0.1m dk+5~ kƿF|xwt;bWLAhhdretiredmhexpmm phoenix_htmlm2.13.2dnil;bWLAhhdretiredmhexpmm phoenix_htmlm2.13.1dnilLbWLAhhd timestampmhexpmm db_connectionm0.1.1hhba aha a9a0LbWLAhhd timestampmhexpmm phoenix_htmlm2.10.1hhba aha a9a07bWLAhhdretiredmhexpmm telemetrym0.4.2dnil[bWLAhhdouter_checksummhexpmmdecimalm1.6.0m $@@s`I*sGHM:bJibWLAhhddepsmhexpmm plug_cowboym2.1.0lhmhexpmmcowboymcowboym~> 2.5dfalsehmhexpmmplugmplugm~> 1.7dfalsejFbWLAhhd timestampmhexpmmphoenixm1.2.5hhba aha a9a06bWLAhhdretiredmhexpmmpostgrexm0.4.2dnil:bWLAhhdretiredmhexpmm phoenix_htmlm2.9.1dnil_bWLAhhdouter_checksummhexpmm plug_cowboym2.2.2m #dۭ7SՈ~^Pbf17']bWLAhhdinner_checksummhexpmmpostgrexm0.13.3m wϲMDZr$3YaDo%51h+sbWLAhhddepsmhexpmm phoenix_htmlm2.0.1lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsejwbWLAhhddepsmhexpmm db_connectionm2.0.1lhmhexpmm connectionm connectionm~> 1.0.2dfalsejbWLAhhddepsmhexpmmphoenixm0.2.5lhmhexpmmex_confmex_confm0.1.1dfalsehmhexpmminflexminflexm0.2.0dfalsehmhexpmmplugmplugm0.4.4dfalsej'bWLAhhddepsmhexpmmphoenixm1.3.2lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4dfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0dfalsej`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.0.0m JnPp~ղi>TCHYF>!\5"bWLAhhddepsmhexpmmphoenixm1.4.2lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruejGbWLAhhd timestampmhexpmmphoenixm0.2.10hhba aha a9a0\bWLAhhdouter_checksummhexpmmpostgrexm0.7.0m p/E-F.p?~) 1.0.1 or ~> 1.1 or ~> 2.1dtruehmhexpmmmimemmimem~> 1.0dfalsejbWLAhhddepsmhexpmm db_connectionm 1.0.0-rc.2lhmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 1.0.0-beta.2dtruejdbWLAhhddepsmhexpmmplugm0.12.1lhmhexpmmcowboymcowboym~> 1.0dtruejCbWLAhhd timestampmhexpmmplugm0.5.2hhba aha a9a0`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.4.0m "s3f?r&Yg~?f׹KwbWLAhhddepsmhexpmm db_connectionm2.0.6lhmhexpmm connectionm connectionm~> 1.0.2dfalsejbbWLAhhdinner_checksummhexpmmphoenix_pubsubm1.0.1m b7|Kչ .: NjYbWLAhhdouter_checksummhexpmmjasonm1.2.2m (򟞮aðvhя!y[bWLAhhdouter_checksummhexpmmphoenixm1.2.0m l@CF/l,86B"D^xQ_UnrbWLAhhddepsmhexpmmphoenix_paramsm0.2.0lhmhexpmmphoenixmphoenixm~> 1.3.3dfalsejMbWLAhhd timestampmhexpmmphoenix_paramsm1.0.1hhba aha a9a0XbWLAhhdinner_checksummhexpmmplugm1.4.1m .g} bWLAhhd timestampmhexpmm telemetryhhba aha a9a0FbWLAhhd timestampmhexpmmphoenixm1.4.0hhba aha a9a0ibWLAhhddepsmhexpmm phoenix_htmlm2.13.2lhmhexpmmplugmplugm~> 1.5dfalsejXbWLAhhdinner_checksummhexpmmplugm1.1.4m .B n[1vMoaB+Ű.Ι\bWLAhhdinner_checksummhexpmmpostgrexm0.8.4m 4Ma W`7ℋ*Z-Ⴛ\ڧ,E5abWLAhhdouter_checksummhexpmm phoenix_htmlm2.10.4m vٙ84*F"\h402i gFbWLAhhd timestampmhexpmmphoenixm1.1.8hhba aha a9a0MbWLAhhd timestampmhexpmmpostgrexm 0.14.0-rc.1hhba aha a9a0LbWLAhhd timestampmhexpmm phoenix_htmlm2.14.2hhba aha a9a0FbWLAhhd timestampmhexpmmphoenixm0.8.0hhba aha a9a05bWLAhhdretiredmhexpmmdecimalm1.1.2dnilKbWLAhhd timestampmhexpmmphoenixm 1.3.0-rc.3hhba aha a9a0ybWLAhhdversionsmhexpmmmimelm0.0.1m1.0.0m1.0.1m1.1.0m1.2.0m1.3.0m1.3.1m1.4.0jYc$ph-6`g^TbWLAhhddepsmhexpmmplugm1.2.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmmimemmimem~> 1.0dfalsej`bWLAhhdouter_checksummhexpmm phoenix_htmlm1.3.0m E~<&[_^ N5bWLAhhdretiredmhexpmmphoenixm1.4.2dnilLbWLAhhd timestampmhexpmm db_connectionm0.1.0hhba aha a9a0:bWLAhhdretiredmhexpmm phoenix_htmlm2.5.1dnil'bWLAhhddepsmhexpmmphoenixm1.3.3lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4dfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0dfalsej'bWLAhhddepsmhexpmmphoenixm1.3.4lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4dfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0dfalsejfbWLAhhddepsmhexpmmjasonm1.1.2lhmhexpmmdecimalmdecimalm~> 1.0dtruejbbWLAhhdinner_checksummhexpmmphoenix_paramsm1.0.0m LrـݨہJWU6)T:bWLAhhddepsmhexpmmplugm1.10.3lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejCbWLAhhd timestampmhexpmmplugm0.6.0hhba aha a9a05bWLAhhdretiredmhexpmmdecimalm0.1.1dnilLbWLAhhd timestampmhexpmm db_connectionm2.1.0hhba aha a9a0LbWLAhhd timestampmhexpmm phoenix_htmlm2.10.2hhba aha a9a0XbWLAhhdouter_checksummhexpmmplugm1.8.3m K8-ĄI**w 3N'4\bWLAhhdouter_checksummhexpmmpostgrexm0.5.4m A㔬 N]1P$LԲJyd<@abWLAhhdouter_checksummhexpmm db_connectionm0.2.4m J}g|i#q?g<8ԧCbWLAhhd timestampmhexpmmmimem1.0.0hhba aha a9a02bWLAhhdretiredmhexpmmplugm1.3.5dnil2bWLAhhdretiredmhexpmmplugm1.0.6dnil_bWLAhhdouter_checksummhexpmm plug_cowboym2.0.1m s nh4jQQ@! 1.0dtruehmhexpmmplugmplugm~> 0.9.0dfalsehmhexpmmpoisonmpoisonm~> 1.2dfalsej[bWLAhhdinner_checksummhexpmmphoenixm0.2.8m _3uDj*EЏ%e I}=cbbWLAhhdinner_checksummhexpmmphoenix_pubsubm1.0.2m Rx^ QQXR?45bWLAhhdretiredmhexpmmphoenixm0.3.1dnil`bWLAhhdinner_checksummhexpmm phoenix_htmlm1.0.0m >ִZPA$<y5PτWDzi FbWLAhhd timestampmhexpmmphoenixm1.1.3hhba aha a9a0dbWLAhhddepsmhexpmmplugm0.12.0lhmhexpmmcowboymcowboym~> 1.0dtruej-bWLAhhddepsmhexpmmdecimalm1.4.0j:bWLAhhdretiredmhexpmm phoenix_htmlm2.9.3dnilbbWLAhhdouter_checksummhexpmmphoenix_paramsm0.1.1m p> ]P_eT )# 00[bWLAhhdinner_checksummhexpmmdecimalm1.8.1m ?_4( 57@)Nޅ3SoEh٬<[bWLAhhdinner_checksummhexpmmphoenixm0.2.6m '1C &Sno1lmbu Q^I2bWLAhhdretiredmhexpmmplugm1.6.1dnilCbWLAhhd timestampmhexpmmplugm0.7.0hhba aha a9a05bWLAhhdretiredmhexpmmphoenixm1.2.4dnil 1.0dtruejFbWLAhhd timestampmhexpmmphoenixm0.2.7hhba aha a9a0jbWLAhhdversionsmhexpmm telemetrylm0.1.0m0.2.0m0.3.0m0.4.0m0.4.1m0.4.2j3bWLAhhdretiredmhexpmmplugm0.11.3dnilFbWLAhhd timestampmhexpmmphoenixm1.0.2hhba aha a9a0YbWLAhhdinner_checksummhexpmmplugm0.11.1m +.W)yyڴ8w1q.o4&\bWLAhhdouter_checksummhexpmmphoenixm0.17.1m L+bP"n tߔҋۑKbWLAhhd timestampmhexpmm phoenix_htmlm2.8.0hhba aha a9a0bWLAhhddepsmhexpmmplugm1.2.3lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmmimemmimem~> 1.0dfalsej[bWLAhhdouter_checksummhexpmmphoenixm1.4.2m Q8|YΥS*rUŨa,AB|F|kMKXXbWLAhhdouter_checksummhexpmmplugm1.5.1m 1-]yuCSN!nj6DbWLAhhd timestampmhexpmmplugm0.12.1hhba aha a9a02bWLAhhdretiredmhexpmmplugm1.3.2dnil\bWLAhhdouter_checksummhexpmmphoenixm0.11.0m @7#&[vnf"ď?O#y*abWLAhhdinner_checksummhexpmm db_connectionm1.1.0m /{XM َV z ڶ&3Tͳ\bWLAhhdinner_checksummhexpmmpostgrexm0.8.0m g]MJEJ`j컆E&З4X abWLAhhdinner_checksummhexpmm phoenix_htmlm2.13.2m |Έ`}+R'1OkA4W\bWLAhhdinner_checksummhexpmmpostgrexm0.9.0m 1"I#Jw<-c 'edBMe\6bWLAhhdretiredmhexpmmpostgrexm0.5.3dnilbWLAhhddepsmhexpmmphoenix_paramsm1.1.0lhmhexpmmdecimalmdecimalm>= 1.7.0dfalsehmhexpmmphoenixmphoenixm>= 1.3.3dfalsej[bWLAhhdinner_checksummhexpmmphoenixm1.1.6m {fi/Z0ڷ=b(ս*BkGCbWLAhhd timestampmhexpmmplugm1.6.2hhba aha a9a0abWLAhhdouter_checksummhexpmm db_connectionm0.1.3m _0}4ٰH>X5!qKXbWLAhhdinner_checksummhexpmmplugm1.6.4m 5aiE/k7vЙs?'G?8bWLAhhdretiredmhexpmm connectionm1.0.4dnilbWLAhhdversionsmhexpmmphoenix_pubsubl m0.0.1m0.1.0m 1.0.0-rc.0m1.0.0m1.0.1m1.0.2m1.1.0m1.1.1m1.1.2m2.0.0jCbWLAhhd timestampmhexpmmplugm1.3.5hhba aha a9a0CbWLAhhd timestampmhexpmmplugm1.3.3hhba aha a9a03bWLAhhdretiredmhexpmmplugm0.11.2dnilLbWLAhhd timestampmhexpmm db_connectionm2.1.1hhba aha a9a0GbWLAhhd timestampmhexpmmphoenixm0.10.0hhba aha a9a07bWLAhhdretiredmhexpmmpostgrexm0.15.0dnil2bWLAhhdretiredmhexpmmplugm1.1.5dnil6bWLAhhdretiredmhexpmmphoenixm0.2.10dnil2bWLAhhdretiredmhexpmmplugm1.3.4dnil5bWLAhhdretiredmhexpmmdecimalm1.4.1dnil;bWLAhhdretiredmhexpmm db_connectionm2.0.5dnilFbWLAhhd timestampmhexpmmdecimalm1.6.0hhba aha a9a0bWLAhhddepsmhexpmmphoenixm1.1.3lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsej[bWLAhhdouter_checksummhexpmmphoenixm1.0.1m s$DeOă&x Uk4ACbWLAhhd timestampmhexpmmmimem1.3.0hhba aha a9a0abWLAhhdinner_checksummhexpmm phoenix_htmlm2.10.5m Ot"D s*VlN3|(O XbWLAhhdinner_checksummhexpmmplugm1.1.6m '3Y8|(71'3PfXbWLAhhdouter_checksummhexpmmplugm1.0.1m Lq ۖ}|e7;dabWLAhhdouter_checksummhexpmm db_connectionm2.2.2m d*@بZo+'$%Ӄq6 MbWLAhhd timestampmhexpmmphoenix_pubsubm0.0.1hhba aha a9a07bWLAhhdretiredmhexpmmpostgrexm0.13.2dnil\bWLAhhdinner_checksummhexpmmpostgrexm0.7.0m ;k%W#Ȝ;*)>bWLAhhdretiredmhexpmm phoenix_htmlm 2.7.0-devdnilKbWLAhhd timestampmhexpmm phoenix_htmlm1.0.0hhba aha a9a0abWLAhhdinner_checksummhexpmm db_connectionm2.0.6m ]yiŵ cHzĪ_9bbWLAhhdinner_checksummhexpmmphoenix_paramsm0.4.2m mREM ms$;XbWLAhhdouter_checksummhexpmmplugm1.1.4m !&=zq(H#10rVƨTmݔ%M[bWLAhhdinner_checksummhexpmmphoenixm1.5.4m ʜ`Ic _%[ g~p?LbWLAhhd timestampmhexpmm phoenix_htmlm2.10.4hhba aha a9a0lbWLAhhddepsmhexpmmpostgrexm0.5.2lhmhexpmmdecimalmdecimalm~> 0.2.1dfalsej5bWLAhhdretiredmhexpmmdecimalm1.2.0dnil7bWLAhhdretiredmhexpmm telemetrym0.4.0dnil]bWLAhhdouter_checksummhexpmmpostgrexm0.15.4m 0euG7}ÿq|3?:=! `bWLAhhdouter_checksummhexpmm phoenix_htmlm1.4.0m jJZ:bxhk ~RsXI֟kLYbWLAhhdouter_checksummhexpmmplugm1.10.2m xGgﳹ%p^f;ō*ؚZYbWLAhhdouter_checksummhexpmmplugm0.14.0m qh5Y#oAf]h0=LbWLAhhd timestampmhexpmm db_connectionm0.2.0hhba aha a9a0[bWLAhhdinner_checksummhexpmmphoenixm1.4.5m n ? e?E 7]t5bWLAhhdretiredmhexpmmphoenixm0.2.7dnilHbWLAhhd timestampmhexpmmpostgrexm0.13.4hhba aha a9a0]bWLAhhdouter_checksummhexpmmplugm 1.5.0-rc.2m 8TUHlH̖"4jaLXbWLAhhdouter_checksummhexpmmmimem1.3.0m ^(`2j` Ɠݫ%y{ffS(3bWLAhhdretiredmhexpmmplugm0.11.0dnilXbWLAhhdouter_checksummhexpmmplugm1.4.3m zLe5"e۹zFk6LabbWLAhhdouter_checksummhexpmmphoenix_paramsm1.1.3m 7"R,ݮ80 j+>_|;bWLAhhdretiredmhexpmm phoenix_htmlm2.12.0dnil]bWLAhhdinner_checksummhexpmmpostgrexm0.15.2m -4=MԊ*5V;(Z5bWLAhhdretiredmhexpmmphoenixm1.2.5dnilFbWLAhhd timestampmhexpmmphoenixm1.2.2hhba aha a9a0abWLAhhdouter_checksummhexpmmpostgrexm 1.0.0-rc.1m SB!J1DLtz+v~O-$f|>o3\bWLAhhdouter_checksummhexpmmpostgrexm0.5.1m taj6[Vn3JMvsbWLAhhddepsmhexpmm phoenix_htmlm2.4.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsejbWLAhhddepsmhexpmmplugm1.5.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.1dtruehmhexpmmmimemmimem~> 1.0dfalsej5bWLAhhdretiredmhexpmmphoenixm1.4.9dnilFbWLAhhd timestampmhexpmmphoenixm1.3.3hhba aha a9a0FbWLAhhd timestampmhexpmmphoenixm0.2.3hhba aha a9a0[bWLAhhdouter_checksummhexpmmdecimalm0.2.1m v2 T80W'u̶J2 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsej\bWLAhhdouter_checksummhexpmmphoenixm0.15.0m ijLvȮB.At"u >V!:bWLAhhdretiredmhexpmm phoenix_htmlm2.3.0dnilabWLAhhdinner_checksummhexpmmpostgrexm 1.0.0-rc.1m &w%!EeFZbWLAhhddepsmhexpmmphoenixm1.5.0lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejFbWLAhhd timestampmhexpmmphoenixm1.5.0hhba aha a9a0*bWLAhhddepsmhexpmmplugm0.5.0jCbWLAhhd timestampmhexpmmplugm0.8.3hhba aha a9a02bWLAhhdretiredmhexpmmplugm1.1.9dnil\bWLAhhdinner_checksummhexpmmpostgrexm0.5.1m ]]<$Ă0g_||^bWLAhhdouter_checksummhexpmmjasonm 1.0.0-rc.3m tE;#:˛V83;jWD+lE,KbWLAhhd timestampmhexpmm phoenix_htmlm2.1.2hhba aha a9a05bWLAhhdretiredmhexpmmphoenixm1.4.0dnilbWLAhhddepsmhexpmmphoenixm0.2.7lhmhexpmmex_confmex_confm0.1.2dfalsehmhexpmminflexminflexm0.2.0dfalsehmhexpmmjazzmjazzm0.1.1dfalsehmhexpmmplugmplugm0.4.4dfalsej7bWLAhhdretiredmhexpmmpostgrexm0.12.2dnil\bWLAhhdouter_checksummhexpmmpostgrexm0.5.2m L~ĵT9 &VYbWLAhhdinner_checksummhexpmmplugm1.10.1m jm-XY;T ҇@/bWLAhhdversionsmhexpmm phoenix_htmll0m1.0.0m1.0.1m1.1.0m1.2.0m1.2.1m1.3.0m1.4.0m 2.0.0-devm2.0.0m2.0.1m2.1.0m2.1.1m2.1.2m2.2.0m2.3.0m2.3.1m 2.4.0-devm2.4.0m2.5.0m2.5.1m2.6.0m2.6.1m2.6.2m 2.7.0-devm2.7.0m2.8.0m2.9.0m2.9.1m2.9.2m2.9.3m2.10.0m2.10.1m2.10.2m2.10.3m2.10.4m2.10.5m2.11.0m2.11.1m2.11.2m2.12.0m2.13.0m2.13.1m2.13.2m2.13.3m2.13.4m2.14.0m2.14.1m2.14.2jCbWLAhhd timestampmhexpmmplugm1.0.4hhba aha a9a0`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.9.0m ŔEtYLjU yL{D+bWLAhhddepsmhexpmmphoenixm0.13.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm>= 0.12.2 and < 2.0.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsehmhexpmmpoolboympoolboym~> 1.5.1 or ~> 1.6dtruejLbWLAhhd timestampmhexpmm db_connectionm2.0.6hhba aha a9a0ibWLAhhddepsmhexpmm phoenix_htmlm2.10.3lhmhexpmmplugmplugm~> 1.0dfalsej[bWLAhhdinner_checksummhexpmmphoenixm0.2.3m ^v& G prYY/ y/$~SibWLAhhddepsmhexpmm phoenix_htmlm2.14.1lhmhexpmmplugmplugm~> 1.5dfalsej;bWLAhhd timestampmhexpmmcowboyhhbaaha aaabWLAhhdouter_checksummhexpmm db_connectionm2.0.3m =yY 7uqtY**ͅI4&4bWLAhhddepsmhexpmmphoenix_pubsubm1.0.2jLbWLAhhd timestampmhexpmm db_connectionm0.2.3hhba aha a9a0DbWLAhhd timestampmhexpmmjasonm1.1.2hhba aha a9a0]bWLAhhdinner_checksummhexpmmpostgrexm0.15.1m #4pA[ڼƆ i%,o;[G/:bWLAhhdretiredmhexpmmphoenixm 1.4.0-rc.0dnil]bWLAhhdouter_checksummhexpmmpostgrexm0.13.0m H*zE\̤8@B;bWLAhhdretiredmhexpmm phoenix_htmlm2.10.5dnilcbWLAhhddepsmhexpmmplugm1.1.6lhmhexpmmcowboymcowboym~> 1.0dtruejbbWLAhhdouter_checksummhexpmmphoenix_pubsubm1.1.2m ig)tѕR@i8[bWLAhhdouter_checksummhexpmmdecimalm1.4.1m #{RNUV]JrV&hL{ʀ_;abWLAhhdouter_checksummhexpmm db_connectionm2.0.4m RKK eP3$#YbWLAhhdinner_checksummhexpmmjasonm1.1.1m ̸@߰o/56MH#X^abWLAhhdouter_checksummhexpmm phoenix_htmlm2.11.0m 6h+2INp#9:@7}~Ne h_)2bWLAhhdretiredmhexpmmplugm1.1.2dnilXbWLAhhdinner_checksummhexpmmmimem0.0.1m s3ާj2Ӭeěs!>&9bWLAhhddepsmhexpmmphoenix_pubsubm 1.0.0-rc.0jYbWLAhhdouter_checksummhexpmmplugm0.11.1m M}O໿Fk΍]/]/9ݸXbWLAhhdinner_checksummhexpmmplugm0.9.0m c"&Ù]!UWrƒD[bWLAhhdouter_checksummhexpmmphoenixm0.5.0m /ߌͥB( P fk.OabWLAhhdouter_checksummhexpmm db_connectionm0.2.2m 8.ߖc0 uMk6;bWLAhhdretiredmhexpmm db_connectionm1.1.1dnil`bWLAhhdouter_checksummhexpmmphoenixm 1.3.0-rc.1m v,TOƿ<~RS7=b]bWLAhhdouter_checksummhexpmmpostgrexm0.14.0m mO 1~SpR3≵u/PsbWLAhhddepsmhexpmm phoenix_htmlm2.3.1lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsejGbWLAhhd timestampmhexpmmpostgrexm0.9.1hhba aha a9a0HbWLAhhd timestampmhexpmmpostgrexm0.14.3hhba aha a9a0XbWLAhhdinner_checksummhexpmmplugm1.3.0m n+?JBE(W Dk̒Dz*bWLAhhddepsmhexpmmplugm0.5.2j[bWLAhhdinner_checksummhexpmmphoenixm1.0.6m ד kK[=rEMWhFbWLAhhd timestampmhexpmmphoenixm0.2.8hhba aha a9a02bWLAhhdretiredmhexpmmplugm1.2.1dnil]bWLAhhdinner_checksummhexpmm telemetrym0.4.0m 9˄L)5sm U84[bWLAhhdinner_checksummhexpmmphoenixm1.0.4m /(L#:ߋkK2}?LJn@bWLAhhdretiredmhexpmm db_connectionm 1.0.0-rc.1dnil`bWLAhhdinner_checksummhexpmmphoenixm 1.4.0-rc.2m _ 4a KCGE(/gͮoYKbWLAhhd timestampmhexpmm phoenix_htmlm2.1.1hhba aha a9a0XbWLAhhdouter_checksummhexpmmplugm1.2.6m P;hQ>y4Ԇ-.rQL$"4bWLAhhddepsmhexpmmplugm1.3.4lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsejabWLAhhdinner_checksummhexpmm phoenix_htmlm2.13.0m ;^lYz ?GALV,A wbWLAhhddepsmhexpmm phoenix_htmlm1.2.0lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0dfalsejbWLAhhdretiredmhexpmm plug_cryptom1.1.1tdmessagem/Wrong default value is used for salt on encryptdreasondRETIRED_INVALIDCbWLAhhd timestampmhexpmmmimem0.0.1hhba aha a9a05bWLAhhdretiredmhexpmmdecimalm0.2.5dnilbbWLAhhdinner_checksummhexpmmphoenix_paramsm0.1.0m Ka$Nt,68M2r]bWLAhhdinner_checksummhexpmmpostgrexm0.14.3m WT8ߖBxp vAwe4Z2bWLAhhdretiredmhexpmmplugm1.6.2dnil:bWLAhhdretiredmhexpmm phoenix_htmlm2.6.2dnilYbWLAhhdinner_checksummhexpmmplugm0.12.0m =o.?Eg b,?(y3ObWLAhhd timestampmhexpmm phoenix_htmlm 2.7.0-devhhba aha a9a0XbWLAhhdouter_checksummhexpmmplugm1.2.1m = ;BoƳa+]>FfbWLAhhdinner_checksummhexpmm db_connectionm 1.0.0-rc.2m Ų2Qm~G=qe-gXFbWLAhhd timestampmhexpmmdecimalm1.5.0hhba aha a9a07bWLAhhdretiredmhexpmm telemetrym0.3.0dnil[bWLAhhdouter_checksummhexpmmphoenixm1.3.0m 5̑l7O2O/]eeSlwbWLAhhddepsmhexpmm db_connectionm2.1.0lhmhexpmm connectionm connectionm~> 1.0.2dfalsejXbWLAhhdinner_checksummhexpmmplugm1.0.2m 4)ȏưMq/gV@Lꗦ1:Iʨ#nbWLAhhddepsmhexpmm plug_cowboym2.1.2lhmhexpmmcowboymcowboym~> 2.5dfalsehmhexpmmplugmplugm~> 1.7dfalsejAbWLAhhdretiredmhexpmmphoenix_pubsubm 1.0.0-rc.0dnilYbWLAhhdouter_checksummhexpmmplugm1.10.0m B*'g; {w[-퐏2dA^J;bWLAhhdretiredmhexpmm db_connectionm0.1.0dnil5bWLAhhdretiredmhexpmmphoenixm1.1.2dnil[bWLAhhdinner_checksummhexpmmphoenixm0.4.0m *S]PoH39B;(X5uhjXbWLAhhdouter_checksummhexpmmplugm1.0.5m -Ьh80"w=-w7bWLAhhdretiredmhexpmmpostgrexm0.15.3dnil6bWLAhhdretiredmhexpmmphoenixm1.4.16dnil4bWLAhhddepsmhexpmmphoenix_pubsubm1.0.0jbWLAhhddepsmhexpmmplugm1.10.1lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dtruej2bWLAhhdretiredmhexpmmplugm0.8.1dnilHbWLAhhd timestampmhexpmmpostgrexm0.10.0hhba aha a9a0[bWLAhhdouter_checksummhexpmmdecimalm1.1.2m zmM8׸8P"H|h=3Q㦑]bWLAhhdinner_checksummhexpmmplugm 1.2.0-rc.0m Yqr}Ђc.4wp2bWLAhhdretiredmhexpmmplugm1.9.0dnilXbWLAhhdinner_checksummhexpmmmimem1.3.0m ^EP O`J4Ǣ QQ[bWLAhhdinner_checksummhexpmmphoenixm0.7.0m *~t@ `fNV][:;MY3bWLAhhdretiredmhexpmmjasonm1.0.0dnil`bWLAhhdouter_checksummhexpmmphoenixm 1.4.0-rc.3m SJjJDv.8[˭FOn>35abWLAhhdinner_checksummhexpmm db_connectionm0.2.2m v I<^Ο+*(mܽ4XbWLAhhdouter_checksummhexpmmplugm0.5.2m }8gD'CviD@ӋVSHbWLAhhd timestampmhexpmmpostgrexm0.11.0hhba aha a9a0XbWLAhhdouter_checksummhexpmmplugm1.1.5m phq=fLKΪP4@P1cM=XbWLAhhdinner_checksummhexpmmplugm1.7.2m ׷u^ }D"7.YbWLAhhdouter_checksummhexpmmplugm0.11.3m O0% Ԣq\%V#4^Ie[bWLAhhdinner_checksummhexpmmphoenixm1.1.4m e7srZVeK@nw<|1CbWLAhhd timestampmhexpmmplugm1.7.0hhba aha a9a09bWLAhhdretiredmhexpmm plug_cowboym2.0.0dnil_bWLAhhdinner_checksummhexpmm plug_cowboym2.1.2m ݵR8ÎD.'2F]|Gŀ^CbWLAhhd timestampmhexpmmplugm1.1.6hhba aha a9a0jbWLAhhddepsmhexpmmpostgrexm0.8.4lhmhexpmmdecimalmdecimalm~> 1.0dfalsejbWLAhhddepsmhexpmmpostgrexm0.15.4lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 2.1dfalsehmhexpmmdecimalmdecimalm~> 1.5dfalsehmhexpmmjasonmjasonm~> 1.0dtruejabWLAhhdouter_checksummhexpmm phoenix_htmlm2.11.1m LEwOJZrBx4iŢgϮ`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.7.0m h׫~~څ1m%s{q ORNwQbWLAhhd timestampmhexpmm db_connectionm 1.0.0-rc.1hhba aha a9a0IbWLAhhd timestampmhexpmmjasonm 1.0.0-rc.1hhba aha a9a0\bWLAhhdouter_checksummhexpmmphoenixm0.16.1m 8NP ޱ7|zɚ*bWLAhhddepsmhexpmmmimem1.0.0j5bWLAhhdretiredmhexpmmdecimalm0.2.0dnil_bWLAhhdinner_checksummhexpmm plug_cowboym2.0.2m `UhhHKncҺڔIxA3w*lbWLAhhddepsmhexpmmplugm1.5.1lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.3dtruehmhexpmmmimemmimem~> 1.0dfalsejXbWLAhhdinner_checksummhexpmmplugm0.5.2m |"Wž/1޹\T5 J]bWLAhhdinner_checksummhexpmm telemetrym0.4.1m 'HHD$GjA|2w(ttRbWLAhhddepsmhexpmmphoenixm1.1.9lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0.5dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsej2bWLAhhdretiredmhexpmmplugm1.1.7dnil[bWLAhhdouter_checksummhexpmmdecimalm1.7.0m wvPZՊOW\_`e]GbWLAhhd timestampmhexpmmpostgrexm0.8.4hhba aha a9a0[bWLAhhdouter_checksummhexpmmphoenixm0.8.0m dgz1R,GdSv*>0bWLAhhddepsmhexpmm connectionm1.0.0j5bWLAhhdretiredmhexpmmphoenixm1.0.0dnil]bWLAhhdouter_checksummhexpmm telemetrym0.4.1m G88.6]g`,V m)ތXHLbWLAhhd timestampmhexpmm db_connectionm2.2.2hhba aha a9a0[bWLAhhdinner_checksummhexpmmphoenixm1.1.7m 4ظ ?TL_?85ȭaUcbWLAhhddepsmhexpmmplugm0.8.3lhmhexpmmcowboymcowboym~> 1.0dtruejXbWLAhhdinner_checksummhexpmmmimem1.0.1m Ó$v}6'qbCM$zQ[bWLAhhdinner_checksummhexpmmphoenixm1.4.2m :P e; T4iM]bWLAhhdouter_checksummhexpmm telemetrym0.3.0m c}11b!1 Z6h!/r-bWLAhhddepsmhexpmmdecimalm0.2.3j[bWLAhhdouter_checksummhexpmmphoenixm0.2.3m o, B)| X0piXbWLAhhdinner_checksummhexpmmplugm1.4.2m ğ :?a`j!UFbWLAhhd timestampmhexpmmphoenixm0.2.1hhba aha a9a0bWLAhhddepsmhexpmmphoenixm1.1.2lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejLbWLAhhd timestampmhexpmm db_connectionm2.0.3hhba aha a9a0FbWLAhhd timestampmhexpmmphoenixm0.6.1hhba aha a9a0wbWLAhhddepsmhexpmm db_connectionm2.0.5lhmhexpmm connectionm connectionm~> 1.0.2dfalsejbbWLAhhdinner_checksummhexpmmphoenix_pubsubm1.1.1m fhׇ$z_i̘Q6 <MbWLAhhd timestampmhexpmmphoenix_pubsubm0.1.0hhba aha a9a0_bWLAhhdinner_checksummhexpmm plug_cowboym2.0.1m טZ̆-DPf\#nQ2Az[bWLAhhdinner_checksummhexpmmphoenixm1.4.7m 弚Lu R}Zl*<awm5bWLAhhdretiredmhexpmmdecimalm1.4.0dnil5bWLAhhdretiredmhexpmmphoenixm1.0.1dnil9bWLAhhdretiredmhexpmm plug_cowboym2.1.2dnilHbWLAhhd timestampmhexpmmpostgrexm0.15.0hhba aha a9a0sbWLAhhddepsmhexpmm phoenix_htmlm2.1.2lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsejWbWLAhhd registry_etagmhexpmm phoenix_htmlm""1afb92af7c2927f48535ea600645d7b3"2bWLAhhdretiredmhexpmmplugm0.9.0dnilLbWLAhhd timestampmhexpmmpostgrexm 1.0.0-rc.0hhba aha a9a0TbWLAhhd registry_etagmhexpmm telemetrym""fd930643205853c2c965911bb8aa90f7"wbWLAhhddepsmhexpmm db_connectionm2.0.0lhmhexpmm connectionm connectionm~> 1.0.2dfalsej5bWLAhhdretiredmhexpmmphoenixm1.1.6dnil[bWLAhhdouter_checksummhexpmmphoenixm0.2.0m D7܆qO6ǁ79*0]P쿺sEi9bWLAhhdretiredmhexpmm plug_cowboym2.3.0dnil[bWLAhhdinner_checksummhexpmmphoenixm1.2.2m 4[RY8ΖlL‰'JDabWLAhhdinner_checksummhexpmm db_connectionm0.1.8m 0}Ա_IU{g]qu>]`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.5.0m rJPwO`.; Sk ۩ ]-A?2bWLAhhdretiredmhexpmmplugm0.5.1dnil5bWLAhhdretiredmhexpmmdecimalm1.0.0dnilbWLAhhddepsmhexpmmphoenix_paramsm1.1.2lhmhexpmmdecimalmdecimalm~> 2.0dfalsehmhexpmmphoenixmphoenixm~> 1.3dfalsejbWLAhhddepsmhexpmmpostgrexm0.10.0lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsej;bWLAhhdretiredmhexpmm phoenix_htmlm2.10.2dnil2bWLAhhdretiredmhexpmmplugm1.4.5dnil[bWLAhhdinner_checksummhexpmmphoenixm1.1.5m >xx*K9YΫK8u2bWLAhhdretiredmhexpmmplugm1.3.3dnilHbWLAhhd timestampmhexpmmplugm 1.5.0-rc.1hhba aha a9a0@bWLAhhdretiredmhexpmm db_connectionm 1.0.0-rc.5dnilbWLAhhddepsmhexpmm plug_cowboym2.2.2lhmhexpmmcowboymcowboym~> 2.7dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejLbWLAhhd timestampmhexpmm db_connectionm0.1.7hhba aha a9a0[bWLAhhdinner_checksummhexpmmdecimalm1.1.2m yie{-S{Q<Қ+HkQlBS -bWLAhhddepsmhexpmmdecimalm0.2.2j`bWLAhhdinner_checksummhexpmm phoenix_htmlm1.0.1m wcC$f-fZ$ mK]d!8Z1xbWLAhhddepsmhexpmmphoenixm1.2.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.1dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsej5bWLAhhdretiredmhexpmmphoenixm0.2.0dnil[bWLAhhdouter_checksummhexpmmdecimalm1.3.1m WVHGrn\οǫ*wwbWLAhhddepsmhexpmm phoenix_htmlm1.0.1lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0dfalsej[bWLAhhdouter_checksummhexpmmphoenixm0.2.9m x*;D4Dc~| d]KbWLAhhd timestampmhexpmmphoenixm 1.4.0-rc.0hhba aha a9a0XbWLAhhdinner_checksummhexpmmmimem1.1.0m =ǸFQ9b4;( cbWLAhhddepsmhexpmmplugm1.1.9lhmhexpmmcowboymcowboym~> 1.0dtruejXbWLAhhdinner_checksummhexpmmplugm1.4.0m '^f^Bl% $m_E;l };bWLAhhdretiredmhexpmm db_connectionm0.2.1dnilabWLAhhdinner_checksummhexpmm phoenix_htmlm2.11.0m m[\VB3x2ba~Weڡl85bWLAhhdretiredmhexpmmphoenixm1.4.5dnilXbWLAhhdinner_checksummhexpmmplugm0.8.1m $0 ;pD/L_awFҐG5bWLAhhdretiredmhexpmmphoenixm1.5.0dnilbWLAhhddepsmhexpmmplugm1.3.5lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsej]bWLAhhdouter_checksummhexpmmpostgrexm0.13.4m u|I?7|U^ufX>@D{s2GbWLAhhd timestampmhexpmmpostgrexm0.8.2hhba aha a9a06bWLAhhdretiredmhexpmmpostgrexm0.5.0dnil6bWLAhhdretiredmhexpmmphoenixm1.4.13dnilXbWLAhhdinner_checksummhexpmmplugm1.0.0m ߶ 3]gN{a!<0CbWLAhhd timestampmhexpmmplugm0.8.2hhba aha a9a03bWLAhhdretiredmhexpmmjasonm1.1.0dnilFbWLAhhd timestampmhexpmmdecimalm1.1.1hhba aha a9a0\bWLAhhdinner_checksummhexpmmphoenixm1.4.12m oZ+6I\VA2dc~w39]lXbWLAhhdouter_checksummhexpmmplugm1.5.0m ͜\c~:ɿPtovͲױΓԠ.R2bWLAhhdretiredmhexpmmmimem1.0.1dnilbbWLAhhdouter_checksummhexpmmphoenix_paramsm1.1.0m ; 5(XZRܫDVǓ VabWLAhhdouter_checksummhexpmm phoenix_htmlm2.10.3m W+w=*.@Vz? 2bWLAhhdretiredmhexpmmplugm1.1.3dnilMbWLAhhd timestampmhexpmmphoenix_paramsm0.4.2hhba aha a9a0]bWLAhhdinner_checksummhexpmmplugm 1.5.0-rc.2m ; _A2heu=fog%CbWLAhhd timestampmhexpmmmimem1.0.1hhba aha a9a0:bWLAhhdretiredmhexpmmphoenixm 1.4.0-rc.1dnilYbWLAhhdinner_checksummhexpmmplugm0.11.0m ΰ`P&o@dS52bWLAhhdretiredmhexpmmplugm0.6.0dnilLbWLAhhd timestampmhexpmm db_connectionm0.2.1hhba aha a9a0[bWLAhhdinner_checksummhexpmmdecimalm1.6.0m Mn]CpӔ42_Ӻp0Y 3bWLAhhdretiredmhexpmmplugm0.11.1dnil bWLAhhddepsmhexpmmphoenixm0.5.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmlinguistmlinguistm~> 0.1.2dfalsehmhexpmmplugmplugm~> 0.8.1dfalsehmhexpmmpoisonmpoisonm~> 1.1dfalsejIbWLAhhd timestampmhexpmm connectionm1.0.3hhba aha a9a0KbWLAhhd timestampmhexpmm phoenix_htmlm2.3.0hhba aha a9a0wbWLAhhddepsmhexpmm phoenix_htmlm1.1.0lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0dfalsejJbWLAhhd timestampmhexpmm plug_cowboym2.2.0hhba aha a9a0]bWLAhhdinner_checksummhexpmmpostgrexm0.15.5m "Y/0"uAD3/abWLAhhdouter_checksummhexpmm db_connectionm1.1.3m _  Pb_Q7y_*-PCbWLAhhd timestampmhexpmmplugm1.2.5hhba aha a9a0_bWLAhhdouter_checksummhexpmm plug_cryptom1.1.0m ͡ 3%eu|%ʮȧ-l=`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.1.0m `+&}C!kI˯E XbWLAhhdouter_checksummhexpmmmimem1.2.0m MpB `UQ`\([+bWLAhhddepsmhexpmmplugm1.7.2lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0dfalsej]bWLAhhdouter_checksummhexpmmplugm 1.5.0-rc.1m A^3\1=ݴGp 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsej:bWLAhhdretiredmhexpmmphoenixm 1.3.0-rc.0dnil[bWLAhhdouter_checksummhexpmmphoenixm1.1.8m n^YF%{(l<: ]'viDXbWLAhhdouter_checksummhexpmmplugm1.3.1m b\P1Yu.e34hDJT<YbWLAhhdouter_checksummhexpmmplugm0.11.2m 0Hи#ƢJ-Pr4:bWLAhhdretiredmhexpmm phoenix_htmlm2.4.0dnil[bWLAhhdouter_checksummhexpmmphoenixm1.3.3m (& 1A E-;4sN0]J+KbWLAhhd timestampmhexpmm phoenix_htmlm2.9.1hhba aha a9a0XbWLAhhdinner_checksummhexpmmplugm1.5.0m "K%IREgpgIF [2bWLAhhdretiredmhexpmmplugm0.8.4dnilHbWLAhhd timestampmhexpmmpostgrexm0.14.2hhba aha a9a0[bWLAhhdouter_checksummhexpmmphoenixm0.6.1m ύK lt~/S3ۗhN1bHbWLAhhd timestampmhexpmmplugm 1.4.0-rc.0hhba aha a9a0)bWLAhhddepsmhexpmmphoenixm 1.4.0-rc.0lhmhexpmmcowboymcowboym~> 1.0 or ~> 2.3dtruehmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.6.2 or ~> 1.7dfalsejbWLAhhddepsmhexpmmphoenixm1.0.6lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0.5dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejRbWLAhhd registry_etagmhexpmmdecimalm""4657f6f92e3c01945c9c9b06b6fd850e"XbWLAhhdouter_checksummhexpmmplugm0.4.3m &[ }yv6[A.qbWLAhhddepsmhexpmmphoenixm1.5.2lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejbWLAhhdversionsmhexpmm db_connectionl'm0.1.0m0.1.1m0.1.2m0.1.3m0.1.4m0.1.5m0.1.6m0.1.7m0.1.8m0.2.0m0.2.1m0.2.2m0.2.3m0.2.4m0.2.5m 1.0.0-rc.0m 1.0.0-rc.1m 1.0.0-rc.2m 1.0.0-rc.3m 1.0.0-rc.4m 1.0.0-rc.5m1.0.0m1.1.0m1.1.1m1.1.2m1.1.3m 2.0.0-rc.0m2.0.0m2.0.1m2.0.2m2.0.3m2.0.4m2.0.5m2.0.6m2.1.0m2.1.1m2.2.0m2.2.1m2.2.2jFbWLAhhd timestampmhexpmmdecimalm1.1.2hhba aha a9a0[bWLAhhdouter_checksummhexpmmphoenixm1.4.6m oI uqqYnb>bWLAhhddepsmhexpmmphoenixm0.2.2lhmhexpmmex_confmex_confm0.1.1dfalsehmhexpmminflexminflexm0.2.0dfalsehmhexpmmjazzmjazzm0.1.0dfalsehmhexpmmplugmplugm0.4.2dfalsej`bWLAhhdouter_checksummhexpmmphoenixm 1.4.0-rc.1m t4<Q7lWHAw>abWLAhhdinner_checksummhexpmm db_connectionm2.1.0m ./bĐkTM " 50CyabWLAhhdinner_checksummhexpmm db_connectionm0.1.3m q&A>DgVjn" f>-bWLAhhddepsmhexpmmdecimalm1.6.0jbWLAhhddepsmhexpmm db_connectionm1.0.0lhmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 1.0dtruej]bWLAhhdinner_checksummhexpmmpostgrexm0.13.0m GrYUȃ $.佝- `bWLAhhdinner_checksummhexpmm phoenix_htmlm2.1.0m ud*7kH|K~9dƀXbWLAhhdinner_checksummhexpmmplugm1.3.6m ߔKø~Y+QJ _abWLAhhdouter_checksummhexpmm db_connectionm0.1.1m @HLàkM 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruejbbWLAhhdouter_checksummhexpmmphoenix_pubsubm0.0.1m SieZc6@1oW: bbWLAhhdinner_checksummhexpmmphoenix_paramsm1.1.1m AY`E_L x 5 abWLAhhdouter_checksummhexpmm phoenix_htmlm2.10.0m .8CN҄W~`s}") .U<=mbWLAhhddepsmhexpmmphoenixm1.0.2lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejYbWLAhhdinner_checksummhexpmmjasonm1.0.0m |#!Aw&Zqo 1.0dtruejDbWLAhhd timestampmhexpmmplugm1.10.2hhba aha a9a0bWLAhhddepsmhexpmm plug_cowboym2.1.1lhmhexpmmcowboymcowboym~> 2.5dfalsehmhexpmmplugmplugm~> 1.7dfalsejrbWLAhhddepsmhexpmmphoenix_paramsm0.1.2lhmhexpmmphoenixmphoenixm~> 1.3.3dfalsej]bWLAhhdouter_checksummhexpmmpostgrexm0.12.1m ۭ{ ]EBIwbWLAhhddepsmhexpmm db_connectionm2.2.2lhmhexpmm connectionm connectionm~> 1.0.2dfalsej2bWLAhhdretiredmhexpmmplugm0.5.2dnilbWLAhhddepsmhexpmmplugm1.2.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmmimemmimem~> 1.0dfalsej\bWLAhhdouter_checksummhexpmmpostgrexm0.5.0m -8;lŜa 8j[(5bWLAhhdretiredmhexpmmphoenixm1.2.2dnil;bWLAhhdretiredmhexpmm phoenix_htmlm2.14.0dnil6bWLAhhdretiredmhexpmmphoenixm0.17.0dnil2bWLAhhdretiredmhexpmmmimem0.0.1dnil bWLAhhddepsmhexpmmphoenixm0.6.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmlinguistmlinguistm~> 0.1.3dfalsehmhexpmmplugmplugm~> 0.8.2dfalsehmhexpmmpoisonmpoisonm~> 1.2dfalsejcbWLAhhddepsmhexpmmplugm0.8.4lhmhexpmmcowboymcowboym~> 1.0dtruej`bWLAhhdinner_checksummhexpmmphoenixm 1.4.0-rc.3m >Y$z^-c㬭}GknډA)gibWLAhhddepsmhexpmm phoenix_htmlm2.10.5lhmhexpmmplugmplugm~> 1.0dfalsej[bWLAhhdouter_checksummhexpmmphoenixm1.4.0m "ڏe=;;vfÉ=B%̄K5bWLAhhdretiredmhexpmmphoenixm1.4.8dnil;bWLAhhdretiredmhexpmm phoenix_htmlm2.11.1dnil 1.0dfalsehmhexpmm db_connectionm db_connectionm ~> 1.0-rc.4dfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsej~bWLAhhdretiredmhexpmmpostgrexm 1.0.0-rc.1tdmessagemSuperseded by v0.13.0-rc.0dreasondRETIRED_INVALID6bWLAhhdretiredmhexpmmphoenixm1.4.11dnilbbWLAhhdinner_checksummhexpmmphoenix_paramsm0.3.0m Kzrv-1 8@FXbWLAhhdinner_checksummhexpmmplugm1.6.2m j{һm]ݕp ΈSQ"KَLbWLAhhd timestampmhexpmm phoenix_htmlm2.13.1hhba aha a9a0KbWLAhhd timestampmhexpmm phoenix_htmlm1.0.1hhba aha a9a0abWLAhhdinner_checksummhexpmm phoenix_htmlm2.14.1m }R* G DI f[bWLAhhdouter_checksummhexpmmphoenixm1.1.0m Q>Ob S.- jbWLAhhddepsmhexpmmpostgrexm0.8.3lhmhexpmmdecimalmdecimalm~> 1.0dfalsejCbWLAhhd timestampmhexpmmplugm1.3.6hhba aha a9a0XbWLAhhdinner_checksummhexpmmplugm1.6.1m /b=P Ϙ Fi~)4XnlLbWLAhhd timestampmhexpmm phoenix_htmlm2.14.0hhba aha a9a05bWLAhhdretiredmhexpmmdecimalm0.2.3dnil bWLAhhddepsmhexpmmphoenixm0.4.0lhmhexpmmcowboymcowboym~> 1.0.0dtruehmhexpmmlinguistmlinguistm~> 0.1.2dfalsehmhexpmmplugmplugm0.7.0dfalsehmhexpmmpoisonmpoisonm~> 1.0.1dfalsej1bWLAhhddepsmhexpmm plug_cryptom1.0.0j2bWLAhhdretiredmhexpmmplugm1.2.6dnil^bWLAhhdinner_checksummhexpmm connectionm1.0.3m 1EAk$I5N2!F|:b[5T6WGbWLAhhd timestampmhexpmmphoenixm0.13.0hhba aha a9a0bbWLAhhdinner_checksummhexpmmpostgrexm 0.14.0-rc.1m %P_Y wD;AcL-bWLAhhddepsmhexpmmdecimalm1.1.2jhbWLAhhddepsmhexpmm phoenix_htmlm2.9.1lhmhexpmmplugmplugm~> 1.0dfalsejHbWLAhhd timestampmhexpmmpostgrexm0.13.2hhba aha a9a0fbWLAhhddepsmhexpmmjasonm1.2.0lhmhexpmmdecimalmdecimalm~> 1.0dtruej4bWLAhhddepsmhexpmmphoenix_pubsubm1.1.2j2bWLAhhdretiredmhexpmmplugm1.4.3dnil6bWLAhhdretiredmhexpmmpostgrexm0.5.5dnil9bWLAhhdretiredmhexpmm plug_cryptom1.1.0dnilLbWLAhhd timestampmhexpmm db_connectionm0.1.3hhba aha a9a0 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 0.7dtruej`bWLAhhdouter_checksummhexpmmdecimalm 1.9.0-rc.0m fuEsW\^KE0|dX< G\bWLAhhdinner_checksummhexpmmpostgrexm0.5.0m '59t.γ pbӴ#SN #bWLAhhddepsmhexpmmpostgrexm0.13.5lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 1.1dfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsejbWLAhhddepsmhexpmmplugm1.2.5lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmmimemmimem~> 1.0dfalsej;bWLAhhdretiredmhexpmm phoenix_htmlm2.11.2dnilLbWLAhhd timestampmhexpmm db_connectionm2.2.1hhba aha a9a0cbWLAhhddepsmhexpmmplugm1.0.4lhmhexpmmcowboymcowboym~> 1.0dtruej]bWLAhhdouter_checksummhexpmmpostgrexm0.10.0m fOzg#rpzLv^`&3WabWLAhhdouter_checksummhexpmm db_connectionm0.1.4m Jq]53cC t. 8O݁O/bWLAhhddepsmhexpmm telemetrym0.4.0jcbWLAhhddepsmhexpmmplugm1.1.3lhmhexpmmcowboymcowboym~> 1.0dtruejbWLAhhddepsmhexpmmphoenixm0.10.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm ~> 0.11.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsehmhexpmmpoolboympoolboym~> 1.4.2dtruejIbWLAhhd timestampmhexpmm connectionm1.0.2hhba aha a9a0[bWLAhhdinner_checksummhexpmmdecimalm1.3.1m { 0.13 or ~> 1.0dfalsejFbWLAhhd timestampmhexpmmphoenixm1.1.7hhba aha a9a05bWLAhhdretiredmhexpmmphoenixm0.7.1dnilbWLAhhddepsmhexpmmplugm1.2.2lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmmimemmimem~> 1.0dfalsej;bWLAhhdretiredmhexpmm db_connectionm2.0.3dnil[bWLAhhdouter_checksummhexpmmphoenixm1.1.7m MRVA1,OO'kT)1\bWLAhhdinner_checksummhexpmmpostgrexm0.8.2m u?. {'f1&A37 +bWLAhhddepsmhexpmmphoenixm0.17.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsej5bWLAhhdretiredmhexpmmphoenixm1.1.0dnilsbWLAhhddepsmhexpmm phoenix_htmlm2.2.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsej`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.9.0m ne7WuG5FD))%cA:k .3[bWLAhhdouter_checksummhexpmmphoenixm1.4.1m [t=] xU&MbWLAhhddepsmhexpmmphoenixm0.2.0lhmhexpmmex_confmex_confm0.1.1dfalsehmhexpmmplugmplugm0.4.2dfalsejXbWLAhhdinner_checksummhexpmmplugm1.8.1m py9pߠ~L֫X qL:GbWLAhhd timestampmhexpmmphoenixm1.4.13hhba aha a9a0XbWLAhhdouter_checksummhexpmmmimem1.3.1m lvj  1Lc KζE8du*bWLAhhddepsmhexpmmmimem1.4.0jYbWLAhhdinner_checksummhexpmmjasonm1.2.2m CpѪΐi\Vx?RKbWLAhhd timestampmhexpmmphoenixm 1.5.0-rc.0hhba aha a9a0FbWLAhhd timestampmhexpmmphoenixm0.3.1hhba aha a9a03bWLAhhdretiredmhexpmmplugm1.10.0dnilbWLAhhddepsmhexpmmplugm1.3.6lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsej2bWLAhhdretiredmhexpmmplugm0.4.2dnilFbWLAhhd timestampmhexpmmphoenixm0.2.6hhba aha a9a0XbWLAhhdouter_checksummhexpmmplugm1.7.2m ޘ%o֭ ͈k=e~`k5bWLAhhdretiredmhexpmmdecimalm1.0.1dnil%bWLAhhddepsmhexpmmphoenixm 1.2.0-rc.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm ~> 1.0.0-rcdfalsehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsej[bWLAhhdouter_checksummhexpmmdecimalm1.8.1m zabWLAhhdouter_checksummhexpmm phoenix_htmlm2.14.2m X%] |/l\ғpE/;bWLAhhdretiredmhexpmm phoenix_htmlm2.13.4dnilbWLAhhddepsmhexpmmpostgrexm0.15.2lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 2.1dfalsehmhexpmmdecimalmdecimalm~> 1.5dfalsehmhexpmmjasonmjasonm~> 1.0dtruejbWLAhhddepsmhexpmmplugm1.10.2lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej[bWLAhhdouter_checksummhexpmmphoenixm1.1.3m Wv pwd6Bf&Ͱp`R J^bWLAhhdouter_checksummhexpmm connectionm1.0.2m l ʬZ~AkڵJK8fa 3bWLAhhdretiredmhexpmmplugm1.10.2dnil2bWLAhhdretiredmhexpmmplugm1.2.0dnilHbWLAhhd timestampmhexpmmpostgrexm0.14.0hhba aha a9a0abWLAhhdinner_checksummhexpmm db_connectionm1.1.1m F[\6uɠR`2L9FbWLAhhd timestampmhexpmmphoenixm0.2.9hhba aha a9a0-bWLAhhddepsmhexpmmdecimalm0.1.1jibWLAhhddepsmhexpmm phoenix_htmlm2.10.2lhmhexpmmplugmplugm~> 1.0dfalsejbWLAhhddepsmhexpmm db_connectionm1.1.1lhmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 1.0dtruejGbWLAhhd timestampmhexpmmpostgrexm0.6.0hhba aha a9a0abWLAhhdouter_checksummhexpmm phoenix_htmlm2.14.0m 0xmra838aQoI?XbWLAhhdouter_checksummhexpmmplugm1.6.3m o/J-s09+ %UpwJ`Qi"bWLAhhddepsmhexpmmphoenixm1.4.1lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruej]bWLAhhdouter_checksummhexpmm telemetrym0.1.0m <1bhxAO\"BMPCD$5bWLAhhdretiredmhexpmmphoenixm1.1.7dnilbWLAhhddepsmhexpmmplugm1.2.6lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmmimemmimem~> 1.0dfalsejCbWLAhhd timestampmhexpmmmimem1.3.1hhba aha a9a06bWLAhhdretiredmhexpmmphoenixm0.13.0dnil2bWLAhhdretiredmhexpmmplugm1.1.8dnil3bWLAhhdretiredmhexpmmplugm0.13.0dnil\bWLAhhdinner_checksummhexpmmphoenixm0.16.0m I~TY$G$nc; aZ%w~(8S]bWLAhhdinner_checksummhexpmmplugm 1.5.0-rc.0m f!T`B sU-m(LabWLAhhdinner_checksummhexpmm db_connectionm0.1.5m ^/ :af@<э|Ot*\5cbWLAhhddepsmhexpmmplugm1.1.7lhmhexpmmcowboymcowboym~> 1.0dtruejibWLAhhddepsmhexpmm phoenix_htmlm2.13.3lhmhexpmmplugmplugm~> 1.5dfalsejFbWLAhhd timestampmhexpmmphoenixm1.4.1hhba aha a9a02bWLAhhdretiredmhexpmmmimem1.3.1dnil\bWLAhhdinner_checksummhexpmmphoenixm1.4.15m \90j3RβV)bX+Y(-bWLAhhddepsmhexpmmdecimalm1.2.0jabWLAhhdouter_checksummhexpmm phoenix_htmlm2.13.2m 4gddے*yz#$`bWLAhhdinner_checksummhexpmmphoenixm 1.3.0-rc.1m KH#L^Xeu7bWLAhhddepsmhexpmmphoenixm1.1.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejabWLAhhdinner_checksummhexpmm db_connectionm2.0.1m ELlnB% Ə& S2 lk bWLAhhddepsmhexpmmphoenixm0.6.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmlinguistmlinguistm~> 0.1.4dfalsehmhexpmmplugmplugm~> 0.8.4dfalsehmhexpmmpoisonmpoisonm~> 1.2dfalsej6bWLAhhdretiredmhexpmmphoenixm0.17.1dnilXbWLAhhdouter_checksummhexpmmplugm1.8.1m LX:fɵA~mS>c-bWLAhhddepsmhexpmmdecimalm1.9.0jCbWLAhhd timestampmhexpmmplugm1.1.4hhba aha a9a0wbWLAhhddepsmhexpmm phoenix_htmlm 2.0.0-devlhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsej2bWLAhhdretiredmhexpmmmimem1.4.0dnil5bWLAhhdretiredmhexpmmphoenixm0.2.1dnil;bWLAhhdretiredmhexpmm phoenix_htmlm2.10.0dnilLbWLAhhd timestampmhexpmm db_connectionm1.1.2hhba aha a9a0IbWLAhhd timestampmhexpmm connectionm1.0.0hhba aha a9a05bWLAhhdretiredmhexpmmphoenixm1.5.1dnilCbWLAhhd timestampmhexpmmplugm1.3.0hhba aha a9a0bbWLAhhdinner_checksummhexpmmphoenix_paramsm0.4.0m UtM;uX`zQ|Y Ԑ[bWLAhhdinner_checksummhexpmmdecimalm1.1.1m [g1ͬoyo(]nVnvKbWLAhhd timestampmhexpmm phoenix_htmlm2.6.2hhba aha a9a0bWLAhhddepsmhexpmm plug_cowboym2.0.1lhmhexpmmcowboymcowboym~> 2.5dfalsehmhexpmmplugmplugm~> 1.7dfalsej 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsej]bWLAhhdouter_checksummhexpmmpostgrexm0.13.1m pEXN 䂺1h{z;SEibWLAhhddepsmhexpmm phoenix_htmlm2.10.4lhmhexpmmplugmplugm~> 1.0dfalsejLbWLAhhd timestampmhexpmm phoenix_htmlm2.14.1hhba aha a9a0hbWLAhhddepsmhexpmm phoenix_htmlm2.6.2lhmhexpmmplugmplugm~> 1.0dfalsejXbWLAhhdouter_checksummhexpmmplugm1.2.3m e”ynw^:݃mrH/-PXbWLAhhdouter_checksummhexpmmplugm1.6.1m s?2IC/;bWLAhhdretiredmhexpmm db_connectionm0.1.4dnil[bWLAhhdouter_checksummhexpmmphoenixm1.5.2m 0G#g5ng* 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejfbWLAhhdouter_checksummhexpmm db_connectionm 1.0.0-rc.1m 3w'+HTnC?T45bWLAhhdretiredmhexpmmphoenixm0.2.5dnil[bWLAhhdouter_checksummhexpmmdecimalm0.2.0m &EQ{-D; Mʝ4$ibWLAhhddepsmhexpmm phoenix_htmlm2.13.0lhmhexpmmplugmplugm~> 1.5dfalsej[bWLAhhdinner_checksummhexpmmphoenixm1.1.8m %J&RG ʩB).A-6T\abWLAhhdouter_checksummhexpmm phoenix_htmlm2.13.4m 򕜸'YuRRL clM_bWLAhhdouter_checksummhexpmm plug_cowboym2.0.0m ?IbЙ$m|/.j) ns:tcbWLAhhddepsmhexpmmplugm1.0.3lhmhexpmmcowboymcowboym~> 1.0dtruej3bWLAhhdretiredmhexpmmplugm0.10.0dnil:bWLAhhdretiredmhexpmm phoenix_htmlm2.1.0dnil2bWLAhhdretiredmhexpmmplugm1.2.2dnil5bWLAhhdretiredmhexpmmphoenixm1.2.3dnil5bWLAhhdretiredmhexpmmphoenixm1.2.0dnilbbWLAhhdouter_checksummhexpmmphoenix_pubsubm2.0.0m -O&wKPYuCSPqCbWLAhhd timestampmhexpmmplugm1.0.5hhba aha a9a0bWLAhhddepsmhexpmm db_connectionm 1.0.0-rc.5lhmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 1.0.0-beta.3dtruej;bWLAhhdretiredmhexpmm db_connectionm0.1.2dnil_bWLAhhdouter_checksummhexpmm plug_cowboym1.0.0m BzD$eE"c2u@JaabWLAhhdinner_checksummhexpmm db_connectionm0.1.4m YUjMxG'*ZZ.8>`bWLAhhdouter_checksummhexpmmphoenixm 1.3.0-rc.2m ap RŸo ,nE4ns,ND7bWLAhhdretiredmhexpmmpostgrexm0.14.1dnil[bWLAhhdouter_checksummhexpmmdecimalm0.2.3m tVX7aCVh`ZV1ւubbWLAhhdinner_checksummhexpmmphoenix_paramsm1.1.2m Tp}%Շz_BF!:ȷryGbWLAhhd timestampmhexpmmphoenixm0.13.1hhba aha a9a0dbWLAhhddepsmhexpmmplugm0.11.3lhmhexpmmcowboymcowboym~> 1.0dtruej]bWLAhhdouter_checksummhexpmmpostgrexm0.11.1m mGOd,@d[w#vO(-[`bWLAhhdouter_checksummhexpmm phoenix_htmlm1.2.0m QR«`ǭ}10R~2*NЇlvbWLAhhddepsmhexpmm db_connectionm0.1.3lhmhexpmmbackoffmbackoffm~> 1.0dfalsehmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 0.7dtruejXbWLAhhdinner_checksummhexpmmmimem1.0.0m 0's;;]ȶbˁw1TJV6bWLAhhdretiredmhexpmmpostgrexm0.9.1dnilabWLAhhdouter_checksummhexpmm phoenix_htmlm2.10.2m u"cLoМ ֭r*1XbWLAhhdouter_checksummhexpmmplugm0.5.3m E}A[; 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 1.1dfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsejbbWLAhhdouter_checksummhexpmmphoenix_paramsm0.2.1m k<#jwDIdNgFO7wbWLAhhddepsmhexpmm db_connectionm2.0.2lhmhexpmm connectionm connectionm~> 1.0.2dfalsej`bWLAhhdouter_checksummhexpmm phoenix_htmlm1.1.0m tD/}"/Gv"{>_vP]1LbWLAhhd timestampmhexpmm phoenix_htmlm2.10.0hhba aha a9a0[bWLAhhdouter_checksummhexpmmphoenixm0.2.5m ~sؒv `1 l,h˕PybѶ1hbWLAhhddepsmhexpmm phoenix_htmlm2.9.2lhmhexpmmplugmplugm~> 1.0dfalsejFbWLAhhd timestampmhexpmmphoenixm1.1.6hhba aha a9a07bWLAhhdretiredmhexpmmpostgrexm0.15.5dnilabWLAhhdinner_checksummhexpmm phoenix_htmlm2.13.3m )/%_LToS`·к38g[bWLAhhdinner_checksummhexpmmphoenixm1.0.3m wafnQ+èzh $\XbWLAhhdinner_checksummhexpmmplugm0.5.3m ͢Q:ID+Y1g%8RM[bWLAhhdinner_checksummhexpmmdecimalm0.2.1m U%qs]A27MXbWLAhhdouter_checksummhexpmmplugm1.2.2m ^Dl]R&~,23~rbWLAhhddepsmhexpmmphoenix_paramsm0.3.0lhmhexpmmphoenixmphoenixm>= 1.3.3dfalsejwbWLAhhddepsmhexpmm phoenix_htmlm1.2.1lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0dfalsejabWLAhhdinner_checksummhexpmm phoenix_htmlm2.11.2m h% 'W [ۓH]doL1f3bWLAhhdretiredmhexpmmplugm0.12.2dnilabWLAhhdouter_checksummhexpmmpostgrexm 1.0.0-rc.0m Tm؆!8"s9ĒW݂ZIhlabWLAhhdouter_checksummhexpmm db_connectionm0.1.7m 2{m2FвIjyl{kMMzXbWLAhhdouter_checksummhexpmmmimem0.0.1m $ ߽#C8Fd7S;:L5C' XbWLAhhdinner_checksummhexpmmplugm1.1.5m VEpAZrS!'Z˒7Bn'XbWLAhhd registry_etagmhexpmm db_connectionm""84fb07dffdadbc16b002d1285963d731"bWLAhhddepsmhexpmmplugm 1.2.0-rc.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmmimemmimem~> 1.0dfalsej[bWLAhhdinner_checksummhexpmmphoenixm0.9.0m cmHZ^j:x:_h[EMbWLAhhd timestampmhexpmmphoenix_paramsm0.4.0hhba aha a9a0YbWLAhhdinner_checksummhexpmmplugm0.11.2m Җ\7İԩ _e_O`*Z[bWLAhhdinner_checksummhexpmmdecimalm1.9.0m 12q$,QK =M`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.0.0m ϲ aq|Ǿoqd$6DbWLAhhd timestampmhexpmmplugm0.10.0hhba aha a9a0DC\bWLAhhdouter_checksummhexpmmphoenixm0.14.0m حTx&y/ݝ985bWLAhhdretiredmhexpmmphoenixm0.1.0dnil-bWLAhhddepsmhexpmmdecimalm2.0.0jDbWLAhhd timestampmhexpmmjasonm1.2.2hhba aha a9a0GbWLAhhd timestampmhexpmmphoenixm0.2.11hhba aha a9a0rbWLAhhddepsmhexpmmphoenix_paramsm0.2.2lhmhexpmmphoenixmphoenixm>= 1.3.3dfalsej`bWLAhhdinner_checksummhexpmmphoenixm 1.4.0-rc.0m k]MvF/x[bcjfj8{.ޤ[bWLAhhdouter_checksummhexpmmphoenixm1.2.4m ]ͤr GM(\rn\qV[XbWLAhhdinner_checksummhexpmmplugm1.0.6m dpBcOfXeyu6LCelIXbWLAhhdouter_checksummhexpmmplugm0.7.0m \ꬽVSOy#M96s-vyXg;bWLAhhdretiredmhexpmm db_connectionm0.1.8dnilLbWLAhhd timestampmhexpmm phoenix_htmlm2.12.0hhba aha a9a0JbWLAhhd timestampmhexpmm plug_cowboym2.0.1hhba aha a9a0_bWLAhhdouter_checksummhexpmm plug_cryptom1.1.1m &Mo A i_bWLAhhdinner_checksummhexpmm plug_cowboym2.2.1m 2'2*G<..4 UP^[bWLAhhdouter_checksummhexpmmphoenixm0.3.1m YHy7^6aM+VSN%'bWLAhhddepsmhexpmmphoenixm1.3.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4dfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0dfalsejMbWLAhhd timestampmhexpmmphoenix_paramsm0.4.1hhba aha a9a0hbWLAhhddepsmhexpmm phoenix_htmlm2.7.0lhmhexpmmplugmplugm~> 1.0dfalsejYbWLAhhdouter_checksummhexpmmplugm1.10.4m #?=.Vaeh`w{g1H-NwFbWLAhhd timestampmhexpmmdecimalm1.0.1hhba aha a9a0-bWLAhhddepsmhexpmmdecimalm1.1.1jbbWLAhhdinner_checksummhexpmmphoenix_paramsm1.0.3m vs8kW1`mM٭2/.+۪|bWLAhhddepsmhexpmm db_connectionm 2.0.0-rc.0lhmhexpmm connectionm connectionm~> 1.0.2dfalsej\bWLAhhdinner_checksummhexpmmphoenixm1.4.13m g'֛QqT`OJ?a46VKbWLAhhd timestampmhexpmm phoenix_htmlm1.4.0hhba aha a9a0bWLAhhddepsmhexpmm plug_cowboym2.3.0lhmhexpmmcowboymcowboym~> 2.7dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej:bWLAhhdretiredmhexpmm phoenix_htmlm2.0.1dnilXbWLAhhdouter_checksummhexpmmplugm0.4.2m Anۅm 4 u4\bWLAhhdinner_checksummhexpmmpostgrexm0.4.2m gt\Fl0={ 5bWLAhhdretiredmhexpmmphoenixm0.4.0dnil`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.9.2m 7` Co񚮘dbw:[.`5bWLAhhdretiredmhexpmmphoenixm0.3.0dnilXbWLAhhdouter_checksummhexpmmplugm1.3.5m X̡(9D/q*tmRHbWLAhhd timestampmhexpmmpostgrexm0.12.2hhba aha a9a0DbWLAhhd timestampmhexpmmplugm0.14.0hhba aha a9a0KbWLAhhddepsmhexpmmphoenixm1.2.4lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm6~> 1.4 or ~> 1.3.3 or ~> 1.2.4 or ~> 1.1.8 or ~> 1.0.5dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsejFbWLAhhd timestampmhexpmmphoenixm1.1.9hhba aha a9a0]bWLAhhdinner_checksummhexpmmpostgrexm0.14.1m c$}JZֹW-Aj&;͊.ICbWLAhhd timestampmhexpmmplugm1.1.2hhba aha a9a0]bWLAhhdinner_checksummhexpmmplugm 1.5.0-rc.1m r>%f-k,\a$B/9&]bWLAhhdouter_checksummhexpmmpostgrexm0.14.3m M-vM!56FgbWLAhhddepsmhexpmm db_connectionm0.1.2lhmhexpmmbackoffmbackoffm~> 1.0dfalsehmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 0.7dtruej[bWLAhhdouter_checksummhexpmmphoenixm1.0.3m e^c hwK>r6)Bdڙ2bWLAhhdretiredmhexpmmplugm1.6.0dnilcbWLAhhddepsmhexpmmplugm0.9.0lhmhexpmmcowboymcowboym~> 1.0dtruej\bWLAhhdinner_checksummhexpmmphoenixm0.11.0m `3dLڜ,\O,pO@CbWLAhhd timestampmhexpmmplugm1.8.3hhba aha a9a0]bWLAhhdinner_checksummhexpmmpostgrexm0.15.4m ]i%y5"j5F#^bWLAhhddepsmhexpmmphoenixm0.2.8lhmhexpmmex_confmex_confm0.1.2dfalsehmhexpmminflexminflexm0.2.0dfalsehmhexpmmjazzmjazzm0.1.1dfalsehmhexpmmplugmplugm0.4.4dfalsej*bWLAhhddepsmhexpmmplugm0.4.3j4bWLAhhddepsmhexpmmphoenix_pubsubm0.0.1jbWLAhhddepsmhexpmmpostgrexm0.14.3lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 2.0dfalsehmhexpmmdecimalmdecimalm~> 1.5dfalsehmhexpmmjasonmjasonm~> 1.0dtruejbWLAhhddepsmhexpmmphoenix_paramsm0.4.1lhmhexpmmdecimalmdecimalm>= 1.7.0dfalsehmhexpmmphoenixmphoenixm>= 1.3.3dfalsej0bWLAhhddepsmhexpmm connectionm1.0.1jXbWLAhhdinner_checksummhexpmmplugm1.2.4m e)Do=."'vu=HϾ:bWLAhhdretiredmhexpmmdecimalm 1.9.0-rc.0dnil-bWLAhhddepsmhexpmmdecimalm1.1.0j`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.6.2m J^X OLiP>0[(D9%>`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.6.2m i{1HT]hBrX5p- _2,\bWLAhhdinner_checksummhexpmmpostgrexm0.8.1m O羸7u;:N_QⰏYbWLAhhd registry_etagmhexpmmphoenix_pubsubm""9c40253b1ea746f7a666b077918a909f"[bWLAhhdinner_checksummhexpmmphoenixm1.5.0m YόsJ0W6eIaij ĮB2bWLAhhddepsmhexpmmdecimalm 2.0.0-rc.0j[bWLAhhdouter_checksummhexpmmphoenixm1.5.0m ƕr9K}bc%P;K Ծ609PbWLAhhd registry_etagmhexpmmjasonm""c7ccb0b0d3701498a0a717862ef103b1"sbWLAhhddepsmhexpmm phoenix_htmlm2.5.1lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsejKbWLAhhd timestampmhexpmm phoenix_htmlm2.1.0hhba aha a9a0bWLAhhddepsmhexpmm db_connectionm 1.0.0-rc.0lhmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 1.0.0-beta.2dtruejabWLAhhdinner_checksummhexpmm db_connectionm0.1.2m ~r ~i`%&Sy!Lm<5bWLAhhdretiredmhexpmmdecimalm1.3.1dnilzbWLAhhdretiredmhexpmm plug_cowboym2.2.0tdmessagemBroken telemetry supportdreasondRETIRED_INVALIDJbWLAhhd timestampmhexpmm plug_cryptom1.1.1hhba aha a9a0abWLAhhdouter_checksummhexpmm db_connectionm0.2.0m ?MQ%u* j*gbWLAhhddepsmhexpmmplugm1.6.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4dtruehmhexpmmmimemmimem~> 1.0dfalsejXbWLAhhdouter_checksummhexpmmplugm1.9.0m ** d4h.I?]MjƼ_#PabWLAhhdouter_checksummhexpmm phoenix_htmlm2.14.1m SmR7[2A z:, _wc$abWLAhhdinner_checksummhexpmm db_connectionm2.0.2m D Q ܠFE@5CH(ʤ3bWLAhhddepsmhexpmm db_connectionm 1.0.0-rc.3lhmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 1.0.0-beta.3dtruej"bWLAhhddepsmhexpmmphoenixm1.4.4lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruejFbWLAhhd timestampmhexpmmphoenixm1.2.0hhba aha a9a0]bWLAhhdinner_checksummhexpmmpostgrexm0.13.2m +ƥEj'L ]'HA^]u]NabWLAhhdinner_checksummhexpmm db_connectionm1.1.3m ;FwyYVXf·]M~WZa6bWLAhhdretiredmhexpmmphoenixm0.15.0dnilFbWLAhhd timestampmhexpmmphoenixm1.0.6hhba aha a9a0]bWLAhhdinner_checksummhexpmmpostgrexm0.14.0m Vc93=5"w&퉂q2YbWLAhhdinner_checksummhexpmmplugm0.12.1m X"!崫*x/ӡ~(#LbWLAhhd timestampmhexpmm db_connectionm0.1.8hhba aha a9a05bWLAhhdretiredmhexpmmphoenixm0.5.0dnil[bWLAhhdouter_checksummhexpmmdecimalm1.8.0m RiNn`PsLfXO[}1xfcXbWLAhhdouter_checksummhexpmmplugm1.6.0m vΛ6ѝ\U^/À.HXbWLAhhdinner_checksummhexpmmplugm1.1.2m ~$ZVɭ\1L>m ٛHE `bWLAhhdinner_checksummhexpmm phoenix_htmlm2.6.1m }S= zn$Ÿf0I["7 R}\bWLAhhdouter_checksummhexpmmphoenixm1.4.16m l2S'7A<u%Wm\$bWLAhhddepsmhexpmmphoenixm0.2.9lhmhexpmmex_confmex_confm0.1.2dfalsehmhexpmminflexminflexm0.2.0dfalsehmhexpmmjazzmjazzm0.1.1dfalsehmhexpmmplugmplugm0.5.0dfalsejAbWLAhhd timestampmhexpmm phoenix_htmlhhba aha a9a09bWLAhhdretiredmhexpmm plug_cowboym2.1.3dnildbWLAhhdouter_checksummhexpmm phoenix_htmlm 2.7.0-devm OhnV.m%VH;Dyd Z_bWLAhhdinner_checksummhexpmm plug_cowboym2.3.0m P\<Pj7WP2f#ݩ`bWLAhhdouter_checksummhexpmmphoenixm 1.2.0-rc.1m _ʨul! ifʂA,]bWLAhhdouter_checksummhexpmm telemetrym0.4.2m -j m{XRr.+#b {*abWLAhhdinner_checksummhexpmmpostgrexm 1.0.0-rc.0m OOnUа|k-=mS,}=KB 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsejXbWLAhhdinner_checksummhexpmmplugm1.9.0m |N&"3Gs$Ë{F}Y$:ZJXbWLAhhdouter_checksummhexpmmplugm1.3.4m bˠ*^MujXR:bWLAhhddepsmhexpmmphoenixm0.13.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm>= 0.12.2 and < 2.0.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsehmhexpmmpoolboympoolboym~> 1.5.1 or ~> 1.6dtruej*bWLAhhddepsmhexpmmplugm0.5.1jKbWLAhhd timestampmhexpmmdecimalm 2.0.0-rc.0hhba aha a9a0 1.0.2dfalsejLbWLAhhd timestampmhexpmm db_connectionm0.2.2hhba aha a9a0FbWLAhhd timestampmhexpmmphoenixm1.1.0hhba aha a9a03bWLAhhdretiredmhexpmmplugm1.10.4dnilbWLAhhddepsmhexpmm plug_cowboym2.1.3lhmhexpmmcowboymcowboym~> 2.5dfalsehmhexpmmplugmplugm~> 1.7dfalsej:bWLAhhdretiredmhexpmm phoenix_htmlm1.4.0dnilCbWLAhhd timestampmhexpmmplugm1.9.0hhba aha a9a0/bWLAhhddepsmhexpmm telemetrym0.4.1j[bWLAhhdouter_checksummhexpmmphoenixm0.1.0m uW X  Tb~ n?p*fbWLAhhdouter_checksummhexpmm db_connectionm 1.0.0-rc.4m b|M,7LHXpS%ҫШ!FbWLAhhd timestampmhexpmmdecimalm2.0.0hhba aha a9a0KbWLAhhd timestampmhexpmmphoenixm 1.4.0-rc.3hhba aha a9a0HbWLAhhd timestampmhexpmm telemetrym0.4.0hhba aha a9a0 1.0dtruej[bWLAhhdouter_checksummhexpmmdecimalm0.2.2m 䑳ƑfZz hӨuR?/ ,.7}.KbWLAhhd timestampmhexpmmphoenixm 1.3.0-rc.0hhba aha a9a09bWLAhhdretiredmhexpmm plug_cryptom1.0.0dnil:bWLAhhdretiredmhexpmmphoenixm 1.4.0-rc.2dnil2bWLAhhdretiredmhexpmmplugm1.0.0dnilbbWLAhhdouter_checksummhexpmmphoenix_paramsm0.1.0m njΚliC"B0 CEHʺ2bWLAhhdretiredmhexpmmplugm1.6.4dnil\bWLAhhdouter_checksummhexpmmphoenixm1.4.10m %jס@)p6=|pnd"t:bWLAhhdretiredmhexpmm phoenix_htmlm1.1.0dnil`bWLAhhdinner_checksummhexpmmphoenixm 1.3.0-rc.2m SJ%h܂o8Bs E"(ت`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.3.0m (&M"$& $bn 2q>/2bWLAhhdretiredmhexpmmmimem1.1.0dnil]bWLAhhdinner_checksummhexpmm telemetrym0.2.0m [@ʣ޳-|OUomk\7L#fw!a1wFbWLAhhd timestampmhexpmmphoenixm1.4.7hhba aha a9a0FbWLAhhd timestampmhexpmmphoenixm1.0.1hhba aha a9a05bWLAhhdretiredmhexpmmphoenixm1.4.3dnil7bWLAhhdretiredmhexpmmpostgrexm0.13.3dnil'bWLAhhddepsmhexpmmphoenixm 1.4.0-rc.2lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruejbWLAhhddepsmhexpmmphoenixm0.17.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsej\bWLAhhdouter_checksummhexpmmphoenixm0.17.0m ,}ce1::lyw)1yDbWLAhhd timestampmhexpmmplugm0.11.2hhba aha a9a0fbWLAhhdouter_checksummhexpmm db_connectionm 1.0.0-rc.5m c˝([eͱbWXfXX?Λ@bWLAhhdretiredmhexpmm db_connectionm 1.0.0-rc.0dnil:bWLAhhdretiredmhexpmm phoenix_htmlm2.3.1dnilKbWLAhhd timestampmhexpmm phoenix_htmlm1.3.0hhba aha a9a0HbWLAhhd timestampmhexpmmpostgrexm0.15.3hhba aha a9a0`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.5.0m D7zp6 . .cKkXfbWLAhhdouter_checksummhexpmm db_connectionm 1.0.0-rc.0m NHtQ9T{;1Em;bWLAhhdretiredmhexpmm db_connectionm2.0.4dnilXbWLAhhdouter_checksummhexpmmplugm0.4.4m 1-W3SXd#_Y:bWLAhhddepsmhexpmm db_connectionm0.2.3lhmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 0.7dtruejbWLAhhddepsmhexpmmpostgrexm0.13.1lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 1.1dfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsejLbWLAhhd timestampmhexpmm db_connectionm2.2.0hhba aha a9a0[bWLAhhdouter_checksummhexpmmphoenixm0.6.0m \&9&)!cigN{Ezx9@bWLAhhd timestampmhexpmm plug_cryptohhba aha a9a0CbWLAhhd timestampmhexpmmplugm1.3.4hhba aha a9a0FbWLAhhd timestampmhexpmmdecimalm1.8.1hhba aha a9a0abWLAhhdinner_checksummhexpmm db_connectionm0.1.7m !@֥{;wvhqB\ՎՃ*bWLAhhddepsmhexpmmplugm1.4.4lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsejXbWLAhhdouter_checksummhexpmmplugm0.9.0m '&PrUv1}V$K8MzTJ abWLAhhdinner_checksummhexpmm db_connectionm0.1.6m f _`==o_Frp 49ӣp\bWLAhhdouter_checksummhexpmmpostgrexm0.5.3m inޤh/ U(w@M9 `bWLAhhdouter_checksummhexpmm phoenix_htmlm2.5.1m |] ^7d"> Q)`XbWLAhhdinner_checksummhexpmmplugm1.4.3m #mw{f*o6jIIJ\bWLAhhdouter_checksummhexpmmphoenixm0.13.0m 2W@ 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsej/bWLAhhddepsmhexpmm telemetrym0.1.0j7bWLAhhdretiredmhexpmmpostgrexm0.12.0dnilKbWLAhhd timestampmhexpmmphoenixm 1.2.0-rc.0hhba aha a9a0:bWLAhhdretiredmhexpmm phoenix_htmlm1.2.0dnilbWLAhhddepsmhexpmmphoenix_paramsm1.0.0lhmhexpmmdecimalmdecimalm>= 1.7.0dfalsehmhexpmmphoenixmphoenixm>= 1.3.3dfalsejabWLAhhdinner_checksummhexpmm phoenix_htmlm2.13.4m =nFd$JI' P*}mء[O_bWLAhhdinner_checksummhexpmm plug_cowboym2.1.3m 8>k߂aa2D\ܶ3abWLAhhdinner_checksummhexpmm phoenix_htmlm2.10.0m iFM|S“4'QA8 JMbWLAhhd timestampmhexpmmphoenix_pubsubm1.1.2hhba aha a9a0_bWLAhhdinner_checksummhexpmm plug_cryptom1.0.0m 4?$b')ԥ- }MJbWLAhhd timestampmhexpmm plug_cowboym2.2.1hhba aha a9a0bbWLAhhdouter_checksummhexpmmphoenix_paramsm0.2.0m l: CpsmՊ`Git>L5bWLAhhdretiredmhexpmmphoenixm0.2.9dnilVbWLAhhdversionsmhexpmmphoenixlcm0.1.0m0.2.0m0.2.1m0.2.2m0.2.3m0.2.4m0.2.5m0.2.6m0.2.7m0.2.8m0.2.9m0.2.10m0.2.11m0.3.0m0.3.1m0.4.0m0.4.1m0.5.0m0.6.0m0.6.1m0.6.2m0.7.0m0.7.1m0.7.2m0.8.0m0.9.0m0.10.0m0.11.0m0.12.0m0.13.0m0.13.1m0.14.0m0.15.0m0.16.0m0.16.1m0.17.0m0.17.1m1.0.0m1.0.1m1.0.2m1.0.3m1.0.4m1.0.5m1.0.6m1.1.0m1.1.1m1.1.2m1.1.3m1.1.4m1.1.5m1.1.6m1.1.7m1.1.8m1.1.9m 1.2.0-rc.0m 1.2.0-rc.1m1.2.0m1.2.1m1.2.2m1.2.3m1.2.4m1.2.5m 1.3.0-rc.0m 1.3.0-rc.1m 1.3.0-rc.2m 1.3.0-rc.3m1.3.0m1.3.1m1.3.2m1.3.3m1.3.4m 1.4.0-rc.0m 1.4.0-rc.1m 1.4.0-rc.2m 1.4.0-rc.3m1.4.0m1.4.1m1.4.2m1.4.3m1.4.4m1.4.5m1.4.6m1.4.7m1.4.8m1.4.9m1.4.10m1.4.11m1.4.12m1.4.13m1.4.14m1.4.15m1.4.16m1.4.17m 1.5.0-rc.0m1.5.0m1.5.1m1.5.2m1.5.3m1.5.4jbWLAhhddepsmhexpmmpostgrexm0.13.4lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 1.1dfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsej`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.7.0m . D4 .C jYg|Yh5T8ՒB GbWLAhhd timestampmhexpmmphoenixm1.4.12hhba aha a9a0bWLAhhddepsmhexpmmphoenixm1.1.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsej 1.0dfalsehmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 0.7dtruejgbWLAhhdinner_checksummhexpmmphoenix_pubsubm 1.0.0-rc.0m \S+@L?1+ xHXbWLAhhdinner_checksummhexpmmplugm0.5.1m X)],{/xL5_RxP)7bWLAhhdretiredmhexpmmpostgrexm0.11.2dnilbWLAhhddepsmhexpmmphoenixm1.5.4lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejFbWLAhhd timestampmhexpmmphoenixm1.5.2hhba aha a9a0[bWLAhhdinner_checksummhexpmmphoenixm1.2.4m ArG^!j^AuH 9 .c[bWLAhhddepsmhexpmmplugm1.6.2lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4dtruehmhexpmmmimemmimem~> 1.0dfalsejabWLAhhdinner_checksummhexpmm db_connectionm0.2.3m q 0.2.0dfalsejIbWLAhhd timestampmhexpmmjasonm 1.0.0-rc.3hhba aha a9a0abWLAhhdouter_checksummhexpmm db_connectionm2.0.6m h~YY'4 *Em-0f;a[bWLAhhdinner_checksummhexpmmphoenixm0.2.1m e/4QDU(R=7qPDdJ zkXbWLAhhdinner_checksummhexpmmmimem1.4.0m PfIDp(aF}/sQ̨/H5і|G5bWLAhhdretiredmhexpmmphoenixm0.9.0dnil7bWLAhhdretiredmhexpmm telemetrym0.4.1dnilbWLAhhddepsmhexpmm db_connectionm0.2.2lhmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 0.7dtruejbbWLAhhdinner_checksummhexpmmphoenix_paramsm1.1.0m L|a0ys~+a#oEo6W,-@5bWLAhhdretiredmhexpmmphoenixm1.0.4dnilFbWLAhhd timestampmhexpmmdecimalm0.2.0hhba aha a9a0[bWLAhhdouter_checksummhexpmmphoenixm1.0.2m VBSkeѠOU~9V#wk`RgzbWLAhhdretiredmhexpmm plug_cowboym2.2.1tdmessagemBroken telemetry supportdreasondRETIRED_INVALID2bWLAhhdretiredmhexpmmplugm1.6.3dnil 1.0.1 or ~> 1.1 or ~> 2.1dtruehmhexpmmmimemmimem~> 1.0dfalsejCbWLAhhd timestampmhexpmmplugm0.9.0hhba aha a9a0\bWLAhhdouter_checksummhexpmmpostgrexm0.8.3m @LMP'`Rۄ+O:Oy`bWLAhhdouter_checksummhexpmm phoenix_htmlm1.0.1m /+P3)׾Jabʶ$§O5o\bWLAhhdinner_checksummhexpmmphoenixm0.17.1m xG.rgk.W%Y49+kY-bWLAhhddepsmhexpmmdecimalm0.2.5j:bWLAhhdretiredmhexpmmphoenixm 1.4.0-rc.3dnilbWLAhhddepsmhexpmmphoenixm0.2.6lhmhexpmmex_confmex_confm0.1.2dfalsehmhexpmminflexminflexm0.2.0dfalsehmhexpmmjazzmjazzm0.1.1dfalsehmhexpmmplugmplugm0.4.4dfalsejCbWLAhhd timestampmhexpmmplugm1.4.1hhba aha a9a0XbWLAhhdinner_checksummhexpmmplugm1.2.5m |؋f4Bf w{їabWLAhhdouter_checksummhexpmm db_connectionm2.0.1m שZwysJAܿ)2I0Ű\1bWLAhhddepsmhexpmmplugm1.4.1lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsej 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsej]bWLAhhdouter_checksummhexpmmplugm 1.2.0-rc.0m ~Ie -JizŅkwiC iXbWLAhhdinner_checksummhexpmmplugm0.7.0m ;\W/ppe5ȖL{dhbbWLAhhdouter_checksummhexpmmphoenix_pubsubm1.1.1m ؐmQayܪ2DV'ŇVxul5bWLAhhdretiredmhexpmmphoenixm1.1.5dnil 1.0dfalsehmhexpmm db_connectionm db_connectionm ~> 1.0-rc.4dfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsej8bWLAhhdretiredmhexpmm connectionm1.0.1dnil[bWLAhhdinner_checksummhexpmmphoenixm0.2.7m A_y~rtw(P3T[i(VlbWLAhhddepsmhexpmmpostgrexm0.4.2lhmhexpmmdecimalmdecimalm~> 0.1.2dfalsej]bWLAhhdouter_checksummhexpmm telemetrym0.2.0m Nqy]YL?C i .x@#2bWLAhhddepsmhexpmmphoenixm1.1.8lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0.5dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsej1bWLAhhddepsmhexpmm plug_cryptom1.1.1j3bWLAhhdretiredmhexpmmplugm0.12.1dnilJbWLAhhd timestampmhexpmm plug_cowboym2.0.0hhba aha a9a0*bWLAhhddepsmhexpmmplugm0.4.2j[bWLAhhdouter_checksummhexpmmphoenixm1.5.1m '+8(y̮og鷐=hx^=#1RbbWLAhhdinner_checksummhexpmmphoenix_pubsubm0.0.1m |ļ[kY`dF>NSKt7bWLAhhdretiredmhexpmmpostgrexm0.14.2dnil]bWLAhhdouter_checksummhexpmmpostgrexm0.13.3m s)S*q',=u9 ODbWLAhhd timestampmhexpmmplugm1.10.3hhba aha a9a0\bWLAhhdinner_checksummhexpmmpostgrexm0.8.3m rQZi񏃡Sb>L%JEmʪ(h2HbWLAhhd timestampmhexpmmpostgrexm0.12.1hhba aha a9a0abWLAhhdinner_checksummhexpmm db_connectionm2.2.1m rT@g*bJƓfbWLAhhddepsmhexpmmplugm1.10.0lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dtruej^bWLAhhdouter_checksummhexpmmjasonm 1.0.0-rc.2m wv3ԉ ĘDo$2bWLAhhdretiredmhexpmmplugm1.2.3dnilabWLAhhdouter_checksummhexpmm db_connectionm2.0.5m x PCw tއ JPNϟ zFYfOsbWLAhhddepsmhexpmm phoenix_htmlm2.1.1lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsejXbWLAhhdinner_checksummhexpmmplugm0.8.2m 9[-!2bA&~YezA`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.4.0m ;ݙ\_^$HENf ^wQ52bWLAhhdretiredmhexpmmplugm0.4.4dnil;bWLAhhdretiredmhexpmm db_connectionm0.2.0dnil6bWLAhhdretiredmhexpmmphoenixm0.10.0dnil\bWLAhhdinner_checksummhexpmmphoenixm1.4.10m aJTUb)M"7-(#4ez$(UbbWLAhhdinner_checksummhexpmmphoenix_pubsubm0.1.0m zڏAI+Y@W<5y5^bWLAhhdouter_checksummhexpmmjasonm 1.0.0-rc.1m Iл;_,n4%G):bWLAhhdretiredmhexpmmphoenixm 1.3.0-rc.2dnilQbWLAhhd timestampmhexpmm db_connectionm 1.0.0-rc.2hhba aha a9a0FbWLAhhd timestampmhexpmmdecimalm1.0.0hhba aha a9a0FbWLAhhd timestampmhexpmmdecimalm1.3.0hhba aha a9a0MbWLAhhd timestampmhexpmmphoenix_paramsm0.2.2hhba aha a9a0[bWLAhhdinner_checksummhexpmmphoenixm1.4.3m Jd7,4rK֑Rv7]g}bWLAhhddepsmhexpmmphoenixm0.16.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 0.14 or ~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsej5bWLAhhdretiredmhexpmmphoenixm1.3.3dnilGbWLAhhd timestampmhexpmmpostgrexm0.5.1hhba aha a9a0[bWLAhhdinner_checksummhexpmmphoenixm0.6.2m {*NP<`]Ksƫj4[bWLAhhdinner_checksummhexpmmdecimalm0.2.0m [.ٙ)b9*O^GLzb}FVbWLAhhd registry_etagmhexpmm plug_cowboym""d9ec1d3801c046c701fa55c601b67a33"@bWLAhhdretiredmhexpmm db_connectionm 1.0.0-rc.4dnilYbWLAhhdinner_checksummhexpmmjasonm1.2.1m (%/F>Kˍ spD5bWLAhhdretiredmhexpmmphoenixm1.5.4dnilXbWLAhhdouter_checksummhexpmmplugm1.2.4m @<*< 0!Ǝ +D(;P}_bWLAhhdinner_checksummhexpmm plug_cryptom1.1.1m K`*BЄq'I5 FfbWLAhhdinner_checksummhexpmm db_connectionm 2.0.0-rc.0m $Fo'~PxMeR:ҿ}/ibWLAhhddepsmhexpmm phoenix_htmlm2.11.0lhmhexpmmplugmplugm~> 1.5dfalsejFbWLAhhd timestampmhexpmmphoenixm1.4.6hhba aha a9a0_bWLAhhdouter_checksummhexpmm plug_cowboym2.1.2m }r%ΆZ#~mopAt %k>c"p[bWLAhhdinner_checksummhexpmmdecimalm1.5.0m C:6C=P)e<7of[CיchHehYbWLAhhdouter_checksummhexpmmplugm1.10.3m z*3jDKʻ8l s9\bWLAhhdinner_checksummhexpmmphoenixm1.4.17m |ު}`݌"ChI@;8[bWLAhhdinner_checksummhexpmmphoenixm0.8.0m ҡ:l $,I 2aEӰ|K[bWLAhhdouter_checksummhexpmmdecimalm0.1.1m ZA57/P,#n^}[WBx;bWLAhhdretiredmhexpmm db_connectionm0.1.7dnil7bWLAhhdretiredmhexpmmplugm 1.5.0-rc.2dnilXbWLAhhdouter_checksummhexpmmplugm1.4.2m P@`%ܧDd[͑Z1"? [GbWLAhhd timestampmhexpmmphoenixm0.11.0hhba aha a9a0wbWLAhhddepsmhexpmm phoenix_htmlm1.4.0lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0dfalsej5bWLAhhdretiredmhexpmmdecimalm1.1.0dnilbWLAhhddepsmhexpmmpostgrexm0.13.0lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 1.1dfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsejbWLAhhddepsmhexpmmpostgrexm0.11.0lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 0.2dfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsej-bWLAhhddepsmhexpmmdecimalm1.3.1j\bWLAhhdinner_checksummhexpmmphoenixm0.17.0m fFznM[|q]TM8^_r,wXbWLAhhdinner_checksummhexpmmplugm1.5.1m [϶5^LG0?l)ҧoASe]bWLAhhdouter_checksummhexpmmpostgrexm0.15.2m u.ȃe. b 3#yi>q2bWLAhhdretiredmhexpmmplugm1.2.4dnilDbWLAhhd timestampmhexpmmjasonm1.1.0hhba aha a9a0bWLAhhddepsmhexpmmphoenixm0.12.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm ~> 0.12.1dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsehmhexpmmpoolboympoolboym~> 1.5.1dtruejbWLAhhddepsmhexpmmphoenixm1.0.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejLbWLAhhd timestampmhexpmm db_connectionm2.0.2hhba aha a9a02bWLAhhdretiredmhexpmmplugm1.3.1dniljbWLAhhddepsmhexpmmphoenixm1.4.14lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsejXbWLAhhdinner_checksummhexpmmplugm1.1.1m UWlٹUh e<>h|,{\{]bWLAhhdinner_checksummhexpmmpostgrexm0.15.0m SI>B/y\:B{Lc8bWLAhhdretiredmhexpmm connectionm1.0.0dnilFbWLAhhd timestampmhexpmmphoenixm1.2.3hhba aha a9a0XbWLAhhdinner_checksummhexpmmplugm1.6.3m C3{*/c5i~LSH,^XbWLAhhdouter_checksummhexpmmplugm0.8.4m "5 3T[ۼ~`2p2(5_bWLAhhdversionsmhexpmmpluglYm0.4.1m0.4.2m0.4.3m0.4.4m0.5.0m0.5.1m0.5.2m0.5.3m0.6.0m0.7.0m0.8.0m0.8.1m0.8.2m0.8.3m0.8.4m0.9.0m0.10.0m0.11.0m0.11.1m0.11.2m0.11.3m0.12.0m0.12.1m0.12.2m0.13.0m0.13.1m0.14.0m1.0.0m1.0.1m1.0.2m1.0.3m1.0.4m1.0.5m1.0.6m1.1.0m1.1.1m1.1.2m1.1.3m1.1.4m1.1.5m1.1.6m1.1.7m1.1.8m1.1.9m 1.2.0-rc.0m1.2.0m1.2.1m1.2.2m1.2.3m1.2.4m1.2.5m1.2.6m1.3.0m1.3.1m1.3.2m1.3.3m1.3.4m1.3.5m1.3.6m 1.4.0-rc.0m1.4.0m1.4.1m1.4.2m1.4.3m1.4.4m1.4.5m 1.5.0-rc.0m 1.5.0-rc.1m 1.5.0-rc.2m1.5.0m1.5.1m1.6.0m1.6.1m1.6.2m1.6.3m1.6.4m1.7.0m1.7.1m1.7.2m1.8.0m1.8.1m1.8.2m1.8.3m1.9.0m1.10.0m1.10.1m1.10.2m1.10.3m1.10.4jFbWLAhhd timestampmhexpmmphoenixm1.3.4hhba aha a9a0XbWLAhhdinner_checksummhexpmmplugm1.8.0m &٬'(&+x )oCwabWLAhhdinner_checksummhexpmm db_connectionm0.1.1m T@EWB*tQ]!-Ι)`bWLAhhdouter_checksummhexpmmphoenixm 1.2.0-rc.0m ߒҏ&=b Oֵ> #iZn`ZSakP[bWLAhhdouter_checksummhexpmmphoenixm0.4.1m 'Y Kj]MunLT܋YgYbWLAhhdinner_checksummhexpmmplugm0.11.3m v;X5!6חtwI`+?WJcbWLAhhddepsmhexpmmplugm1.0.5lhmhexpmmcowboymcowboym~> 1.0dtruejabWLAhhdinner_checksummhexpmm phoenix_htmlm2.12.0m KfUd=6UFֵSL+Fv">bWLAhhdretiredmhexpmm phoenix_htmlm 2.4.0-devdnildbWLAhhddepsmhexpmmplugm0.14.0lhmhexpmmcowboymcowboym~> 1.0dtruej]bWLAhhdinner_checksummhexpmm telemetrym0.3.0m <Gqc <"f" :*:jguJs;bWLAhhdretiredmhexpmm phoenix_htmlm2.10.4dnil8bWLAhhdretiredmhexpmm connectionm1.0.2dnilbWLAhhddepsmhexpmmpostgrexm0.11.1lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 0.2dfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsej'bWLAhhddepsmhexpmmpostgrexm 0.14.0-rc.1lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm ~> 2.0-rc.0dfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsehmhexpmmjasonmjasonm~> 1.0dtruejYbWLAhhdouter_checksummhexpmmjasonm1.1.1m cEϬ2^4gry:42•%#LRbWLAhhd timestampmhexpmmphoenix_pubsubm 1.0.0-rc.0hhba aha a9a0HbWLAhhd timestampmhexpmm telemetrym0.2.0hhba aha a9a0CbWLAhhd timestampmhexpmmplugm1.2.0hhba aha a9a0abWLAhhdinner_checksummhexpmm db_connectionm2.0.0m ⌇5)U]dV=4 M^6bWLAhhdretiredmhexpmmpostgrexm0.5.4dnil[bWLAhhdinner_checksummhexpmmdecimalm1.2.0m F)`q(.W{Gkkh'}M dcRiK [bWLAhhdinner_checksummhexpmmphoenixm0.2.9m (N76eVҫbnK;L}dbbWLAhhdouter_checksummhexpmmphoenix_paramsm0.4.2m '͓aoD&ZŶz`)}tkKbWLAhhddepsmhexpmmphoenixm0.11.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm ~> 0.11.3dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsehmhexpmmpoolboympoolboym~> 1.4.2dtruejHbWLAhhd timestampmhexpmmplugm 1.5.0-rc.2hhba aha a9a0[bWLAhhdouter_checksummhexpmmphoenixm1.1.9m 1gxف 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0dfalsehmhexpmm telemetrym telemetrym~> 0.4dtruejDbWLAhhd timestampmhexpmmplugm0.13.0hhba aha a9a0YbWLAhhdinner_checksummhexpmmplugm1.10.3m ξv7u9jܠiN4n?Bƒ_bWLAhhdouter_checksummhexpmm plug_cowboym2.0.2m DSݢiu| Mq 1bWLAhhddepsmhexpmmplugm1.4.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsej\bWLAhhdouter_checksummhexpmmphoenixm1.4.15m dv#f mW7JܻiI5bWLAhhdretiredmhexpmmphoenixm0.2.3dnilfbWLAhhdouter_checksummhexpmm db_connectionm 1.0.0-rc.3m )Xω+IOW10KT@%2B 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.1dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej2bWLAhhdretiredmhexpmmplugm1.4.1dnil]bWLAhhdinner_checksummhexpmmpostgrexm0.11.0m ۙt*Wml'E- 6`B,bWLAhhddepsmhexpmmphoenixm 1.3.0-rc.2lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.3.2 or ~> 1.4dfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0dfalsejMbWLAhhd timestampmhexpmmphoenix_paramsm0.2.1hhba aha a9a0FbWLAhhd timestampmhexpmmphoenixm0.7.1hhba aha a9a0[bWLAhhdouter_checksummhexpmmphoenixm1.0.0m s/L!p_1iRV䒥W[bWLAhhdouter_checksummhexpmmphoenixm1.4.8m 9e" MrJgjt--*bWLAhhddepsmhexpmmmimem1.2.0jcbWLAhhddepsmhexpmmplugm1.0.0lhmhexpmmcowboymcowboym~> 1.0dtruej]bWLAhhdouter_checksummhexpmmpostgrexm0.12.2m YwB(N)0*$}bbWLAhhdouter_checksummhexpmmphoenix_pubsubm1.0.2m o6L]kӨ4lVˮft2bWLAhhdretiredmhexpmmplugm1.1.6dnil5bWLAhhdretiredmhexpmmphoenixm0.7.0dnilCbWLAhhd timestampmhexpmmplugm1.2.3hhba aha a9a0bWLAhhddepsmhexpmmplugm1.3.3lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsejbWLAhhddepsmhexpmmpostgrexm0.11.2lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm ~> 1.0-rcdfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsej3bWLAhhdretiredmhexpmmjasonm1.0.1dnil6bWLAhhdretiredmhexpmmpostgrexm0.8.3dnil\bWLAhhdouter_checksummhexpmmpostgrexm0.9.0m MjOe7@U(;8Om!gy?@$`bWLAhhdouter_checksummhexpmmdecimalm 2.0.0-rc.0m %^,>8W$d. 0.13 or ~> 1.0dfalsejbbWLAhhdinner_checksummhexpmmphoenix_pubsubm1.1.0m ^%/dcfa-PSYD2bWLAhhdretiredmhexpmmplugm1.7.2dnil[bWLAhhdouter_checksummhexpmmdecimalm1.9.0m 45h֒>u[] o}`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.6.0m =cEGw=v2ztUi7bWLAhhdretiredmhexpmmpostgrexm0.14.0dnil:bWLAhhdretiredmhexpmm phoenix_htmlm2.7.0dnilKbWLAhhd timestampmhexpmm phoenix_htmlm2.3.1hhba aha a9a02bWLAhhdretiredmhexpmmplugm1.4.2dnil[bWLAhhdouter_checksummhexpmmphoenixm1.1.2m dƼmd/(VYZ#D_bWLAhhdinner_checksummhexpmm plug_cryptom1.1.2m чW,mq6)%򵀡VB0';bWLAhhdretiredmhexpmm db_connectionm0.2.2dnil3bWLAhhdretiredmhexpmmjasonm1.2.0dnil_bWLAhhdouter_checksummhexpmm plug_cowboym2.1.0m lѽM?G}gaSՍ@CzF2[bWLAhhdouter_checksummhexpmmphoenixm1.2.2m vLJUԾ=er*b+Qb: ڨ`( ylbWLAhhddepsmhexpmm phoenix_htmlm 2.7.0-devlhmhexpmmplugmplugm~> 1.0dfalsejbWLAhhddepsmhexpmmpostgrexm0.14.2lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 2.0dfalsehmhexpmmdecimalmdecimalm~> 1.5dfalsehmhexpmmjasonmjasonm~> 1.0dtruejfbWLAhhdinner_checksummhexpmm db_connectionm 1.0.0-rc.5m zhf|/uƜ*F7 [bWLAhhdinner_checksummhexpmmdecimalm0.2.2m b^BJ;@>ҜLz:K sbWLAhhddepsmhexpmm phoenix_htmlm2.5.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsej5bWLAhhdretiredmhexpmmphoenixm0.8.0dnil]bWLAhhdinner_checksummhexpmmplugm 1.4.0-rc.0m Solm$a63;3r~[ YbWLAhhdinner_checksummhexpmmjasonm1.2.0m 4-$!-?]HCv 8yY8H:bWLAhhdretiredmhexpmm phoenix_htmlm2.5.0dnil[bWLAhhdinner_checksummhexpmmphoenixm1.4.4m ],4C 6Wq9wrh2N[bWLAhhdouter_checksummhexpmmphoenixm1.3.1m e+a!= ETsyFML2hX2([bWLAhhdinner_checksummhexpmmphoenixm0.2.0m lE$ KMb™ua@Eh4 0.13 or ~> 1.0dfalsejDbWLAhhdversionsmhexpmmdecimallm0.1.1m0.1.2m0.2.0m0.2.1m0.2.2m0.2.3m0.2.4m0.2.5m1.0.0m1.0.1m1.1.0m1.1.1m1.1.2m1.2.0m1.3.0m1.3.1m1.4.0m1.4.1m1.5.0m1.6.0m1.7.0m1.8.0m1.8.1m 1.9.0-rc.0m1.9.0m 2.0.0-rc.0m2.0.0jVbWLAhhddepsmhexpmmphoenixm 1.2.0-rc.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmmimemmimem~> 1.0dfalsehmhexpmmphoenix_pubsubmphoenix_pubsubm ~> 1.0.0-rcdfalsehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsejcbWLAhhddepsmhexpmmplugm1.1.4lhmhexpmmcowboymcowboym~> 1.0dtruej;bWLAhhdretiredmhexpmm db_connectionm1.1.0dnilcbWLAhhdouter_checksummhexpmm connectionm 1.0.0-rc.1m Z{ax:`Np!rjH0bWLAhhddepsmhexpmm connectionm1.0.3jCbWLAhhd timestampmhexpmmmimem1.4.0hhba aha a9a0CbWLAhhd timestampmhexpmmplugm1.1.0hhba aha a9a0-bWLAhhddepsmhexpmmdecimalm1.7.0j[bWLAhhdinner_checksummhexpmmphoenixm1.0.5m E)PQ!jflW1)dXbWLAhhdinner_checksummhexpmmplugm1.0.3m LQp\!Svx$XbWLAhhdinner_checksummhexpmmplugm0.5.0m r"cuM>͖ x7ɮ4|)uR#vt`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.8.0m wu`֫{%ɯ)dNH"Θ ]bWLAhhdinner_checksummhexpmmpostgrexm0.13.4m 1TQچjEm1Щ#nZA[bWLAhhdouter_checksummhexpmmphoenixm0.4.0m 碆B~@9YUOb*bXbWLAhhdinner_checksummhexpmmplugm1.0.5m $W .:ѵȫ2RU)wJbWLAhhddepsmhexpmm plug_cowboym2.0.0lhmhexpmmcowboymcowboym~> 2.5dfalsehmhexpmmplugmplugm~> 1.7dfalsejXbWLAhhdouter_checksummhexpmmplugm1.0.0m YC1S [(R-7Vc bWLAhhddepsmhexpmm plug_cowboym1.0.0lhmhexpmmcowboymcowboym~> 1.0dfalsehmhexpmmplugmplugm~> 1.7dfalsej[bWLAhhdinner_checksummhexpmmphoenixm0.3.1m wg!m m~!SPbJXig0a+2bWLAhhdretiredmhexpmmplugm1.7.0dnil]bWLAhhdinner_checksummhexpmmpostgrexm0.10.0m XimaѩtRqM"M2Տ-XbWLAhhdinner_checksummhexpmmplugm0.8.0m aC-N`|~<>&ҸEX.ędbWLAhhddepsmhexpmmplugm0.10.0lhmhexpmmcowboymcowboym~> 1.0dtruejbWLAhhddepsmhexpmmphoenix_paramsm1.1.1lhmhexpmmdecimalmdecimalm>= 1.7.0dfalsehmhexpmmphoenixmphoenixm>= 1.3.3dfalsejbWLAhhddepsmhexpmmphoenixm0.2.11lhmhexpmmex_confmex_confm0.1.2dfalsehmhexpmminflexminflexm0.2.4dfalsehmhexpmmjazzmjazzm0.1.2dfalsehmhexpmmplugmplugm0.5.1dfalsej5bWLAhhdretiredmhexpmmdecimalm1.1.1dnil[bWLAhhdouter_checksummhexpmmdecimalm2.0.0m 4fnUި}7b2j# DwabWLAhhdouter_checksummhexpmm db_connectionm2.0.0m jE//<ьήVv HbWLAhhd timestampmhexpmm telemetrym0.3.0hhba aha a9a05bWLAhhdretiredmhexpmmphoenixm0.7.2dnil bWLAhhddepsmhexpmmphoenixm0.6.2lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmlinguistmlinguistm~> 0.1.4dfalsehmhexpmmplugmplugm~> 0.8.4dfalsehmhexpmmpoisonmpoisonm~> 1.2dfalsejCbWLAhhd timestampmhexpmmplugm1.8.2hhba aha a9a0\bWLAhhdinner_checksummhexpmmphoenixm0.2.10m !/I7תck[%NS ^bWLAhhdouter_checksummhexpmm connectionm1.0.1m B KK{41ヺs[bWLAhhdinner_checksummhexpmmphoenixm1.3.0m LJzo PgA]$>ʼnCbWLAhhd timestampmhexpmmplugm1.4.0hhba aha a9a0LbWLAhhd timestampmhexpmm db_connectionm2.0.0hhba aha a9a0`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.5.1m cSE\&m'BipgB_XbWLAhhdinner_checksummhexpmmplugm1.3.2m غ.,}i!YdU Uac-GbWLAhhd timestampmhexpmmpostgrexm0.5.3hhba aha a9a0;bWLAhhdretiredmhexpmm phoenix_htmlm2.10.3dniljbWLAhhddepsmhexpmmpostgrexm0.8.1lhmhexpmmdecimalmdecimalm~> 1.0dfalsej 0.2.1dfalsejVbWLAhhd registry_etagmhexpmm plug_cryptom""2e69c8aae7f0030e224e283fdf6117dc"XbWLAhhdouter_checksummhexpmmplugm1.1.8m -nOWz?~iW6F1؀,J[bWLAhhdinner_checksummhexpmmdecimalm1.0.0m 4") h_S)3rm"K^5bWLAhhdretiredmhexpmmphoenixm1.5.3dnilDbWLAhhd timestampmhexpmmplugm0.11.0hhba aha a9a0ibWLAhhddepsmhexpmm phoenix_htmlm2.13.4lhmhexpmmplugmplugm~> 1.5dfalsejGbWLAhhd timestampmhexpmmpostgrexm0.5.5hhba aha a9a0`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.0.1m ߲8MςÝzffSpo&jbWLAhhddepsmhexpmmpostgrexm0.9.0lhmhexpmmdecimalmdecimalm~> 1.0dfalsej\bWLAhhdinner_checksummhexpmmphoenixm0.14.0m q;{;  )=oR b/HbWLAhhd timestampmhexpmmpostgrexm0.11.1hhba aha a9a0jbWLAhhddepsmhexpmmphoenixm1.4.13lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsej 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 2.1dfalsehmhexpmmdecimalmdecimalm~> 1.5dfalsehmhexpmmjasonmjasonm~> 1.0dtruejHbWLAhhd timestampmhexpmmpostgrexm0.15.5hhba aha a9a0KbWLAhhd timestampmhexpmm phoenix_htmlm2.6.0hhba aha a9a0FbWLAhhd timestampmhexpmmphoenixm0.9.0hhba aha a9a0CbWLAhhd timestampmhexpmmplugm0.8.0hhba aha a9a06bWLAhhdretiredmhexpmmpostgrexm0.7.0dnilFbWLAhhd timestampmhexpmmdecimalm0.2.4hhba aha a9a0XbWLAhhdouter_checksummhexpmmmimem1.0.1m ^m r,)G FbWLAhhd timestampmhexpmmphoenixm1.4.4hhba aha a9a02bWLAhhdretiredmhexpmmplugm0.5.0dniljbWLAhhddepsmhexpmmphoenixm1.4.11lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsej!5Ù~ 0bWLAhhddepsmhexpmm connectionm1.0.4j`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.3.1m ?in`"yԡ_n}fo0-BLbWLAhhd timestampmhexpmmpostgrexm 1.0.0-rc.1hhba aha a9a0[bWLAhhdouter_checksummhexpmmphoenixm1.1.4m We#ΒB;C?DpQ YzebWLAhhddepsmhexpmmplugm0.8.1lhmhexpmmcowboymcowboym~> 1.0.0dtruej"bWLAhhddepsmhexpmmphoenixm1.4.0lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruejObWLAhhd timestampmhexpmm phoenix_htmlm 2.4.0-devhhba aha a9a0`bWLAhhdinner_checksummhexpmmphoenixm 1.3.0-rc.0m Ni!{19n}o⾊шùG2bWLAhhdretiredmhexpmmplugm1.3.6dnilbbWLAhhdouter_checksummhexpmmpostgrexm 0.14.0-rc.1m =8HMܦBϕwO5r"iYbWLAhhdouter_checksummhexpmmjasonm1.2.0m gGWyL:>N;|cNNJYu.LbWLAhhd timestampmhexpmm phoenix_htmlm2.11.0hhba aha a9a0bWLAhhddepsmhexpmmphoenixm1.5.3lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej]bWLAhhdinner_checksummhexpmmpostgrexm0.12.2m ׄHx3\8)c%&XcbWLAhhddepsmhexpmmplugm1.1.0lhmhexpmmcowboymcowboym~> 1.0dtruej7bWLAhhdretiredmhexpmmpostgrexm0.11.0dnilMbWLAhhd timestampmhexpmmphoenix_pubsubm1.0.2hhba aha a9a0XbWLAhhdinner_checksummhexpmmplugm1.2.1m ܇ Te N+o>&t'ibWLAhhddepsmhexpmm phoenix_htmlm2.11.1lhmhexpmmplugmplugm~> 1.5dfalsej5bWLAhhddepsmhexpmm connectionm 1.0.0-rc.1j2bWLAhhdretiredmhexpmmplugm1.0.2dnilabWLAhhdouter_checksummhexpmm phoenix_htmlm2.13.1m BϩNޖ3@ٜ4]Om鲳BdǡdGbWLAhhd timestampmhexpmmphoenixm0.12.0hhba aha a9a0]bWLAhhdinner_checksummhexpmmpostgrexm0.11.2m U5<\mn%VӞ'FmߴB<4ibWLAhhddepsmhexpmm phoenix_htmlm2.10.0lhmhexpmmplugmplugm~> 1.0dfalsejFbWLAhhd timestampmhexpmmphoenixm1.4.9hhba aha a9a0\bWLAhhdinner_checksummhexpmmpostgrexm0.5.5m d : .υ!ȗ6!̫bWLAhhddepsmhexpmm db_connectionm0.1.1lhmhexpmmbackoffmbackoffm~> 1.0dfalsehmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 0.7dtruejXbWLAhhdinner_checksummhexpmmplugm1.6.0m 8Lb->$rE[QTKwSo\bWLAhhdinner_checksummhexpmmpostgrexm0.5.3m i'Jz5 APp@l/s+OlbWLAhhddepsmhexpmmpostgrexm0.6.0lhmhexpmmdecimalmdecimalm~> 0.2.3dfalsej[bWLAhhdouter_checksummhexpmmdecimalm1.0.0m T ! oC d0'${ՓBt[bWLAhhdouter_checksummhexpmmphoenixm1.0.6m 7.zUH}c[u뷃ܞ^6mü SbWLAhhd registry_etagmhexpmmpostgrexm""35d9bc39d21b39e549bb40de95a08cda"bWLAhhddepsmhexpmmphoenix_paramsm0.4.0lhmhexpmmdecimalmdecimalm>= 1.7.0dfalsehmhexpmmphoenixmphoenixm>= 1.3.3dfalsej'bWLAhhddepsmhexpmmphoenixm 1.4.0-rc.3lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruejbWLAhhddepsmhexpmmphoenixm0.2.3lhmhexpmmex_confmex_confm0.1.1dfalsehmhexpmminflexminflexm0.2.0dfalsehmhexpmmjazzmjazzm0.1.0dfalsehmhexpmmplugmplugm0.4.3dfalsej 1.8dfalsehmhexpmmphoenixmphoenixm~> 1.3dfalsej[bWLAhhdinner_checksummhexpmmphoenixm1.2.0m ޹%OLSLߘ )|b]Wb|;f]bWLAhhdinner_checksummhexpmm telemetrym0.4.2m (ɒE^s"M;kb_#:sPXps/EbWLAhhddepsmhexpmmphoenixm1.2.2lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.1dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsejFbWLAhhd timestampmhexpmmphoenixm0.7.2hhba aha a9a0XbWLAhhdinner_checksummhexpmmmimem1.3.1m 01u \w=R=sxM<HbWLAhhd timestampmhexpmmpostgrexm0.14.1hhba aha a9a0jbWLAhhddepsmhexpmmpostgrexm0.8.2lhmhexpmmdecimalmdecimalm~> 1.0dfalsej5bWLAhhdretiredmhexpmmdecimalm1.7.0dnil_bWLAhhdouter_checksummhexpmm plug_cowboym2.2.1m ;C$F (nz b䎷#YTh ]޶XbWLAhhdouter_checksummhexpmmplugm1.6.2m q<W&a +UlALu{XbWLAhhdouter_checksummhexpmmplugm0.8.1m Pߙ%5s}_ݡD/TD][bWLAhhdinner_checksummhexpmmdecimalm0.1.1m }VRqtgNpjY$[bWLAhhdouter_checksummhexpmmphoenixm0.7.1m 7R8CIY<+h Aa&G3bWLAhhdretiredmhexpmmjasonm1.1.1dnilFbWLAhhd timestampmhexpmmphoenixm0.6.0hhba aha a9a0dbWLAhhddepsmhexpmmplugm0.13.1lhmhexpmmcowboymcowboym~> 1.0dtruejbWLAhhdversionsmhexpmmphoenix_paramslm0.1.0m0.1.1m0.1.2m0.2.0m0.2.1m0.2.2m0.3.0m0.4.0m0.4.1m0.4.2m1.0.0m1.0.1m1.0.3m1.1.0m1.1.1m1.1.2m1.1.3j-bWLAhhddepsmhexpmmdecimalm0.2.1j77Myd kFbWLAhhd timestampmhexpmmdecimalm0.1.2hhba aha a9a0:bWLAhhdretiredmhexpmm phoenix_htmlm2.2.0dnil2bWLAhhdretiredmhexpmmmimem1.2.0dnil]bWLAhhdouter_checksummhexpmmpostgrexm0.15.0m ,O|]_ "I6XsL5+bWLAhhddepsmhexpmm plug_cowboym2.0.2lhmhexpmmcowboymcowboym~> 2.5dfalsehmhexpmmplugmplugm~> 1.7dfalsejdbWLAhhdouter_checksummhexpmm phoenix_htmlm 2.4.0-devm 0a/@/։ꠦ=;Aj:bWLAhhdretiredmhexpmm phoenix_htmlm1.0.1dnilbWLAhhddepsmhexpmmplugm1.4.5lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsejBbWLAhhd timestampmhexpmm db_connectionhhba aha a9a0abWLAhhdinner_checksummhexpmm db_connectionm0.1.0m ҇4gMbzYw|} bGbWLAhhd timestampmhexpmmpostgrexm0.7.0hhba aha a9a0:bWLAhhdretiredmhexpmmphoenixm 1.5.0-rc.0dnilpbWLAhhdversionsmhexpmm connectionlm 1.0.0-rc.1m1.0.0m1.0.1m1.0.2m1.0.3m1.0.4j 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm,~> 1.3.3 or ~> 1.2.4 or ~> 1.1.8 or ~> 1.0.5dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsejJbWLAhhd timestampmhexpmm plug_cowboym2.0.2hhba aha a9a0YbWLAhhdinner_checksummhexpmmjasonm1.0.1m d6K<p3T]s.g]bWLAhhdinner_checksummhexpmmpostgrexm0.11.1m XѪVBsbETϸ7rkjJXbWLAhhdinner_checksummhexpmmplugm0.6.0m b#C(lm6 t{-abWLAhhdouter_checksummhexpmm db_connectionm1.1.1m oxZ#L2* ?~1rF5DQbWLAhhd timestampmhexpmm db_connectionm 1.0.0-rc.0hhba aha a9a0CbWLAhhd timestampmhexpmmplugm0.4.1hhba aha a9a0,bWLAhhddepsmhexpmmphoenixm 1.3.0-rc.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.3.2 or ~> 1.4dfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0dfalsejLbWLAhhd timestampmhexpmm phoenix_htmlm2.11.2hhba aha a9a06bWLAhhdretiredmhexpmmphoenixm1.4.10dnilXbWLAhhdouter_checksummhexpmmplugm1.4.0m p>PPSm mɆEkbWLAhhddepsmhexpmmjasonm 1.0.0-rc.1lhmhexpmmdecimalmdecimalm~> 1.0dtruej[bWLAhhdinner_checksummhexpmmphoenixm1.4.0m Vs_>;1IԴ6fb{abWLAhhdinner_checksummhexpmm db_connectionm1.0.0m c>R TjfM4# KYl!Y,֤ abWLAhhdouter_checksummhexpmm phoenix_htmlm2.10.5m O82cηF|IUTu yQ`^E3bWLAhhdretiredmhexpmmplugm0.12.0dnilKbWLAhhd timestampmhexpmmphoenixm 1.3.0-rc.2hhba aha a9a0CbWLAhhd timestampmhexpmmplugm0.8.1hhba aha a9a0CbWLAhhd timestampmhexpmmplugm1.8.0hhba aha a9a0CbWLAhhd timestampmhexpmmplugm1.0.0hhba aha a9a02bWLAhhdretiredmhexpmmplugm1.1.4dnilfbWLAhhddepsmhexpmmjasonm1.1.1lhmhexpmmdecimalmdecimalm~> 1.0dtruej^bWLAhhdinner_checksummhexpmm connectionm1.0.4m "w>f%Jaj}YvfbWLAhhddepsmhexpmmjasonm1.2.1lhmhexpmmdecimalmdecimalm~> 1.0dtruej=bWLAhhdretiredmhexpmm connectionm 1.0.0-rc.1dnilabWLAhhdouter_checksummhexpmm db_connectionm2.1.0m MނBƗg`z" [XbWLAhhdinner_checksummhexpmmplugm1.8.3m ym.aNL Be ZhDxsbWLAhhddepsmhexpmm phoenix_htmlm2.1.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsejXbWLAhhdouter_checksummhexpmmplugm1.1.1m  E!/&uW><fJ]pqbWLAhhddepsmhexpmmphoenixm0.3.0lhmhexpmmex_confmex_confm0.1.2dfalsehmhexpmminflexminflexm0.2.4dfalsehmhexpmmjazzmjazzm0.1.2dfalsehmhexpmmplugmplugm0.5.1dfalsejrbWLAhhddepsmhexpmmphoenix_paramsm0.2.1lhmhexpmmphoenixmphoenixm>= 1.3.3dfalsejXbWLAhhdouter_checksummhexpmmplugm1.1.0m iijK'%.? -ޙ/قKabWLAhhdouter_checksummhexpmm db_connectionm0.2.5m C[oչ6rd߸i^$Zp/abWLAhhdouter_checksummhexpmm db_connectionm2.2.1m +-?@ND;}efCTk+RQy!¦abWLAhhdouter_checksummhexpmm db_connectionm0.2.1m #4@xkLtƳ6@F-gbWLAhhdouter_checksummhexpmmphoenix_pubsubm 1.0.0-rc.0m v\f_vM^Z"q9ۏ ;W_[bWLAhhdouter_checksummhexpmmphoenixm1.1.1m Iu꿣 CMMbK[bWLAhhdinner_checksummhexpmmphoenixm1.0.1m .ٝrl#H[;'r9p؂3bWLAhhddepsmhexpmmphoenixm0.2.4lhmhexpmmex_confmex_confm0.1.1dfalsehmhexpmminflexminflexm0.2.0dfalsehmhexpmmplugmplugm0.4.3dfalsejibWLAhhddepsmhexpmm phoenix_htmlm2.14.2lhmhexpmmplugmplugm~> 1.5dfalsejKbWLAhhd timestampmhexpmmphoenixm 1.2.0-rc.1hhba aha a9a0[bWLAhhdouter_checksummhexpmmphoenixm0.7.0m $y|6; w'Px`j1TELbWLAhhd timestampmhexpmm db_connectionm0.1.5hhba aha a9a0@bWLAhhd timestampmhexpmm plug_cowboyhhba aha a9a0IbWLAhhd timestampmhexpmm connectionm1.0.4hhba aha a9a04bWLAhhddepsmhexpmmphoenix_pubsubm0.1.0jabWLAhhdouter_checksummhexpmm db_connectionm0.2.3m haԂBfAUEW,+JI oabWLAhhdinner_checksummhexpmm db_connectionm0.2.4m Q\?{ffCx6mC(TXbWLAhhdouter_checksummhexpmmplugm1.8.0m L [8Ɵ*x}kTot$XbWLAhhdinner_checksummhexpmmplugm1.1.8m ۋmyRDjp2CXbWLAhhdinner_checksummhexpmmplugm0.8.3m ˯eF[q|C8S>!VTObWLAhhd registry_etagmhexpmmplugm""a35d954d250109e64532cce56060f54b"~bWLAhhdretiredmhexpmmpostgrexm 1.0.0-rc.0tdmessagemSuperseded by v0.13.0-rc.0dreasondRETIRED_INVALIDYbWLAhhdouter_checksummhexpmmplugm1.10.1m R%1EII*,]bWLAhhdinner_checksummhexpmmpostgrexm0.13.5m =)6>CzKm$+bbWLAhhdouter_checksummhexpmmphoenix_paramsm0.4.0m W؟}tE+qǹnԾXp&/'p`bWLAhhdouter_checksummhexpmm phoenix_htmlm1.2.1m N ĪI,"sin ]}FbWLAhhd timestampmhexpmmphoenixm1.5.3hhba aha a9a0bWLAhhddepsmhexpmm db_connectionm0.1.8lhmhexpmmbackoffmbackoffm1.1.1dfalsehmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 0.7dtruejXbWLAhhdversionsmhexpmm plug_cryptolm1.0.0m1.1.0m1.1.1m1.1.2jabWLAhhdouter_checksummhexpmm db_connectionm0.1.5m uXһ >>ʃjGmoۢMc8bWLAhhdretiredmhexpmmjasonm 1.0.0-rc.1dnil[bWLAhhdinner_checksummhexpmmphoenixm1.3.2m *QpkNn~ˊ,q痍\sxbWLAhhdversionsmhexpmm plug_cowboyl m1.0.0m2.0.0m2.0.1m2.0.2m2.1.0m2.1.1m2.1.2m2.1.3m2.2.0m2.2.1m2.2.2m2.3.0jGbWLAhhd timestampmhexpmmpostgrexm0.5.4hhba aha a9a0FbWLAhhd timestampmhexpmmphoenixm0.2.5hhba aha a9a0GbWLAhhd timestampmhexpmmpostgrexm0.5.0hhba aha a9a0`bWLAhhdinner_checksummhexpmmphoenixm 1.5.0-rc.0m 8;njxPZh>"Ξǣ,[bWLAhhdouter_checksummhexpmmphoenixm1.2.3m ̷ĨK/c?W?8%it<€3bWLAhhdretiredmhexpmmplugm1.10.3dnil;bWLAhhdretiredmhexpmm db_connectionm2.1.1dnilhbWLAhhddepsmhexpmm phoenix_htmlm2.9.0lhmhexpmmplugmplugm~> 1.0dfalsej\bWLAhhdinner_checksummhexpmmphoenixm0.15.0m JRlá=z^J@"!KlbWLAhhddepsmhexpmmpostgrexm0.5.3lhmhexpmmdecimalmdecimalm~> 0.2.3dfalsejhbWLAhhddepsmhexpmm phoenix_htmlm2.6.1lhmhexpmmplugmplugm~> 1.0dfalsejabWLAhhdinner_checksummhexpmm db_connectionm0.2.1m CaC,ʔ'-ŀҁ L^bWLAhhdinner_checksummhexpmmjasonm 1.0.0-rc.1m BNnn|+dcXah=S`F%XbWLAhhdouter_checksummhexpmmplugm0.5.1m '#ryNP9Xjk_C.j/[bWLAhhdinner_checksummhexpmmdecimalm1.7.0m 0ֵ,TfcsYPߞf S_~Ū2bWLAhhdretiredmhexpmmplugm0.5.3dnilbWLAhhddepsmhexpmmpostgrexm0.13.2lhmhexpmm connectionm connectionm~> 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 1.1dfalsehmhexpmmdecimalmdecimalm~> 1.0dfalsej\bWLAhhdouter_checksummhexpmmphoenixm1.4.12m X3mw1*=o4%F0[bWLAhhdouter_checksummhexpmmphoenixm1.4.9m 3&oUu xOՔ_tz읰.cbWLAhhddepsmhexpmmplugm0.8.2lhmhexpmmcowboymcowboym~> 1.0dtruej^bWLAhhdinner_checksummhexpmm connectionm1.0.2m mAAfL.ĸu0iG(/0xjbWLAhhddepsmhexpmmpostgrexm0.9.1lhmhexpmmdecimalmdecimalm~> 1.0dfalsej[bWLAhhdouter_checksummhexpmmphoenixm1.4.3m ܨwg*Khb+l0Bňi1X[bWLAhhdinner_checksummhexpmmphoenixm1.5.2m {]lNvA I/jIgGbWLAhhd timestampmhexpmmphoenixm1.4.16hhba aha a9a0/bWLAhhddepsmhexpmm telemetrym0.2.0jFbWLAhhd timestampmhexpmmphoenixm1.3.1hhba aha a9a0]bWLAhhdouter_checksummhexpmmpostgrexm0.11.2m <$<ʎ sk{aJFQ$uBSXbWLAhhdinner_checksummhexpmmplugm1.1.7m @MMerFL/YH"[fbWLAhhdinner_checksummhexpmm db_connectionm 1.0.0-rc.0m ޽OLk;L l9c-ĵGbWLAhhd timestampmhexpmmpostgrexm0.5.2hhba aha a9a0bWLAhhddepsmhexpmmphoenix_paramsm1.0.1lhmhexpmmdecimalmdecimalm>= 1.7.0dfalsehmhexpmmphoenixmphoenixm>= 1.3.3dfalsej6bWLAhhdretiredmhexpmmpostgrexm0.8.0dnil[bWLAhhdinner_checksummhexpmmdecimalm1.4.1m Ps""|* ږ[ pebWLAhhddepsmhexpmmplugm0.5.3lhmhexpmmcowboymcowboym~> 1.0.0dtruejLbWLAhhd timestampmhexpmm phoenix_htmlm2.11.1hhba aha a9a02bWLAhhdretiredmhexpmmplugm0.4.1dnil5bWLAhhdretiredmhexpmmdecimalm1.8.0dnilCbWLAhhd timestampmhexpmmplugm1.1.1hhba aha a9a0[bWLAhhdinner_checksummhexpmmdecimalm0.2.4m qt6Dh2A?k't*PgLICVFbWLAhhd timestampmhexpmmdecimalm0.2.2hhba aha a9a0abWLAhhdinner_checksummhexpmm db_connectionm2.2.0m #與`$^EАUˇj슶ޢBL$AN8XbWLAhhdouter_checksummhexpmmplugm1.0.6m mK-7e ͦS'5bWLAhhdretiredmhexpmmphoenixm0.2.2dnil 1.0dfalsehmhexpmm db_connectionm db_connectionm~> 2.0dfalsehmhexpmmdecimalmdecimalm~> 1.5dfalsehmhexpmmjasonmjasonm~> 1.0dtruejhbWLAhhddepsmhexpmm phoenix_htmlm2.9.3lhmhexpmmplugmplugm~> 1.0dfalsej_bWLAhhdouter_checksummhexpmm plug_cryptom1.0.0m sh/AL]oi;tNXwY7bWLAhhdretiredmhexpmmplugm 1.5.0-rc.0dnilbWLAhhddepsmhexpmmphoenixm0.9.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm ~> 0.10.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsehmhexpmmpoolboympoolboym~> 1.4.2dtruejXbWLAhhdouter_checksummhexpmmplugm1.1.3m 0c倉w<\zQY/OZ~N3oCbWLAhhd timestampmhexpmmplugm1.0.3hhba aha a9a0GbWLAhhd timestampmhexpmmpostgrexm0.8.3hhba aha a9a0`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.3.1m 7i8x }6JyHhk2bWLAhhdretiredmhexpmmplugm1.8.3dnil2bWLAhhdretiredmhexpmmplugm1.0.4dnil5bWLAhhdretiredmhexpmmdecimalm1.9.0dnilbWLAhhddepsmhexpmm db_connectionm0.1.6lhmhexpmmbackoffmbackoffm~> 1.0dfalsehmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 0.7dtruej[bWLAhhdouter_checksummhexpmmphoenixm0.3.0m syeݞ 5aREx#7vK"wbWLAhhddepsmhexpmm phoenix_htmlm1.0.0lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0dfalsej_bWLAhhdinner_checksummhexpmm plug_cowboym2.2.2m z ]盒2ĔvLȐ6bWLAhhdretiredmhexpmmphoenixm0.12.0dnil5bWLAhhdretiredmhexpmmdecimalm0.2.4dnilbWLAhhddepsmhexpmmphoenixm1.1.6lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsej2bWLAhhdretiredmhexpmmplugm1.1.1dnilCbWLAhhd timestampmhexpmmplugm1.6.4hhba aha a9a0^bWLAhhdinner_checksummhexpmm connectionm1.0.0m ʀdtMu;L_o&Ii)ږGbWLAhhd timestampmhexpmmphoenixm1.4.15hhba aha a9a0FbWLAhhd timestampmhexpmmdecimalm0.2.3hhba aha a9a0MbWLAhhd timestampmhexpmmpostgrexm 0.14.0-rc.0hhba aha a9a0FbWLAhhd timestampmhexpmmphoenixm1.4.8hhba aha a9a0YbWLAhhdouter_checksummhexpmmplugm0.11.0m "89fأXI?J\sT68EhGCbWLAhhd timestampmhexpmmplugm0.4.2hhba aha a9a0bbWLAhhdinner_checksummhexpmmphoenix_paramsm1.1.3m 2{kJkf c}u1{ j`bWLAhhdinner_checksummhexpmmphoenixm 1.3.0-rc.3m 7 9 5r}F!Yj 0DXbWLAhhdouter_checksummhexpmmplugm1.0.2m A› Nm~Q@LÅDyS]bWLAhhdinner_checksummhexpmmpostgrexm0.12.1m /F:D/B~;΁ |2bWLAhhdretiredmhexpmmplugm0.8.0dnilbWLAhhddepsmhexpmm db_connectionm 1.0.0-rc.1lhmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 1.0.0-beta.2dtruejCbWLAhhd timestampmhexpmmphoenix_pubsubhhba aha a9a0FbWLAhhd timestampmhexpmmdecimalm1.4.1hhba aha a9a0bWLAhhddepsmhexpmmplugm1.2.4lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmmimemmimem~> 1.0dfalsej`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.9.1m pqm e=EFxenX".Hi6DX?VV5E6M^^AE@uUEh5bWLAhhdretiredmhexpmmdecimalm0.2.1dnil`bWLAhhdinner_checksummhexpmm phoenix_htmlm1.2.1m Sg%pXyOdoa⸻9tlbWLAhhddepsmhexpmm db_connectionm0.2.1lhmhexpmm connectionm connectionm~> 1.0.2dfalsehmhexpmmpoolboympoolboym~> 1.5dtruehmhexpmmsbrokermsbrokerm~> 0.7dtruejCbWLAhhd timestampmhexpmmplugm1.7.1hhba aha a9a0MbWLAhhd timestampmhexpmmphoenix_paramsm0.2.0hhba aha a9a0hex-2.4.2/test/fixtures/registries/20210915.ets000066400000000000000000006750631517471540100207610ustar00rootroot00000000000000cXM bWLAhhdidZd nonode@nohost?6Ώhddecentralized_countersdfalsehdread_concurrencydfalsehdwrite_concurrencydfalsehd compresseddfalsehdmemoryb5hdownerXd nonode@nohost hdheirdnonehdnamedElixir.Hex.Devhdsizebhdnoded nonode@nohosthd named_tabledfalsehdtypedsethdkeyposahd protectiond protectedhd major_versionahd minor_versionahd extended_infoj 1.0dfalsejCbWLAhhd timestampmhexpmmplugm0.4.2hhbaahaa4a5bWLAhhdretiredmhexpmmphoenixm1.0.2dnil5bWLAhhdretiredmhexpmmdecimalm1.5.0dnilXbWLAhhdouter_checksummhexpmmplugm0.8.1m Pߙ%5s}_ݡD/TD]_bWLAhhdinner_checksummhexpmm plug_cowboym2.4.1m wP'.TWIP_Zc )5bWLAhhdretiredmhexpmmphoenixm0.2.2dnilHbWLAhhd timestampmhexpmm telemetrym0.4.1hhbaahaa4a5bWLAhhdretiredmhexpmmphoenixm1.5.6dnilCbWLAhhd timestampmhexpmmplugm1.1.2hhbaahaa4asbWLAhhddepsmhexpmm phoenix_htmlm2.5.1lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsejCbWLAhhd timestampmhexpmmmimem1.3.1hhbaahaaa.XbWLAhhdinner_checksummhexpmmplugm1.0.1m N=XB.@9l t hbWLAhhddepsmhexpmm phoenix_htmlm2.8.0lhmhexpmmplugmplugm~> 1.0dfalsej[bWLAhhdinner_checksummhexpmmdecimalm1.1.2m yie{-S{Q<Қ+HkQlBS [bWLAhhdouter_checksummhexpmmphoenixm0.4.1m 'Y Kj]MunLT܋Yg[bWLAhhdouter_checksummhexpmmdecimalm1.8.0m RiNn`PsLfXO[}1xfcAbWLAhhd timestampmhexpmm phoenix_htmlhhba a ha aa bbWLAhhdinner_checksummhexpmmphoenix_pubsubm2.0.0m vq{h읒H֍G9_CbWLAhhd timestampmhexpmmplugm1.6.0hhbaahaa4a:bWLAhhdretiredmhexpmmphoenixm 1.3.0-rc.0dnil 1.0dtruej+bWLAhhddepsmhexpmmranchm1.7.1j>bWLAhhdretiredmhexpmmcowboy_telemetrym0.2.0dnilZbWLAhhdinner_checksummhexpmmcowlibm2.2.0m ^7L%L021得bU5bWLAhhdretiredmhexpmmdecimalm1.4.1dnilCbWLAhhd timestampmhexpmmplugm1.0.2hhbaahaa4a2bWLAhhdretiredmhexpmmmimem1.2.0dnilKbWLAhhd timestampmhexpmm phoenix_htmlm1.3.0hhba a ha aa CbWLAhhd timestampmhexpmmplugm0.8.4hhbaahaa4asbWLAhhddepsmhexpmm phoenix_htmlm2.2.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsej3bWLAhhdretiredmhexpmmjasonm1.0.0dnil^bWLAhhdouter_checksummhexpmmranchm 2.0.0-rc.1m P77_5#(!ݡRPibWLAhhddepsmhexpmm phoenix_htmlm2.11.0lhmhexpmmplugmplugm~> 1.5dfalsej9bWLAhhdretiredmhexpmm plug_cryptom1.1.2dnilYbWLAhhdouter_checksummhexpmmplugm1.11.1m #RNOX|;9.%4( N:YbWLAhhdouter_checksummhexpmmranchm2.0.0m H@b<-:|/SLTQؤ*YbWLAhhdinner_checksummhexpmmplugm0.11.2m Җ\7İԩ _e_O`*ZHbWLAhhd timestampmhexpmmplugm 1.4.0-rc.0hhbaahaa4adbWLAhhddepsmhexpmmplugm0.13.1lhmhexpmmcowboymcowboym~> 1.0dtruej5bWLAhhdretiredmhexpmmphoenixm1.2.3dnilObWLAhhd timestampmhexpmm phoenix_htmlm 2.0.0-devhhba a ha aa fbWLAhhddepsmhexpmmjasonm1.1.2lhmhexpmmdecimalmdecimalm~> 1.0dtruejXbWLAhhdinner_checksummhexpmmplugm1.6.0m 8Lb->$rE[QTKwSoabWLAhhdouter_checksummhexpmm phoenix_htmlm2.14.2m X%] |/l\ғpE/5bWLAhhdretiredmhexpmmphoenixm1.0.5dnilDbWLAhhd timestampmhexpmmranchm1.6.1hhba a ha aa +bWLAhhddepsmhexpmmranchm1.2.1jFbWLAhhd timestampmhexpmmphoenixm1.3.3hhba a ha aa [bWLAhhdouter_checksummhexpmmdecimalm0.2.0m &EQ{-D; Mʝ4$JbWLAhhd timestampmhexpmm plug_cowboym2.2.2hhba a ha aa [bWLAhhdouter_checksummhexpmmphoenixm0.2.9m x*;D4Dc~| d]`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.3.0m 'կ z9X>b+Qb: ڨ`( y_bWLAhhdouter_checksummhexpmm plug_cowboym2.5.2m nt``@"svIb5D5bWLAhhdretiredmhexpmmdecimalm2.0.0dnil6bWLAhhdretiredmhexpmmphoenixm0.17.0dnil\bWLAhhdouter_checksummhexpmmphoenixm0.16.0m !,@X _F(bWLAhhddepsmhexpmmplugm1.5.1lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.3dtruehmhexpmmmimemmimem~> 1.0dfalsejabWLAhhdinner_checksummhexpmm phoenix_htmlm2.12.0m KfUd=6UFֵSL+Fv"[bWLAhhdinner_checksummhexpmmphoenixm0.9.0m cmHZ^j:x:_h[E-bWLAhhddepsmhexpmmdecimalm0.2.3jEbWLAhhd timestampmhexpmmcowlibm2.8.0hhbaahaaa.XbWLAhhdinner_checksummhexpmmplugm0.6.0m b#C(lm6 t{-`bWLAhhdinner_checksummhexpmmdecimalm 1.9.0-rc.0m cfja8{@<Lp}2bWLAhhdretiredmhexpmmplugm1.1.7dnil2bWLAhhdretiredmhexpmmplugm1.0.2dnil-bWLAhhddepsmhexpmmdecimalm1.9.0j:bWLAhhdretiredmhexpmm phoenix_htmlm2.0.0dnilDbWLAhhd timestampmhexpmmplugm0.11.0hhbaahaa4aZbWLAhhdouter_checksummhexpmmcowlibm1.0.0m MV~ϓ9t51aM=ZtkbWLAhhddepsmhexpmmjasonm 1.0.0-rc.1lhmhexpmmdecimalmdecimalm~> 1.0dtruejXbWLAhhdinner_checksummhexpmmplugm1.2.0m IkcJI׀:gΜ෹% Y :bWLAhhdretiredmhexpmm phoenix_htmlm2.4.0dnil3bWLAhhdretiredmhexpmmplugm1.10.1dnilwbWLAhhddepsmhexpmm phoenix_htmlm1.4.0lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0dfalsej1bWLAhhddepsmhexpmm plug_cryptom1.1.0jbWLAhhddepsmhexpmmplugm1.6.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4dtruehmhexpmmmimemmimem~> 1.0dfalsejYbWLAhhdouter_checksummhexpmmplugm0.11.2m 0Hи#ƢJ-Pr4YbWLAhhdinner_checksummhexpmmranchm1.6.2m m 2.7dfalsehmhexpmmcowboy_telemetrymcowboy_telemetrym~> 0.3dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej5bWLAhhdretiredmhexpmmdecimalm1.0.0dnil3bWLAhhdretiredmhexpmmranchm1.6.2dnil[bWLAhhdouter_checksummhexpmmphoenixm1.5.6m Ӛ0kj')\y,po竻=3mSyV5bWLAhhdretiredmhexpmmphoenixm1.4.1dnil9bWLAhhdretiredmhexpmm plug_cowboym2.1.3dnilZbWLAhhdouter_checksummhexpmmpoisonm1.0.2m 0AX55cfWLbWLAhhd timestampmhexpmm phoenix_htmlm2.10.5hhba a ha aa ,bWLAhhddepsmhexpmmpoisonm1.2.1j5bWLAhhdretiredmhexpmmphoenixm1.0.4dnil4bWLAhhddepsmhexpmmphoenix_pubsubm1.1.2jbWLAhhddepsmhexpmmphoenixm1.5.5lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejZbWLAhhdouter_checksummhexpmmcowlibm2.3.0m ֕sTht]MwN؛XbWLAhhdinner_checksummhexpmmplugm1.1.8m ۋmyRDjp2C4bWLAhhdretiredmhexpmmcowlibm2.2.0dnil[bWLAhhdinner_checksummhexpmmphoenixm1.1.9m Gւ52(t,[֛A}9ytY_[bWLAhhdouter_checksummhexpmmdecimalm0.2.4m eBB:QN.cGJ9@5"5bWLAhhdretiredmhexpmmdecimalm0.1.2dnilbWLAhhddepsmhexpmmplugm1.6.3lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4dtruehmhexpmmmimemmimem~> 1.0dfalsej2bWLAhhdretiredmhexpmmplugm1.3.2dnilCbWLAhhd timestampmhexpmmplugm1.6.1hhbaahaa4a[bWLAhhdouter_checksummhexpmmdecimalm1.3.0m B 0 1.0.0dfalsehmhexpmmranchmranchm~> 1.0dfalsej`bWLAhhdouter_checksummhexpmmphoenixm 1.4.0-rc.2m &h (0"B}pe:8BibWLAhhddepsmhexpmm phoenix_htmlm2.10.0lhmhexpmmplugmplugm~> 1.0dfalsejFbWLAhhd timestampmhexpmmphoenixm1.1.4hhba a ha aa &bWLAhd last_updatehhba ahaaabWLAhhddepsmhexpmmcowboy_telemetrym0.4.0lhmhexpmmcowboymcowboym~> 2.7dfalsehmhexpmm telemetrym telemetrym~> 1.0dfalsejbbWLAhhdouter_checksummhexpmmphoenix_pubsubm1.1.0m B.*ҞKna߄*cz;e,MbWLAhhd timestampmhexpmmphoenix_pubsubm1.0.1hhba a ha aa ibWLAhhddepsmhexpmm phoenix_htmlm2.13.4lhmhexpmmplugmplugm~> 1.5dfalsejbWLAhhddepsmhexpmmcowboym2.4.0lhmhexpmmcowlibmcowlibm~> 2.3.0dfalsehmhexpmmranchmranchm~> 1.5.0dfalsejCbWLAhhd timestampmhexpmmplugm0.5.2hhbaahaa4aXbWLAhhdinner_checksummhexpmmplugm1.2.5m |؋f4Bf w{ї[bWLAhhdinner_checksummhexpmmdecimalm0.2.5m @[x:`&'?ԀC{Xk4NêYbWLAhhdouter_checksummhexpmmplugm1.11.0m -c?\/iuhܻ,t0vCbWLAhhd timestampmhexpmmmimem1.0.1hhbaahaaa.4bWLAhhdretiredmhexpmmcowboym1.1.0dnil-bWLAhhddepsmhexpmmdecimalm1.0.0jJbWLAhhd timestampmhexpmm plug_cryptom1.1.1hhbaahaaa.[bWLAhhdouter_checksummhexpmmdecimalm1.1.0m  }9 ,SHmga8-k!bWLAhhdversionsmhexpmmpoisonlm1.0.0m1.0.1m1.0.2m1.0.3m1.1.0m1.1.1m1.2.0m1.2.1m1.3.0m1.3.1m1.4.0m1.5.0m1.5.1m1.5.2m2.0.0m2.0.1m2.1.0m2.2.0m3.0.0m3.1.0m4.0.0m4.0.1m5.0.0j2bWLAhhdretiredmhexpmmplugm1.2.2dnil2bWLAhhdretiredmhexpmmplugm0.5.3dnil-bWLAhhddepsmhexpmmdecimalm1.5.0j[bWLAhhdinner_checksummhexpmmphoenixm1.0.6m ד kK[=rEMWhbWLAhhddepsmhexpmmplugm 1.5.0-rc.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.1dtruehmhexpmmmimemmimem~> 1.0dfalsejXbWLAhhdouter_checksummhexpmmplugm1.1.1m  E!/&uW><fJ]pqabWLAhhdouter_checksummhexpmm phoenix_htmlm2.10.1m H%[6id~^d hh\bWLAhhdouter_checksummhexpmmphoenixm0.12.0m -V>dOva1MSdUC~68 dbWLAhhddepsmhexpmmplugm0.10.0lhmhexpmmcowboymcowboym~> 1.0dtruejXbWLAhhdouter_checksummhexpmmmimem1.5.0m ULU"I=-:ޝ 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruejCbWLAhhd timestampmhexpmmmimem1.5.0hhbaahaaa.bWLAhhdversionsmhexpmmmimel m0.0.1m1.0.0m1.0.1m1.1.0m1.2.0m1.3.0m1.3.1m1.4.0m1.5.0m1.6.0m2.0.0m2.0.1j4bWLAhhdretiredmhexpmmcowlibm2.5.1dnilVbWLAhhd registry_etagmhexpmm plug_cowboym""bb2b114bf8e3996487c6fc8358247b5e"2bWLAhhdretiredmhexpmmplugm1.3.6dnil2bWLAhhdretiredmhexpmmplugm0.9.0dnil:bWLAhhdretiredmhexpmm phoenix_htmlm2.6.2dnilDbWLAhhd timestampmhexpmmplugm1.12.1hhbaahaa4a2bWLAhhdretiredmhexpmmplugm1.7.0dnil[bWLAhhdouter_checksummhexpmmdecimalm2.0.0m 4fnUި}7b2j# DwbWLAhhddepsmhexpmmcowboym1.0.2lhmhexpmmcowlibmcowlibm~> 1.0.0dfalsehmhexpmmranchmranchm~> 1.0dfalsejdbWLAhhdinner_checksummhexpmmcowboy_telemetrym0.1.0m i{8:JX:iCB"S'wC5bWLAhhdretiredmhexpmmphoenixm0.7.0dnilZbWLAhhdouter_checksummhexpmmcowboym2.9.0m ,rKNI|crS T\g5bWLAhhdretiredmhexpmmphoenixm1.4.0dnil:bWLAhhd timestampmhexpmmranchhhba a ha aa YbWLAhhdinner_checksummhexpmmplugm1.12.1m dVx`'ۜb=ۖMl5}4bWLAhhdretiredmhexpmmcowboym2.2.1dnilZbWLAhhdouter_checksummhexpmmcowboym1.1.1m VrnE|5@ ECLyBSXZ866bWLAhhdretiredmhexpmmphoenixm1.5.12dnil,bWLAhhddepsmhexpmmcowlibm2.2.1j,bWLAhhddepsmhexpmmpoisonm1.0.3j4bWLAhhdretiredmhexpmmcowlibm2.4.0dnilFbWLAhhd timestampmhexpmmphoenixm0.6.2hhba a ha aa \bWLAhhdouter_checksummhexpmmphoenixm0.2.11m I0BDaT>@y dƀ:bWLAhhdretiredmhexpmm phoenix_htmlm1.0.1dnil3bWLAhhdretiredmhexpmmplugm0.14.0dnil-bWLAhhddepsmhexpmmdecimalm1.2.0jXbWLAhhdouter_checksummhexpmmmimem0.0.1m $ ߽#C8Fd7S;:L5C' :bWLAhhdretiredmhexpmmphoenixm 1.3.0-rc.1dnilXbWLAhhdouter_checksummhexpmmplugm1.7.2m ޘ%o֭ ͈k=e~`k2bWLAhhdretiredmhexpmmmimem1.3.0dnil2bWLAhhdretiredmhexpmmplugm0.8.3dnil\bWLAhhdinner_checksummhexpmmphoenixm0.2.10m !/I7תck[%NS \bWLAhhdouter_checksummhexpmmphoenixm1.4.11m 7#os3s^oB~Xᤲ"bWLAhhddepsmhexpmmphoenixm1.4.1lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruej5bWLAhhdretiredmhexpmmphoenixm1.2.0dnilXbWLAhhdinner_checksummhexpmmplugm1.8.0m &٬'(&+x )oCw[bWLAhhdinner_checksummhexpmmphoenixm1.0.3m wafnQ+èzh $\CbWLAhhd timestampmhexpmmplugm1.0.3hhbaahaa4aZbWLAhhdinner_checksummhexpmmcowboym2.2.1m Cj"*uPQB`j2<[bWLAhhdouter_checksummhexpmmphoenixm1.5.0m ƕr9K}bc%P;K Ծ609bWLAhhddepsmhexpmmplugm1.11.0lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejZbWLAhhdinner_checksummhexpmmcowlibm1.0.2m v,:S o!s 7}=~߄bbWLAhhdversionsmhexpmm phoenix_htmll5m1.0.0m1.0.1m1.1.0m1.2.0m1.2.1m1.3.0m1.4.0m 2.0.0-devm2.0.0m2.0.1m2.1.0m2.1.1m2.1.2m2.2.0m2.3.0m2.3.1m 2.4.0-devm2.4.0m2.5.0m2.5.1m2.6.0m2.6.1m2.6.2m 2.7.0-devm2.7.0m2.8.0m2.9.0m2.9.1m2.9.2m2.9.3m2.10.0m2.10.1m2.10.2m2.10.3m2.10.4m2.10.5m2.11.0m2.11.1m2.11.2m2.12.0m2.13.0m2.13.1m2.13.2m2.13.3m2.13.4m2.14.0m2.14.1m2.14.2m2.14.3m3.0.0m3.0.1m3.0.2m3.0.3jjbWLAhhddepsmhexpmmphoenixm1.4.11lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsej:bWLAhhdretiredmhexpmmphoenixm 1.3.0-rc.3dnil[bWLAhhdinner_checksummhexpmmphoenixm0.3.1m wg!m m~!SPbJXig0a+_bWLAhhdouter_checksummhexpmm plug_cryptom1.1.0m ͡ 3%eu|%ʮȧ-l=^bWLAhhdouter_checksummhexpmmjasonm 1.0.0-rc.2m wv3ԉ ĘDo$*bWLAhhddepsmhexpmmplugm0.5.0j9bWLAhhdretiredmhexpmm plug_cryptom1.1.0dnilXbWLAhhdinner_checksummhexpmmplugm1.4.3m #mw{f*o6jIIJ1bWLAhhddepsmhexpmm plug_cryptom1.1.2jwbWLAhhddepsmhexpmm phoenix_htmlm 2.0.0-devlhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsej,bWLAhhddepsmhexpmmcowlibm1.1.0j:bWLAhhdretiredmhexpmmphoenixm 1.2.0-rc.0dnil4bWLAhhdretiredmhexpmmpoisonm5.0.0dnilFbWLAhhd timestampmhexpmmphoenixm1.1.7hhba a ha aa ZbWLAhhdouter_checksummhexpmmcowboym2.3.0m Pq&'0CQ[W#h0Cg c,bWLAhhddepsmhexpmmcowlibm2.2.0j`bWLAhhdouter_checksummhexpmmphoenixm 1.4.0-rc.0m a0>Yc$ph-6`g^TbWLAhhddepsmhexpmmcowboym1.1.1lhmhexpmmcowlibmcowlibm~> 1.0.2dfalsehmhexpmmranchmranchm~> 1.3.1dfalsej`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.6.2m i{1HT]hBrX5p- _2,EbWLAhhd timestampmhexpmmcowboym1.0.0hhbaahaaa.5bWLAhhdretiredmhexpmmphoenixm1.1.8dnil[bWLAhhdouter_checksummhexpmmphoenixm0.2.8m YyT%#E*i| nfq;Ѝނ2bWLAhhdretiredmhexpmmplugm1.1.5dnil[bWLAhhdouter_checksummhexpmmphoenixm1.1.0m Q>Ob S.- bWLAhhddepsmhexpmmcowboym2.6.2lhmhexpmmcowlibmcowlibm~> 2.7.2dfalsehmhexpmmranchmranchm~> 1.7.1dfalsejZbWLAhhdouter_checksummhexpmmcowboym1.0.0m D:FܧY^11 _+h_bWLAhhdinner_checksummhexpmm plug_cowboym2.5.2m bL`Y~,#'#~9k䘈EbWLAhhd timestampmhexpmmpoisonm1.0.2hhba a ha a.a\bWLAhhdinner_checksummhexpmmphoenixm1.4.16m ,fV|D8 3B7*Bn=:HC5bWLAhhdretiredmhexpmmdecimalm1.8.1dnilbWLAhhddepsmhexpmmplugm1.2.2lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmmimemmimem~> 1.0dfalsej9bWLAhhdretiredmhexpmm plug_cowboym2.0.1dnil5bWLAhhdretiredmhexpmmphoenixm1.0.3dnilZbWLAhhdinner_checksummhexpmmpoisonm4.0.1m UoכX[{1 4 K-sW CbWLAhhd timestampmhexpmmplugm1.3.2hhbaahaa4aXbWLAhhdinner_checksummhexpmmplugm1.0.0m ߶ 3]gN{a!<0[bWLAhhdouter_checksummhexpmmphoenixm0.1.0m uW X  Tb~ n?p*:bWLAhhdretiredmhexpmmphoenixm 1.3.0-rc.2dnil8bWLAhhdretiredmhexpmmranchm 2.0.0-rc.1dnilHbWLAhhd timestampmhexpmmplugm 1.5.0-rc.0hhbaahaa4aFbWLAhhd timestampmhexpmmphoenixm1.4.2hhba a ha aa [bWLAhhdouter_checksummhexpmmphoenixm1.4.7m S>ke5qN4'ՠ$]myZbWLAhhdouter_checksummhexpmmpoisonm1.4.0m qZI99:N֝P`bf2bWLAhhdretiredmhexpmmplugm0.5.0dnil]bWLAhhdouter_checksummhexpmm telemetrym0.2.0m Nqy]YL?C i .x@#2:bWLAhhdretiredmhexpmmdecimalm 1.9.0-rc.0dnilZbWLAhhdouter_checksummhexpmmpoisonm2.1.0m ,9|S>!AN< C,cCbWLAhhd timestampmhexpmmplugm1.0.4hhbaahaa4aYbWLAhhdinner_checksummhexpmmranchm1.6.1m & rL~<l7Bb_]a"35bWLAhhdretiredmhexpmmphoenixm1.5.4dnilZbWLAhhdouter_checksummhexpmmpoisonm1.0.0m ~)E#b :Q G{[bWLAhhdinner_checksummhexpmmcowlibm2.10.1m TBkN48^Y/F\GިCbWLAhhd timestampmhexpmmmimem0.0.1hhbaahaaa.FbWLAhhd timestampmhexpmmphoenixm1.1.1hhba a ha aa fbWLAhhddepsmhexpmmjasonm1.1.1lhmhexpmmdecimalmdecimalm~> 1.0dtruej/bWLAhhddepsmhexpmm telemetrym0.2.0j3bWLAhhdretiredmhexpmmranchm1.6.0dnil5bWLAhhdretiredmhexpmmcowlibm2.10.1dnilFbWLAhhd timestampmhexpmmphoenixm1.3.2hhba a ha aa FbWLAhhd timestampmhexpmmphoenixm1.5.0hhba a ha aa 5bWLAhhdretiredmhexpmmphoenixm1.4.9dnilEbWLAhhd timestampmhexpmmcowlibm2.2.1hhbaahaaa.[bWLAhhdinner_checksummhexpmmdecimalm0.2.1m U%qs]A27MXbWLAhhdouter_checksummhexpmmplugm1.1.9m SPi]I\+B*'3$EbWLAhhd timestampmhexpmmpoisonm1.4.0hhba a ha a.aZbWLAhhdouter_checksummhexpmmpoisonm1.3.1m ׍嫺ؙ/m 0FUZ`ҊXabWLAhhdouter_checksummhexpmm phoenix_htmlm2.10.3m W+w=*.@Vz? 2bWLAhhdversionsmhexpmmcowliblm1.0.0m1.0.1m1.0.2m1.1.0m1.2.0m1.3.0m2.0.0m2.0.1m2.1.0m2.2.0m2.2.1m2.3.0m2.4.0m2.5.0m2.5.1m2.6.0m2.7.0m2.7.1m2.7.2m2.7.3m2.8.0m2.9.0m2.9.1m2.10.0m2.10.1m2.11.0jYbWLAhhdinner_checksummhexpmmranchm1.2.1m ,{F|Oiva~(bsbWLAhhddepsmhexpmm phoenix_htmlm2.0.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsej:bWLAhhdretiredmhexpmmphoenixm 1.4.0-rc.3dnilgbWLAhhddepsmhexpmmpoisonm5.0.0lhmhexpmmdecimalmdecimalm~> 2.0dtruejbWLAhhddepsmhexpmm plug_cowboym2.0.2lhmhexpmmcowboymcowboym~> 2.5dfalsehmhexpmmplugmplugm~> 1.7dfalsej`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.4.0m ;ݙ\_^$HENf ^wQ5[bWLAhhdinner_checksummhexpmmphoenixm0.5.0m tlAe-8æ%Z6@**bWLAhhddepsmhexpmmmimem1.2.0jZbWLAhhdinner_checksummhexpmmcowlibm2.0.0m —qG=RX#-S4 )PbWLAhhddepsmhexpmmcowboym2.0.0lhmhexpmmcowlibmcowlibm~> 2.0.0dfalsehmhexpmmranchmranchm~> 1.4.0dfalsejbWLAhhddepsmhexpmmphoenixm1.1.3lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsejXbWLAhhdouter_checksummhexpmmmimem2.0.1m z Үbas8ߑcB*bWLAhhddepsmhexpmmplugm0.4.4j2bWLAhhdretiredmhexpmmplugm1.8.2dnil5bWLAhhdretiredmhexpmmdecimalm0.2.5dnil[bWLAhhdinner_checksummhexpmmdecimalm0.2.3m r}$޿>4m\lZ$KbWLAhhd timestampmhexpmmdecimalm 1.9.0-rc.0hhbaaha aa:abWLAhhdinner_checksummhexpmm phoenix_htmlm2.13.1m KS(ߠUi7K[ /YbWLAhhdinner_checksummhexpmmplugm0.12.1m X"!崫*x/ӡ~(#_bWLAhhdinner_checksummhexpmm plug_cowboym2.1.2m ݵR8ÎD.'2F]|Gŀ^:bWLAhhdretiredmhexpmm phoenix_htmlm2.3.1dnilbWLAhhddepsmhexpmmplugm1.10.1lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dtruej`bWLAhhdouter_checksummhexpmmphoenixm 1.3.0-rc.0m 5PN 1.0dtruejFbWLAhhd timestampmhexpmmphoenixm0.4.1hhba a ha aa LbWLAhhd timestampmhexpmm phoenix_htmlm2.10.2hhba a ha aa XbWLAhhdinner_checksummhexpmmplugm1.6.2m j{һm]ݕp ΈSQ"KَZbWLAhhdinner_checksummhexpmmcowboym1.1.2m a)Ze``-7 po)ܩ;wbWLAhhddepsmhexpmm phoenix_htmlm1.1.0lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0dfalsej`bWLAhhdinner_checksummhexpmmphoenixm 1.3.0-rc.1m KH#L^Xeu7bWLAhhddepsmhexpmmphoenixm0.12.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm ~> 0.12.1dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsehmhexpmmpoolboympoolboym~> 1.5.1dtruejdbWLAhhdinner_checksummhexpmmcowboy_telemetrym0.3.1m ѡׯ'fTzF'CؠAgTbWLAhhddepsmhexpmm plug_cowboym2.1.1lhmhexpmmcowboymcowboym~> 2.5dfalsehmhexpmmplugmplugm~> 1.7dfalsej-bWLAhhddepsmhexpmmdecimalm0.1.2jObWLAhhd timestampmhexpmm phoenix_htmlm 2.7.0-devhhba a ha aa cbWLAhhddepsmhexpmmplugm0.9.0lhmhexpmmcowboymcowboym~> 1.0dtruej[bWLAhhdouter_checksummhexpmmcowlibm2.10.1m Bq(q֘ꣳ.ihD<\tD]bWLAhhdouter_checksummhexpmm telemetrym0.4.0m |1p| 0-Ow?A3bWLAhhdretiredmhexpmmplugm0.13.0dnil[bWLAhhdinner_checksummhexpmmdecimalm1.3.0m Afx`E|Rӣ՗M'}3bWLAhhdretiredmhexpmmplugm1.10.0dnilFbWLAhhd timestampmhexpmmdecimalm1.4.0hhbaaha aa:bWLAhhddepsmhexpmmphoenixm0.2.4lhmhexpmmex_confmex_confm0.1.1dfalsehmhexpmminflexminflexm0.2.0dfalsehmhexpmmplugmplugm0.4.3dfalsejYbWLAhhdouter_checksummhexpmmplugm0.12.1m 4+`%`bȩ@L)^DFPbWLAhhd registry_etagmhexpmmranchm""db832848a90690400d75bacdc5ccd01e"bWLAhhddepsmhexpmmphoenixm0.7.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmpoisonmpoisonm~> 1.2dfalsejZbWLAhhdouter_checksummhexpmmpoisonm3.1.0m fs>{W83vmωTGZbWLAhhdinner_checksummhexpmmpoisonm1.2.0m Am^‹vF-MHK@vgܩ4bWLAhhdretiredmhexpmmpoisonm1.0.0dnilXbWLAhhdinner_checksummhexpmmplugm0.9.0m c"&Ù]!UWrƒD2bWLAhhdretiredmhexpmmplugm1.1.8dnil2bWLAhhdretiredmhexpmmplugm1.7.1dnilMbWLAhhd timestampmhexpmmphoenix_pubsubm0.0.1hhba a ha aa ZbWLAhhdouter_checksummhexpmmcowboym2.2.1m J[S7XJ6ѐ⺄^ק]=5bWLAhhdretiredmhexpmmdecimalm0.2.0dnilZbWLAhhdouter_checksummhexpmmpoisonm1.0.1m a4&)•C}  EbWLAhhd timestampmhexpmmcowboym1.0.4hhbaahaaa.[bWLAhhdinner_checksummhexpmmphoenixm1.1.6m {fi/Z0ڷ=b(ս*BkGZbWLAhhdouter_checksummhexpmmpoisonm2.2.0m Q C9a(AtėRL([yz^bWLAhhdinner_checksummhexpmmranchm 2.0.0-rc.1m J^.CD[jo[>FbWLAhhd timestampmhexpmmphoenixm1.5.1hhba a ha aa ]bWLAhhdinner_checksummhexpmmplugm 1.4.0-rc.0m Solm$a63;3r~[ FbWLAhhd timestampmhexpmmphoenixm1.5.6hhba a ha aa [bWLAhhdouter_checksummhexpmmphoenixm0.3.1m YHy7^6aM+VSN%XbWLAhhdouter_checksummhexpmmplugm1.7.1m ڥ ċl2 b|uUM^bWLAhhddepsmhexpmmphoenixm0.2.6lhmhexpmmex_confmex_confm0.1.2dfalsehmhexpmminflexminflexm0.2.0dfalsehmhexpmmjazzmjazzm0.1.1dfalsehmhexpmmplugmplugm0.4.4dfalsejFbWLAhhd timestampmhexpmmcowlibm2.10.0hhbaahaaa.`bWLAhhdouter_checksummhexpmm phoenix_htmlm3.0.0m 7=O),[v,2 ^HK@Pf,,ibWLAhhddepsmhexpmm phoenix_htmlm2.14.2lhmhexpmmplugmplugm~> 1.5dfalsejEbWLAhhd timestampmhexpmmcowboym2.6.3hhbaahaaa.JbWLAhhd timestampmhexpmm plug_cowboym2.0.1hhba a ha aa ZbWLAhhdinner_checksummhexpmmcowboym2.5.0m Nn'.܏CGV7[bWLAhhdinner_checksummhexpmmphoenixm1.3.3m _9災Dj,y|XUfcbWLAhhddepsmhexpmmplugm1.1.8lhmhexpmmcowboymcowboym~> 1.0dtruejYbWLAhhdouter_checksummhexpmmranchm1.3.1m ?2ߑ< J _\q,Cud,YbWLAhhdouter_checksummhexpmmplugm1.12.1m ~yw{mڑJ qeE*cL 6bWLAhhdretiredmhexpmmphoenixm1.4.15dnilCbWLAhhd timestampmhexpmmplugm1.6.3hhbaahaa4a,bWLAhhddepsmhexpmmcowlibm2.4.0j2bWLAhhdretiredmhexpmmplugm1.1.9dnil5bWLAhhdretiredmhexpmmphoenixm1.4.4dnilObWLAhhd timestampmhexpmmcowboy_telemetrym0.1.0hhbaahaaazbWLAhhdretiredmhexpmm plug_cowboym2.2.2tdmessagemBroken telemetry supportdreasondRETIRED_INVALIDFbWLAhhd timestampmhexpmmphoenixm1.2.3hhba a ha aa \bWLAhhdinner_checksummhexpmmphoenixm0.11.0m `3dLڜ,\O,pO@-bWLAhhddepsmhexpmmdecimalm0.1.1jXbWLAhhdinner_checksummhexpmmplugm1.5.1m [϶5^LG0?l)ҧoASe[bWLAhhdinner_checksummhexpmmphoenixm1.5.9m 66՝{7C`Z"|~%CbWLAhhd timestampmhexpmmplugm1.8.1hhbaahaa4aZbWLAhhdouter_checksummhexpmmcowlibm2.7.1m /`sx\Sk]Eh)6^CbWLAhhd timestampmhexpmmphoenix_pubsubhhba a ha aa KbWLAhhd timestampmhexpmm phoenix_htmlm3.0.1hhba a ha aa bWLAhhddepsmhexpmmphoenixm1.1.9lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0.5dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsejYbWLAhhdouter_checksummhexpmmranchm1.8.0m Ih/]QWgm)Ym^R--bWLAhhddepsmhexpmmdecimalm1.8.0jbWLAhhddepsmhexpmmcowboy_telemetrym0.3.1lhmhexpmmcowboymcowboym~> 2.7dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej4bWLAhhdretiredmhexpmmpoisonm1.0.1dnil,bWLAhhddepsmhexpmmcowlibm2.6.0jFbWLAhhd timestampmhexpmmphoenixm1.4.4hhba a ha aa bWLAhhddepsmhexpmmphoenixm1.0.6lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0.5dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejbWLAhhddepsmhexpmmcowboym1.0.0lhmhexpmmcowlibmcowlibm~> 1.0.0dfalsehmhexpmmranchmranchm~> 1.0dfalsejdbWLAhhddepsmhexpmmplugm0.11.0lhmhexpmmcowboymcowboym~> 1.0dtruejXbWLAhhdouter_checksummhexpmmplugm0.5.3m E}A[; 2.7dfalsehmhexpmmcowboy_telemetrymcowboy_telemetrym~> 0.3dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej_bWLAhhdinner_checksummhexpmm plug_cowboym2.1.3m 8>k߂aa2D\ܶ34bWLAhhdretiredmhexpmmcowlibm2.7.3dnilFbWLAhhd timestampmhexpmmphoenixm0.9.0hhba a ha aa 7bWLAhhdretiredmhexpmmplugm 1.5.0-rc.2dnil2bWLAhhdretiredmhexpmmplugm1.2.4dnilabWLAhhdinner_checksummhexpmm phoenix_htmlm2.10.4m 2I1c| p?-Mm4bWLAhhdretiredmhexpmmcowboym2.6.1dnilEbWLAhhd timestampmhexpmmpoisonm1.3.1hhba a ha a.a3bWLAhhdretiredmhexpmmplugm0.12.1dnil[bWLAhhdinner_checksummhexpmmdecimalm1.4.1m Ps""|* ږ[ p[bWLAhhdinner_checksummhexpmmphoenixm0.8.0m ҡ:l $,I 2aEӰ|KbbWLAhhdinner_checksummhexpmmphoenix_pubsubm1.1.1m fhׇ$z_i̘Q6 <ibWLAhhddepsmhexpmmphoenixm1.4.8lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsej_bWLAhhdouter_checksummhexpmm plug_cryptom1.2.2m czE𣀟y:KsHݞ(ZbWLAhhdouter_checksummhexpmmcowboym1.1.2m v;#>o$O̍q|CWsI|7bWLAhhdretiredmhexpmm telemetrym0.4.1dnilEbWLAhhd timestampmhexpmmpoisonm1.0.1hhba a ha a.aXbWLAhhdinner_checksummhexpmmplugm0.8.0m aC-N`|~<>&ҸEX.ęZbWLAhhdinner_checksummhexpmmcowlibm1.0.1m Zcr6)BdڙbWLAhhddepsmhexpmmphoenixm1.5.9lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13 or ~> 3.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej]bWLAhhdouter_checksummhexpmm telemetrym0.1.0m <1bhxAO\"BMPCD$XbWLAhhdouter_checksummhexpmmplugm1.3.5m X̡(9D/q*tmRdbWLAhhdouter_checksummhexpmmcowboy_telemetrym0.3.0m B")P]Un$b"O!gS[bWLAhhdinner_checksummhexpmmphoenixm0.3.0m |&QP?Xߛ*, b\^G>&݌bWLAhhddepsmhexpmmplugm1.4.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsej`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.1.2m {*= 0.12.2 and < 2.0.0dfalsej5bWLAhhdretiredmhexpmmphoenixm1.3.1dnilYbWLAhhdinner_checksummhexpmmranchm2.0.0m זaqT2V]mh$zIf[bWLAhhdouter_checksummhexpmmphoenixm1.4.5m kZJk9uhEA$s[bWLAhhdouter_checksummhexpmmphoenixm1.1.4m We#ΒB;C?DpQ YzXbWLAhhdouter_checksummhexpmmplugm1.8.1m LX:fɵA~mS>cjbWLAhhddepsmhexpmmphoenixm1.4.16lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsej_bWLAhhdouter_checksummhexpmm plug_cowboym2.5.0m [,%XtF8k<4VR쀛vz\bWLAhhdouter_checksummhexpmmphoenixm1.4.16m l2S'7A<u%Wm\$ZbWLAhhdouter_checksummhexpmmpoisonm1.0.3m c+\L.V;5+5l-i-fXbWLAhhdinner_checksummhexpmmplugm1.4.4m 'Tvb',5 ӾQG5NFtĴ6bWLAhhdretiredmhexpmmphoenixm0.10.0dnilZbWLAhhdinner_checksummhexpmmcowboym1.0.0m дj^_UCFokV}RbR|dU]XbWLAhhdinner_checksummhexpmmplugm1.7.2m ׷u^ }D"7.CbWLAhhd timestampmhexpmmplugm0.4.4hhbaahaa4a2bWLAhhdretiredmhexpmmplugm0.4.2dnil[bWLAhhdinner_checksummhexpmmphoenixm1.1.3m ZK`c`o^݆ p*<,azlXbWLAhhdinner_checksummhexpmmmimem1.0.1m Ó$v}6'qbCM$zQ5bWLAhhdretiredmhexpmmdecimalm0.2.3dnilDbWLAhhd timestampmhexpmmranchm1.3.0hhba a ha aa abWLAhhdinner_checksummhexpmm phoenix_htmlm2.10.3m C-7.zmYxCDQA"75YbWLAhhdouter_checksummhexpmmjasonm1.0.1m QQjL Y= hv%W4 CbWLAhhd timestampmhexpmmplugm1.6.4hhbaahaa4a2bWLAhhdretiredmhexpmmplugm1.3.5dnilebWLAhhddepsmhexpmmplugm0.7.0lhmhexpmmcowboymcowboym~> 1.0.0dtruej]bWLAhhdouter_checksummhexpmmplugm 1.2.0-rc.0m ~Ie -JizŅkwiC iZbWLAhhdouter_checksummhexpmmpoisonm4.0.1m 6K9KaY mkߗo%EbWLAhhd timestampmhexpmmpoisonm1.5.0hhba a ha a.aabWLAhhdouter_checksummhexpmm phoenix_htmlm2.13.0m kyi,(vS؜'miwu}oaKZ[bWLAhhdouter_checksummhexpmmphoenixm0.7.0m $y|6; w'Px`j1TEEbWLAhhd timestampmhexpmmcowlibm2.4.0hhbaahaaa.\bWLAhhdinner_checksummhexpmmphoenixm1.5.10m >bmrt 2?UI㫻ӋRsbWLAhhddepsmhexpmm phoenix_htmlm2.1.2lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsej\bWLAhhdouter_checksummhexpmmphoenixm1.5.12m sOdl/o?gS 2AbWLAhhddepsmhexpmmphoenixm1.5.11lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13 or ~> 3.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej[bWLAhhdinner_checksummhexpmmphoenixm1.5.3m @NH4X? [b0;IabWLAhhdinner_checksummhexpmm phoenix_htmlm2.14.3m Q CW]0=WxieBqbWLAhhdversionsmhexpmmphoenix_pubsubl m0.0.1m0.1.0m 1.0.0-rc.0m1.0.0m1.0.1m1.0.2m1.1.0m1.1.1m1.1.2m2.0.0jKbWLAhhd timestampmhexpmmphoenixm 1.4.0-rc.0hhba a ha aa CbWLAhhd timestampmhexpmmplugm1.5.0hhbaahaa4aXbWLAhhdinner_checksummhexpmmplugm1.1.3m ,Cp~imL`Z[bWLAhhdouter_checksummhexpmmdecimalm0.2.5m .-h̆ lok$+FXnYGCbWLAhhd timestampmhexpmmplugm1.1.8hhbaahaa4a*bWLAhhddepsmhexpmmplugm0.4.3j\bWLAhhdouter_checksummhexpmmphoenixm0.15.0m ijLvȮB.At"u >V!ibWLAhhddepsmhexpmm phoenix_htmlm2.11.2lhmhexpmmplugmplugm~> 1.5dfalsejjbWLAhhddepsmhexpmmphoenixm1.4.12lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsejbWLAhhddepsmhexpmmphoenixm1.1.2lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsej`bWLAhhdinner_checksummhexpmm phoenix_htmlm1.2.1m Sg%pXyOdoa⸻9tlKbWLAhhd timestampmhexpmm phoenix_htmlm1.2.0hhba a ha aa abWLAhhdouter_checksummhexpmm phoenix_htmlm2.10.5m O82cηF|IUTu yQ`^E`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.7.0m . D4 .C jYg|Yh5T8ՒB ,bWLAhhddepsmhexpmmpoisonm1.2.0jCbWLAhhd timestampmhexpmmplugm0.8.2hhbaahaa4aXbWLAhhdinner_checksummhexpmmplugm0.5.2m |"Wž/1޹\T5 J:bWLAhhdretiredmhexpmmphoenixm 1.4.0-rc.2dnil 1.0dfalsej,bWLAhhddepsmhexpmmpoisonm4.0.0jbWLAhhddepsmhexpmmplugm1.8.0lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0dfalsehmhexpmm telemetrym telemetrym~> 0.4dtruejbWLAhhddepsmhexpmmplugm 1.5.0-rc.2lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.1dtruehmhexpmmmimemmimem~> 1.0dfalsejCbWLAhhd timestampmhexpmmmimem1.6.0hhbaahaaa.ZbWLAhhdouter_checksummhexpmmcowlibm2.8.0m yT0!P(iR=L02ZbWLAhhdinner_checksummhexpmmcowlibm2.7.0m >nwV/U`YF-v/î>CbWLAhhddepsmhexpmmcowboym1.1.2lhmhexpmmcowlibmcowlibm~> 1.0.2dfalsehmhexpmmranchmranchm~> 1.3.2dfalsejZbWLAhhdinner_checksummhexpmmpoisonm1.0.0m IY+9s2x#Fc|v#_4bWLAhhdretiredmhexpmmcowlibm2.9.0dnilGbWLAhhd timestampmhexpmmphoenixm1.4.14hhba a ha aa 5bWLAhhdretiredmhexpmmphoenixm0.3.1dnil5bWLAhhdretiredmhexpmmphoenixm1.4.5dnilFbWLAhhd timestampmhexpmmdecimalm0.2.0hhbaaha aa:LbWLAhhd timestampmhexpmm phoenix_htmlm2.13.2hhba a ha aa FbWLAhhd timestampmhexpmmdecimalm1.2.0hhbaaha aa:9bWLAhhdretiredmhexpmm plug_cryptom1.2.1dnil5bWLAhhdretiredmhexpmmdecimalm1.1.1dnil4bWLAhhdretiredmhexpmmcowlibm1.3.0dnilXbWLAhhdinner_checksummhexpmmplugm1.3.1m FuB93pL^?B`dūU5bWLAhhdretiredmhexpmmphoenixm1.4.7dnilsbWLAhhddepsmhexpmm phoenix_htmlm2.5.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsejabWLAhhdinner_checksummhexpmm phoenix_htmlm2.10.0m iFM|S“4'QA8 JZbWLAhhdouter_checksummhexpmmpoisonm1.1.1m W|4vLl$GcH0kb! gXbWLAhhdinner_checksummhexpmmplugm1.4.0m '^f^Bl% $m_E;l }[bWLAhhdouter_checksummhexpmmphoenixm0.7.1m 7R8CIY<+h Aa&GFbWLAhhd timestampmhexpmmphoenixm0.1.0hhba a ha aa YbWLAhhdinner_checksummhexpmmranchm1.3.2m ZM\|e 2.5dfalsehmhexpmmplugmplugm~> 1.7dfalsejCbWLAhhd timestampmhexpmmplugm1.7.2hhbaahaa4aCbWLAhhd timestampmhexpmmplugm1.3.4hhbaahaa4abWLAhhddepsmhexpmmphoenixm1.1.7lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsejXbWLAhhdouter_checksummhexpmmplugm1.6.3m o/J-s09+ %UpwJ`Qi[bWLAhhdouter_checksummhexpmmphoenixm1.3.4m I&`L^F/YYi6zĠS-;bWLAhhdretiredmhexpmm phoenix_htmlm2.14.1dnil[bWLAhhdinner_checksummhexpmmphoenixm1.5.8m qϧ7M9yB_ik\ZF!-bWLAhhddepsmhexpmmdecimalm1.1.0j+bWLAhhddepsmhexpmmranchm1.6.0j[bWLAhhdouter_checksummhexpmmphoenixm1.5.1m '+8(y̮og鷐=hx^=#1RKbWLAhhddepsmhexpmmphoenixm1.2.3lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm6~> 1.4 or ~> 1.3.3 or ~> 1.2.4 or ~> 1.1.8 or ~> 1.0.5dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsejKbWLAhhd timestampmhexpmm phoenix_htmlm2.5.1hhba a ha aa _bWLAhhdinner_checksummhexpmm plug_cowboym2.0.2m `UhhHKncҺڔIxA3w*lbWLAhhddepsmhexpmmphoenixm1.5.7lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejbWLAhhddepsmhexpmmphoenixm1.1.6lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsej%bWLAhhddepsmhexpmmphoenixm 1.2.0-rc.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm ~> 1.0.0-rcdfalsehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsejGbWLAhhd timestampmhexpmmphoenixm0.12.0hhba a ha aa [bWLAhhdouter_checksummhexpmmphoenixm0.7.2m leuevoLGgұzoZbWLAhhdinner_checksummhexpmmcowboym2.3.0m &)HoAUdC(KVѐxOVbG$5bWLAhhdretiredmhexpmmphoenixm0.6.2dnil[bWLAhhdouter_checksummhexpmmphoenixm0.6.2m ATg98TA^eVAXbWLAhhdouter_checksummhexpmmmimem1.0.0m ~gG틓\Tzy,OLm}?\IbWLAhhd timestampmhexpmmranchm 2.0.0-rc.1hhba a ha aa YbWLAhhdinner_checksummhexpmmplugm1.10.0m e)\TE&7䨃4vHuM>bWLAhhdretiredmhexpmmcowboy_telemetrym0.4.0dnil:bWLAhhdretiredmhexpmm phoenix_htmlm1.2.1dnil5bWLAhhdretiredmhexpmmphoenixm1.5.9dnilGbWLAhhd timestampmhexpmmphoenixm0.16.1hhba a ha aa zbWLAhhdretiredmhexpmm plug_cowboym2.2.1tdmessagemBroken telemetry supportdreasondRETIRED_INVALIDKbWLAhhd timestampmhexpmm phoenix_htmlm2.4.0hhba a ha aa GbWLAhhd timestampmhexpmmphoenixm1.4.17hhba a ha aa bWLAhhddepsmhexpmmplugm1.12.0lhmhexpmmmimemmimem~> 1.0 or ~> 2.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4 or ~> 1.0dfalsejCbWLAhhd timestampmhexpmmplugm1.5.1hhbaahaa4aYbWLAhhdinner_checksummhexpmmjasonm1.1.0m 4/thՆS\[ WRO^bWLAhhdinner_checksummhexpmmranchm 2.0.0-rc.2m Hm4$2|;*vxQAsKbWLAhhd timestampmhexpmmphoenixm 1.3.0-rc.1hhba a ha aa EbWLAhhd timestampmhexpmmcowlibm1.1.0hhbaahaaa.+bWLAhhddepsmhexpmmranchm1.1.0j:bWLAhhdretiredmhexpmm phoenix_htmlm2.2.0dnilbWLAhhddepsmhexpmm plug_cowboym2.2.0lhmhexpmmcowboymcowboym~> 2.7dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej[bWLAhhdouter_checksummhexpmmphoenixm1.2.3m ̷ĨK/c?W?8%it<€2bWLAhhdretiredmhexpmmplugm1.2.0dnil,bWLAhhddepsmhexpmmphoenixm 1.3.0-rc.2lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.3.2 or ~> 1.4dfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0dfalsej`bWLAhhdinner_checksummhexpmmphoenixm 1.4.0-rc.2m _ 4a KCGE(/gͮoYXbWLAhhdouter_checksummhexpmmplugm0.8.0m s1eL>:cv6-+JbWLAhhd timestampmhexpmm plug_cowboym2.2.1hhba a ha aa DbWLAhhd timestampmhexpmmjasonm1.2.2hhbaaha aa:`bWLAhhdouter_checksummhexpmmphoenixm 1.3.0-rc.2m ap RŸo ,nE4ns,NDIbWLAhhd timestampmhexpmmranchm 2.0.0-rc.2hhba a ha aa ,bWLAhhddepsmhexpmmpoisonm1.1.0j,bWLAhhddepsmhexpmmcowlibm2.0.0j[bWLAhhdinner_checksummhexpmmphoenixm0.2.9m (N76eVҫbnK;L}dZbWLAhhdinner_checksummhexpmmpoisonm1.0.2m M;su@Sޱ@oVa. 7@S8CbWLAhhd timestampmhexpmmplugm1.1.4hhbaahaa4a_bWLAhhdinner_checksummhexpmm plug_cryptom1.2.1m \D'RU%"_0mZM-n:bWLAhhdretiredmhexpmmphoenixm 1.5.0-rc.0dnil[bWLAhhdouter_checksummhexpmmphoenixm1.1.7m MRVA1,OO'kT)1sbWLAhhddepsmhexpmm phoenix_htmlm2.3.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsejXbWLAhhdouter_checksummhexpmmplugm0.7.0m \ꬽVSOy#M96s-vyXg5bWLAhhdretiredmhexpmmphoenixm0.8.0dnil3bWLAhhdretiredmhexpmmjasonm1.1.1dnil5bWLAhhdretiredmhexpmmdecimalm1.1.0dnilEbWLAhhd timestampmhexpmmcowlibm2.9.0hhbaahaaa.[bWLAhhdouter_checksummhexpmmphoenixm1.3.0m 5̑l7O2O/]eeSlcbWLAhhddepsmhexpmmplugm1.0.0lhmhexpmmcowboymcowboym~> 1.0dtruej*bWLAhhddepsmhexpmmplugm0.5.2j/bWLAhhddepsmhexpmm telemetrym0.3.0j1bWLAhhddepsmhexpmm plug_cryptom1.2.2j[bWLAhhdinner_checksummhexpmmphoenixm1.2.0m ޹%OLSLߘ )|b]Wb|;f5bWLAhhdretiredmhexpmmdecimalm1.0.1dnilFbWLAhhd timestampmhexpmmphoenixm0.2.9hhba a ha aa TbWLAhhd registry_etagmhexpmm telemetrym""4a441cbd068ab1fc1e6f8fabddac30ff"[bWLAhhdouter_checksummhexpmmdecimalm0.1.2m Jޗ֊YJ AJ'mFbWLAhhd timestampmhexpmmphoenixm1.1.5hhba a ha aa [bWLAhhdinner_checksummhexpmmphoenixm1.1.2m kl`ٛ D3x e&Qm4|}4bWLAhhdretiredmhexpmmcowboym2.3.0dnilbWLAhhddepsmhexpmmplugm1.11.1lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejXbWLAhhdouter_checksummhexpmmplugm1.1.4m !&=zq(H#10rVƨTmݔ%MXbWLAhhdinner_checksummhexpmmplugm0.4.3m }.sD 2.7dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej2bWLAhhdretiredmhexpmmplugm1.6.0dnilbWLAhhddepsmhexpmmphoenixm 1.6.0-rc.0lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmm phoenix_viewm phoenix_viewm~> 1.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4 or ~> 1.0dfalsej3bWLAhhdretiredmhexpmmranchm1.3.2dnilYbWLAhhdinner_checksummhexpmmjasonm1.0.0m |#!Aw&Zqo5bWLAhhdretiredmhexpmmphoenixm0.5.0dnil[bWLAhhdinner_checksummhexpmmdecimalm1.7.0m 0ֵ,TfcsYPߞf S_~Ū2bWLAhhdretiredmhexpmmplugm1.2.3dnil,bWLAhhddepsmhexpmmcowlibm1.0.1j_bWLAhhdouter_checksummhexpmm plug_cowboym2.1.0m lѽM?G}gaSՍ@CzF2bWLAhhddepsmhexpmmcowboym2.9.0lhmhexpmmcowlibmcowlibm2.11.0dfalsehmhexpmmranchmranchm1.8.0dfalsej[bWLAhhdouter_checksummhexpmmphoenixm1.4.0m "ڏe=;;vfÉ=B%̄K`bWLAhhdouter_checksummhexpmmphoenixm 1.3.0-rc.1m v,TOƿ<~RS7=bCbWLAhhd timestampmhexpmmplugm1.8.0hhbaahaa4a>bWLAhhdretiredmhexpmmcowboy_telemetrym0.3.1dnilbWLAhhddepsmhexpmmplugm1.5.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.1dtruehmhexpmmmimemmimem~> 1.0dfalsejFbWLAhhd timestampmhexpmmphoenixm0.2.4hhba a ha aa EbWLAhhd timestampmhexpmmcowlibm2.7.3hhbaahaaa.;bWLAhhdretiredmhexpmm phoenix_htmlm2.12.0dnil[bWLAhhdouter_checksummhexpmmdecimalm1.8.1m zybWLAhhdretiredmhexpmmcowboym2.6.2tdmessagemcowlib version 2.7.2 retireddreasondRETIRED_INVALIDEbWLAhhd timestampmhexpmmcowboym2.2.0hhbaahaaa.FbWLAhhd timestampmhexpmmphoenixm0.2.3hhba a ha aa XbWLAhhdinner_checksummhexpmmplugm1.0.5m $W .:ѵȫ2RU)wJ[bWLAhhdouter_checksummhexpmmphoenixm1.3.1m e+a!= ETsyFML2hX2(4bWLAhhdretiredmhexpmmpoisonm1.3.1dnil\bWLAhhdouter_checksummhexpmmphoenixm0.13.0m 2W@ j(;["ֵ"bWLAhhddepsmhexpmmphoenixm1.4.6lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruej[bWLAhhdouter_checksummhexpmmphoenixm0.2.5m ~sؒv `1 l,h˕PybѶ1[bWLAhhdinner_checksummhexpmmphoenixm0.2.0m lE$ KMb™ua@Eh4[bWLAhhdinner_checksummhexpmmdecimalm1.6.0m Mn]CpӔ42_Ӻp0Y ebWLAhhddepsmhexpmmplugm0.8.1lhmhexpmmcowboymcowboym~> 1.0.0dtruejZbWLAhhdinner_checksummhexpmmpoisonm2.0.1m $6Ѷ~UkRI QsbWLAhhddepsmhexpmm plug_cowboym1.0.0lhmhexpmmcowboymcowboym~> 1.0dfalsehmhexpmmplugmplugm~> 1.7dfalsej7bWLAhhdretiredmhexpmmplugm 1.5.0-rc.0dnil9bWLAhhdretiredmhexpmm plug_cryptom1.2.0dnilXbWLAhhdinner_checksummhexpmmplugm1.6.1m /b=P Ϙ Fi~)4XnlabWLAhhdouter_checksummhexpmm phoenix_htmlm2.13.1m BϩNޖ3@ٜ4]Om鲳Bdǡd"bWLAhhddepsmhexpmmphoenixm1.4.2lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruejIbWLAhhd timestampmhexpmmjasonm 1.0.0-rc.3hhbaaha aa:dbWLAhhdouter_checksummhexpmm phoenix_htmlm 2.4.0-devm 0a/@/։ꠦ=;Aj_bWLAhhdinner_checksummhexpmm plug_cowboym1.0.0m .*}4 tm3_EX0=h' nzDbWLAhhd timestampmhexpmmplugm1.11.0hhbaahaa4aDbWLAhhd timestampmhexpmmranchm2.0.0hhba a ha aa  bWLAhhddepsmhexpmmphoenixm0.5.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmlinguistmlinguistm~> 0.1.2dfalsehmhexpmmplugmplugm~> 0.8.1dfalsehmhexpmmpoisonmpoisonm~> 1.1dfalsejFbWLAhhd timestampmhexpmmdecimalm0.2.2hhbaaha aa:FbWLAhhd timestampmhexpmmphoenixm0.3.1hhba a ha aa `bWLAhhdinner_checksummhexpmmphoenixm 1.3.0-rc.0m Ni!{19n}o⾊шùGDbWLAhhd timestampmhexpmmplugm0.11.3hhbaahaa4aFbWLAhhd timestampmhexpmmdecimalm0.1.1hhbaaha aa:'bWLAhhddepsmhexpmmphoenixm1.3.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4dfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0dfalsejbWLAhhddepsmhexpmmplugm1.10.3lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejbWLAhhddepsmhexpmmcowboym2.2.1lhmhexpmmcowlibmcowlibm~> 2.1.0dfalsehmhexpmmranchmranchm~> 1.4.0dfalsejXbWLAhhdouter_checksummhexpmmplugm1.6.2m q<W&a +UlALu{\bWLAhhdinner_checksummhexpmmphoenixm0.10.0m ,~33gaTyV0A*IX+bWLAhhddepsmhexpmmranchm1.6.1j:bWLAhhdretiredmhexpmm phoenix_htmlm2.5.0dnilEbWLAhhd timestampmhexpmmpoisonm1.2.1hhba a ha a.aCbWLAhhd timestampmhexpmmplugm1.4.5hhbaahaa4a_bWLAhhdouter_checksummhexpmm plug_cowboym2.1.1m O W[9DXm}rLs}6bWLAhhdretiredmhexpmmphoenixm0.15.0dnilFbWLAhhd timestampmhexpmmphoenixm0.7.2hhba a ha aa 2bWLAhhdretiredmhexpmmplugm0.8.4dnil7bWLAhhdretiredmhexpmm telemetrym0.1.0dnilCbWLAhhd timestampmhexpmmplugm1.6.2hhbaahaa4a[bWLAhhdouter_checksummhexpmmphoenixm1.4.3m ܨwg*Khb+l0Bňi1X`bWLAhhdouter_checksummhexpmmphoenixm 1.5.0-rc.0m 7}ʋNEBt_y e%gfXbWLAhhdinner_checksummhexpmmmimem1.4.0m PfIDp(aF}/sQ̨/H5і|GXbWLAhhdouter_checksummhexpmmplugm0.9.0m '&PrUv1}V$K8MzTJ bWLAhhddepsmhexpmmphoenixm1.0.3lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejdbWLAhhdouter_checksummhexpmm phoenix_htmlm 2.7.0-devm OhnV.m%VH;Dyd Z[bWLAhhdouter_checksummhexpmmphoenixm1.5.9m ~K |/ ] 4gyWJuB}FbWLAhhd timestampmhexpmmphoenixm1.4.8hhba a ha aa `bWLAhhdouter_checksummhexpmmphoenixm 1.2.0-rc.1m _ʨul! ifʂA,5bWLAhhdretiredmhexpmmdecimalm1.6.0dnilCbWLAhhd timestampmhexpmmplugm0.4.1hhbaahaa4aDbWLAhhd timestampmhexpmmranchm1.7.1hhba a ha aa jbWLAhhddepsmhexpmmphoenixm1.4.14lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsejYbWLAhhdouter_checksummhexpmmplugm1.10.4m #?=.Vaeh`w{g1H-NwQbWLAhhd registry_etagmhexpmmcowlibm""02967331d4715db91bfad19c64a3a0fa"3bWLAhhdretiredmhexpmmplugm1.10.2dnil`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.9.0m ŔEtYLjU yL{D+FbWLAhhd timestampmhexpmmphoenixm1.1.6hhba a ha aa [bWLAhhdinner_checksummhexpmmdecimalm1.1.1m [g1ͬoyo(]nVnv4bWLAhhdretiredmhexpmmcowboym2.5.0dnilZbWLAhhdouter_checksummhexpmmcowlibm2.2.1m 1NdEf8K2kRh-bWLAhhddepsmhexpmmdecimalm0.2.5jabWLAhhdouter_checksummhexpmm phoenix_htmlm2.10.4m vٙ84*F"\h402i gKbWLAhhd timestampmhexpmmphoenixm 1.4.0-rc.3hhba a ha aa ]bWLAhhdouter_checksummhexpmm telemetrym0.4.1m G88.6]g`,V m)ތXHDbWLAhhd timestampmhexpmmplugm0.12.0hhbaahaa4aXbWLAhhdouter_checksummhexpmmplugm1.2.4m @<*< 0!Ǝ +D(;P}wbWLAhhddepsmhexpmm phoenix_htmlm1.0.1lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0dfalsejHbWLAhhd timestampmhexpmmplugm 1.5.0-rc.1hhbaahaa4aXbWLAhhdouter_checksummhexpmmplugm1.1.7m ֵePvEb}DزVAZbWLAhhdouter_checksummhexpmmpoisonm5.0.0m a bYO9Bn7bWLAhhdretiredmhexpmm telemetrym0.4.0dnil[bWLAhhdinner_checksummhexpmmphoenixm1.5.6m ʹC$+ArAhufFbWLAhhd timestampmhexpmmphoenixm1.0.0hhba a ha aa KbWLAhhd timestampmhexpmm phoenix_htmlm3.0.3hhba a ha aa `bWLAhhdouter_checksummhexpmmphoenixm 1.4.0-rc.3m SJjJDv.8[˭FOn>35[bWLAhhdinner_checksummhexpmmphoenixm1.4.2m :P e; T4iMbWLAhhddepsmhexpmmphoenixm0.7.2lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 0.9.0dfalsehmhexpmmpoisonmpoisonm~> 1.2dfalsej[bWLAhhdinner_checksummhexpmmphoenixm0.2.2m "r4|2_IijJPtT fBabWLAhhdouter_checksummhexpmm phoenix_htmlm2.14.3m ֗ZkCۈGˣS}_[bWLAhhdinner_checksummhexpmmphoenixm1.0.2m ] 2EmUah⽶GzC2fPbWLAhhd registry_etagmhexpmmjasonm""c7ccb0b0d3701498a0a717862ef103b1"[bWLAhhdouter_checksummhexpmmdecimalm1.1.2m zmM8׸8P"H|h=3Q㦑2bWLAhhdretiredmhexpmmplugm1.6.1dnil;bWLAhhdretiredmhexpmm phoenix_htmlm2.13.1dnilbWLAhhdretiredmhexpmmcowboy_telemetrym0.1.0dnilbWLAhhddepsmhexpmmphoenixm1.1.8lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0.5dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsejKbWLAhhd timestampmhexpmm phoenix_htmlm2.1.1hhba a ha aa dbWLAhhdouter_checksummhexpmm phoenix_htmlm 2.0.0-devm 0WyʀZPlϠG!ȪbWLAhhddepsmhexpmmplugm1.3.6lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsejbWLAhhdretiredmhexpmmcowlibm2.7.1tdmessagem+Published with invalid version in .app filedreasondRETIRED_INVALIDkbWLAhhddepsmhexpmmjasonm 1.0.0-rc.3lhmhexpmmdecimalmdecimalm~> 1.0dtruej]bWLAhhdouter_checksummhexpmm telemetrym0.4.3m r6_֊b %&˂^T$DvA;bWLAhhdretiredmhexpmm phoenix_htmlm2.11.2dnil7bWLAhhdretiredmhexpmm telemetrym0.3.0dnil]bWLAhhdouter_checksummhexpmmplugm 1.4.0-rc.0m \u6q ]v8' ln*AM}~\bWLAhhdouter_checksummhexpmmphoenixm1.5.10m ꥨZA&ƪo>3 Z}YbWLAhhdouter_checksummhexpmmranchm1.1.0m 9>eg][ŽS?`@WHXbWLAhhdinner_checksummhexpmmplugm0.4.1m S?P>UjWz cc "KbWLAhhd timestampmhexpmm phoenix_htmlm1.0.0hhba a ha aa ^bWLAhhdinner_checksummhexpmmjasonm 1.0.0-rc.3m RHr `D>PŕQOˣfƔ~FbWLAhhd timestampmhexpmmdecimalm1.3.0hhbaaha aa:2bWLAhhdretiredmhexpmmplugm1.6.3dnil[bWLAhhdouter_checksummhexpmmphoenixm1.5.5m n.3oZ7xU$N' F;[bWLAhhdouter_checksummhexpmmphoenixm0.2.2m hm1%Ũ1oLR2/(8MXco7bWLAhhdretiredmhexpmm telemetrym0.2.0dnilbbWLAhhdinner_checksummhexpmmphoenix_pubsubm1.0.0m "FY'x@ʇa A ,W[bWLAhhdinner_checksummhexpmmphoenixm1.4.7m 弚Lu R}Zl*<awmXbWLAhhdinner_checksummhexpmmplugm1.3.3m پ$7NG sЕIί#CbWLAhhd timestampmhexpmmplugm1.3.0hhbaahaa4a_bWLAhhdinner_checksummhexpmm plug_cowboym2.1.1m (ۥUbuwK35bWLAhhdretiredmhexpmmphoenixm0.2.1dnilGbWLAhhd timestampmhexpmmphoenixm0.13.0hhba a ha aa 6bWLAhhdretiredmhexpmmphoenixm0.2.10dnil2bWLAhhdretiredmhexpmmmimem1.3.1dnil[bWLAhhdouter_checksummhexpmmphoenixm1.1.2m dƼmd/(VYZ#D/bWLAhhddepsmhexpmm telemetrym0.4.2j`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.1.2m gKHuE?zDFx9 7to6DbWLAhhd timestampmhexpmmranchm1.6.0hhba a ha aa ibWLAhhddepsmhexpmm phoenix_htmlm2.13.1lhmhexpmmplugmplugm~> 1.5dfalsej-bWLAhhddepsmhexpmmdecimalm1.7.0jXbWLAhhdinner_checksummhexpmmmimem1.1.0m =ǸFQ9b4;( ;bWLAhhdretiredmhexpmm phoenix_htmlm2.14.0dnil4bWLAhhdretiredmhexpmmcowboym2.0.0dnilZbWLAhhdouter_checksummhexpmmcowlibm1.2.0m IeBa:%(]ʆ=MgI=[bWLAhhdouter_checksummhexpmmphoenixm1.0.4m Y_:oTIj- {O5&foi3bWLAhhdretiredmhexpmmranchm1.3.1dnilcbWLAhhddepsmhexpmmplugm1.0.1lhmhexpmmcowboymcowboym~> 1.0dtruejLbWLAhhd timestampmhexpmm phoenix_htmlm2.13.4hhba a ha aa JbWLAhhd timestampmhexpmm plug_cryptom1.2.2hhbaahaaa.DbWLAhhd timestampmhexpmmranchm1.4.0hhba a ha aa jbWLAhhddepsmhexpmmphoenixm1.4.10lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsejEbWLAhhd timestampmhexpmmpoisonm3.0.0hhba a ha a.abWLAhhddepsmhexpmm plug_cowboym2.1.2lhmhexpmmcowboymcowboym~> 2.5dfalsehmhexpmmplugmplugm~> 1.7dfalsej[bWLAhhdinner_checksummhexpmmphoenixm1.2.3m ֧>8YwDXIHHbWLAhhd timestampmhexpmm telemetrym1.0.0hhbaahaa4a7bWLAhhdretiredmhexpmmplugm 1.5.0-rc.1dnilFbWLAhhd timestampmhexpmmphoenixm1.2.4hhba a ha aa CbWLAhhd timestampmhexpmmmimem2.0.1hhbaahaaa.2bWLAhhdretiredmhexpmmplugm0.8.1dnilXbWLAhhdouter_checksummhexpmmmimem1.1.0m 3 ګVh\ã8)'( _M[bWLAhhdouter_checksummhexpmmphoenixm1.2.2m vLJUԾ=er*bWLAhhdretiredmhexpmmcowboy_telemetrym0.3.0dnilXbWLAhhdinner_checksummhexpmmplugm1.2.3m P W8r^ @&sS3,UsYbWLAhhdinner_checksummhexpmmjasonm1.2.0m 4-$!-?]HCv 8yY8HFbWLAhhd timestampmhexpmmdecimalm1.8.1hhbaaha aa:CbWLAhhd timestampmhexpmmplugm1.4.4hhbaahaa4aEbWLAhhd timestampmhexpmmpoisonm4.0.1hhba a ha a.aFbWLAhhd timestampmhexpmmphoenixm0.2.1hhba a ha aa 5bWLAhhdretiredmhexpmmphoenixm1.5.0dnil[bWLAhhdouter_checksummhexpmmdecimalm1.4.0m vqN/q5 :=8K-zEFq#/bWLAhhddepsmhexpmm telemetrym0.4.1jCbWLAhhd timestampmhexpmmplugm1.3.6hhbaahaa4aibWLAhhddepsmhexpmm phoenix_htmlm2.13.2lhmhexpmmplugmplugm~> 1.5dfalsej bWLAhhddepsmhexpmmphoenixm0.6.2lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmlinguistmlinguistm~> 0.1.4dfalsehmhexpmmplugmplugm~> 0.8.4dfalsehmhexpmmpoisonmpoisonm~> 1.2dfalsej4bWLAhhdretiredmhexpmmpoisonm2.0.1dnilXbWLAhhdouter_checksummhexpmmplugm1.3.4m bˠ*^MujXR:]bWLAhhdouter_checksummhexpmm telemetrym1.0.0m s Y(NF$3U(~ɮvng8PzZbWLAhhdouter_checksummhexpmmcowboym2.7.0m j9ƪa#a*:R{pgHbWLAhhd timestampmhexpmm telemetrym0.4.3hhbaahaa4a0bWLAhhddepsmhexpmmranchm 2.0.0-rc.1jXbWLAhhdinner_checksummhexpmmplugm1.4.5m {sS&,^ ]$ '=KbWLAhhd timestampmhexpmm phoenix_htmlm2.6.1hhba a ha aa bWLAhhddepsmhexpmm plug_cowboym2.1.3lhmhexpmmcowboymcowboym~> 2.5dfalsehmhexpmmplugmplugm~> 1.7dfalsejCbWLAhhd timestampmhexpmmplugm0.8.1hhbaahaa4a2bWLAhhdretiredmhexpmmmimem1.0.0dnil`bWLAhhdinner_checksummhexpmm phoenix_htmlm1.2.0m Aگ^˸L^9sgSX9HX[bWLAhhdinner_checksummhexpmmphoenixm0.2.1m e/4QDU(R=7qPDdJ zk5bWLAhhdretiredmhexpmmphoenixm1.3.4dnilbWLAhhddepsmhexpmmphoenixm0.2.11lhmhexpmmex_confmex_confm0.1.2dfalsehmhexpmminflexminflexm0.2.4dfalsehmhexpmmjazzmjazzm0.1.2dfalsehmhexpmmplugmplugm0.5.1dfalsej3bWLAhhdretiredmhexpmmplugm1.11.1dnilYbWLAhhdinner_checksummhexpmmjasonm1.1.2m =g#&LqTVM=!dbWLAhhddepsmhexpmmphoenixm0.2.2lhmhexpmmex_confmex_confm0.1.1dfalsehmhexpmminflexminflexm0.2.0dfalsehmhexpmmjazzmjazzm0.1.0dfalsehmhexpmmplugmplugm0.4.2dfalsej`bWLAhhdinner_checksummhexpmmphoenixm 1.4.0-rc.1m rZsVAE fKjtFbWLAhhd timestampmhexpmmphoenixm1.2.1hhba a ha aa /bWLAhhddepsmhexpmm telemetrym0.4.3j;bWLAhhdretiredmhexpmm phoenix_htmlm2.10.5dnilZbWLAhhdinner_checksummhexpmmcowlibm2.0.1m M^pt ZSڂ8FbWLAhhd timestampmhexpmmdecimalm0.2.1hhbaaha aa:2bWLAhhdretiredmhexpmmplugm1.4.1dnilXbWLAhhdouter_checksummhexpmmplugm1.1.0m iijK'%.? -ޙ/قKEbWLAhhd timestampmhexpmmpoisonm2.0.1hhba a ha a.abWLAhhddepsmhexpmmphoenixm0.3.0lhmhexpmmex_confmex_confm0.1.2dfalsehmhexpmminflexminflexm0.2.4dfalsehmhexpmmjazzmjazzm0.1.2dfalsehmhexpmmplugmplugm0.5.1dfalsej4bWLAhhdretiredmhexpmmpoisonm1.1.0dnilFbWLAhhd timestampmhexpmmdecimalm1.9.0hhbaaha aa:XbWLAhhdinner_checksummhexpmmplugm1.4.1m .g} 1.0dtruehmhexpmmlinguistmlinguistm~> 0.1.3dfalsehmhexpmmplugmplugm~> 0.8.2dfalsehmhexpmmpoisonmpoisonm~> 1.2dfalsej[bWLAhhdouter_checksummhexpmmdecimalm0.2.2m 䑳ƑfZz hӨuR?/ ,.7}.KbWLAhhd timestampmhexpmm phoenix_htmlm2.3.1hhba a ha aa 5bWLAhhdretiredmhexpmmdecimalm1.2.0dnil-bWLAhhddepsmhexpmmdecimalm0.2.1j,bWLAhhddepsmhexpmmcowlibm2.9.1j[bWLAhhd registry_etagmhexpmmcowboy_telemetrym""fa0b78215ec75029c429f147ec08d767"XbWLAhhdinner_checksummhexpmmplugm1.1.5m VEpAZrS!'Z˒7Bn'_bWLAhhdouter_checksummhexpmm plug_cryptom1.1.1m &Mo A i>bWLAhhdretiredmhexpmm phoenix_htmlm 2.4.0-devdnilKbWLAhhd timestampmhexpmm phoenix_htmlm2.2.0hhba a ha aa ZbWLAhhdouter_checksummhexpmmcowboym2.4.0m ]wYG09uf++]AJbWLAhhddepsmhexpmmphoenixm0.2.1lhmhexpmmex_confmex_confm0.1.1dfalsehmhexpmminflexminflexm0.2.0dfalsehmhexpmmplugmplugm0.4.2dfalsejXbWLAhhdinner_checksummhexpmmmimem1.2.0m x+6 ӴQ@уֈ[ 4_bWLAhhdinner_checksummhexpmm plug_cowboym2.0.1m טZ̆-DPf\#nQ2AzbWLAhhddepsmhexpmmplugm1.4.3lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsej\bWLAhhdinner_checksummhexpmmphoenixm1.5.11m `՗J1O:ɰ6FΚG`y 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej`bWLAhhdouter_checksummhexpmm phoenix_htmlm1.0.0m Eg^-"t"ޟR ʻu;ɒXw\(abWLAhhdinner_checksummhexpmm phoenix_htmlm2.13.4m =nFd$JI' P*}mء[OWLAhhdouter_checksummhexpmmphoenixm1.1.6m %Gd1D p4~ M_$#5* )4XbWLAhhdouter_checksummhexpmmplugm1.2.2m ^Dl]R&~,23~YbWLAhhdouter_checksummhexpmmjasonm1.1.1m cEϬ2^4gry:42•%#LCbWLAhhd timestampmhexpmmplugm1.1.3hhbaahaa4aZbWLAhhdinner_checksummhexpmmpoisonm1.3.1m ]B߂^.>)jJ[/2}L0RbWLAhhd registry_etagmhexpmmdecimalm""4657f6f92e3c01945c9c9b06b6fd850e"ZbWLAhhdouter_checksummhexpmmcowlibm2.9.0m ;? B-ⵂ'[_헏zL,bWLAhhddepsmhexpmmcowlibm2.5.1jZbWLAhhdouter_checksummhexpmmcowlibm2.5.0m Ivs`ZjNP" N\bWLAhhdinner_checksummhexpmmphoenixm0.13.0m FN6"K]m/6Mݛ%;bWLAhhdretiredmhexpmm phoenix_htmlm2.10.4dnilYbWLAhhdouter_checksummhexpmmplugm1.10.1m R%1EII*,abWLAhhdouter_checksummhexpmm phoenix_htmlm2.10.2m u"cLoМ ֭r*1,bWLAhhddepsmhexpmmpoisonm4.0.1jYbWLAhhdinner_checksummhexpmmranchm1.4.0m '/y4wK r]lٔkXbWLAhhdinner_checksummhexpmmplugm0.5.1m X)],{/xL5_RxP)2bWLAhhdretiredmhexpmmplugm0.4.3dnilZbWLAhhdouter_checksummhexpmmpoisonm1.2.1m {hhb4>B`4.#FbWLAhhd timestampmhexpmmdecimalm2.0.0hhbaaha aa:3bWLAhhdretiredmhexpmmjasonm1.2.1dnil[bWLAhhdouter_checksummhexpmmphoenixm1.0.0m s/L!p_1iRV䒥WbWLAhhddepsmhexpmmcowboym2.6.3lhmhexpmmcowlibmcowlibm~> 2.7.3dfalsehmhexpmmranchmranchm~> 1.7.1dfalsejEbWLAhhd timestampmhexpmmcowboym1.0.1hhbaahaaa.XbWLAhhdouter_checksummhexpmmplugm0.5.2m }8gD'CviD@ӋVSXbWLAhhdouter_checksummhexpmmplugm0.4.4m 1-W3SXd#_Y:[bWLAhhdouter_checksummhexpmmphoenixm1.5.8m 5У/H6z;.I!%C2UZbWLAhhdinner_checksummhexpmmpoisonm1.2.1m P$:er ts IZbWLAhhdinner_checksummhexpmmcowboym1.0.2m j/c[ai@,?H37_bWLAhhdinner_checksummhexpmm plug_cowboym2.3.0m P\<Pj7WP2f#ݩ]bWLAhhdinner_checksummhexpmmplugm 1.5.0-rc.1m r>%f-k,\a$B/9&fbWLAhhddepsmhexpmmjasonm1.1.0lhmhexpmmdecimalmdecimalm~> 1.0dtruej6bWLAhhdretiredmhexpmmphoenixm1.4.12dnilZbWLAhhdouter_checksummhexpmmcowlibm2.0.1m t\"gu"!qdr 2bWLAhhdretiredmhexpmmplugm1.1.4dnil[bWLAhhdouter_checksummhexpmmdecimalm1.9.0m 45h֒>u[] o}XbWLAhhdinner_checksummhexpmmplugm1.9.0m |N&"3Gs$Ë{F}Y$:ZJZbWLAhhdouter_checksummhexpmmcowlibm2.5.1m 4¶:k@UD%,S]EbWLAhhd timestampmhexpmmcowboym1.1.2hhbaahaaa.bWLAhhddepsmhexpmmplugm1.9.0lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0dfalsehmhexpmm telemetrym telemetrym~> 0.4dtruejZbWLAhhdouter_checksummhexpmmcowlibm1.0.2m b-:96jŚ)7'D`bWLAhhdouter_checksummhexpmmphoenixm 1.2.0-rc.0m ߒҏ&=b Oֵ> #iZn`ZSakPCbWLAhhd timestampmhexpmmmimem1.0.0hhbaahaaa.ZbWLAhhdinner_checksummhexpmmcowboym2.8.0m bW٬Pta˜@SɥW{׼f}XbWLAhhdouter_checksummhexpmmplugm1.4.5m (k4oA뽰ĸ^CbWLAhhd timestampmhexpmmplugm1.7.1hhbaahaa4axx*K9YΫK8uFbWLAhhd timestampmhexpmmdecimalm1.4.1hhbaaha aa:4bWLAhhdretiredmhexpmmpoisonm2.1.0dnil,bWLAhhddepsmhexpmmcowlibm2.3.0j6bWLAhhdretiredmhexpmmphoenixm0.16.0dnil]bWLAhhdinner_checksummhexpmm telemetrym0.3.0m <Gqc <"f" :*:jguJs5bWLAhhdretiredmhexpmmdecimalm1.4.0dnilabWLAhhdouter_checksummhexpmm phoenix_htmlm2.11.0m 6h+2INp#9:@7}~Ne h_)IbWLAhhd timestampmhexpmmjasonm 1.0.0-rc.1hhbaaha aa:5bWLAhhdretiredmhexpmmphoenixm1.5.5dnilKbWLAhhd timestampmhexpmm phoenix_htmlm1.1.0hhba a ha aa \bWLAhhdouter_checksummhexpmmphoenixm1.4.14m IX%G{eeq [v+XTEbWLAhhd timestampmhexpmmpoisonm1.1.1hhba a ha a.aZbWLAhhdouter_checksummhexpmmpoisonm2.0.0m \um'A ߫' إCqjbWLAhhddepsmhexpmmphoenixm1.4.15lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsej,bWLAhhddepsmhexpmmpoisonm1.3.0j[bWLAhhdinner_checksummhexpmmphoenixm0.2.6m '1C &Sno1lmbu Q^IXbWLAhhdinner_checksummhexpmmplugm1.2.2m Ͻ!L?퍏K͛3bWLAhhdretiredmhexpmmjasonm1.0.1dnilLbWLAhhd timestampmhexpmm phoenix_htmlm2.10.4hhba a ha aa bWLAhhddepsmhexpmmphoenixm0.8.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 0.9.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsej3bWLAhhdretiredmhexpmmplugm0.11.3dnildbWLAhhdouter_checksummhexpmmcowboy_telemetrym0.4.0m }Eeb՟#5j˸819tJbWLAhhd timestampmhexpmm plug_cowboym2.0.2hhba a ha aa YbWLAhhdinner_checksummhexpmmjasonm1.0.1m d6K<p3T]s.g[bWLAhhdinner_checksummhexpmmdecimalm2.0.0m LlWCG/By+VB֗,bWLAhhddepsmhexpmmpoisonm1.1.1j:bWLAhhdretiredmhexpmm phoenix_htmlm2.7.0dnil4bWLAhhddepsmhexpmmphoenix_pubsubm2.0.0j2bWLAhhdretiredmhexpmmmimem1.6.0dnilcbWLAhhddepsmhexpmmplugm1.1.0lhmhexpmmcowboymcowboym~> 1.0dtruej4bWLAhhdretiredmhexpmmcowboym2.1.0dnilJbWLAhhd timestampmhexpmm plug_cowboym2.2.0hhba a ha aa CbWLAhhd timestampmhexpmmplugm0.5.3hhbaahaa4aFbWLAhhd timestampmhexpmmdecimalm1.8.0hhbaaha aa:CbWLAhhd timestampmhexpmmplugm1.2.5hhbaahaa4aabWLAhhdinner_checksummhexpmm phoenix_htmlm2.10.2m IclL^>5,uaB1?*GbWLAhhd timestampmhexpmmphoenixm1.4.11hhba a ha aa HbWLAhhd timestampmhexpmm telemetrym0.1.0hhbaahaa4a`bWLAhhdouter_checksummhexpmm phoenix_htmlm1.1.0m tD/}"/Gv"{>_vP]1YbWLAhhdouter_checksummhexpmmplugm0.12.0m LeEo448W }L<NJEbWLAhhd timestampmhexpmmcowlibm2.0.1hhbaahaaa.5bWLAhhdretiredmhexpmmdecimalm1.7.0dnil_bWLAhhdinner_checksummhexpmm plug_cryptom1.0.0m 4?$b')ԥ- }M+bWLAhhddepsmhexpmmranchm1.0.0jbbWLAhhdinner_checksummhexpmmphoenix_pubsubm1.1.2m Il0;.n[::75KM*Z>EbWLAhhd timestampmhexpmmpoisonm2.1.0hhba a ha a.a[bWLAhhdinner_checksummhexpmmdecimalm1.0.0m 4") h_S)3rm"K^GbWLAhhd timestampmhexpmmphoenixm0.2.10hhba a ha aa 3bWLAhhdretiredmhexpmmplugm0.11.1dnil4bWLAhhdretiredmhexpmmcowboym1.0.1dnil:bWLAhhdretiredmhexpmmphoenixm 1.6.0-rc.0dnil\bWLAhhdinner_checksummhexpmmphoenixm0.15.0m JRlá=z^J@"!KibWLAhhddepsmhexpmm phoenix_htmlm2.14.1lhmhexpmmplugmplugm~> 1.5dfalsejZbWLAhhdinner_checksummhexpmmcowlibm2.3.0m Վ7NO|.jNdL6bWLAhhdretiredmhexpmmphoenixm0.11.0dnil\bWLAhhdouter_checksummhexpmmphoenixm1.4.13m vZہbl{_QYb`tRv}"bWLAhhddepsmhexpmmphoenixm1.4.3lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruejFbWLAhhd timestampmhexpmmdecimalm0.2.4hhbaaha aa:bWLAhhddepsmhexpmmphoenixm1.5.1lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej9bWLAhhdretiredmhexpmm plug_cowboym2.4.0dnilYbWLAhhdinner_checksummhexpmmplugm0.11.0m ΰ`P&o@dS5,bWLAhhddepsmhexpmmpoisonm3.0.0jbWLAhhdversionsmhexpmmranchlm1.0.0m1.1.0m1.2.0m1.2.1m1.3.0m1.3.1m1.3.2m1.4.0m1.5.0m1.6.0m1.6.1m1.6.2m1.7.0m1.7.1m1.8.0m 2.0.0-rc.1m 2.0.0-rc.2m2.0.0m2.1.0jbWLAhhddepsmhexpmmphoenixm1.5.8lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejObWLAhhd timestampmhexpmmcowboy_telemetrym0.3.1hhbaahaaaObWLAhhd timestampmhexpmmcowboy_telemetrym0.3.0hhbaahaaa\bWLAhhdinner_checksummhexpmmphoenixm1.4.13m g'֛QqT`OJ?a46V9bWLAhhdretiredmhexpmm plug_cryptom1.2.2dnil`bWLAhhdinner_checksummhexpmm phoenix_htmlm3.0.2m q}_!"^d*0bWLAhhddepsmhexpmm plug_cowboym2.2.1lhmhexpmmcowboymcowboym~> 2.7dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej:bWLAhhdretiredmhexpmmdecimalm 2.0.0-rc.0dnilCbWLAhhd timestampmhexpmmplugm1.4.1hhbaahaa4a[bWLAhhdinner_checksummhexpmmphoenixm1.5.4m ʜ`Ic _%[ g~p?`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.1.1m *Zbu6n4"0A(uTW705bWLAhhdretiredmhexpmmphoenixm1.4.2dnil4bWLAhhdretiredmhexpmmcowboym1.0.0dnil bWLAhhddepsmhexpmmphoenixm0.4.0lhmhexpmmcowboymcowboym~> 1.0.0dtruehmhexpmmlinguistmlinguistm~> 0.1.2dfalsehmhexpmmplugmplugm0.7.0dfalsehmhexpmmpoisonmpoisonm~> 1.0.1dfalsej4bWLAhhddepsmhexpmmphoenix_pubsubm1.1.0jObWLAhhd timestampmhexpmmcowboy_telemetrym0.2.0hhbaahaaa2bWLAhhdretiredmhexpmmplugm1.8.0dnilcbWLAhhddepsmhexpmmplugm1.0.3lhmhexpmmcowboymcowboym~> 1.0dtruej`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.3.1m ?in`"yԡ_n}fo0-B_bWLAhhdinner_checksummhexpmm plug_cryptom1.1.0m HCՐbOH%oGU]ɇT.bWLAhhddepsmhexpmm plug_cowboym2.2.2lhmhexpmmcowboymcowboym~> 2.7dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejYbWLAhhdouter_checksummhexpmmranchm1.2.0m Qzע- ɱOÙGbWLAhhd timestampmhexpmmphoenixm0.16.0hhba a ha aa [bWLAhhdinner_checksummhexpmmdecimalm1.4.0m eqjSӦEf(Ze.J3JbWLAhhd timestampmhexpmm plug_cryptom1.2.0hhbaahaaa.[bWLAhhdouter_checksummhexpmmphoenixm0.2.4m 1=Trū9cJ q4)(8}`bWLAhhdouter_checksummhexpmmdecimalm 1.9.0-rc.0m fuEsW\^KE0|dX< GEbWLAhhd timestampmhexpmmcowlibm1.0.1hhbaahaaa.zbWLAhhdretiredmhexpmm plug_cowboym2.2.0tdmessagemBroken telemetry supportdreasondRETIRED_INVALID6bWLAhhdretiredmhexpmmphoenixm1.4.10dnil;bWLAhhdretiredmhexpmm phoenix_htmlm2.11.0dnil;bWLAhhdretiredmhexpmm phoenix_htmlm2.13.2dnil,bWLAhhddepsmhexpmmphoenixm 1.3.0-rc.3lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.3.2 or ~> 1.4dfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0dfalsej-bWLAhhddepsmhexpmmdecimalm1.0.1j*bWLAhhddepsmhexpmmmimem2.0.0jdbWLAhhddepsmhexpmmplugm0.13.0lhmhexpmmcowboymcowboym~> 1.0dtruejXbWLAhhdouter_checksummhexpmmplugm1.6.4m iE;2 0 tH`EbWLAhhd timestampmhexpmmpoisonm1.0.0hhba a ha a.aYbWLAhhdinner_checksummhexpmmranchm1.3.0m ,U]X@]'.-v/YbWLAhhdinner_checksummhexpmmplugm1.12.0m 9kmRlE,Sh؅Ym0ZbWLAhhdouter_checksummhexpmmpoisonm1.5.2m JYܭȹ4UNdz+Gg8:f?:bWLAhhdretiredmhexpmm phoenix_htmlm2.9.1dnil_bWLAhhdinner_checksummhexpmm plug_cowboym2.0.0m r+ 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejYbWLAhhdouter_checksummhexpmmplugm0.11.0m "89fأXI?J\sT68EhGCbWLAhhd timestampmhexpmmplugm0.6.0hhbaahaa4abWLAhhddepsmhexpmmphoenixm1.1.4lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsejXbWLAhhdinner_checksummhexpmmplugm1.7.1m er.t%O6N >sbWLAhhddepsmhexpmm phoenix_htmlm2.3.1lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsej6bWLAhhdretiredmhexpmmphoenixm1.5.10dnil3bWLAhhdretiredmhexpmmranchm1.1.0dnil`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.7.0m h׫~~څ1m%s{q ORNw[bWLAhhdouter_checksummhexpmmphoenixm0.3.0m syeݞ 5aREx#7vK"[bWLAhhdinner_checksummhexpmmdecimalm1.2.0m F)`q(.W{Gkkh'}M dcRiK abWLAhhdouter_checksummhexpmm phoenix_htmlm2.14.1m SmR7[2A z:, _wc$_bWLAhhdouter_checksummhexpmm plug_cowboym2.4.1m !z}*[h(㩧R~P2@S2bWLAhhdretiredmhexpmmplugm1.1.0dnilabWLAhhdinner_checksummhexpmm phoenix_htmlm2.14.0m Ƽ(_Z=,XiB{eZhbWLAhhddepsmhexpmm phoenix_htmlm2.9.3lhmhexpmmplugmplugm~> 1.0dfalsejbbWLAhhdouter_checksummhexpmmphoenix_pubsubm2.0.0m -O&wKPYuCSPqXbWLAhhdinner_checksummhexpmmplugm1.2.6m a6ՀEʫz:( :$"J 1.0dtruehmhexpmmlinguistmlinguistm~> 0.1.4dfalsehmhexpmmplugmplugm~> 0.8.4dfalsehmhexpmmpoisonmpoisonm~> 1.2dfalsejXbWLAhhdinner_checksummhexpmmmimem2.0.0m ާpxxFب?fG V[bWLAhhdouter_checksummhexpmmphoenixm0.6.0m \&9&)!cigN{Ezx9[bWLAhhdinner_checksummhexpmmdecimalm0.1.2m iڠV;j\åiTYj+bWLAhhddepsmhexpmmranchm2.0.0jXbWLAhhdouter_checksummhexpmmplugm1.0.4m ,翈QEfss0Lӛ[Q[bWLAhhdouter_checksummhexpmmphoenixm1.2.0m l@CF/l,86B"D^xQ_Un\bWLAhhdinner_checksummhexpmmphoenixm1.4.14m ڭh\@WF>ˇj슶ޢBL$AN8XbWLAhhdinner_checksummhexpmmmimem0.0.1m s3ާj2Ӭeěs!>&ZbWLAhhdouter_checksummhexpmmcowboym1.0.4m jh 3SBcSLڦbWLAhhddepsmhexpmmphoenixm0.11.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm ~> 0.11.3dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsehmhexpmmpoolboympoolboym~> 1.4.2dtruej2bWLAhhdretiredmhexpmmplugm1.4.5dnilObWLAhhd registry_etagmhexpmmplugm""4d85e7c2ce3c20c0441d3cb07bf25c4c"EbWLAhhd timestampmhexpmmcowlibm2.0.0hhbaahaaa.ibWLAhhddepsmhexpmm phoenix_htmlm2.10.1lhmhexpmmplugmplugm~> 1.0dfalsej>bWLAhhdretiredmhexpmm phoenix_htmlm 2.0.0-devdnilXbWLAhhdouter_checksummhexpmmplugm1.2.5m >m?ªc`p\ѥLEuEbWLAhhd timestampmhexpmmcowboym2.6.0hhbaahaaa.3bWLAhhdretiredmhexpmmplugm1.10.4dnilZbWLAhhdinner_checksummhexpmmcowboym2.1.0m i;#Ji5q0k9͡ł>RXFbWLAhhd timestampmhexpmmphoenixm1.0.2hhba a ha aa FbWLAhhd timestampmhexpmmphoenixm0.2.6hhba a ha aa 5bWLAhhdretiredmhexpmmphoenixm0.2.6dnilFbWLAhhd timestampmhexpmmphoenixm1.0.3hhba a ha aa YbWLAhhdouter_checksummhexpmmplugm0.13.0m +oĦ%W{US1o":gL_bWLAhhddepsmhexpmmphoenixm0.17.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsej2bWLAhhdretiredmhexpmmplugm1.3.4dnil]bWLAhhdouter_checksummhexpmmplugm 1.5.0-rc.2m 8TUHlH̖"4jaLZbWLAhhdinner_checksummhexpmmcowlibm2.4.0m R_(zJ{kf'DpHeڎ:bWLAhhdretiredmhexpmm phoenix_htmlm1.1.0dnilbWLAhhddepsmhexpmmphoenixm1.5.12lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13 or ~> 3.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejKbWLAhhd timestampmhexpmm phoenix_htmlm3.0.2hhba a ha aa KbWLAhhd timestampmhexpmm phoenix_htmlm1.2.1hhba a ha aa CbWLAhhd timestampmhexpmmplugm0.5.1hhbaahaa4aibWLAhhddepsmhexpmmphoenixm1.4.7lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsej[bWLAhhdinner_checksummhexpmmdecimalm1.9.0m 12q$,QK =M,bWLAhhddepsmhexpmmcowlibm2.1.0j*bWLAhhddepsmhexpmmplugm0.4.1jCbWLAhhd timestampmhexpmmplugm0.8.0hhbaahaa4a`bWLAhhdouter_checksummhexpmm phoenix_htmlm3.0.1m PUi]O[vkT z.::bWLAhhdretiredmhexpmm phoenix_htmlm2.1.2dnil4bWLAhhdretiredmhexpmmpoisonm1.3.0dnilIbWLAhhd timestampmhexpmmjasonm 1.0.0-rc.2hhbaaha aa:ZbWLAhhdinner_checksummhexpmmcowlibm2.9.0m esj*td ˢIKabWLAhhdinner_checksummhexpmm phoenix_htmlm2.11.2m h% 'W [ۓH]doL1f,bWLAhhddepsmhexpmmpoisonm1.5.1j`bWLAhhdinner_checksummhexpmmdecimalm 2.0.0-rc.0m ĸK%5 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruej[bWLAhhdinner_checksummhexpmmphoenixm0.6.0m wކM`H ٤>!CՖ6:bWLAhhdretiredmhexpmm phoenix_htmlm2.9.2dnilbWLAhhddepsmhexpmmphoenixm0.3.1lhmhexpmmex_confmex_confm0.1.3dfalsehmhexpmminflexminflexm0.2.4dfalsehmhexpmmjazzmjazzm0.1.2dfalsehmhexpmmplugmplugm0.5.1dfalsejKbWLAhhd timestampmhexpmm phoenix_htmlm2.7.0hhba a ha aa dbWLAhhddepsmhexpmmplugm0.11.1lhmhexpmmcowboymcowboym~> 1.0dtruej3bWLAhhdretiredmhexpmmranchm1.2.0dnil_bWLAhhdouter_checksummhexpmm plug_cowboym2.2.0m NrrlgzyGMR;(Ѯ\}[XbWLAhhdinner_checksummhexpmmplugm1.3.2m غ.,}i!YdU Uac-'bWLAhhddepsmhexpmmphoenixm1.3.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4dfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0dfalsej2bWLAhhdretiredmhexpmmplugm1.1.1dnil_bWLAhhdouter_checksummhexpmm plug_cowboym2.0.0m ?IbЙ$m|/.j) ns:tXbWLAhhdouter_checksummhexpmmplugm1.8.3m K8-ĄI**w 3N'4ZbWLAhhdinner_checksummhexpmmcowboym1.0.4m $ߟ#3p:'"l穿ix;bWLAhhdretiredmhexpmm phoenix_htmlm2.10.3dnil[bWLAhhdinner_checksummhexpmmdecimalm1.1.0m 33s/}z_ 0-g\ʁ*c>r`bWLAhhdinner_checksummhexpmm phoenix_htmlm1.3.0m $lGT?iM*7Pq+_?;bWLAhhdretiredmhexpmm phoenix_htmlm2.10.1dnil4bWLAhhdretiredmhexpmmcowlibm1.0.1dnil3bWLAhhdretiredmhexpmmplugm1.12.0dnil]bWLAhhdouter_checksummhexpmmplugm 1.5.0-rc.1m A^3\1=ݴGp 1.0 or ~> 2.0dtruej1bWLAhhddepsmhexpmm plug_cryptom1.2.1jbWLAhhddepsmhexpmm plug_cowboym2.0.0lhmhexpmmcowboymcowboym~> 2.5dfalsehmhexpmmplugmplugm~> 1.7dfalsej]bWLAhhdouter_checksummhexpmm telemetrym0.4.2m -j m{XRr.+#b {*DbWLAhhd timestampmhexpmmplugm0.11.2hhbaahaa4a5bWLAhhdretiredmhexpmmphoenixm1.1.9dnilCbWLAhhd timestampmhexpmmplugm0.5.0hhbaahaa4aJbWLAhhd timestampmhexpmm plug_cowboym2.0.0hhba a ha aa @bWLAhhd timestampmhexpmm plug_cowboyhhba a ha aa bWLAhhddepsmhexpmmplugm1.3.2lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsej-bWLAhhddepsmhexpmmdecimalm0.2.2j[bWLAhhdinner_checksummhexpmmphoenixm1.5.7m )#:$EK _Fi=&&\bWLAhhdouter_checksummhexpmmphoenixm1.5.11m 1ê"t 6y_1ւvᴙzֺ$YbWLAhhdinner_checksummhexpmmplugm0.11.3m v;X5!6חtwI`+?WJhbWLAhhddepsmhexpmm phoenix_htmlm2.6.1lhmhexpmmplugmplugm~> 1.0dfalsejFbWLAhhd timestampmhexpmmphoenixm1.5.3hhba a ha aa `bWLAhhdinner_checksummhexpmmphoenixm 1.3.0-rc.3m 7 9 5r}F!Yj 0DZbWLAhhdinner_checksummhexpmmpoisonm1.1.1m  5?# z·8sbWLAhhddepsmhexpmm phoenix_htmlm2.1.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsej+bWLAhhddepsmhexpmmranchm1.3.2j+bWLAhhddepsmhexpmmranchm1.8.0jCbWLAhhd timestampmhexpmmplugm0.4.3hhbaahaa4a[bWLAhhdinner_checksummhexpmmphoenixm1.5.5m Z~(8RMCj5Ex鴽7bWLAhhdretiredmhexpmm telemetrym0.4.3dnil_bWLAhhdouter_checksummhexpmm plug_cowboym1.0.0m BzD$eE"c2u@JabWLAhhddepsmhexpmmphoenixm1.2.2lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.1dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsejKbWLAhhd timestampmhexpmm phoenix_htmlm2.1.2hhba a ha aa 5bWLAhhdretiredmhexpmmphoenixm1.1.1dnilKbWLAhhd timestampmhexpmmphoenixm 1.3.0-rc.3hhba a ha aa 4bWLAhhdretiredmhexpmmpoisonm1.0.3dnil`bWLAhhdouter_checksummhexpmmphoenixm 1.4.0-rc.1m t4<Q7lWHAw>bWLAhhddepsmhexpmmplugm1.2.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmmimemmimem~> 1.0dfalsejXbWLAhhdouter_checksummhexpmmmimem1.3.0m ^(`2j` Ɠݫ%y{ffS(lbWLAhhddepsmhexpmm phoenix_htmlm 2.7.0-devlhmhexpmmplugmplugm~> 1.0dfalsej[bWLAhhdinner_checksummhexpmmphoenixm0.2.8m _3uDj*EЏ%e I}=cZbWLAhhdouter_checksummhexpmmcowboym2.6.3m X)?t6.ZkZ. abWLAhhdinner_checksummhexpmm phoenix_htmlm2.11.1m wRnsd7RX͡2f&~bbWLAhhdouter_checksummhexpmmphoenix_pubsubm0.0.1m SieZc6@1oW: DbWLAhhd timestampmhexpmmjasonm1.0.0hhbaaha aa:YbWLAhhdinner_checksummhexpmmranchm1.0.0m NBewim֦{u%`3YbWLAhhdinner_checksummhexpmmplugm0.14.0m KN&ïF9VgD|Az[`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.1.1m /q?e*;155~`N*m[bWLAhhdouter_checksummhexpmmphoenixm1.1.9m 1gxف 1.0dtruejFbWLAhhd timestampmhexpmmphoenixm0.6.1hhba a ha aa ;bWLAhhdretiredmhexpmm phoenix_htmlm2.11.1dnil:bWLAhhdretiredmhexpmm phoenix_htmlm2.9.0dnil`bWLAhhdouter_checksummhexpmmdecimalm 2.0.0-rc.0m %^,>8W$d. 1.0dtruehmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejdbWLAhhddepsmhexpmmplugm0.14.0lhmhexpmmcowboymcowboym~> 1.0dtruejcbWLAhhddepsmhexpmmplugm1.1.3lhmhexpmmcowboymcowboym~> 1.0dtruej2bWLAhhdretiredmhexpmmplugm1.7.2dnilXbWLAhhdouter_checksummhexpmmplugm0.8.2m ɗg)&'(|VVԃ7 XbWLAhhdouter_checksummhexpmmplugm0.4.2m Anۅm 4 u4`bWLAhhdinner_checksummhexpmmphoenixm 1.6.0-rc.0m Xx,-"}1 t 4f?p2bWLAhhddepsmhexpmmdecimalm 2.0.0-rc.0jXbWLAhhdinner_checksummhexpmmplugm1.8.2m ڤ I9@wa\;Y t@[bWLAhhdinner_checksummhexpmmphoenixm1.5.0m YόsJ0W6eIaij ĮB`bWLAhhdouter_checksummhexpmm phoenix_htmlm3.0.2m ]RoRQy̿PCoO5bWLAhhdretiredmhexpmmphoenixm1.1.3dnilGbWLAhhd timestampmhexpmmphoenixm0.15.0hhba a ha aa XbWLAhhdouter_checksummhexpmmplugm1.5.0m ͜\c~:ɿPtovͲױΓԠ.R1bWLAhhddepsmhexpmm plug_cryptom1.0.0jYbWLAhhdinner_checksummhexpmmplugm1.10.4m AѢqSgd[գ}*?92bWLAhhdretiredmhexpmmmimem1.0.1dnilhbWLAhhddepsmhexpmm phoenix_htmlm2.9.1lhmhexpmmplugmplugm~> 1.0dfalsej2bWLAhhdretiredmhexpmmplugm0.8.0dnilZbWLAhhdinner_checksummhexpmmcowlibm2.7.3m  M_ γ}1FvL_bWLAhhdinner_checksummhexpmm plug_cryptom1.1.1m K`*BЄq'I5 FCbWLAhhd timestampmhexpmmplugm1.1.5hhbaahaa4aDbWLAhhd timestampmhexpmmjasonm1.1.2hhbaaha aa:EbWLAhhd timestampmhexpmmcowlibm2.7.1hhbaahaaa.ZbWLAhhdinner_checksummhexpmmcowboym2.2.0m R3xk;{^K| a杈vbWLAhhddepsmhexpmmcowboym2.5.0lhmhexpmmcowlibmcowlibm~> 2.6.0dfalsehmhexpmmranchmranchm~> 1.6.2dfalsejbWLAhhddepsmhexpmmplugm 1.5.0-rc.1lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.1dtruehmhexpmmmimemmimem~> 1.0dfalsej[bWLAhhdouter_checksummhexpmmphoenixm1.5.4m NQmއh.}^0\۳5[bWLAhhdinner_checksummhexpmmphoenixm1.0.0m QyՉy ecoN*[bWLAhhdouter_checksummhexpmmdecimalm1.0.0m T ! oC d0'${ՓBt,bWLAhhddepsmhexpmmcowlibm2.7.2j2bWLAhhdretiredmhexpmmplugm0.5.2dnilDbWLAhhd timestampmhexpmmplugm1.11.1hhbaahaa4a]bWLAhhdinner_checksummhexpmmplugm 1.5.0-rc.0m f!T`B sU-m(L[bWLAhhdouter_checksummhexpmmphoenixm1.1.3m Wv pwd6Bf&Ͱp`R J2bWLAhhdretiredmhexpmmplugm1.2.6dnilMbWLAhhd timestampmhexpmmphoenix_pubsubm0.1.0hhba a ha aa CbWLAhhd timestampmhexpmmplugm1.4.2hhbaahaa4a,bWLAhhddepsmhexpmmcowlibm2.5.0j0bWLAhhddepsmhexpmmranchm 2.0.0-rc.2jZbWLAhhdouter_checksummhexpmmpoisonm1.2.0m :b#%?)A@ʽĭXZbʦ[XbWLAhhdinner_checksummhexpmmplugm1.1.9m r!gкgfk8邗#3(7$s,bWLAhhddepsmhexpmmpoisonm1.0.0jEbWLAhhd timestampmhexpmmcowboym1.0.3hhbaahaaa.2bWLAhhdretiredmhexpmmplugm0.8.2dnil[bWLAhhdinner_checksummhexpmmphoenixm1.4.9m tm t3M=<ʱud5C4Ay.ft4bWLAhhdretiredmhexpmmpoisonm2.0.0dnilEbWLAhhd timestampmhexpmmcowlibm1.2.0hhbaahaaa.8bWLAhhdretiredmhexpmmranchm 2.0.0-rc.2dnildbWLAhhdouter_checksummhexpmmcowboy_telemetrym0.1.0m ٖqczPJ~|L4be3DbWLAhhd timestampmhexpmmplugm1.10.0hhbaahaa4aKbWLAhhd timestampmhexpmm phoenix_htmlm3.0.0hhba a ha aa EbWLAhhd timestampmhexpmmcowboym2.5.0hhbaahaaa.`bWLAhhdouter_checksummhexpmmphoenixm 1.6.0-rc.0m * 4M*/eJ8!v.dooIDbWLAhhd timestampmhexpmmranchm2.1.0hhba a ha aa 4bWLAhhdretiredmhexpmmcowlibm2.7.0dnilFbWLAhhd timestampmhexpmmphoenixm1.5.5hhba a ha aa `bWLAhhdouter_checksummhexpmm phoenix_htmlm1.0.1m /+P3)׾Jabʶ$§O5o[bWLAhhdouter_checksummhexpmmphoenixm0.8.0m dgz1R,GdSv*>\bWLAhhdinner_checksummhexpmmphoenixm1.5.12m u 88ji3?6CUfgbWLAhhddepsmhexpmm phoenix_htmlm3.0.2lhmhexpmmplugmplugm~> 1.5dtruejFbWLAhhd timestampmhexpmmphoenixm0.4.0hhba a ha aa cbWLAhhddepsmhexpmmplugm1.1.9lhmhexpmmcowboymcowboym~> 1.0dtruej[bWLAhhdouter_checksummhexpmmphoenixm1.5.7m wLDţx+ j {| x_bWLAhhdinner_checksummhexpmm plug_cowboym2.2.0m 1{Qp"YrA z $4bWLAhhdretiredmhexpmmcowboym2.6.3dnilbWLAhhddepsmhexpmmplugm1.2.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmmimemmimem~> 1.0dfalsejXbWLAhhdouter_checksummhexpmmplugm0.4.1m Hd,dÎY7qrwܩ:`bWLAhhdinner_checksummhexpmm phoenix_htmlm1.0.1m wcC$f-fZ$ mK]d!8Z1xEbWLAhhd timestampmhexpmmcowboym2.9.0hhbaahaaa.[bWLAhhdinner_checksummhexpmmphoenixm1.0.4m /(L#:ߋkK2}?LJnGbWLAhhd timestampmhexpmmphoenixm1.4.12hhba a ha aa [bWLAhhdinner_checksummhexpmmphoenixm1.3.0m LJzo PgA]$>ʼn]bWLAhhdinner_checksummhexpmm telemetrym0.4.2m (ɒE^s"M;kb_#:sPXps/E>bWLAhhdretiredmhexpmm phoenix_htmlm 2.7.0-devdnilJbWLAhhd timestampmhexpmm plug_cryptom1.0.0hhbaahaaa.XbWLAhhdinner_checksummhexpmmplugm0.8.4m D?+ _gxfbS)`_NcbWLAhhddepsmhexpmmplugm1.1.1lhmhexpmmcowboymcowboym~> 1.0dtruej-bWLAhhddepsmhexpmmdecimalm1.1.1j2bWLAhhdretiredmhexpmmplugm1.5.1dnil\bWLAhhdinner_checksummhexpmmphoenixm1.4.10m aJTUb)M"7-(#4ez$(UYbWLAhhdouter_checksummhexpmmranchm1.5.0m *{iR=K[e"ɘE2PfbWLAhhddepsmhexpmmjasonm1.0.0lhmhexpmmdecimalmdecimalm~> 1.0dtruejXbWLAhhdouter_checksummhexpmmplugm1.0.0m YC1S [(R-7Vc -bWLAhhddepsmhexpmmphoenixm0.1.0j\bWLAhhdinner_checksummhexpmmphoenixm1.4.11m b÷lz>XPڧǦ K XbWLAhhdouter_checksummhexpmmplugm1.1.3m 0c倉w<\zQY/OZ~N3o3bWLAhhdretiredmhexpmmjasonm1.1.2dnilLbWLAhhd timestampmhexpmm phoenix_htmlm2.10.1hhba a ha aa fbWLAhhddepsmhexpmmjasonm1.0.1lhmhexpmmdecimalmdecimalm~> 1.0dtruejCbWLAhhd timestampmhexpmmplugm1.2.4hhbaahaa4a4bWLAhhdretiredmhexpmmcowlibm1.2.0dnilZbWLAhhdouter_checksummhexpmmcowboym2.6.2m CC^`1lhr`A{FbWLAhhd timestampmhexpmmphoenixm1.2.2hhba a ha aa XbWLAhhdouter_checksummhexpmmplugm1.3.2m jhZ84 cM^~2bWLAhhdretiredmhexpmmmimem2.0.0dnilZbWLAhhdinner_checksummhexpmmpoisonm1.5.1m ¯_7t`FkJdB ;c4sjI:bWLAhhdretiredmhexpmm phoenix_htmlm1.4.0dnil5bWLAhhdretiredmhexpmmphoenixm0.2.5dnilbWLAhhddepsmhexpmmphoenixm 1.5.0-rc.0lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.1dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejbWLAhhddepsmhexpmm plug_cowboym2.3.0lhmhexpmmcowboymcowboym~> 2.7dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.6.0m =cEGw=v2ztUi,bWLAhhddepsmhexpmmpoisonm1.5.2j[bWLAhhdouter_checksummhexpmmphoenixm0.9.0m ' =/ˍvVފ(tOP#ji3bWLAhhdretiredmhexpmmplugm1.12.1dnil;bWLAhhd timestampmhexpmmcowboyhhbaahaaa.YbWLAhhdouter_checksummhexpmmplugm1.10.2m xGgﳹ%p^f;ō*ؚZ;bWLAhhdretiredmhexpmm phoenix_htmlm2.13.0dnil]bWLAhhdinner_checksummhexpmm telemetrym0.2.0m [@ʣ޳-|OUomk\7L#fw!a1w*bWLAhhddepsmhexpmmmimem1.0.0j`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.5.1m cSE\&m'BipgB_EbWLAhhd timestampmhexpmmpoisonm1.0.3hhba a ha a.aQbWLAhhd registry_etagmhexpmmpoisonm""644f196a2afe2fb023680a25490fecdd"ZbWLAhhdouter_checksummhexpmmcowboym1.1.0m t]"n){z>Wx=$ތ"w^ZbWLAhhdinner_checksummhexpmmcowboym2.6.0m 5Lj>>39mܻB#dm"bWLAhhddepsmhexpmmphoenixm1.4.4lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruej2bWLAhhdretiredmhexpmmplugm1.3.0dnil3bWLAhhdretiredmhexpmmranchm1.0.0dnilbWLAhhddepsmhexpmmplugm1.6.2lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4dtruehmhexpmmmimemmimem~> 1.0dfalsejZbWLAhhdinner_checksummhexpmmpoisonm1.0.1m {Uۯn)S"F[bWLAhhdouter_checksummhexpmmdecimalm1.4.1m #{RNUV]JrV&hL{ʀ_; 1.0dtruehmhexpmmpoisonmpoisonm~> 1.2dfalsej*bWLAhhddepsmhexpmmmimem1.3.1jXbWLAhhdouter_checksummhexpmmplugm1.1.6m #d):4p]mϤ\bWLAhhddepsmhexpmmcowboy_telemetrym0.3.0lhmhexpmmcowboymcowboym~> 2.7dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejFbWLAhhd timestampmhexpmmphoenixm1.2.0hhba a ha aa DbWLAhhd timestampmhexpmmjasonm1.2.1hhbaaha aa:bbWLAhhdinner_checksummhexpmmphoenix_pubsubm0.0.1m |ļ[kY`dF>NSKtYbWLAhhdouter_checksummhexpmmplugm1.12.0m RnCKhʙ8F;9bWLAhhdretiredmhexpmm plug_cowboym1.0.0dnilabWLAhhdinner_checksummhexpmm phoenix_htmlm2.13.2m |Έ`}+R'1OkA4WjbWLAhhddepsmhexpmmphoenixm1.4.13lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsejXbWLAhhdinner_checksummhexpmmplugm1.1.7m @MMerFL/YH"[bWLAhhddepsmhexpmmcowboym2.6.0lhmhexpmmcowlibmcowlibm~> 2.7.0dfalsehmhexpmmranchmranchm~> 1.7.0dfalsejbWLAhhddepsmhexpmmplugm1.3.5lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsejibWLAhhddepsmhexpmm phoenix_htmlm2.10.4lhmhexpmmplugmplugm~> 1.0dfalsejabWLAhhdinner_checksummhexpmm phoenix_htmlm2.13.0m ;^lYz ?GALV,A 5bWLAhhdretiredmhexpmmphoenixm1.0.1dnilXbWLAhhdinner_checksummhexpmmplugm1.1.4m .B n[1vMoaB+Ű.Ι[bWLAhhdouter_checksummhexpmmphoenixm1.2.1m ;f`5 nz 0.13 or ~> 1.0dfalsej5bWLAhhdretiredmhexpmmphoenixm1.1.0dnil7bWLAhhdretiredmhexpmmplugm 1.4.0-rc.0dnilFbWLAhhd timestampmhexpmmphoenixm1.2.5hhba a ha aa 5bWLAhhdretiredmhexpmmphoenixm0.6.1dnil[bWLAhhdinner_checksummhexpmmdecimalm1.8.1m ?_4( 57@)Nޅ3SoEh٬<ZbWLAhhdouter_checksummhexpmmcowboym1.0.1m  injY?Ka8'ũ9bWLAhhdretiredmhexpmm plug_cryptom1.0.0dnil,bWLAhhddepsmhexpmmpoisonm3.1.0jabWLAhhdouter_checksummhexpmm phoenix_htmlm2.13.3m ӗ1TWm%u_U7%gLbWLAhhd timestampmhexpmm phoenix_htmlm2.10.3hhba a ha aa fbWLAhhddepsmhexpmmjasonm1.2.1lhmhexpmmdecimalmdecimalm~> 1.0dtruejLbWLAhhd timestampmhexpmm phoenix_htmlm2.11.2hhba a ha aa 3bWLAhhdretiredmhexpmmranchm2.0.0dnilLbWLAhhd timestampmhexpmm phoenix_htmlm2.12.0hhba a ha aa KbWLAhhd timestampmhexpmm phoenix_htmlm2.6.0hhba a ha aa FbWLAhhd timestampmhexpmmdecimalm1.1.2hhbaaha aa:CbWLAhhd timestampmhexpmmplugm1.8.2hhbaahaa4aXbWLAhhdinner_checksummhexpmmplugm1.0.6m dpBcOfXeyu6LCelIEbWLAhhd timestampmhexpmmcowboym1.1.1hhbaahaaa.`bWLAhhdouter_checksummhexpmm phoenix_htmlm3.0.3m *eaĻ~) T~-KbWLAhhd timestampmhexpmm phoenix_htmlm1.0.1hhba a ha aa [bWLAhhdouter_checksummhexpmmphoenixm1.0.6m 7.zUH}c[u뷃ܞ^6mü cbWLAhhddepsmhexpmmplugm1.0.4lhmhexpmmcowboymcowboym~> 1.0dtruejZbWLAhhdinner_checksummhexpmmcowlibm2.7.1m |[bfĘy_d{I5`FbWLAhhd timestampmhexpmmphoenixm0.5.0hhba a ha aa [bWLAhhdouter_checksummhexpmmphoenixm0.2.7m :=ќxלFaG5=Z;@vB9ϕ;bWLAhhdretiredmhexpmm phoenix_htmlm2.10.2dnilYbWLAhhdouter_checksummhexpmmplugm1.10.3m z*3jDKʻ8l s9ZbWLAhhdinner_checksummhexpmmpoisonm2.0.0m e'0kIt U&Ca6bWLAhhdretiredmhexpmmphoenixm1.4.13dnil[bWLAhhdinner_checksummhexpmmphoenixm1.0.5m E)PQ!jflW1)d5bWLAhhdretiredmhexpmmdecimalm0.1.1dnilYbWLAhhdinner_checksummhexpmmplugm0.13.0m /:.Gűw υIl4KbWLAhhddepsmhexpmmphoenixm0.13.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm>= 0.12.2 and < 2.0.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsehmhexpmmpoolboympoolboym~> 1.5.1 or ~> 1.6dtruejKbWLAhhd timestampmhexpmmphoenixm 1.4.0-rc.2hhba a ha aa :bWLAhhd timestampmhexpmmjasonhhbaaha aa:,bWLAhhddepsmhexpmmpoisonm2.1.0j[bWLAhhdouter_checksummhexpmmcowlibm2.10.0m /- vt)u!H{*MN[bWLAhhdinner_checksummhexpmmphoenixm0.7.2m HN!3rWDvQ b{wf;[bWLAhhdouter_checksummhexpmmphoenixm1.0.5m иdm 0˨YOvo//7׬)e1 ^bWLAhhdouter_checksummhexpmmranchm 2.0.0-rc.2m gY`sC0o=Ho[;ZbWLAhhdinner_checksummhexpmmcowlibm2.5.0m bXKK%֋ IFrLbWLAhhd timestampmhexpmm phoenix_htmlm2.14.3hhba a ha aa [bWLAhhdinner_checksummhexpmmphoenixm1.1.8m %J&RG ʩB).A-6T\CbWLAhhd timestampmhexpmmmimem1.3.0hhbaahaaa.XbWLAhhdinner_checksummhexpmmplugm1.1.0m iHI Pjy=W%ZbWLAhhdouter_checksummhexpmmcowboym1.0.2m 89·,Gq[OLX?4bWLAhhddepsmhexpmmphoenix_pubsubm0.0.1jYbWLAhhdouter_checksummhexpmmjasonm1.1.0m #aJc5o( ǫh_QЄR68l5bWLAhhdretiredmhexpmmdecimalm0.2.2dnilFbWLAhhd timestampmhexpmmphoenixm0.2.8hhba a ha aa 2bWLAhhdretiredmhexpmmplugm0.6.0dnilXbWLAhhdouter_checksummhexpmmplugm1.4.4m _~AVƐ:݈x53,/Cm:T2bWLAhhdretiredmhexpmmplugm1.5.0dnilbWLAhhddepsmhexpmmphoenixm1.5.2lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejYbWLAhhdinner_checksummhexpmmranchm1.8.0m z 2{dzYLDsYz5bWLAhhdretiredmhexpmmphoenixm1.3.3dnil]bWLAhhdinner_checksummhexpmm telemetrym0.4.1m 'HHD$GjA|2w(ttR*bWLAhhddepsmhexpmmmimem1.3.0jabWLAhhdouter_checksummhexpmm phoenix_htmlm2.13.2m 4gddے*yz#$YbWLAhhdinner_checksummhexpmmplugm1.11.1m +fgSa4 oCѲ-SY9bWLAhhdretiredmhexpmm plug_cowboym2.4.1dnilXbWLAhhdouter_checksummhexpmmplugm0.4.3m &[ }yv6[A.q`bWLAhhdinner_checksummhexpmm phoenix_htmlm1.4.0m 9܍u7| ?׮:jzcbWLAhhddepsmhexpmmplugm0.8.4lhmhexpmmcowboymcowboym~> 1.0dtruejZbWLAhhdouter_checksummhexpmmcowlibm2.1.0m }!:,KK#M)YV-fm2ZbWLAhhdouter_checksummhexpmmpoisonm1.5.0m wIO-l,2575.幜yaCbWLAhhd timestampmhexpmmmimem1.4.0hhbaahaaa.ZbWLAhhdinner_checksummhexpmmcowboym2.4.0m /ȥdZ_TtG3Ro]K bWLAhhddepsmhexpmmphoenixm1.2.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.1dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsej`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.1.0m `+&}C!kI˯E MbWLAhhd timestampmhexpmmphoenix_pubsubm1.1.1hhba a ha aa 5bWLAhhdretiredmhexpmmphoenixm1.5.8dnil[bWLAhhdinner_checksummhexpmmphoenixm1.1.1m XH.!ޜnT-cI!_aG,0 C$2bWLAhhdretiredmhexpmmplugm1.0.6dnilabWLAhhdouter_checksummhexpmm phoenix_htmlm2.12.0m y /DGP)%kmn^wbWLAhhddepsmhexpmm phoenix_htmlm1.0.0lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0dfalsejbWLAhhddepsmhexpmmcowboym1.1.0lhmhexpmmcowlibmcowlibm~> 1.0.2dfalsehmhexpmmranchmranchm~> 1.2.0dfalsejFbWLAhhd timestampmhexpmmdecimalm1.0.0hhbaaha aa:5bWLAhhdretiredmhexpmmphoenixm1.0.0dnilibWLAhhddepsmhexpmm phoenix_htmlm2.11.1lhmhexpmmplugmplugm~> 1.5dfalsejJbWLAhhd timestampmhexpmm plug_cowboym2.4.0hhba a ha aa FbWLAhhd timestampmhexpmmphoenixm1.4.3hhba a ha aa YbWLAhhdinner_checksummhexpmmranchm1.7.1m kQ s:IvFZG љȁ:bWLAhhdretiredmhexpmmphoenixm 1.4.0-rc.0dnil[bWLAhhdouter_checksummhexpmmdecimalm1.1.1m ?6"/+ 2.5dfalsehmhexpmmplugmplugm~> 1.7dfalsejFbWLAhhd timestampmhexpmmphoenixm1.1.9hhba a ha aa HbWLAhhd timestampmhexpmm telemetrym0.4.0hhbaahaa4aXbWLAhhdouter_checksummhexpmmplugm1.2.1m = ;BoƳa+]>F[bWLAhhdouter_checksummhexpmmphoenixm1.4.9m 3&oUu xOՔ_tz읰.5bWLAhhdretiredmhexpmmphoenixm0.9.0dnil-bWLAhhddepsmhexpmmdecimalm1.1.2j\bWLAhhdouter_checksummhexpmmphoenixm0.17.1m L+bP"n tߔҋۑ4bWLAhhdretiredmhexpmmpoisonm1.0.2dnilwbWLAhhddepsmhexpmm phoenix_htmlm 2.4.0-devlhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsej9bWLAhhdretiredmhexpmm plug_cowboym2.1.0dnilfbWLAhhddepsmhexpmmjasonm1.2.0lhmhexpmmdecimalmdecimalm~> 1.0dtruejbbWLAhhdouter_checksummhexpmmphoenix_pubsubm1.1.1m ؐmQayܪ2DV'ŇVxul5bWLAhhdretiredmhexpmmphoenixm0.2.9dnilkbWLAhhddepsmhexpmmjasonm 1.0.0-rc.2lhmhexpmmdecimalmdecimalm~> 1.0dtruej[bWLAhhdinner_checksummhexpmmphoenixm0.2.3m ^v& G prY 1.5dfalsej`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.6.0m uXk_<Pׁ\}L͖{'5bWLAhhdretiredmhexpmmdecimalm1.1.2dnil3bWLAhhdretiredmhexpmmplugm0.10.0dnil2bWLAhhdretiredmhexpmmplugm1.1.2dnilbWLAhhdversionsmhexpmmcowboylm1.0.0m1.0.1m1.0.2m1.0.3m1.0.4m1.1.0m1.1.1m1.1.2m2.0.0m2.1.0m2.2.0m2.2.1m2.2.2m2.3.0m2.4.0m2.5.0m2.6.0m2.6.1m2.6.2m2.6.3m2.7.0m2.8.0m2.9.0jsbWLAhhddepsmhexpmm phoenix_htmlm2.4.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsejabWLAhhdinner_checksummhexpmm phoenix_htmlm2.10.1m ,ƭwLx@1D>Qy!¦AbWLAhhddepsmhexpmmphoenixm1.2.5lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm,~> 1.3.3 or ~> 1.2.4 or ~> 1.1.8 or ~> 1.0.5dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsej5bWLAhhdretiredmhexpmmphoenixm0.6.0dnilXbWLAhhdouter_checksummhexpmmplugm1.3.0m RD5~mVQ5)_4E3-GbWLAhhd timestampmhexpmmphoenixm1.5.12hhba a ha aa XbWLAhhdouter_checksummhexpmmplugm1.4.0m p>PPSm mɆE5bWLAhhdretiredmhexpmmphoenixm0.2.4dnilCbWLAhhd timestampmhexpmmplugm1.2.3hhbaahaa4a)bWLAhhddepsmhexpmmphoenixm 1.4.0-rc.0lhmhexpmmcowboymcowboym~> 1.0 or ~> 2.3dtruehmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.6.2 or ~> 1.7dfalsej'bWLAhhddepsmhexpmmphoenixm 1.4.0-rc.2lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruejDbWLAhhd timestampmhexpmmplugm0.13.0hhbaahaa4a`bWLAhhdinner_checksummhexpmm phoenix_htmlm1.0.0m >ִZPA$<y5PτWDzi gbWLAhhddepsmhexpmm phoenix_htmlm3.0.1lhmhexpmmplugmplugm~> 1.5dtruejZbWLAhhdinner_checksummhexpmmcowlibm1.0.0m 9} fVԆ2s$ 47i֔AdbWLAhhddepsmhexpmmplugm0.12.2lhmhexpmmcowboymcowboym~> 1.0dtruejEbWLAhhd timestampmhexpmmcowlibm2.7.2hhbaahaaa.ObWLAhhd timestampmhexpmm phoenix_htmlm 2.4.0-devhhba a ha aa [bWLAhhdouter_checksummhexpmmcowlibm2.11.0m +>EeumI L\ ҫm҇XbWLAhhdouter_checksummhexpmmmimem1.2.0m MpB `UQ`\([+DbWLAhhd timestampmhexpmmplugm0.12.1hhbaahaa4a`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.2.0m tO$w^WGwLUcՌAEbWLAhhd timestampmhexpmmpoisonm1.5.1hhba a ha a.a`bWLAhhdouter_checksummhexpmm phoenix_htmlm1.2.1m N ĪI,"sin ]}6bWLAhhdretiredmhexpmmphoenixm1.4.11dnilXbWLAhhdouter_checksummhexpmmplugm1.0.1m Lq ۖ}|e7;d[bWLAhhdinner_checksummhexpmmphoenixm1.4.5m n ? e?E 7]t,bWLAhhddepsmhexpmmcowlibm2.7.1j:bWLAhhdretiredmhexpmm phoenix_htmlm2.8.0dnil4bWLAhhdretiredmhexpmmpoisonm3.1.0dnilLbWLAhhd timestampmhexpmm phoenix_htmlm2.13.3hhba a ha aa 2bWLAhhdretiredmhexpmmplugm1.0.4dnilFbWLAhhd timestampmhexpmmdecimalm1.5.0hhbaaha aa:5bWLAhhdretiredmhexpmmphoenixm1.3.0dnilZbWLAhhdouter_checksummhexpmmpoisonm4.0.0m dxv$$q$U. ƍg'Z_bWLAhhdinner_checksummhexpmm plug_cryptom1.2.2m eEq2$G}`C@jM"tҀyMgbWLAhhddepsmhexpmm phoenix_htmlm3.0.3lhmhexpmmplugmplugm~> 1.5dtruej[bWLAhhdouter_checksummhexpmmphoenixm1.0.1m s$DeOă&x Uk4A4bWLAhhdretiredmhexpmmpoisonm4.0.1dnil[bWLAhhdinner_checksummhexpmmphoenixm1.4.8m -íIc$dQNzSXbWLAhhdouter_checksummhexpmmplugm0.8.3m L(G/1hMKKzXbWLAhhdinner_checksummhexpmmmimem2.0.1m n$2MMׇ7VDbWLAhhd timestampmhexpmmranchm1.0.0hhba a ha aa FbWLAhhd timestampmhexpmmphoenixm1.3.1hhba a ha aa DbWLAhhd timestampmhexpmmranchm1.7.0hhba a ha aa LbWLAhhd timestampmhexpmm phoenix_htmlm2.11.1hhba a ha aa \bWLAhhdouter_checksummhexpmmphoenixm1.4.17m :]z=vR_n{7cԮ\bWLAhhdinner_checksummhexpmmphoenixm0.17.1m xG.rgk.W%Y49+kY3bWLAhhdretiredmhexpmmplugm0.12.0dnilXbWLAhhdouter_checksummhexpmmplugm1.5.1m 1-]yuCSN!nj6[bWLAhhdinner_checksummhexpmmdecimalm0.2.2m b^BJ;@>ҜLz:K \bWLAhhdouter_checksummhexpmmphoenixm0.2.10m eXNil ?-_0qA $Q"KbWLAhhd timestampmhexpmm phoenix_htmlm2.6.2hhba a ha aa JbWLAhhd timestampmhexpmm plug_cowboym2.5.1hhba a ha aa JbWLAhhd timestampmhexpmm plug_cowboym2.1.2hhba a ha aa _bWLAhhdinner_checksummhexpmm plug_cowboym2.5.1m |oE>tDdAO(-hGേa,bWLAhhddepsmhexpmmphoenixm 1.3.0-rc.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.3.2 or ~> 1.4dfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0dfalsejXbWLAhhdouter_checksummhexpmmplugm1.1.8m -nOWz?~iW6F1؀,JabWLAhhdouter_checksummhexpmm phoenix_htmlm2.13.4m 򕜸'YuRRL clMZbWLAhhdouter_checksummhexpmmcowboym2.6.0m |z'b#EF2N\~ x[(2944bWLAhhdretiredmhexpmmcowboym2.2.2dnil5bWLAhhdretiredmhexpmmcowlibm2.11.0dnilYbWLAhhdouter_checksummhexpmmplugm0.13.1m P|u>p;y%N}퐉EwI FbWLAhhd timestampmhexpmmdecimalm0.1.2hhbaaha aa:DbWLAhhd timestampmhexpmmranchm1.1.0hhba a ha aa 2.2.1dfalsehmhexpmmranchmranchm~> 1.4.0dfalsej\bWLAhhdinner_checksummhexpmmphoenixm0.16.1m 6F''-apJBo1tdq:DbWLAhhd timestampmhexpmmplugm0.10.0hhbaahaa4a`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.0.0m ϲ aq|Ǿoqd$6:bWLAhhdretiredmhexpmm phoenix_htmlm1.3.0dnilEbWLAhhd timestampmhexpmmpoisonm1.2.0hhba a ha a.a`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.9.3m Z!"C$/TOx0OKLC% XX:bWLAhhdretiredmhexpmmphoenixm 1.2.0-rc.1dnil5bWLAhhdretiredmhexpmmphoenixm0.2.0dnil;bWLAhhdretiredmhexpmm phoenix_htmlm2.13.3dnilbWLAhhddepsmhexpmmphoenixm1.0.4lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsej;bWLAhhdretiredmhexpmm phoenix_htmlm2.10.0dnilbbWLAhhdouter_checksummhexpmmphoenix_pubsubm1.0.0m ,{?\MfVI$iqrXbWLAhhdinner_checksummhexpmmplugm1.8.1m py9pߠ~L֫X qL:3bWLAhhdretiredmhexpmmranchm1.7.0dnilXbWLAhhdouter_checksummhexpmmplugm0.5.0m CQ rU!w*q LkSצoZbWLAhhdinner_checksummhexpmmcowlibm2.7.2m 2ϑ15ͥ#BbWLAhhddepsmhexpmmplugm1.7.1lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0dfalsej5bWLAhhdretiredmhexpmmphoenixm0.7.2dnil[bWLAhhdinner_checksummhexpmmphoenixm0.4.0m *S]PoH39B;(X5uhj2bWLAhhdretiredmhexpmmmimem1.4.0dnilYbWLAhhdouter_checksummhexpmmplugm0.12.2m ncoŸ> iv,i7n|-1bWLAhhddepsmhexpmm plug_cryptom1.2.0jbWLAhhddepsmhexpmmplugm1.7.2lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0dfalsejZbWLAhhdouter_checksummhexpmmpoisonm1.1.0m 8ws@] ૢfS 3bWLAhhdretiredmhexpmmplugm0.13.1dnilZbWLAhhdinner_checksummhexpmmcowboym1.1.1m  ԃ]]=m mMJHV#n_bWLAhhdinner_checksummhexpmm plug_cowboym2.5.0m QɘG:RvLpeZbWLAhhdouter_checksummhexpmmcowlibm1.1.0m LQucIWϕ;{xׂZbWLAhhdinner_checksummhexpmmpoisonm1.5.0m `b:oF45%%8gͽWP2HKbWLAhhd timestampmhexpmm phoenix_htmlm2.3.0hhba a ha aa [bWLAhhdouter_checksummhexpmmphoenixm1.3.2m Cn [N24z9Or|Ex۶bWLAhhddepsmhexpmmplugm1.3.4lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsej`bWLAhhdouter_checksummhexpmm phoenix_htmlm1.2.0m QR«`ǭ}10R~2*NЇlv5bWLAhhdretiredmhexpmmphoenixm1.2.2dnil[bWLAhhdinner_checksummhexpmmdecimalm0.2.0m [.ٙ)b9*O^GLzb}F5bWLAhhdretiredmhexpmmphoenixm0.4.0dnilDbWLAhhd timestampmhexpmmplugm0.13.1hhbaahaa4a`bWLAhhdinner_checksummhexpmmphoenixm 1.2.0-rc.0m ?uF&ːIbJZ} |3ҋbWLAhhddepsmhexpmmplugm1.3.3lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsej5bWLAhhdretiredmhexpmmphoenixm1.4.3dnilXbWLAhhdouter_checksummhexpmmplugm1.0.3m 1&|=lAM5!n_bWLAhhdouter_checksummhexpmm plug_cowboym2.2.1m ;C$F (nz b䎷#YTh ]޶:bWLAhhdretiredmhexpmm phoenix_htmlm2.9.3dnildbWLAhhdinner_checksummhexpmmcowboy_telemetrym0.4.0m 9XwM *:PWjZ !VTFbWLAhhd timestampmhexpmmphoenixm1.4.1hhba a ha aa 2bWLAhhdretiredmhexpmmplugm1.2.5dnil,bWLAhhddepsmhexpmmpoisonm1.0.2jHbWLAhhd timestampmhexpmm telemetrym0.3.0hhbaahaa4a*bWLAhhddepsmhexpmmmimem0.0.1jbWLAhhddepsmhexpmmplugm1.3.1lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsejXbWLAhhdinner_checksummhexpmmplugm0.8.2m 9[-!2bA&~YezAEbWLAhhd timestampmhexpmmpoisonm3.1.0hhba a ha a.aZbWLAhhdinner_checksummhexpmmcowboym2.0.0m loVQwp(ml$5aDbWLAhhd timestampmhexpmmranchm1.3.2hhba a ha aa DbWLAhhd timestampmhexpmmjasonm1.1.0hhbaaha aa:YbWLAhhdinner_checksummhexpmmplugm0.10.0m D?K/Fr"E*ScˋXbWLAhhdouter_checksummhexpmmmimem1.6.0m 1a?!=`}(+Z[bWLAhhdouter_checksummhexpmmphoenixm1.4.1m [t=] xU&MYbWLAhhdouter_checksummhexpmmranchm1.3.2m nVI:$3:0%Fr R52bWLAhhdretiredmhexpmmmimem0.0.1dnil5bWLAhhdretiredmhexpmmphoenixm0.2.7dnil`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.5.0m rJPwO`.; Sk ۩ ]-A?4bWLAhhdretiredmhexpmmpoisonm1.5.2dnilXbWLAhhdinner_checksummhexpmmplugm0.4.2m ¤X {p&ʜKbWLAhhd timestampmhexpmm phoenix_htmlm2.0.1hhba a ha aa ZbWLAhhdinner_checksummhexpmmpoisonm1.1.0m ge xgkn]9+9t QcbWLAhhddepsmhexpmmplugm1.1.2lhmhexpmmcowboymcowboym~> 1.0dtruej,bWLAhhddepsmhexpmmcowlibm2.9.0jibWLAhhddepsmhexpmm phoenix_htmlm2.10.5lhmhexpmmplugmplugm~> 1.0dfalsej4bWLAhhdretiredmhexpmmpoisonm1.1.1dnilXbWLAhhdouter_checksummhexpmmplugm1.1.5m phq=fLKΪP4@P1cM=XbWLAhhdinner_checksummhexpmmplugm0.7.0m ;\W/ppe5ȖL{dh:bWLAhhdretiredmhexpmmphoenixm 1.4.0-rc.1dnilYbWLAhhdinner_checksummhexpmmplugm1.10.2m y4\}FTTŇd8\wZcbWLAhhddepsmhexpmmplugm1.0.5lhmhexpmmcowboymcowboym~> 1.0dtruej5bWLAhhdretiredmhexpmmdecimalm0.2.1dnilYbWLAhhdouter_checksummhexpmmplugm1.10.0m B*'g; {w[-퐏2dA^JZbWLAhhdinner_checksummhexpmmcowboym1.0.3m _lStsn~I._&TL+EbWLAhhd timestampmhexpmmcowboym1.0.2hhbaahaaa.:bWLAhhdretiredmhexpmm phoenix_htmlm1.2.0dnil[bWLAhhdouter_checksummhexpmmdecimalm1.3.1m WVHGrn\οǫ*w-bWLAhhddepsmhexpmmdecimalm0.2.0j[bWLAhhdinner_checksummhexpmmphoenixm0.2.4m Nr[+:Igʨ(4Y\]bWLAhhdinner_checksummhexpmmplugm 1.5.0-rc.2m ; _A2heu=fog%_bWLAhhdinner_checksummhexpmm plug_cowboym2.1.0m Wh<:9Բ[NŎJnj' 0 XbWLAhhdouter_checksummhexpmmplugm0.6.0m ' ]R\bWLAhhddepsmhexpmmphoenixm1.5.3lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej,bWLAhhddepsmhexpmmcowlibm1.3.0jibWLAhhddepsmhexpmm phoenix_htmlm2.13.0lhmhexpmmplugmplugm~> 1.5dfalsej+bWLAhhddepsmhexpmmranchm1.7.0jZbWLAhhdouter_checksummhexpmmcowlibm2.7.0m YRoS"oۋJg'||ުmC؎ZbWLAhhdouter_checksummhexpmmcowboym2.0.0m l| CSߟ?,=XѰM+e>VUx5bWLAhhdretiredmhexpmmphoenixm1.1.4dnil`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.9.1m pqm e=EFxenX".Hi6DX?bbWLAhhdouter_checksummhexpmmphoenix_pubsubm0.1.0m ktp\5Ikz@}9N\ҡVb9_bWLAhhdinner_checksummhexpmm plug_cryptom1.1.2m чW,mq6)%򵀡VB0'-bWLAhhddepsmhexpmmdecimalm1.4.0jEbWLAhhd timestampmhexpmmpoisonm2.0.0hhba a ha a.aYbWLAhhd registry_etagmhexpmmphoenix_pubsubm""9c40253b1ea746f7a666b077918a909f"bWLAhhddepsmhexpmmphoenixm0.2.8lhmhexpmmex_confmex_confm0.1.2dfalsehmhexpmminflexminflexm0.2.0dfalsehmhexpmmjazzmjazzm0.1.1dfalsehmhexpmmplugmplugm0.4.4dfalsej[bWLAhhdouter_checksummhexpmmphoenixm0.2.6m xJlӐ,ϔMwYe`?>bWLAhhd timestampmhexpmm telemetryhhbaahaa4aCbWLAhhd timestampmhexpmmplugm1.1.7hhbaahaa4aibWLAhhddepsmhexpmm phoenix_htmlm2.10.2lhmhexpmmplugmplugm~> 1.0dfalsej[bWLAhhdouter_checksummhexpmmdecimalm1.2.0m t"HD2HDM_dш`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.8.0m V -*J= z%߻!FbWLAhhd timestampmhexpmmphoenixm1.4.9hhba a ha aa bWLAhhddepsmhexpmmplugm1.12.1lhmhexpmmmimemmimem~> 1.0 or ~> 2.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4.3 or ~> 1.0dfalsejdbWLAhhddepsmhexpmmplugm0.11.2lhmhexpmmcowboymcowboym~> 1.0dtruej4bWLAhhdretiredmhexpmmcowlibm2.0.1dnil+bWLAhhddepsmhexpmmranchm1.5.0jEbWLAhhd timestampmhexpmmcowlibm1.3.0hhbaahaaa.dbWLAhhdinner_checksummhexpmmcowboy_telemetrym0.2.0m ,CP!ԕu'VRa-<) 5bWLAhhdretiredmhexpmmphoenixm1.5.7dnilDbWLAhhd timestampmhexpmmranchm1.3.1hhba a ha aa JbWLAhhd timestampmhexpmm plug_cowboym1.0.0hhba a ha aa 5bWLAhhdretiredmhexpmmphoenixm1.2.1dnilYbWLAhhdinner_checksummhexpmmplugm0.11.1m +.W)yyڴ8w1q.o4&XbWLAhhdinner_checksummhexpmmplugm1.0.3m LQp\!Svx$[bWLAhhdinner_checksummhexpmmphoenixm0.6.1m 0Y 9cQ#hư(* qZbWLAhhdinner_checksummhexpmmpoisonm1.4.0m ZєW/d#WFNwy%EbWLAhhd timestampmhexpmmcowlibm2.5.1hhbaahaaa.XbWLAhhdinner_checksummhexpmmplugm0.5.3m ͢Q:ID+Y1g%8RMZbWLAhhdouter_checksummhexpmmcowlibm1.0.1m OkG L& #؏As\#SZbWLAhhdouter_checksummhexpmmcowboym2.1.0m š6h2ߊ 6r&[bWLAhhdouter_checksummhexpmmphoenixm1.4.2m Q8|YΥS*rUŨa,AB|F|kMKXFbWLAhhd timestampmhexpmmphoenixm1.1.2hhba a ha aa bWLAhhddepsmhexpmmcowboym1.0.1lhmhexpmmcowlibmcowlibm~> 1.0.0dfalsehmhexpmmranchmranchm~> 1.0dfalsejZbWLAhhdouter_checksummhexpmmpoisonm3.0.0m = /MBi'zC$氏|'!XbWLAhhdouter_checksummhexpmmplugm1.7.0m ~ƫjxXoDm* CbWLAhhd timestampmhexpmmplugm0.7.0hhbaahaa4abWLAhhddepsmhexpmmplugm1.7.0lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0dfalsej6bWLAhhdretiredmhexpmmphoenixm1.4.17dnilXbWLAhhdinner_checksummhexpmmmimem1.6.0m ڽv`ȒPV߰ MbWLAhhd timestampmhexpmmphoenix_pubsubm1.0.0hhba a ha aa 3bWLAhhdretiredmhexpmmplugm0.11.0dnilbWLAhhdversionsmhexpmm plug_cowboylm1.0.0m2.0.0m2.0.1m2.0.2m2.1.0m2.1.1m2.1.2m2.1.3m2.2.0m2.2.1m2.2.2m2.3.0m2.4.0m2.4.1m2.5.0m2.5.1m2.5.2jGbWLAhhd timestampmhexpmmphoenixm1.5.10hhba a ha aa 9bWLAhhdretiredmhexpmm plug_cowboym2.1.2dnilYbWLAhhdinner_checksummhexpmmranchm1.1.0m mی*'̨\T5XţU:{eT[EbWLAhhd timestampmhexpmmcowboym2.2.1hhbaahaaa.*bWLAhhddepsmhexpmmmimem2.0.1jgbWLAhhdinner_checksummhexpmmphoenix_pubsubm 1.0.0-rc.0m \S+@L?1+ xH\bWLAhhdinner_checksummhexpmmphoenixm0.17.0m fFznM[|q]TM8^_r,wwbWLAhhddepsmhexpmm phoenix_htmlm1.2.1lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0dfalsej4bWLAhhdretiredmhexpmmcowboym1.0.4dnil_bWLAhhdouter_checksummhexpmm plug_cowboym2.1.2m }r%ΆZ#~mopAt %k>c"pXbWLAhhdouter_checksummhexpmmplugm1.6.0m vΛ6ѝ\U^/À.HFbWLAhhd timestampmhexpmmphoenixm1.1.3hhba a ha aa 5bWLAhhdretiredmhexpmmdecimalm1.9.0dnilebWLAhhddepsmhexpmmplugm0.8.0lhmhexpmmcowboymcowboym~> 1.0.0dtruejZbWLAhhdinner_checksummhexpmmcowlibm2.1.0m 6X=C@@ >Oٗ aLXbWLAhhdouter_checksummhexpmmplugm1.6.1m s?2IC/bWLAhhdretiredmhexpmmcowlibm2.7.2tdmessagem+Published with invalid version in .app filedreasondRETIRED_INVALID4bWLAhhdretiredmhexpmmcowlibm2.0.0dnilabWLAhhdinner_checksummhexpmm phoenix_htmlm2.13.3m )/%_LToS`·к38g\bWLAhhdinner_checksummhexpmmphoenixm1.4.17m |ު}`݌"ChI@;8gbWLAhhddepsmhexpmm phoenix_htmlm3.0.0lhmhexpmmplugmplugm~> 1.5dtruej5bWLAhhdretiredmhexpmmdecimalm1.3.0dnilZbWLAhhdinner_checksummhexpmmcowboym1.1.0m cr~p:,oz"[DA44bWLAhhdretiredmhexpmmcowboym1.0.3dnil5bWLAhhdretiredmhexpmmphoenixm1.1.6dnil;bWLAhhdretiredmhexpmm phoenix_htmlm2.14.2dnilKbWLAhhd timestampmhexpmmphoenixm 1.3.0-rc.2hhba a ha aa ,bWLAhhddepsmhexpmmpoisonm1.5.0jbWLAhdversionaXbWLAhhdinner_checksummhexpmmplugm1.1.2m ~$ZVɭ\1L>m ٛHE ZbWLAhhdinner_checksummhexpmmpoisonm5.0.0m ҵEAW.Ww$F:TKn טU;$JbWLAhhd timestampmhexpmm plug_cryptom1.2.1hhbaahaaa.bWLAhhddepsmhexpmmphoenixm0.16.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 0.14 or ~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsej]bWLAhhdinner_checksummhexpmm telemetrym1.0.0m E:,$7T 0R5bWLAhhdretiredmhexpmmphoenixm1.5.3dnil[bWLAhhdouter_checksummhexpmmphoenixm0.2.1m +glXOptDt hT9ky[9{-4bWLAhhdretiredmhexpmmcowboym1.1.2dnilYbWLAhhdouter_checksummhexpmmplugm0.14.0m qh5Y#oAf]h0=4bWLAhhdretiredmhexpmmcowboym2.4.0dnilGbWLAhhd timestampmhexpmmphoenixm0.17.0hhba a ha aa bWLAhhddepsmhexpmmplugm 1.2.0-rc.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmmimemmimem~> 1.0dfalsejbWLAhhddepsmhexpmmphoenixm0.2.5lhmhexpmmex_confmex_confm0.1.1dfalsehmhexpmminflexminflexm0.2.0dfalsehmhexpmmplugmplugm0.4.4dfalsejbWLAhhddepsmhexpmmcowboym2.2.2lhmhexpmmcowlibmcowlibm~> 2.1.0dfalsehmhexpmmranchmranchm~> 1.4.0dfalsej'bWLAhhddepsmhexpmmphoenixm1.3.3lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4dfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0dfalsej`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.3.1m 7i8x }6JyHhk`bWLAhhdouter_checksummhexpmm phoenix_htmlm1.3.0m E~<&[_^ NWbWLAhhd registry_etagmhexpmm phoenix_htmlm""0ecf0546b4ca52ab2dbe5ee3a933fc6a"YbWLAhhdinner_checksummhexpmmplugm1.11.0m rRUbúퟎ?`qGӎ'bWLAhhddepsmhexpmmphoenixm 1.4.0-rc.3lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruejbWLAhhddepsmhexpmmphoenixm1.1.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejXbWLAhhdouter_checksummhexpmmplugm1.2.0m '8êBJm0ʭOh::AbWLAhhdretiredmhexpmmphoenix_pubsubm 1.0.0-rc.0dnil-bWLAhhddepsmhexpmmdecimalm1.3.0j[bWLAhhdinner_checksummhexpmmphoenixm1.2.4m ArG^!j^AuH 9 .c[6bWLAhhdretiredmhexpmmphoenixm1.4.14dnilbWLAhhdversionsmhexpmmplugl]m0.4.1m0.4.2m0.4.3m0.4.4m0.5.0m0.5.1m0.5.2m0.5.3m0.6.0m0.7.0m0.8.0m0.8.1m0.8.2m0.8.3m0.8.4m0.9.0m0.10.0m0.11.0m0.11.1m0.11.2m0.11.3m0.12.0m0.12.1m0.12.2m0.13.0m0.13.1m0.14.0m1.0.0m1.0.1m1.0.2m1.0.3m1.0.4m1.0.5m1.0.6m1.1.0m1.1.1m1.1.2m1.1.3m1.1.4m1.1.5m1.1.6m1.1.7m1.1.8m1.1.9m 1.2.0-rc.0m1.2.0m1.2.1m1.2.2m1.2.3m1.2.4m1.2.5m1.2.6m1.3.0m1.3.1m1.3.2m1.3.3m1.3.4m1.3.5m1.3.6m 1.4.0-rc.0m1.4.0m1.4.1m1.4.2m1.4.3m1.4.4m1.4.5m 1.5.0-rc.0m 1.5.0-rc.1m 1.5.0-rc.2m1.5.0m1.5.1m1.6.0m1.6.1m1.6.2m1.6.3m1.6.4m1.7.0m1.7.1m1.7.2m1.8.0m1.8.1m1.8.2m1.8.3m1.9.0m1.10.0m1.10.1m1.10.2m1.10.3m1.10.4m1.11.0m1.11.1m1.12.0m1.12.1j`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.8.0m wu`֫{%ɯ)dNH"Θ 9bWLAhhdretiredmhexpmm plug_cowboym2.1.1dnilKbWLAhhddepsmhexpmmphoenixm1.2.4lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm6~> 1.4 or ~> 1.3.3 or ~> 1.2.4 or ~> 1.1.8 or ~> 1.0.5dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsej`bWLAhhdinner_checksummhexpmm phoenix_htmlm3.0.0m HkR!p;i(InE*+bWLAhhddepsmhexpmmplugm1.10.0lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dtruej[bWLAhhdinner_checksummhexpmmphoenixm1.2.5m Z_"Y^#*evH#CbWLAhhd timestampmhexpmmplugm1.2.6hhbaahaa4aKbWLAhhd timestampmhexpmm phoenix_htmlm2.0.0hhba a ha aa FbWLAhhd timestampmhexpmmcowlibm2.10.1hhbaahaaa.4bWLAhhdretiredmhexpmmcowlibm2.2.1dnilEbWLAhhd timestampmhexpmmpoisonm1.1.0hhba a ha a.aYbWLAhhdinner_checksummhexpmmranchm1.3.1m =OYALRz z)HE-. ryvhbWLAhhddepsmhexpmm phoenix_htmlm2.6.2lhmhexpmmplugmplugm~> 1.0dfalsej:bWLAhhdretiredmhexpmm phoenix_htmlm1.0.0dnilKbWLAhhd timestampmhexpmmphoenixm 1.2.0-rc.1hhba a ha aa FbWLAhhd timestampmhexpmmphoenixm0.7.1hhba a ha aa `bWLAhhdouter_checksummhexpmm phoenix_htmlm2.0.1m Sf&]rJsu7 eUyB7~XbWLAhhdinner_checksummhexpmmplugm1.0.4m ׼fFOshM$L3m*bWLAhhddepsmhexpmm plug_cowboym2.5.1lhmhexpmmcowboymcowboym~> 2.7dfalsehmhexpmmcowboy_telemetrymcowboy_telemetrym~> 0.3dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm telemetrym telemetrym~> 0.4 or ~> 1.0dfalsejbWLAhhddepsmhexpmmplugm1.4.1lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsejFbWLAhhd timestampmhexpmmphoenixm1.1.8hhba a ha aa [bWLAhhdinner_checksummhexpmmphoenixm1.4.0m Vs_>;1IԴ6fb{`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.6.2m J^X OLiP>0[(D9%>ZbWLAhhdinner_checksummhexpmmcowboym1.0.1m  Q˻u 4R eh3]@FbWLAhhd timestampmhexpmmcowlibm2.11.0hhbaahaaa.JbWLAhhd timestampmhexpmm plug_cowboym2.1.1hhba a ha aa 2bWLAhhdretiredmhexpmmmimem1.5.0dnil'bWLAhhddepsmhexpmmphoenixm1.3.2lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4dfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0dfalsejcbWLAhhddepsmhexpmmplugm1.1.7lhmhexpmmcowboymcowboym~> 1.0dtruej`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.3.0m (&M"$& $bn 2q>/bbWLAhhdinner_checksummhexpmmphoenix_pubsubm1.0.1m b7|Kչ .: NjXbWLAhhdinner_checksummhexpmmplugm1.3.4m :8?YERI44GHwbWLAhhddepsmhexpmmcowboym2.6.1lhmhexpmmcowlibmcowlibm~> 2.7.0dfalsehmhexpmmranchmranchm~> 1.7.1dfalsejEbWLAhhd timestampmhexpmmpoisonm4.0.0hhba a ha a.abWLAhhddepsmhexpmmplugm1.3.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsej`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.9.1m ȽP#4|oA iOT]bWLAhhdinner_checksummhexpmm telemetrym0.4.3m d(c)<٦&:f;șW_bWLAhhdouter_checksummhexpmm plug_cryptom1.2.1m iaп2ӑT].7h9ax,`bWLAhhdouter_checksummhexpmm phoenix_htmlm1.4.0m jJZ:bxhk ~RsXI֟kLdbWLAhhddepsmhexpmmplugm0.11.3lhmhexpmmcowboymcowboym~> 1.0dtruejXbWLAhhdinner_checksummhexpmmplugm0.5.0m r"cuM>͖ x7ɮ4|)uR#vtObWLAhhd timestampmhexpmmcowboy_telemetrym0.4.0hhbaahaaa4bWLAhhdretiredmhexpmmcowboym2.2.0dnil^bWLAhhdinner_checksummhexpmmjasonm 1.0.0-rc.2m *]f?v7@.FV)2bWLAhhdretiredmhexpmmplugm1.4.0dnil[bWLAhhdinner_checksummhexpmmphoenixm1.0.1m .ٝrl#H[;'r9p؂3XbWLAhhdinner_checksummhexpmmplugm1.6.3m C3{*/c5i~LSH,^2bWLAhhdretiredmhexpmmplugm1.4.3dnil`bWLAhhdinner_checksummhexpmm phoenix_htmlm3.0.3m 2-p~^++iy}[ 0JbWLAhhd timestampmhexpmm plug_cowboym2.4.1hhba a ha aa \bWLAhhdinner_checksummhexpmmphoenixm0.14.0m q;{;  )=oR b/_bWLAhhdouter_checksummhexpmm plug_cryptom1.0.0m sh/AL]oi;tNXwY5bWLAhhdretiredmhexpmmphoenixm0.1.0dnil5bWLAhhdretiredmhexpmmphoenixm0.7.1dnil5bWLAhhdretiredmhexpmmphoenixm1.4.8dnilDbWLAhhdversionsmhexpmmdecimallm0.1.1m0.1.2m0.2.0m0.2.1m0.2.2m0.2.3m0.2.4m0.2.5m1.0.0m1.0.1m1.1.0m1.1.1m1.1.2m1.2.0m1.3.0m1.3.1m1.4.0m1.4.1m1.5.0m1.6.0m1.7.0m1.8.0m1.8.1m 1.9.0-rc.0m1.9.0m 2.0.0-rc.0m2.0.0j[bWLAhhdinner_checksummhexpmmphoenixm1.4.1m c(e|e|bLB#FbWLAhhd timestampmhexpmmphoenixm0.2.2hhba a ha aa DbWLAhhd timestampmhexpmmplugm1.12.0hhbaahaa4a7bWLAhhdretiredmhexpmm telemetrym1.0.0dnil5bWLAhhdretiredmhexpmmphoenixm1.5.1dnil\bWLAhhdouter_checksummhexpmmphoenixm0.11.0m @7#&[vnf"ď?O#y*[bWLAhhdinner_checksummhexpmmphoenixm1.3.2m *QpkNn~ˊ,q痍\sxDbWLAhhd timestampmhexpmmplugm0.11.1hhbaahaa4aZbWLAhhdouter_checksummhexpmmcowboym2.8.0m FCJmM,u=г]T_[bWLAhhdouter_checksummhexpmmphoenixm1.5.2m 0G#g5ng*y4Ԇ-.rQL$"4MbWLAhhd timestampmhexpmmphoenix_pubsubm1.0.2hhba a ha aa bbWLAhhdinner_checksummhexpmmphoenix_pubsubm0.1.0m zڏAI+Y@W<5y5EbWLAhhd timestampmhexpmmpoisonm1.3.0hhba a ha a.aXbWLAhhdouter_checksummhexpmmplugm1.3.3m C4t~ws/[4 };EbWLAhhd timestampmhexpmmcowboym2.2.2hhbaahaaa.dbWLAhhdinner_checksummhexpmm phoenix_htmlm 2.0.0-devm 3{ۼ/"\4s9sHee[}(e[bWLAhhdinner_checksummhexpmmdecimalm1.8.0m F. _ ţB~5HZes4.abWLAhhdouter_checksummhexpmm phoenix_htmlm2.10.0m .8CN҄W~`s}") .U<=mDbWLAhhd timestampmhexpmmjasonm1.1.1hhbaaha aa:bbWLAhhdinner_checksummhexpmmphoenix_pubsubm1.1.0m ^%/dcfa-PSYD2bWLAhhdretiredmhexpmmmimem2.0.1dnil[bWLAhhdouter_checksummhexpmmphoenixm1.4.8m 9e" MrJgjt--EbWLAhhd timestampmhexpmmcowlibm2.7.0hhbaahaaa.4bWLAhhdretiredmhexpmmcowlibm2.5.0dnil[bWLAhhdouter_checksummhexpmmphoenixm1.5.3m d 3ZiVHd(68:#\׶|%:bWLAhhdretiredmhexpmm phoenix_htmlm3.0.2dnil`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.0.0m JnPp~ղi>TCHYF>!\5+bWLAhhddepsmhexpmmranchm1.6.2j3bWLAhhdretiredmhexpmmranchm1.7.1dnil\bWLAhhdouter_checksummhexpmmphoenixm1.4.15m dv#f mW7JܻiIFbWLAhhd timestampmhexpmmphoenixm1.3.4hhba a ha aa gbWLAhhdouter_checksummhexpmmphoenix_pubsubm 1.0.0-rc.0m v\f_vM^Z"q9ۏ ;W_EbWLAhhd timestampmhexpmmcowlibm2.2.0hhbaahaaa.jbWLAhhddepsmhexpmmphoenixm1.4.17lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsej2bWLAhhddepsmhexpmmdecimalm 1.9.0-rc.0jbWLAhhddepsmhexpmmcowboym2.7.0lhmhexpmmcowlibmcowlibm~> 2.8.0dfalsehmhexpmmranchmranchm~> 1.7.1dfalsej[bWLAhhdinner_checksummhexpmmphoenixm1.1.0m :>VV5E6M^^AE@uUEhDbWLAhhd timestampmhexpmmplugm1.10.2hhbaahaa4aGbWLAhhd timestampmhexpmmphoenixm1.4.10hhba a ha aa FbWLAhhd timestampmhexpmmphoenixm0.7.0hhba a ha aa bWLAhhddepsmhexpmmplugm1.8.3lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0dfalsehmhexpmm telemetrym telemetrym~> 0.4dtruej9bWLAhhdretiredmhexpmm plug_cowboym2.3.0dnil*bWLAhhddepsmhexpmmplugm0.5.1j[bWLAhhdouter_checksummhexpmmphoenixm1.0.2m VBSkeѠOU~9V#wk`Rg8bWLAhhdretiredmhexpmmjasonm 1.0.0-rc.2dnil,bWLAhhddepsmhexpmmcowlibm2.8.0jbWLAhhddepsmhexpmmphoenixm1.2.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.1dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsejGbWLAhhd timestampmhexpmmphoenixm0.10.0hhba a ha aa 4bWLAhhdretiredmhexpmmpoisonm1.5.1dnil2bWLAhhdretiredmhexpmmplugm1.1.6dnil+bWLAhhddepsmhexpmmranchm1.3.1jbWLAhhddepsmhexpmmplugm1.4.4lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsej5bWLAhhdretiredmhexpmmdecimalm1.8.0dnilCbWLAhhd timestampmhexpmmplugm1.1.0hhbaahaa4a2bWLAhhdretiredmhexpmmplugm1.0.1dnil\bWLAhhdouter_checksummhexpmmphoenixm0.14.0m حTx&y/ݝ98ZbWLAhhdinner_checksummhexpmmcowboym2.2.2m lcMڋ+ m 1.5dfalsejJbWLAhhd timestampmhexpmm plug_cowboym2.5.2hhba a ha aa bWLAhhddepsmhexpmmphoenixm0.10.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm ~> 0.11.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsehmhexpmmpoolboympoolboym~> 1.4.2dtruej2bWLAhhdretiredmhexpmmplugm0.4.1dnil4bWLAhhddepsmhexpmmphoenix_pubsubm1.0.1j/bWLAhhddepsmhexpmm telemetrym0.1.0jZbWLAhhdouter_checksummhexpmmcowboym2.2.0m ]`>_J40BN./d5|ZbWLAhhdinner_checksummhexpmmcowlibm1.2.0m / VE;>-b' N\,`@bWLAhhd timestampmhexpmm plug_cryptohhbaahaaa.RbWLAhhd registry_etagmhexpmmphoenixm""b1c4092fd2fdcc64525a93181cd45bcd"6bWLAhhdretiredmhexpmmphoenixm1.5.11dnil3bWLAhhdretiredmhexpmmplugm0.12.2dnil4bWLAhhdretiredmhexpmmcowlibm2.3.0dnilFbWLAhhd timestampmhexpmmphoenixm1.0.6hhba a ha aa `bWLAhhdinner_checksummhexpmm phoenix_htmlm2.1.0m ud*7kH|K~9dƀbWLAhhddepsmhexpmmphoenixm0.9.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm ~> 0.10.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsehmhexpmmpoolboympoolboym~> 1.4.2dtruej*bWLAhhddepsmhexpmmmimem1.0.1jXbWLAhhdinner_checksummhexpmmplugm0.4.4m ÃnOWs˧'S/M)WW`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.9.2m 7` Co񚮘dbw:[.`,bWLAhhddepsmhexpmmcowlibm2.0.1j2bWLAhhdretiredmhexpmmplugm0.5.1dnil[bWLAhhdouter_checksummhexpmmphoenixm1.2.4m ]ͤr GM(\rn\qV[\bWLAhhdinner_checksummhexpmmphoenixm0.12.0m Bp5 »O+hȠ& F!@ F2,bWLAhhdretiredmhexpmm plug_cryptom1.1.1tdmessagem/Wrong default value is used for salt on encryptdreasondRETIRED_INVALID9bWLAhhd timestampmhexpmmmimehhbaahaaa.4bWLAhhddepsmhexpmmphoenix_pubsubm1.0.0j'bWLAhhddepsmhexpmmphoenixm1.3.4lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4dfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0dfalsej[bWLAhhdouter_checksummhexpmmdecimalm0.2.3m tVX7aCVh`ZV1ւu2bWLAhhdretiredmhexpmmmimem1.1.0dnil4bWLAhhdretiredmhexpmmcowlibm1.0.2dnil_bWLAhhdouter_checksummhexpmm plug_cowboym2.0.1m s nh4jQQ@! 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej2bWLAhhdretiredmhexpmmplugm1.8.1dnil[bWLAhhdouter_checksummhexpmmphoenixm0.4.0m 碆B~@9YUOb*b/bWLAhhddepsmhexpmm telemetrym0.4.0jCbWLAhhd timestampmhexpmmplugm1.0.5hhbaahaa4aKbWLAhhd timestampmhexpmm phoenix_htmlm2.1.0hhba a ha aa dbWLAhhdinner_checksummhexpmm phoenix_htmlm 2.7.0-devm <|nKx;&y-0GY]bWLAhhdouter_checksummhexpmm telemetrym0.3.0m c}11b!1 Z6h!/rsbWLAhhddepsmhexpmm phoenix_htmlm2.6.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsejCbWLAhhd timestampmhexpmmplugm1.0.6hhbaahaa4a[bWLAhhdouter_checksummhexpmmdecimalm1.0.1m dk+5~ kƿF|xwt[bWLAhhdouter_checksummhexpmmphoenixm1.3.3m (& 1A E-;4sN0]J+DbWLAhhd timestampmhexpmmranchm1.2.1hhba a ha aa _bWLAhhdouter_checksummhexpmm plug_cryptom1.2.0m S8DRU| $ ih?XbWLAhhdinner_checksummhexpmmplugm1.7.0m ͌U17 HuJ%#nYbWLAhhdouter_checksummhexpmmranchm2.1.0m $N*au' Ő$v)J2Wޏ;yi,bWLAhhddepsmhexpmmcowlibm1.2.0jbWLAhhddepsmhexpmmplugm1.8.2lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0dfalsehmhexpmm telemetrym telemetrym~> 0.4dtruejLbWLAhhd timestampmhexpmm phoenix_htmlm2.10.0hhba a ha aa [bWLAhhdouter_checksummhexpmmphoenixm0.5.0m /ߌͥB( P fk.O4bWLAhhdretiredmhexpmmcowboym1.1.1dnilEbWLAhhd timestampmhexpmmcowlibm2.5.0hhbaahaaa.CbWLAhhd timestampmhexpmmplugm1.3.5hhbaahaa4abWLAhhddepsmhexpmmcowboy_telemetrym0.1.0lhmhexpmmcowboymcowboym~> 2.7dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejgbWLAhhdversionsmhexpmmcowboy_telemetrylm0.1.0m0.2.0m0.3.0m0.3.1m0.4.0j`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.6.1m }S= zn$Ÿf0I["7 R}4bWLAhhdretiredmhexpmmpoisonm2.2.0dnil,bWLAhhddepsmhexpmmpoisonm1.4.0j[bWLAhhdinner_checksummhexpmmcowlibm2.10.0m âɹ5.[ZAib}!CbWLAhhd timestampmhexpmmmimem2.0.0hhbaahaaa.FbWLAhhd timestampmhexpmmdecimalm1.7.0hhbaaha aa:CbWLAhhd timestampmhexpmmplugm0.9.0hhbaahaa4avbWLAhhdversionsmhexpmm plug_cryptolm1.0.0m1.1.0m1.1.1m1.1.2m1.2.0m1.2.1m1.2.2jDbWLAhhd timestampmhexpmmplugm1.10.3hhbaahaa4a`bWLAhhdinner_checksummhexpmmphoenixm 1.5.0-rc.0m 8;njxPZh>"Ξǣ,DbWLAhhd timestampmhexpmmjasonm1.0.1hhbaaha aa:FbWLAhhd timestampmhexpmmphoenixm1.5.9hhba a ha aa XbWLAhhdouter_checksummhexpmmplugm1.3.1m b\P1Yu.e34hDJT<\bWLAhhdouter_checksummhexpmmphoenixm0.10.0m @` L)-%T[bWLAhhdouter_checksummhexpmmphoenixm1.1.5m ebHm O^<<9Ubj}še]H:bWLAhhdretiredmhexpmm phoenix_htmlm2.1.1dnil[bWLAhhdinner_checksummhexpmmphoenixm1.2.2m 4[RY8ΖlL‰'JDhbWLAhhddepsmhexpmm phoenix_htmlm2.9.0lhmhexpmmplugmplugm~> 1.0dfalsej2bWLAhhdretiredmhexpmmplugm1.2.1dnil*bWLAhhddepsmhexpmmmimem1.4.0j 1.0 or ~> 2.5dtruehmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.6.4 or ~> 1.7dfalsej[bWLAhhdinner_checksummhexpmmphoenixm0.7.0m *~t@ `fNV][:;MYGbWLAhhd timestampmhexpmmphoenixm0.2.11hhba a ha aa EbWLAhhd timestampmhexpmmcowboym2.3.0hhbaahaaa.:bWLAhhdretiredmhexpmm phoenix_htmlm2.3.0dnil2bWLAhhdretiredmhexpmmplugm1.8.3dnilibWLAhhddepsmhexpmm phoenix_htmlm2.14.0lhmhexpmmplugmplugm~> 1.5dfalsej3bWLAhhdretiredmhexpmmjasonm1.2.0dnil9bWLAhhdretiredmhexpmm plug_cowboym2.5.2dnilbbWLAhhdouter_checksummhexpmmphoenix_pubsubm1.0.2m o6L]kӨ4lVˮftCbWLAhhd timestampmhexpmmplugm1.7.0hhbaahaa4a[bWLAhhdinner_checksummhexpmmphoenixm0.1.0m Hyr M{=w-bWLAhhddepsmhexpmmphoenixm0.17.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejYbWLAhhdinner_checksummhexpmmplugm0.13.1m GA2wY!^asoK/}5bWLAhhdretiredmhexpmmphoenixm0.4.1dnil;bWLAhhdretiredmhexpmm phoenix_htmlm2.13.4dnil5bWLAhhdretiredmhexpmmphoenixm1.1.5dnilbWLAhhddepsmhexpmmphoenixm1.1.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejHbWLAhhd timestampmhexpmm telemetrym0.2.0hhbaahaa4aDbWLAhhd timestampmhexpmmplugm0.12.2hhbaahaa4a+bWLAhhddepsmhexpmmranchm1.3.0jFbWLAhhd timestampmhexpmmphoenixm0.2.0hhba a ha aa LbWLAhhd timestampmhexpmm phoenix_htmlm2.14.0hhba a ha aa `bWLAhhdinner_checksummhexpmmphoenixm 1.4.0-rc.0m k]MvF/x[bcjfj8{.ޤ3bWLAhhdretiredmhexpmmjasonm1.2.2dnilEbWLAhhd timestampmhexpmmcowlibm1.0.2hhbaahaaa.XbWLAhhdinner_checksummhexpmmplugm1.0.2m 4)ȏưMq/gV@Lꗦ1:Iʨ#n2bWLAhhdretiredmhexpmmplugm1.9.0dnilFbWLAhhd timestampmhexpmmdecimalm1.6.0hhbaaha aa::bWLAhhdretiredmhexpmm phoenix_htmlm2.6.1dnilcbWLAhhddepsmhexpmmplugm1.1.4lhmhexpmmcowboymcowboym~> 1.0dtruej[bWLAhhdinner_checksummhexpmmcowlibm2.11.0m FbV.vwn  cKbWLAhhd timestampmhexpmm phoenix_htmlm2.8.0hhba a ha aa YbWLAhhdouter_checksummhexpmmranchm1.2.1m Wa\7\#U0ű%ZЎ[FbWLAhhd timestampmhexpmmphoenixm1.0.4hhba a ha aa wbWLAhhddepsmhexpmm phoenix_htmlm1.2.0lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0dfalsejbWLAhhddepsmhexpmmphoenixm1.5.0lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej\bWLAhhdouter_checksummhexpmmphoenixm1.4.10m %jס@)p6=|pnd"tcbWLAhhddepsmhexpmmplugm1.1.5lhmhexpmmcowboymcowboym~> 1.0dtruejZbWLAhhdinner_checksummhexpmmpoisonm1.3.0m 1|S7Yx.k^mF4bWLAhhdretiredmhexpmmcowboym2.9.0dnil:bWLAhhdretiredmhexpmm phoenix_htmlm2.5.1dnil9bWLAhhddepsmhexpmmphoenix_pubsubm 1.0.0-rc.0jFbWLAhhd timestampmhexpmmphoenixm1.3.0hhba a ha aa [bWLAhhdinner_checksummhexpmmdecimalm0.2.4m qt6Dh2A?k't*PgLICVbWLAhhdversionsmhexpmmphoenixllm0.1.0m0.2.0m0.2.1m0.2.2m0.2.3m0.2.4m0.2.5m0.2.6m0.2.7m0.2.8m0.2.9m0.2.10m0.2.11m0.3.0m0.3.1m0.4.0m0.4.1m0.5.0m0.6.0m0.6.1m0.6.2m0.7.0m0.7.1m0.7.2m0.8.0m0.9.0m0.10.0m0.11.0m0.12.0m0.13.0m0.13.1m0.14.0m0.15.0m0.16.0m0.16.1m0.17.0m0.17.1m1.0.0m1.0.1m1.0.2m1.0.3m1.0.4m1.0.5m1.0.6m1.1.0m1.1.1m1.1.2m1.1.3m1.1.4m1.1.5m1.1.6m1.1.7m1.1.8m1.1.9m 1.2.0-rc.0m 1.2.0-rc.1m1.2.0m1.2.1m1.2.2m1.2.3m1.2.4m1.2.5m 1.3.0-rc.0m 1.3.0-rc.1m 1.3.0-rc.2m 1.3.0-rc.3m1.3.0m1.3.1m1.3.2m1.3.3m1.3.4m 1.4.0-rc.0m 1.4.0-rc.1m 1.4.0-rc.2m 1.4.0-rc.3m1.4.0m1.4.1m1.4.2m1.4.3m1.4.4m1.4.5m1.4.6m1.4.7m1.4.8m1.4.9m1.4.10m1.4.11m1.4.12m1.4.13m1.4.14m1.4.15m1.4.16m1.4.17m 1.5.0-rc.0m1.5.0m1.5.1m1.5.2m1.5.3m1.5.4m1.5.5m1.5.6m1.5.7m1.5.8m1.5.9m1.5.10m1.5.11m1.5.12m 1.6.0-rc.0jEbWLAhhd timestampmhexpmmcowboym2.7.0hhbaahaaa._bWLAhhdouter_checksummhexpmm plug_cowboym2.2.2m #dۭ7SՈ~^Pbf17'6bWLAhhdretiredmhexpmmphoenixm1.4.16dnil`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.9.2m MI?V`1= f[0g7iqKNoXbWLAhhdinner_checksummhexpmmplugm1.3.5m up*afm,T˺及Y ;p1,bWLAhhddepsmhexpmmpoisonm1.3.1j;bWLAhhdretiredmhexpmm phoenix_htmlm2.14.3dnilDbWLAhhd timestampmhexpmmplugm1.10.4hhbaahaa4aibWLAhhddepsmhexpmmphoenixm1.4.9lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1dfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0dtruehmhexpmm telemetrym telemetrym~> 0.4dfalsejKbWLAhhd timestampmhexpmmphoenixm 1.5.0-rc.0hhba a ha aa  bWLAhhddepsmhexpmm plug_cowboym2.4.0lhmhexpmmcowboymcowboym~> 2.7dfalsehmhexpmmcowboy_telemetrymcowboy_telemetrym~> 0.3dfalsehmhexpmmplugmplugm~> 1.7dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej-bWLAhhddepsmhexpmmcowlibm2.11.0j5bWLAhhdretiredmhexpmmcowlibm2.10.0dnil4bWLAhhdretiredmhexpmmcowboym2.8.0dnilKbWLAhhd timestampmhexpmmdecimalm 2.0.0-rc.0hhbaaha aa:2bWLAhhdretiredmhexpmmplugm1.3.3dnilXbWLAhhdinner_checksummhexpmmmimem1.0.0m 0's;;]ȶbˁw1TJVJbWLAhhd timestampmhexpmm plug_cryptom1.1.2hhbaahaaa.:bWLAhhdretiredmhexpmm phoenix_htmlm2.6.0dnilGbWLAhhd timestampmhexpmmphoenixm0.11.0hhba a ha aa 4bWLAhhdretiredmhexpmmcowboym2.6.0dnilFbWLAhhd timestampmhexpmmphoenixm1.4.5hhba a ha aa cbWLAhhddepsmhexpmmplugm1.0.6lhmhexpmmcowboymcowboym~> 1.0dtruej2bWLAhhdretiredmhexpmmplugm0.4.4dnil-bWLAhhddepsmhexpmmdecimalm1.3.1j_bWLAhhdinner_checksummhexpmm plug_cryptom1.2.0m c3ף_ӚWgm-CbWLAhhd timestampmhexpmmplugm1.3.3hhbaahaa4a6bWLAhhdretiredmhexpmmphoenixm0.2.11dnilFbWLAhhd timestampmhexpmmphoenixm1.5.7hhba a ha aa ibWLAhhddepsmhexpmm phoenix_htmlm2.14.3lhmhexpmmplugmplugm~> 1.5dfalsejbWLAhhddepsmhexpmmphoenixm0.2.9lhmhexpmmex_confmex_confm0.1.2dfalsehmhexpmminflexminflexm0.2.0dfalsehmhexpmmjazzmjazzm0.1.1dfalsehmhexpmmplugmplugm0.5.0dfalsej2bWLAhhdretiredmhexpmmplugm1.6.2dnilKbWLAhhd timestampmhexpmmphoenixm 1.4.0-rc.1hhba a ha aa XbWLAhhdinner_checksummhexpmmplugm1.3.0m n+?JBE(W Dk̒Dz3bWLAhhdretiredmhexpmmplugm0.11.2dnil3bWLAhhdretiredmhexpmmplugm1.11.0dnil2bWLAhhdretiredmhexpmmplugm1.1.3dnilXbWLAhhdinner_checksummhexpmmplugm1.1.1m UWlٹUh e<>h|,{\{-bWLAhhddepsmhexpmmcowlibm2.10.0j`bWLAhhdouter_checksummhexpmmphoenixm 1.3.0-rc.3m >tm~Ļsb`&x ewYbWLAhhdouter_checksummhexpmmjasonm1.0.0m l@eLc# H;g͠yCbWLAhhd timestampmhexpmmplugm1.2.2hhbaahaa4aGbWLAhhd timestampmhexpmmphoenixm0.13.1hhba a ha aa -bWLAhhddepsmhexpmmdecimalm0.2.4jZbWLAhhdinner_checksummhexpmmpoisonm3.0.0m b^d:RMl|ȶ- x(ƒ 1XbWLAhhdouter_checksummhexpmmplugm1.9.0m ** d4h.I?]MjƼ_#PCbWLAhhd timestampmhexpmmplugm1.0.1hhbaahaa4a[bWLAhhdouter_checksummhexpmmdecimalm1.5.0m  &XUMu_]r?3ɻ9W⡭.abWLAhhdinner_checksummhexpmm phoenix_htmlm2.11.0m m[\VB3x2ba~Weڡl8:bWLAhhdretiredmhexpmm phoenix_htmlm2.0.1dnilVbWLAhhddepsmhexpmmphoenixm 1.2.0-rc.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmmimemmimem~> 1.0dfalsehmhexpmmphoenix_pubsubmphoenix_pubsubm ~> 1.0.0-rcdfalsehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsejXbWLAhhdinner_checksummhexpmmplugm1.6.4m 5aiE/k7vЙs?'G?8bWLAhhdretiredmhexpmmjasonm 1.0.0-rc.3dnilXbWLAhhdouter_checksummhexpmmmimem1.3.1m lvj  1Lc KζE8du8bWLAhhdretiredmhexpmmjasonm 1.0.0-rc.1dnilKbWLAhhd timestampmhexpmm phoenix_htmlm1.4.0hhba a ha aa [bWLAhhdouter_checksummhexpmmphoenixm1.1.1m Iu꿣 CMMbK,bWLAhhddepsmhexpmmcowlibm2.7.0j[bWLAhhdinner_checksummhexpmmphoenixm1.1.7m 4ظ ?TL_?85ȭaU2bWLAhhdretiredmhexpmmplugm1.6.4dnil,bWLAhhddepsmhexpmmphoenixm 1.3.0-rc.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0dfalsehmhexpmmplugmplugm~> 1.3.2 or ~> 1.4dfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0dfalsej[bWLAhhdouter_checksummhexpmmphoenixm1.1.8m n^YF%{(l<: ]'viDYbWLAhhdinner_checksummhexpmmranchm1.2.0m Hpjp W~\-`yݲ S`ibWLAhhddepsmhexpmmphoenixm0.13.1lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm>= 0.12.2 and < 2.0.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsehmhexpmmpoolboympoolboym~> 1.5.1 or ~> 1.6dtruejZbWLAhhdinner_checksummhexpmmcowlibm1.3.0m l(cעcsC+VF z;9CbWLAhhd timestampmhexpmmplugm1.2.1hhbaahaa4aGbWLAhhd timestampmhexpmmphoenixm0.14.0hhba a ha aa YbWLAhhdouter_checksummhexpmmplugm0.11.1m M}O໿Fk΍]/]/9ݸZbWLAhhdouter_checksummhexpmmpoisonm1.3.0m v QN F%\B6Üq>N_JbWLAhhd timestampmhexpmm plug_cowboym2.3.0hhba a ha aa 6bWLAhhdretiredmhexpmmphoenixm0.17.1dnilYbWLAhhdouter_checksummhexpmmranchm1.6.2m ex֘wkf; mE'CXbWLAhhdinner_checksummhexpmmplugm1.8.3m ym.aNL Be ZhDxYbWLAhhdouter_checksummhexpmmranchm1.3.0m j֫j$Ս҃+7>ụ) [8FbWLAhhd timestampmhexpmmphoenixm1.4.0hhba a ha aa FbWLAhhd timestampmhexpmmphoenixm1.5.8hhba a ha aa [bWLAhhdinner_checksummhexpmmphoenixm1.3.4m ^U#:{mဿ,TS#I<*%=`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.4.0m "s3f?r&Yg~?f׹K_bWLAhhdinner_checksummhexpmm plug_cowboym2.2.2m z ]盒2ĔvLȐXbWLAhhdinner_checksummhexpmmplugm1.1.6m '3Y8|(71'3Pf^bWLAhhdinner_checksummhexpmmjasonm 1.0.0-rc.1m BNnn|+dcXah=S`F%XbWLAhhdouter_checksummhexpmmmimem1.0.1m ^m r,)G XbWLAhhdouter_checksummhexpmmplugm1.8.0m L [8Ɵ*x}kTot$YbWLAhhdouter_checksummhexpmmranchm1.6.0m apeFh=1E^sa`]J7ZbWLAhhdinner_checksummhexpmmcowlibm2.8.0m x}J!s >qr+#Ȥ5bWLAhhdretiredmhexpmmphoenixm0.2.3dnilDbWLAhhd timestampmhexpmmplugm1.10.1hhbaahaa4abWLAhhddepsmhexpmmcowboym2.2.0lhmhexpmmcowlibmcowlibm~> 2.1.0dfalsehmhexpmmranchmranchm~> 1.4.0dfalsej`bWLAhhdinner_checksummhexpmm phoenix_htmlm1.1.0m UL-\^'u2˖ut(DbWLAhhd timestampmhexpmmjasonm1.2.0hhbaaha aa:4bWLAhhddepsmhexpmmphoenix_pubsubm1.1.1jbWLAhhddepsmhexpmmplugm1.6.4lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4dtruehmhexpmmmimemmimem~> 1.0dfalsej9bWLAhhdretiredmhexpmm plug_cowboym2.5.1dnil6bWLAhhdretiredmhexpmmphoenixm0.12.0dnil[bWLAhhdinner_checksummhexpmmphoenixm1.4.6m 5 xj.HQxvlYbbWLAhhdouter_checksummhexpmmphoenix_pubsubm1.1.2m ig)tѕR@i8MbWLAhhd timestampmhexpmmphoenix_pubsubm2.0.0hhba a ha aa GbWLAhhd timestampmhexpmmphoenixm1.4.15hhba a ha aa  bWLAhhddepsmhexpmmphoenixm0.4.1lhmhexpmmcowboymcowboym~> 1.0.0dtruehmhexpmmlinguistmlinguistm~> 0.1.2dfalsehmhexpmmplugmplugm0.7.0dfalsehmhexpmmpoisonmpoisonm~> 1.1.0dfalsej6bWLAhhdretiredmhexpmmphoenixm0.13.1dnilFbWLAhhd timestampmhexpmmphoenixm1.4.7hhba a ha aa `bWLAhhdinner_checksummhexpmm phoenix_htmlm3.0.1m 5 __T81y@&'[SM2bWLAhhdretiredmhexpmmplugm1.0.5dnilbWLAhhddepsmhexpmmphoenixm0.15.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm>= 0.13.1 and < 2.0.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejJbWLAhhd timestampmhexpmm plug_cryptom1.1.0hhbaahaaa.ZbWLAhhdouter_checksummhexpmmpoisonm2.0.1m 4j9󴛛vGFQDxvC!Bv+LbWLAhhd timestampmhexpmm phoenix_htmlm2.14.1hhba a ha aa bWLAhhddepsmhexpmmphoenixm1.1.5lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0dfalsejXbWLAhhdinner_checksummhexpmmplugm1.2.1m ܇ Te N+o>&t'CbWLAhhd timestampmhexpmmplugm1.2.0hhbaahaa4a`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.9.3m ]*⹰sQ.Nkr׿1MJRKbWLAhhd timestampmhexpmm phoenix_htmlm2.9.1hhba a ha aa 7bWLAhhdretiredmhexpmmplugm 1.2.0-rc.0dnilXbWLAhhdouter_checksummhexpmmmimem1.4.0m uB"?p#KjGO?jzOFbWLAhhd timestampmhexpmmphoenixm1.5.2hhba a ha aa KbWLAhhd timestampmhexpmmphoenixm 1.6.0-rc.0hhba a ha aa ,bWLAhhddepsmhexpmmpoisonm2.0.1jGbWLAhhd timestampmhexpmmphoenixm1.4.13hhba a ha aa 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejXbWLAhhdinner_checksummhexpmmplugm1.3.6m ߔKø~Y+QJ _EbWLAhhd timestampmhexpmmcowboym2.0.0hhbaahaaa.[bWLAhhdinner_checksummhexpmmphoenixm0.2.5m ",Ҍ5 L<ؼ߬32bWLAhhdretiredmhexpmmplugm1.3.1dnil[bWLAhhdouter_checksummhexpmmphoenixm0.2.0m D7܆qO6ǁ79*0]P쿺sEiCbWLAhhd timestampmhexpmmplugm1.1.6hhbaahaa4aCbWLAhhd timestampmhexpmmplugm1.1.1hhbaahaa4a3bWLAhhdretiredmhexpmmranchm1.6.1dnilEbWLAhhd timestampmhexpmmcowlibm2.3.0hhbaahaaa.`bWLAhhdinner_checksummhexpmmphoenixm 1.3.0-rc.2m SJ%h܂o8Bs E"(ت4bWLAhhdretiredmhexpmmcowboym2.7.0dnil_bWLAhhdouter_checksummhexpmm plug_cowboym2.0.2m DSݢiu| Mq 1bWLAhhdversionsmhexpmmjasonl m 1.0.0-rc.1m 1.0.0-rc.2m 1.0.0-rc.3m1.0.0m1.0.1m1.1.0m1.1.1m1.1.2m1.2.0m1.2.1m1.2.2jebWLAhhddepsmhexpmmplugm0.6.0lhmhexpmmcowboymcowboym~> 1.0.0dtruej[bWLAhhdouter_checksummhexpmmphoenixm0.6.1m ύK lt~/S3ۗhN1bsbWLAhhddepsmhexpmm phoenix_htmlm2.1.1lhmhexpmmplugmplugm~> 0.13 or ~> 1.0dfalsejbWLAhhddepsmhexpmmcowboym2.1.0lhmhexpmmcowlibmcowlibm~> 2.0.1dfalsehmhexpmmranchmranchm~> 1.4.0dfalsejYbWLAhhdinner_checksummhexpmmjasonm1.2.2m CpѪΐi\Vx?R*bWLAhhddepsmhexpmmmimem1.6.0jcbWLAhhddepsmhexpmmplugm1.0.2lhmhexpmmcowboymcowboym~> 1.0dtruej+bWLAhhddepsmhexpmmranchm2.1.0jbWLAhhddepsmhexpmmplugm1.2.6lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmmimemmimem~> 1.0dfalsejFbWLAhhd timestampmhexpmmdecimalm0.2.3hhbaaha aa:cbWLAhhddepsmhexpmmplugm0.8.2lhmhexpmmcowboymcowboym~> 1.0dtruejEbWLAhhd timestampmhexpmmcowlibm2.9.1hhbaahaaa.FbWLAhhd timestampmhexpmmphoenixm0.2.5hhba a ha aa XbWLAhhdinner_checksummhexpmmplugm1.2.4m e)Do=."'vu=HϾKbWLAhhd timestampmhexpmmphoenixm 1.2.0-rc.0hhba a ha aa [bWLAhhdinner_checksummhexpmmdecimalm1.3.1m { 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsej^bWLAhhdouter_checksummhexpmmjasonm 1.0.0-rc.3m tE;#:˛V83;jWD+lE,LbWLAhhd timestampmhexpmm phoenix_htmlm2.13.1hhba a ha aa ZbWLAhhdouter_checksummhexpmmcowboym2.5.0m v=%3vqW>,,δ )90ZbWLAhhdouter_checksummhexpmmcowlibm2.6.0m Eo*e)YU7,-3$SILbWLAhhd timestampmhexpmm phoenix_htmlm2.11.0hhba a ha aa `bWLAhhdouter_checksummhexpmm phoenix_htmlm2.5.1m |] ^7d"> Q)`MbWLAhhd timestampmhexpmmphoenix_pubsubm1.1.2hhba a ha aa VbWLAhhd registry_etagmhexpmm plug_cryptom""29f1eae1dd8d5dc4c084385d2b97a633"4bWLAhhdretiredmhexpmmpoisonm3.0.0dnilbWLAhhddepsmhexpmmplugm1.8.1lhmhexpmmmimemmimem~> 1.0dfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0dfalsehmhexpmm telemetrym telemetrym~> 0.4dtruejbWLAhhddepsmhexpmmplugm 1.4.0-rc.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsejabWLAhhdinner_checksummhexpmm phoenix_htmlm2.14.2m r?Hd0P}ٜ lwRFO=V>ZbWLAhhdinner_checksummhexpmmpoisonm1.5.2m V ߷D=#!" XեꄉbWLAhhddepsmhexpmmplugm1.2.4lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmmimemmimem~> 1.0dfalsej,bWLAhhddepsmhexpmmpoisonm2.0.0jabWLAhhdouter_checksummhexpmm phoenix_htmlm2.11.2m /T06!lEZ n'CbWLAhhd timestampmhexpmmplugm0.8.3hhbaahaa4a,bWLAhhddepsmhexpmmpoisonm1.0.1j5bWLAhhdretiredmhexpmmphoenixm1.5.2dnil`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.6.1m nIDb$ݲ޺`Ao>!5Ù~ `bWLAhhdinner_checksummhexpmmphoenixm 1.4.0-rc.3m >Y$z^-c㬭}GknډA)gbWLAhhddepsmhexpmmplugm1.6.1lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4dtruehmhexpmmmimemmimem~> 1.0dfalsejbWLAhhddepsmhexpmmplugm1.4.2lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsejbWLAhhddepsmhexpmm plug_cowboym2.5.2lhmhexpmmcowboymcowboym~> 2.7dfalsehmhexpmmcowboy_telemetrymcowboy_telemetrym~> 0.3dfalsehmhexpmmplugmplugm~> 1.7dfalsej[bWLAhhdinner_checksummhexpmmphoenixm1.5.2m {]lNvA I/jIg,bWLAhhddepsmhexpmmcowlibm1.0.0j\bWLAhhdouter_checksummhexpmmphoenixm0.13.1m Z. MeC9fԮZB/bWLAhhddepsmhexpmm telemetrym1.0.0jXbWLAhhdouter_checksummhexpmmplugm1.3.6m ?/ ߶? V.\od/uR-}[bWLAhhdinner_checksummhexpmmdecimalm1.0.1m Jćs1K܃ǒ+l ZbWLAhhdinner_checksummhexpmmpoisonm1.0.3m Ljxq߉Õԓ7O]bWLAhhdinner_checksummhexpmm telemetrym0.4.0m 9˄L)5sm U84EbWLAhhd timestampmhexpmmcowlibm2.1.0hhbaahaaa.bWLAhhddepsmhexpmmphoenixm0.16.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 0.14 or ~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejYbWLAhhdouter_checksummhexpmmranchm1.6.1m &O]4b uj[X}b+']X8#nDbWLAhhd timestampmhexpmmranchm1.8.0hhba a ha aa [bWLAhhdouter_checksummhexpmmphoenixm1.4.6m oI uqqYnb>9bWLAhhdretiredmhexpmm plug_cowboym2.5.0dnil5bWLAhhdretiredmhexpmmphoenixm1.0.6dnilbWLAhhddepsmhexpmmplugm1.4.5lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1dtruehmhexpmmmimemmimem~> 1.0dfalsejFbWLAhhd timestampmhexpmmdecimalm1.1.0hhbaaha aa:hbWLAhhddepsmhexpmm phoenix_htmlm2.9.2lhmhexpmmplugmplugm~> 1.0dfalsejbbWLAhhdouter_checksummhexpmmphoenix_pubsubm1.0.1m J/a?bsBqv˗ ӘXbWLAhhdouter_checksummhexpmmplugm1.8.2m TȻ,(#GU(1 VGbWLAhhd timestampmhexpmmphoenixm1.5.11hhba a ha aa 4bWLAhhdretiredmhexpmmpoisonm4.0.0dnilDbWLAhhd timestampmhexpmmranchm1.6.2hhba a ha aa HbWLAhhd timestampmhexpmm telemetrym0.4.2hhbaahaa4adbWLAhhdinner_checksummhexpmm phoenix_htmlm 2.4.0-devm 4 }hd2+6V5bWLAhhdretiredmhexpmmdecimalm0.2.4dnil+bWLAhhddepsmhexpmmranchm1.2.0jbWLAhhddepsmhexpmmplugm1.2.3lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmmimemmimem~> 1.0dfalsejZbWLAhhdouter_checksummhexpmmcowlibm2.2.0m ס ;X/-fy7Y@^wZ46bWLAhhdretiredmhexpmmphoenixm0.13.0dnilHbWLAhhd timestampmhexpmmplugm 1.5.0-rc.2hhbaahaa4abWLAhhddepsmhexpmmphoenixm1.0.5lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsej\bWLAhhdinner_checksummhexpmmphoenixm1.4.15m \90j3RβV)bX+Y(ZbWLAhhdouter_checksummhexpmmcowboym1.0.3m :SY]NR [=ȖJfuId5bWLAhhdretiredmhexpmmphoenixm1.2.5dnilEbWLAhhd timestampmhexpmmcowboym1.1.0hhbaahaaa.[bWLAhhdouter_checksummhexpmmdecimalm0.1.1m ZA57/P,#n^}[WBxYbWLAhhdouter_checksummhexpmmplugm0.11.3m O0% Ԣq\%V#4^IebbWLAhhdinner_checksummhexpmmphoenix_pubsubm1.0.2m Rx^ QQXR?44bWLAhhdretiredmhexpmmpoisonm1.2.0dnilCbWLAhhd timestampmhexpmmplugm1.9.0hhbaahaa4aabWLAhhdinner_checksummhexpmm phoenix_htmlm2.10.5m Ot"D s*VlN3|(O ZbWLAhhdinner_checksummhexpmmpoisonm3.1.0m cfo%٤o5V Y;2nHFedbWLAhhddepsmhexpmmplugm0.12.1lhmhexpmmcowboymcowboym~> 1.0dtruejZbWLAhhdinner_checksummhexpmmcowlibm1.1.0m , ؁^l^GZLbWLAhhd timestampmhexpmm phoenix_htmlm2.14.2hhba a ha aa bWLAhhddepsmhexpmmplugm1.2.5lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmmimemmimem~> 1.0dfalsejXbWLAhhdouter_checksummhexpmmplugm1.4.1m ʕ'PpL]MLH,kObWLAhhddepsmhexpmmphoenixm1.5.10lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13 or ~> 3.0dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejJbWLAhhd timestampmhexpmm plug_cowboym2.5.0hhba a ha aa KbWLAhhd timestampmhexpmm phoenix_htmlm2.5.0hhba a ha aa ~bWLAhhdversionsmhexpmm telemetrylm0.1.0m0.2.0m0.3.0m0.4.0m0.4.1m0.4.2m0.4.3m1.0.0j3bWLAhhdretiredmhexpmmjasonm1.1.0dnil3bWLAhhdretiredmhexpmmranchm1.4.0dnilZbWLAhhdouter_checksummhexpmmcowlibm2.0.0m (!gEFD3v5eLѰFbWLAhhd timestampmhexpmmphoenixm1.0.1hhba a ha aa [bWLAhhdinner_checksummhexpmmphoenixm1.4.3m Jd7,4rK֑Rv7]g}JbWLAhhd timestampmhexpmm plug_cowboym2.1.0hhba a ha aa XbWLAhhdinner_checksummhexpmmmimem1.3.0m ^EP O`J4Ǣ QQ_bWLAhhdouter_checksummhexpmm plug_cowboym2.3.0m YZp?Vhp!u̬ۏ\~YbWLAhhdouter_checksummhexpmmjasonm1.2.0m gGWyL:>N;|cNNJYu.bWLAhhddepsmhexpmmcowboym1.0.3lhmhexpmmcowlibmcowlibm~> 1.0.0dfalsehmhexpmmranchmranchm~> 1.0dfalsejYbWLAhhdouter_checksummhexpmmranchm1.7.0m YP:V[/hL0HCM9A,'DbWLAhhd timestampmhexpmmranchm1.2.0hhba a ha aa :bWLAhhdretiredmhexpmm phoenix_htmlm3.0.0dnil`bWLAhhdinner_checksummhexpmm phoenix_htmlm2.0.1m ߲8MςÝzffSpo&YbWLAhhdouter_checksummhexpmmranchm1.7.1m E'x}ٝabY4].ǯ5bWLAhhdretiredmhexpmmphoenixm1.4.6dnilFbWLAhhd timestampmhexpmmdecimalm1.3.1hhbaaha aa:YbWLAhhdinner_checksummhexpmmranchm2.1.0m "atDkmU@++3g;r[bWLAhhdouter_checksummhexpmmdecimalm1.6.0m $@@s`I*sGHM:bJiCbWLAhhd timestampmhexpmmplugm1.1.9hhbaahaa4abWLAhhddepsmhexpmmphoenixm0.2.0lhmhexpmmex_confmex_confm0.1.1dfalsehmhexpmmplugmplugm0.4.2dfalsejZbWLAhhdinner_checksummhexpmmcowboym2.6.1m ou|3{;17rxE[ڡTxY?bWLAhhddepsmhexpmmcowboym2.8.0lhmhexpmmcowlibmcowlibm~> 2.9.1dfalsehmhexpmmranchmranchm~> 1.7.1dfalsej3bWLAhhdretiredmhexpmmranchm1.8.0dnilYbWLAhhdouter_checksummhexpmmranchm1.4.0m S "]CG<(L _`bWLAhhdouter_checksummhexpmm phoenix_htmlm2.9.0m ne7WuG5FD))%cA:k .3ebWLAhhddepsmhexpmmplugm0.5.3lhmhexpmmcowboymcowboym~> 1.0.0dtruej9bWLAhhdretiredmhexpmm plug_cowboym2.0.2dnil4bWLAhhdretiredmhexpmmcowlibm1.0.0dnilbWLAhhddepsmhexpmmphoenixm0.2.3lhmhexpmmex_confmex_confm0.1.1dfalsehmhexpmminflexminflexm0.2.0dfalsehmhexpmmjazzmjazzm0.1.0dfalsehmhexpmmplugmplugm0.4.3dfalsejbWLAhhddepsmhexpmmphoenixm1.5.6lhmhexpmmjasonmjasonm~> 1.0dtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13dtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0dfalsehmhexpmmplugmplugm~> 1.10dfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2dtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej[bWLAhhdinner_checksummhexpmmdecimalm1.5.0m C:6C=P)e<7of[CיchHehCbWLAhhd timestampmhexpmmplugm1.4.3hhbaahaa4aZbWLAhhdinner_checksummhexpmmpoisonm2.2.0m GcwwoG}d(A&vW'SJcYbWLAhhdinner_checksummhexpmmplugm1.10.1m jm-XY;T ҇@ZbWLAhhdinner_checksummhexpmmcowboym2.6.2m -n3=-ҿ] 莲ASe>4MɍXbWLAhhdinner_checksummhexpmmmimem1.5.0m >^8m6?/z ;Z>[bWLAhhdouter_checksummhexpmmphoenixm1.4.4m DEL@3aho9O3i^t7mZ;y ~DbWLAhhd timestampmhexpmmranchm1.5.0hhba a ha aa ,bWLAhhddepsmhexpmmcowlibm1.0.2jYbWLAhhdinner_checksummhexpmmjasonm1.2.1m (%/F>Kˍ spD[bWLAhhdouter_checksummhexpmmphoenixm1.2.5m S2&īaipHdRα8ZbWLAhhdinner_checksummhexpmmcowlibm2.9.1m a $[P ;fW\xFbWLAhhd timestampmhexpmmdecimalm1.0.1hhbaaha aa:3bWLAhhdretiredmhexpmmranchm2.1.0dnilKbWLAhhd timestampmhexpmm phoenix_htmlm2.9.0hhba a ha aa bWLAhhddepsmhexpmmphoenixm1.0.0lhmhexpmmcowboymcowboym~> 1.0dtruehmhexpmmplugmplugm~> 1.0dfalsehmhexpmmpoisonmpoisonm~> 1.3dfalsejFbWLAhhd timestampmhexpmmdecimalm1.1.1hhbaaha aa:[bWLAhhdinner_checksummhexpmmdecimalm0.1.1m }VRqtgNpjY$XbWLAhhdouter_checksummhexpmmplugm1.0.2m A› Nm~Q@LÅDySXbWLAhhdouter_checksummhexpmmplugm0.8.4m "5 3T[ۼ~`2p2(5_ZbWLAhhdouter_checksummhexpmmcowlibm2.7.2m .6ЖH U;h~_>k LU@J/_bWLAhhdinner_checksummhexpmm plug_cowboym2.2.1m 2'2*G<..4 UP^hex-2.4.2/test/fixtures/registries/20210926.ets000066400000000000000000001201751517471540100207500ustar00rootroot00000000000000cXM bWLAhhdidZd nonode@nohostxk M]@hddecentralized_countersdfalsehdread_concurrencydfalsehdwrite_concurrencydfalsehd compresseddfalsehdmemoryb>hdownerXd nonode@nohosthdheirdnonehdnamedElixir.Hex.Devhdsizebhdnoded nonode@nohosthd named_tabledfalsehdtypedsethdkeyposahd protectiond protectedhd major_versionahd minor_versionahd extended_infoj-bWLAhhddepsmhexpmmdecimalm1.3.0j/bWLAhhddepsmhexpmm telemetrym0.4.0j5bWLAhhdretiredmhexpmmdecimalm0.2.3dnil_bWLAhhdinner_checksummhexpmm chromic_pdfm0.5.2m Hg[]EdבVXo] (f⋟a[bWLAhhdouter_checksummhexpmmpoolboym1.3.0m 25c*rutR<ϰP+aDbWLAhhd timestampmhexpmmjasonm1.2.2hhba ahaaa4]bWLAhhdouter_checksummhexpmm telemetrym0.4.0m |1p| 0-Ow?A[bWLAhhdinner_checksummhexpmmdecimalm0.2.0m [.ٙ)b9*O^GLzb}F-bWLAhhddepsmhexpmmdecimalm1.0.1j_bWLAhhdinner_checksummhexpmm chromic_pdfm0.6.2m m_#]dn!v}(ɀ^ D?-bWLAhhddepsmhexpmmdecimalm1.7.0j_bWLAhhdouter_checksummhexpmm nimble_poolm0.1.0m 4:b C *#p9 Ox\uZbWLAhhddepsmhexpmm chromic_pdfm0.4.0lhmhexpmmjasonmjasonm~> 1.1dfalsehmhexpmmpoolboympoolboym~> 1.5dfalsej`bWLAhhdinner_checksummhexpmmdecimalm 1.9.0-rc.0m cfja8{@<Lp}9bWLAhhdretiredmhexpmm chromic_pdfm1.1.1dnil9bWLAhhdretiredmhexpmm chromic_pdfm0.6.1dnil-bWLAhhddepsmhexpmmdecimalm1.1.0j[bWLAhhdouter_checksummhexpmmdecimalm0.1.1m ZA57/P,#n^}[WBxbWLAhhddepsmhexpmm chromic_pdfm0.7.0lhmhexpmmjasonmjasonm~> 1.1dfalsehmhexpmm nimble_poolm nimble_poolm~> 0.2.3dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejbWLAhhddepsmhexpmm chromic_pdfm0.5.2lhmhexpmmjasonmjasonm~> 1.1dfalsehmhexpmmpoolboympoolboym~> 1.5dfalsejxbWLAhhddepsmhexpmmtelemetry_pollerm0.1.0lhmhexpmm telemetrym telemetrym~> 0.2.0dfalsejbWLAhhddepsmhexpmm chromic_pdfm0.6.2lhmhexpmmjasonmjasonm~> 1.1dfalsehmhexpmm nimble_poolm nimble_poolm~> 0.2.3dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsejdbWLAhhdinner_checksummhexpmmtelemetry_pollerm0.4.1m P=j;8R戄 v: :udbWLAhhdouter_checksummhexpmmtelemetry_pollerm0.4.0m 7MRg_3h䱤9ڌ}B3gFbWLAhhd timestampmhexpmmdecimalm1.2.0hhba ahaaa4>bWLAhhdretiredmhexpmmtelemetry_pollerm0.5.0dnil[bWLAhhdinner_checksummhexpmmdecimalm0.2.3m r}$޿>4m\lZ$FbWLAhhd timestampmhexpmmdecimalm0.2.0hhba ahaaa49bWLAhhdretiredmhexpmm chromic_pdfm1.1.0dnil-bWLAhhddepsmhexpmmpoolboym1.4.0j9bWLAhhdretiredmhexpmm chromic_pdfm0.3.0dnil[bWLAhhdouter_checksummhexpmmdecimalm1.1.2m zmM8׸8P"H|h=3Q㦑7bWLAhhdretiredmhexpmm telemetrym0.4.3dnil[bWLAhhdouter_checksummhexpmmdecimalm1.9.0m 45h֒>u[] o}^bWLAhhdouter_checksummhexpmmjasonm 1.0.0-rc.1m Iл;_,n4%G)fbWLAhhddepsmhexpmmjasonm1.1.0lhmhexpmmdecimalmdecimalm~> 1.0dtruejFbWLAhhd timestampmhexpmmdecimalm1.4.0hhba ahaaa4[bWLAhhdouter_checksummhexpmmdecimalm1.1.1m ?6"/+ 1.0dtruejObWLAhhd timestampmhexpmmtelemetry_pollerm0.1.0hhbaaha a*a 9bWLAhhdretiredmhexpmm chromic_pdfm0.5.2dnil5bWLAhhdretiredmhexpmmdecimalm1.0.0dnil>bWLAhhdretiredmhexpmmtelemetry_pollerm1.0.0dnil[bWLAhhdinner_checksummhexpmmdecimalm1.0.0m 4") h_S)3rm"K^JbWLAhhd timestampmhexpmm chromic_pdfm0.5.0hhba aha a8a [bWLAhhdouter_checksummhexpmmpoolboym1.5.2m חT@գh%ѥaɕ( FbWLAhhd timestampmhexpmmpoolboym1.2.1hhbaaha a9a[bWLAhhdouter_checksummhexpmmdecimalm1.0.1m dk+5~ kƿF|xwt9bWLAhhdretiredmhexpmm chromic_pdfm0.7.0dnil_bWLAhhdinner_checksummhexpmm nimble_poolm0.1.0m վ' cNI'{9oxN_bWLAhhdouter_checksummhexpmm nimble_poolm0.2.4m 6~q7vNj̵{'m(%5g}QdbWLAhhdouter_checksummhexpmmtelemetry_pollerm0.2.0m m>\IIid!}^g[bWLAhhdinner_checksummhexpmmpoolboym1.5.0m ]q:k{L= W@QQGZ#=m[bWLAhhdouter_checksummhexpmmdecimalm2.0.0m 4fnUި}7b2j# Dw5bWLAhhdretiredmhexpmmdecimalm0.2.5dnilbWLAhhddepsmhexpmm chromic_pdfm0.1.0lhmhexpmmjasonmjasonm~> 1.1dfalsehmhexpmmpoolboympoolboym~> 1.5dfalsejObWLAhhd timestampmhexpmmtelemetry_pollerm0.3.0hhbaaha a*a [bWLAhhdouter_checksummhexpmmpoolboym1.2.0m w~:02[iYbWLAhhdouter_checksummhexpmmjasonm1.1.1m cEϬ2^4gry:42•%#LKbWLAhhd timestampmhexpmmdecimalm 1.9.0-rc.0hhba ahaaa4&bWLAhd last_updatehhba ahaaaYbWLAhhdinner_checksummhexpmmjasonm1.1.0m 4/thՆS\[ WRO[bWLAhhdouter_checksummhexpmmdecimalm1.8.0m RiNn`PsLfXO[}1xfc]bWLAhhdouter_checksummhexpmm telemetrym0.4.2m -j m{XRr.+#b {*lbWLAhhdversionsmhexpmm nimble_poollm0.1.0m0.2.0m0.2.1m0.2.2m0.2.3m0.2.4jbWLAhhddepsmhexpmm chromic_pdfm0.6.0lhmhexpmmjasonmjasonm~> 1.1dfalsehmhexpmm nimble_poolm nimble_poolm~> 0.2.3dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej-bWLAhhddepsmhexpmmdecimalm1.5.0j_bWLAhhdouter_checksummhexpmm chromic_pdfm0.7.0m VT5;TvGL˻;oVJbWLAhhd timestampmhexpmm nimble_poolm0.2.4hhbaahaaa!_bWLAhhdinner_checksummhexpmm nimble_poolm0.2.1m QF4yNjq, "V8-锑_kJbWLAhhdversionsmhexpmmpoolboyl m1.2.0m1.2.1m1.3.0m1.4.0m1.4.1m1.4.2m1.5.0m1.5.1m1.5.2j_bWLAhhdouter_checksummhexpmm chromic_pdfm0.7.1m Z2DUNKܼ3V4Aй7bWLAhhdretiredmhexpmm telemetrym0.3.0dnilYbWLAhhdouter_checksummhexpmmjasonm1.0.0m l@eLc# H;g͠y[bWLAhhdinner_checksummhexpmmpoolboym1.2.0m RjᙎJ^DX}#^T#N65bWLAhhdretiredmhexpmmdecimalm1.3.1dnilObWLAhhd timestampmhexpmmtelemetry_pollerm0.4.1hhbaaha a*a HbWLAhhd timestampmhexpmm telemetrym0.4.1hhbaaha aa[bWLAhhdouter_checksummhexpmmpoolboym1.4.1m 0QJ` ϥA?dbWLAhhdinner_checksummhexpmmtelemetry_pollerm0.2.0m ߏmRΚ] &*mu9|j R#7bWLAhhdretiredmhexpmm telemetrym0.4.2dnilYbWLAhhdinner_checksummhexpmmjasonm1.1.1m ̸@߰o/56MH#X^[bWLAhhdouter_checksummhexpmmdecimalm1.4.0m vqN/q5 :=8K-zEFq#5bWLAhhdretiredmhexpmmdecimalm1.9.0dnil[bWLAhhdouter_checksummhexpmmdecimalm1.0.0m T ! oC d0'${ՓBt9bWLAhhdretiredmhexpmm chromic_pdfm0.5.0dnil[bWLAhhdinner_checksummhexpmmdecimalm1.3.1m {N;|cNNJYu.5bWLAhhdretiredmhexpmmdecimalm0.2.4dnilYbWLAhhdouter_checksummhexpmmjasonm1.2.2m (򟞮aðvhя!y^bWLAhhdouter_checksummhexpmmjasonm 1.0.0-rc.2m wv3ԉ ĘDo$-bWLAhhddepsmhexpmmpoolboym1.5.2j-bWLAhhddepsmhexpmmdecimalm0.2.4jIbWLAhhd timestampmhexpmmjasonm 1.0.0-rc.1hhba ahaaa41bWLAhhddepsmhexpmm nimble_poolm0.2.3j[bWLAhhd registry_etagmhexpmmtelemetry_pollerm""5c9b38c868f4640a594548ebba5bc657"[bWLAhhdinner_checksummhexpmmdecimalm0.2.2m b^BJ;@>ҜLz:K [bWLAhhdouter_checksummhexpmmdecimalm1.1.0m  }9 ,SHmga8-k!HbWLAhhd timestampmhexpmm telemetrym0.1.0hhbaaha aaRbWLAhhd registry_etagmhexpmmpoolboym""624f36e367e5b64af565501d4ca9ce2c"FbWLAhhd timestampmhexpmmdecimalm1.8.0hhba ahaaa4EbWLAhhd timestampmhexpmmtelemetry_pollerhhbaaha a*a bWLAhhddepsmhexpmm chromic_pdfm0.7.1lhmhexpmmjasonmjasonm~> 1.1dfalsehmhexpmm nimble_poolm nimble_poolm~> 0.2.3dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej/bWLAhhddepsmhexpmm telemetrym0.3.0jFbWLAhhd timestampmhexpmmdecimalm0.2.4hhba ahaaa4bWLAhhddepsmhexpmm chromic_pdfm0.5.1lhmhexpmmjasonmjasonm~> 1.1dfalsehmhexpmmpoolboympoolboym~> 1.5dfalsej_bWLAhhdouter_checksummhexpmm chromic_pdfm1.0.0m P H @4gENciZ1À2] bVFbWLAhhd timestampmhexpmmdecimalm0.2.1hhba ahaaa4FbWLAhhd timestampmhexpmmpoolboym1.2.0hhbaaha a9a-bWLAhhddepsmhexpmmdecimalm0.2.3j[bWLAhhdouter_checksummhexpmmdecimalm0.2.0m &EQ{-D; Mʝ4$bWLAhhddepsmhexpmm chromic_pdfm0.7.2lhmhexpmmjasonmjasonm~> 1.1dfalsehmhexpmm nimble_poolm nimble_poolm~> 0.2.3dfalsehmhexpmm telemetrym telemetrym~> 0.4.2dfalsej9bWLAhhdretiredmhexpmm chromic_pdfm1.0.0dnil_bWLAhhdinner_checksummhexpmm nimble_poolm0.2.2m w  C<{sK&´1 ;clFbWLAhhd timestampmhexpmmdecimalm1.9.0hhba ahaaa4-bWLAhhddepsmhexpmmdecimalm1.3.1j_bWLAhhdouter_checksummhexpmm nimble_poolm0.2.1m Z[>b~Ƭ>D@,~- yl[bWLAhhdouter_checksummhexpmmdecimalm0.2.4m eBB:QN.cGJ9@5"-bWLAhhddepsmhexpmmdecimalm2.0.0j-bWLAhhddepsmhexpmmdecimalm0.2.5j_bWLAhhdinner_checksummhexpmm chromic_pdfm0.7.2m ꑍ {ibn 9 upOJbWLAhhd timestampmhexpmm chromic_pdfm0.5.1hhba aha a8a -bWLAhhddepsmhexpmmdecimalm1.9.0j[bWLAhhdouter_checksummhexpmmdecimalm1.5.0m  &XUMu_]r?3ɻ9W⡭.-bWLAhhddepsmhexpmmdecimalm1.1.1j1bWLAhhddepsmhexpmm nimble_poolm0.2.2j_bWLAhhdinner_checksummhexpmm chromic_pdfm0.3.0m ?Gx1[v?%%}/grp0`bWLAhhdouter_checksummhexpmmdecimalm 2.0.0-rc.0m %^,>8W$d. 0.4dfalsej5bWLAhhdretiredmhexpmmpoolboym1.4.2dnil3bWLAhhdretiredmhexpmmjasonm1.2.0dnilfbWLAhhddepsmhexpmmjasonm1.0.0lhmhexpmmdecimalmdecimalm~> 1.0dtruejFbWLAhhd timestampmhexpmmdecimalm2.0.0hhba ahaaa4PbWLAhhd registry_etagmhexpmmjasonm""c7ccb0b0d3701498a0a717862ef103b1"3bWLAhhdretiredmhexpmmjasonm1.2.1dnil[bWLAhhdinner_checksummhexpmmdecimalm1.7.0m 0ֵ,TfcsYPߞf S_~Ū[bWLAhhdouter_checksummhexpmmdecimalm0.2.3m tVX7aCVh`ZV1ւu>bWLAhhdretiredmhexpmmtelemetry_pollerm0.4.1dnil]bWLAhhdinner_checksummhexpmm telemetrym0.1.0m s 1 3|m*c8y+A<_bWLAhhdouter_checksummhexpmm chromic_pdfm0.5.1m Ul\&S*;cڰY#KF5 (kbWLAhhddepsmhexpmmjasonm 1.0.0-rc.3lhmhexpmmdecimalmdecimalm~> 1.0dtruejFbWLAhhd timestampmhexpmmdecimalm1.4.1hhba ahaaa4FbWLAhhd timestampmhexpmmdecimalm0.2.5hhba ahaaa4[bWLAhhdouter_checksummhexpmmdecimalm0.2.5m .-h̆ lok$+FXnYG-bWLAhhddepsmhexpmmdecimalm1.0.0j-bWLAhhddepsmhexpmmpoolboym1.4.1jFbWLAhhd timestampmhexpmmdecimalm1.3.0hhba ahaaa4]bWLAhhdinner_checksummhexpmm telemetrym0.4.3m d(c)<٦&:f;șW_bWLAhhdinner_checksummhexpmm chromic_pdfm1.0.0m TíT )@EGO>U+l~!C1bWLAhhddepsmhexpmm nimble_poolm0.2.0j[bWLAhhdouter_checksummhexpmmpoolboym1.5.0m P\"?GU?fD)N6~ s$ik~[bWLAhhdinner_checksummhexpmmdecimalm1.1.2m yie{-S{Q<Қ+HkQlBS VbWLAhhd registry_etagmhexpmm nimble_poolm""06c85751bbe7bda60efea8ca3d0ebf94"HbWLAhhd timestampmhexpmm telemetrym1.0.0hhbaaha aa5bWLAhhdretiredmhexpmmpoolboym1.4.0dnilFbWLAhhd timestampmhexpmmdecimalm1.8.1hhba ahaaa4_bWLAhhdouter_checksummhexpmm chromic_pdfm1.1.0m NUӘ|saGeR{e7;[bWLAhhdinner_checksummhexpmmdecimalm1.3.0m Afx`E|Rӣ՗M'}vbWLAhhddepsmhexpmmtelemetry_pollerm0.2.0lhmhexpmm telemetrym telemetrym~> 0.3dfalsej-bWLAhhddepsmhexpmmpoolboym1.5.1j5bWLAhhdretiredmhexpmmpoolboym1.5.0dnilbWLAhhddepsmhexpmm chromic_pdfm0.5.0lhmhexpmmjasonmjasonm~> 1.1dfalsehmhexpmmpoolboympoolboym~> 1.5dfalsejdbWLAhhdinner_checksummhexpmmtelemetry_pollerm1.0.0m ۑBNnso> %剠~S[bWLAhhdinner_checksummhexpmmpoolboym1.5.1m kF9С=i&W~YSZwϛl2bWLAhhddepsmhexpmmdecimalm 1.9.0-rc.0j@bWLAhhd timestampmhexpmm chromic_pdfhhba aha a8a HbWLAhhd timestampmhexpmm telemetrym0.4.3hhbaaha aa_bWLAhhdinner_checksummhexpmm nimble_poolm0.2.3m K߇ϋ@67֪+='[TKQ[bWLAhhdinner_checksummhexpmmdecimalm1.8.1m ?_4( 57@)Nޅ3SoEh٬<IbWLAhhd timestampmhexpmmjasonm 1.0.0-rc.3hhba ahaaa4>bWLAhhdretiredmhexpmmtelemetry_pollerm0.3.0dnil7bWLAhhdretiredmhexpmm telemetrym0.4.1dnilbWLAhhddepsmhexpmm chromic_pdfm0.3.0lhmhexpmmjasonmjasonm~> 1.1dfalsehmhexpmmpoolboympoolboym~> 1.5dfalsej[bWLAhhdouter_checksummhexpmmpoolboym1.2.1m ^M |P磃b+pSYy/%]b#iFbWLAhhd timestampmhexpmmdecimalm0.2.2hhba ahaaa4FbWLAhhd timestampmhexpmmdecimalm0.1.1hhba ahaaa4^bWLAhhdinner_checksummhexpmmjasonm 1.0.0-rc.1m BNnn|+dcXah=S`F%]bWLAhhdinner_checksummhexpmm telemetrym0.4.1m 'HHD$GjA|2w(ttR5bWLAhhdretiredmhexpmmdecimalm1.4.1dnil9bWLAhhdretiredmhexpmm chromic_pdfm0.5.1dnil_bWLAhhdouter_checksummhexpmm chromic_pdfm0.2.0m +±;p#z[&q(:oa9Ó X_bWLAhhdinner_checksummhexpmm chromic_pdfm1.1.1m j QFV07:Rz-yiZ_bWLAhhdinner_checksummhexpmm chromic_pdfm1.1.0m oGFs-+8AvLE@ xX(Vs]bWLAhhdinner_checksummhexpmm telemetrym0.2.0m [@ʣ޳-|OUomk\7L#fw!a1w/bWLAhhddepsmhexpmm telemetrym0.4.3j]bWLAhhdinner_checksummhexpmm telemetrym0.4.0m 9˄L)5sm U84]bWLAhhdouter_checksummhexpmm telemetrym1.0.0m s Y(NF$3U(~ɮvng8Pz2bWLAhhddepsmhexpmmdecimalm 2.0.0-rc.0j_bWLAhhdinner_checksummhexpmm chromic_pdfm0.7.1m [Lʘ^Qd&rB-F0BY-bWLAhhddepsmhexpmmpoolboym1.5.0jJbWLAhhd timestampmhexpmm chromic_pdfm0.6.2hhba aha a8a ObWLAhhd timestampmhexpmmtelemetry_pollerm0.5.1hhbaaha a*a 3bWLAhhdretiredmhexpmmjasonm1.2.2dnilYbWLAhhdinner_checksummhexpmmjasonm1.2.0m 4-$!-?]HCv 8yY8HdbWLAhhdinner_checksummhexpmmtelemetry_pollerm0.5.1m !6 V(5RpX>*FbWLAhhd timestampmhexpmmdecimalm1.5.0hhba ahaaa4bWLAhhdretiredmhexpmm nimble_poolm0.2.2tdmessagemstrategy: :lifo is not usefuldreasondRETIRED_INVALID^bWLAhhdinner_checksummhexpmmjasonm 1.0.0-rc.2m *]f?v7@.FV)[bWLAhhdinner_checksummhexpmmdecimalm1.9.0m 12q$,QK =M[bWLAhhdouter_checksummhexpmmdecimalm0.2.2m 䑳ƑfZz hӨuR?/ ,.7}.FbWLAhhd timestampmhexpmmpoolboym1.4.2hhbaaha a9a-bWLAhhddepsmhexpmmpoolboym1.2.1jbWLAhhdversionsmhexpmm chromic_pdflm0.1.0m0.2.0m0.3.0m0.3.1m0.4.0m0.5.0m0.5.1m0.5.2m0.6.0m0.6.1m0.6.2m0.7.0m0.7.1m0.7.2m1.0.0m1.1.0m1.1.1j_bWLAhhdouter_checksummhexpmm nimble_poolm0.2.0m ]Gp8z[bWLAhhdinner_checksummhexpmmdecimalm0.1.2m iڠV;j\åiTYjdbWLAhhdinner_checksummhexpmmtelemetry_pollerm0.4.0m dޥKw`@#]]L>V+T29bWLAhhdretiredmhexpmm nimble_poolm0.2.0dnilFbWLAhhd timestampmhexpmmdecimalm0.1.2hhba ahaaa4DbWLAhhd timestampmhexpmmjasonm1.2.1hhba ahaaa4JbWLAhhd timestampmhexpmm chromic_pdfm1.0.0hhba aha a8a 5bWLAhhdretiredmhexpmmdecimalm0.2.2dnil9bWLAhhdretiredmhexpmm chromic_pdfm0.7.1dnilYbWLAhhdinner_checksummhexpmmjasonm1.0.1m d6K<p3T]s.gFbWLAhhd timestampmhexpmmdecimalm1.1.2hhba ahaaa4[bWLAhhdinner_checksummhexpmmpoolboym1.2.1m }X͉05&lإ孈5bWLAhhdretiredmhexpmmdecimalm1.0.1dnil[bWLAhhdouter_checksummhexpmmdecimalm1.7.0m wvPZՊOW\_`e]vbWLAhhddepsmhexpmmtelemetry_pollerm0.4.1lhmhexpmm telemetrym telemetrym~> 0.4dfalsej_bWLAhhdinner_checksummhexpmm chromic_pdfm0.6.1m CMN-W2Qo,Ĥ78n~(bWLAhhdretiredmhexpmmtelemetry_pollerm0.2.0dnil1bWLAhhddepsmhexpmm nimble_poolm0.1.0jFbWLAhhd timestampmhexpmmpoolboym1.3.0hhbaaha a9a_bWLAhhdouter_checksummhexpmm chromic_pdfm1.1.1m +02jA&Lk@k|K`=JIy{5bWLAhhdretiredmhexpmmdecimalm1.2.0dnilJbWLAhhd timestampmhexpmm chromic_pdfm1.1.0hhba aha a8a 1bWLAhhddepsmhexpmm nimble_poolm0.2.1jJbWLAhhd timestampmhexpmm chromic_pdfm0.1.0hhba aha a8a /bWLAhhddepsmhexpmm telemetrym1.0.0j]bWLAhhdinner_checksummhexpmm telemetrym0.4.2m (ɒE^s"M;kb_#:sPXps/E_bWLAhhdouter_checksummhexpmm nimble_poolm0.2.3m g}49+krI_p mXQ=P>bWLAhhdretiredmhexpmmtelemetry_pollerm0.5.1dnilbWLAhhddepsmhexpmm chromic_pdfm1.1.0lhmhexpmmjasonmjasonm~> 1.1dfalsehmhexpmm nimble_poolm nimble_poolm~> 0.2.3dfalsehmhexpmm telemetrym telemetrym~> 0.4.2dfalsej_bWLAhhdouter_checksummhexpmm chromic_pdfm0.7.2m +O|*N:숽s{ϸYbWLAhhdinner_checksummhexpmmjasonm1.0.0m |#!Aw&Zqo7bWLAhhdretiredmhexpmm telemetrym1.0.0dnil9bWLAhhdretiredmhexpmm chromic_pdfm0.3.1dnil5bWLAhhdretiredmhexpmmdecimalm1.5.0dnil5bWLAhhdretiredmhexpmmdecimalm1.1.1dnil5bWLAhhdretiredmhexpmmpoolboym1.2.1dnil>bWLAhhdretiredmhexpmmtelemetry_pollerm0.4.0dnil3bWLAhhdretiredmhexpmmjasonm1.0.1dnil5bWLAhhdretiredmhexpmmdecimalm1.3.0dnildbWLAhhdinner_checksummhexpmmtelemetry_pollerm0.3.0m | ˝EyqZ~US+ɡ#g [bWLAhhdinner_checksummhexpmmpoolboym1.4.1m ~pDG|j;Ҋ*GYZGz_bWLAhhdinner_checksummhexpmm chromic_pdfm0.4.0m ~mpTwmj{4&i{ 4hFbWLAhhd timestampmhexpmmpoolboym1.4.1hhbaaha a9a_bWLAhhdouter_checksummhexpmm chromic_pdfm0.6.0m EWC?O c )5X-!/JVB`bWLAhhdouter_checksummhexpmmdecimalm 1.9.0-rc.0m fuEsW\^KE0|dX< GJbWLAhhd timestampmhexpmm chromic_pdfm0.7.2hhba aha a8a [bWLAhhdouter_checksummhexpmmdecimalm1.3.1m WVHGrn\οǫ*wDbWLAhhd timestampmhexpmmjasonm1.0.0hhba ahaaa45bWLAhhdretiredmhexpmmdecimalm0.2.0dnil[bWLAhhdinner_checksummhexpmmdecimalm1.2.0m F)`q(.W{Gkkh'}M dcRiK 5bWLAhhdretiredmhexpmmdecimalm0.1.2dnil5bWLAhhdretiredmhexpmmdecimalm0.1.1dnil5bWLAhhdretiredmhexpmmpoolboym1.4.1dnilJbWLAhhd timestampmhexpmm chromic_pdfm0.3.1hhba aha a8a [bWLAhhdinner_checksummhexpmmpoolboym1.4.2m apΛa?úD)֔7l+3-[bWLAhhdinner_checksummhexpmmdecimalm0.2.5m @[x:`&'?ԀC{Xk4NêJbWLAhhd timestampmhexpmm chromic_pdfm0.7.0hhba aha a8a [bWLAhhdinner_checksummhexpmmdecimalm0.2.1m U%qs]A27M_bWLAhhdinner_checksummhexpmm chromic_pdfm0.1.0m ?S8(Z,}?Zū5)yDbWLAhhd timestampmhexpmmjasonm1.1.2hhba ahaaa4[bWLAhhdouter_checksummhexpmmdecimalm1.2.0m t"HD2HDM_dшfbWLAhhddepsmhexpmmjasonm1.0.1lhmhexpmmdecimalmdecimalm~> 1.0dtruejvbWLAhhddepsmhexpmmtelemetry_pollerm0.5.0lhmhexpmm telemetrym telemetrym~> 0.4dfalsej5bWLAhhdretiredmhexpmmdecimalm1.1.2dnilJbWLAhhd timestampmhexpmm chromic_pdfm0.2.0hhba aha a8a fbWLAhhddepsmhexpmmjasonm1.2.1lhmhexpmmdecimalmdecimalm~> 1.0dtruej7bWLAhhdretiredmhexpmm telemetrym0.4.0dnil[bWLAhhdinner_checksummhexpmmdecimalm1.6.0m Mn]CpӔ42_Ӻp0Y [bWLAhhdouter_checksummhexpmmdecimalm1.3.0m B 0 1.0 or ~> 2.0dtruej5bWLAhhdretiredmhexpmmdecimalm1.7.0dnil5bWLAhhdretiredmhexpmmdecimalm1.8.0dnilHbWLAhhd timestampmhexpmm telemetrym0.2.0hhbaaha aaYbWLAhhdinner_checksummhexpmmjasonm1.1.2m =g#&LqTVM=!d-bWLAhhddepsmhexpmmdecimalm1.8.1j-bWLAhhddepsmhexpmmdecimalm0.2.0j-bWLAhhddepsmhexpmmdecimalm0.1.1jFbWLAhhd timestampmhexpmmpoolboym1.5.2hhbaaha a9a[bWLAhhdinner_checksummhexpmmdecimalm2.0.0m LlWCG/By+VB֗-bWLAhhddepsmhexpmmdecimalm1.8.0jFbWLAhhd timestampmhexpmmdecimalm1.6.0hhba ahaaa4_bWLAhhdinner_checksummhexpmm chromic_pdfm0.2.0m Z$8oKKq|jUR4x_bWLAhhdinner_checksummhexpmm chromic_pdfm0.3.1m wBQBH&71r̽[bWLAhhdinner_checksummhexpmmpoolboym1.4.0m BcP gHM&H`n)w|D_KbWLAhhd timestampmhexpmmdecimalm 2.0.0-rc.0hhba ahaaa4YbWLAhhdinner_checksummhexpmmjasonm1.2.2m CpѪΐi\Vx?R[bWLAhhdouter_checksummhexpmmpoolboym1.4.0m uzުc 1.0dtruejvbWLAhhddepsmhexpmmtelemetry_pollerm0.5.1lhmhexpmm telemetrym telemetrym~> 0.4dfalsejYbWLAhhdinner_checksummhexpmmjasonm1.2.1m (%/F>Kˍ spDdbWLAhhdouter_checksummhexpmmtelemetry_pollerm0.1.0m V 1.1dfalsehmhexpmmpoolboympoolboym~> 1.5dfalsejDbWLAhhdversionsmhexpmmdecimallm0.1.1m0.1.2m0.2.0m0.2.1m0.2.2m0.2.3m0.2.4m0.2.5m1.0.0m1.0.1m1.1.0m1.1.1m1.1.2m1.2.0m1.3.0m1.3.1m1.4.0m1.4.1m1.5.0m1.6.0m1.7.0m1.8.0m1.8.1m 1.9.0-rc.0m1.9.0m 2.0.0-rc.0m2.0.0jJbWLAhhd timestampmhexpmm chromic_pdfm0.7.1hhba aha a8a 8bWLAhhdretiredmhexpmmjasonm 1.0.0-rc.1dnilJbWLAhhd timestampmhexpmm chromic_pdfm0.4.0hhba aha a8a [bWLAhhdinner_checksummhexpmmdecimalm1.8.0m F. _ ţB~5HZes4.[bWLAhhdinner_checksummhexpmmdecimalm0.2.4m qt6Dh2A?k't*PgLICV6bWLAhhddepsmhexpmmtelemetry_pollerm0.4.0jfbWLAhhddepsmhexpmmjasonm1.1.2lhmhexpmmdecimalmdecimalm~> 1.0dtruejDbWLAhhd timestampmhexpmmjasonm1.1.1hhba ahaaa4FbWLAhhd timestampmhexpmmpoolboym1.5.0hhbaaha a9a_bWLAhhdouter_checksummhexpmm chromic_pdfm0.6.2m ov9/:E"^c,Xt 5bWLAhhdretiredmhexpmmdecimalm1.1.0dnil]bWLAhhdouter_checksummhexpmm telemetrym0.1.0m <1bhxAO\"BMPCD$8bWLAhhdretiredmhexpmmjasonm 1.0.0-rc.3dnildbWLAhhdouter_checksummhexpmmtelemetry_pollerm0.5.0m i[ wL,|Ćn4 &,>bWLAhhd timestampmhexpmm telemetryhhbaaha aafbWLAhhddepsmhexpmmjasonm1.1.1lhmhexpmmdecimalmdecimalm~> 1.0dtruej7bWLAhhdretiredmhexpmm telemetrym0.1.0dnil[bWLAhhdinner_checksummhexpmmdecimalm1.4.0m eqjSӦEf(Ze.J3bWLAhdversiona[bWLAhhdouter_checksummhexpmmdecimalm1.8.1m zVbWLAhhd registry_etagmhexpmm chromic_pdfm""0d9ce686ad99a9b52eff34be0e96ada6"[bWLAhhdouter_checksummhexpmmdecimalm1.4.1m #{RNUV]JrV&hL{ʀ_;ObWLAhhd timestampmhexpmmtelemetry_pollerm0.2.0hhbaaha a*a ]bWLAhhdinner_checksummhexpmm telemetrym0.3.0m <Gqc <"f" :*:jguJsJbWLAhhd timestampmhexpmm nimble_poolm0.2.3hhbaahaaa!bWLAhhddepsmhexpmm chromic_pdfm0.6.1lhmhexpmmjasonmjasonm~> 1.1dfalsehmhexpmm nimble_poolm nimble_poolm~> 0.2.3dfalsehmhexpmm telemetrym telemetrym~> 0.4dfalsej:bWLAhhdretiredmhexpmmdecimalm 1.9.0-rc.0dnildbWLAhhdouter_checksummhexpmmtelemetry_pollerm0.5.1m Lrnz ѳ3py|_ f?_bWLAhhdouter_checksummhexpmm chromic_pdfm0.6.1m 4z6,nAh'%TߠDh*JbWLAhhd timestampmhexpmm nimble_poolm0.2.2hhbaahaaa!_bWLAhhdouter_checksummhexpmm chromic_pdfm0.5.0m `>Ņ.yCzMU x2]bWLAhhdinner_checksummhexpmm telemetrym1.0.0m E:,$7T 0R3bWLAhhdretiredmhexpmmjasonm1.1.2dnil/bWLAhhddepsmhexpmm telemetrym0.1.0j9bWLAhhdretiredmhexpmm chromic_pdfm0.7.2dnilHbWLAhhd timestampmhexpmm telemetrym0.3.0hhbaaha aaJbWLAhhd timestampmhexpmm chromic_pdfm1.1.1hhba aha a8a 1.0dfalsej_bWLAhhdinner_checksummhexpmm nimble_poolm0.2.4m =}Y^ 2p0ͶJELJ`?wFbWLAhhd timestampmhexpmmpoolboym1.5.1hhbaaha a9a[bWLAhhdinner_checksummhexpmmpoolboym1.3.0m Aw rl,%VV)ٽ*|b~J-bWLAhhddepsmhexpmmdecimalm0.1.2j5bWLAhhdretiredmhexpmmpoolboym1.2.0dnil[bWLAhhdinner_checksummhexpmmpoolboym1.5.2m 9+zE@έyCv/]0P,kDbWLAhhd timestampmhexpmmjasonm1.1.0hhba ahaaa4ObWLAhhd timestampmhexpmmtelemetry_pollerm0.5.0hhbaaha a*a HbWLAhhd timestampmhexpmm telemetrym0.4.0hhbaaha aa^bWLAhhdinner_checksummhexpmmjasonm 1.0.0-rc.3m RHr `D>PŕQOˣfƔ~9bWLAhhdretiredmhexpmm chromic_pdfm0.4.0dnil_bWLAhhdouter_checksummhexpmm chromic_pdfm0.1.0m h'Nv!5R f!KJKdbWLAhhdinner_checksummhexpmmtelemetry_pollerm0.5.0m GpUӜQִ#lib]Tޣ٥ K~bWLAhhdversionsmhexpmm telemetrylm0.1.0m0.2.0m0.3.0m0.4.0m0.4.1m0.4.2m0.4.3m1.0.0j9bWLAhhdretiredmhexpmm nimble_poolm0.2.4dnil3bWLAhhdretiredmhexpmmjasonm1.1.1dnilkbWLAhhddepsmhexpmmjasonm 1.0.0-rc.2lhmhexpmmdecimalmdecimalm~> 1.0dtruej9bWLAhhdretiredmhexpmm chromic_pdfm0.2.0dnil5bWLAhhdretiredmhexpmmdecimalm1.6.0dnilFbWLAhhd timestampmhexpmmdecimalm1.3.1hhba ahaaa4YbWLAhhdouter_checksummhexpmmjasonm1.1.2m CX :.4]C$=:FbWLAhhd timestampmhexpmmdecimalm1.1.1hhba ahaaa4-bWLAhhddepsmhexpmmpoolboym1.4.2j]bWLAhhdouter_checksummhexpmm telemetrym0.4.1m G88.6]g`,V m)ތXHbWLAhhdversionsmhexpmmjasonl m 1.0.0-rc.1m 1.0.0-rc.2m 1.0.0-rc.3m1.0.0m1.0.1m1.1.0m1.1.1m1.1.2m1.2.0m1.2.1m1.2.2j-bWLAhhddepsmhexpmmdecimalm1.4.1jFbWLAhhd timestampmhexpmmdecimalm1.0.1hhba ahaaa4[bWLAhhdinner_checksummhexpmmdecimalm1.1.0m 33s/}z_ 0-g\ʁ*c>r>bWLAhhdretiredmhexpmmtelemetry_pollerm0.1.0dnil9bWLAhhdretiredmhexpmm nimble_poolm0.2.3dnil@bWLAhhd timestampmhexpmm nimble_poolhhbaahaaa!3bWLAhhdretiredmhexpmmjasonm1.0.0dnil9bWLAhhdretiredmhexpmm chromic_pdfm0.6.2dnilbWLAhhddepsmhexpmm chromic_pdfm0.3.1lhmhexpmmjasonmjasonm~> 1.1dfalsehmhexpmmpoolboympoolboym~> 1.5dfalsej`bWLAhhdinner_checksummhexpmmdecimalm 2.0.0-rc.0m ĸK%5 1.1dfalsehmhexpmm nimble_poolm nimble_poolm~> 0.2.3dfalsehmhexpmm telemetrym telemetrym~> 0.4.2dfalsej_bWLAhhdinner_checksummhexpmm nimble_poolm0.2.0m %kaD="ivxgm>o-bWLAhhddepsmhexpmmpoolboym1.2.0jRbWLAhhd registry_etagmhexpmmdecimalm""4657f6f92e3c01945c9c9b06b6fd850e"DbWLAhhd timestampmhexpmmjasonm1.2.0hhba ahaaa4bWLAhhddepsmhexpmm chromic_pdfm1.1.1lhmhexpmmjasonmjasonm~> 1.1dfalsehmhexpmm nimble_poolm nimble_poolm~> 0.2.3dfalsehmhexpmm telemetrym telemetrym~> 0.4 or ~> 1.0dfalsej-bWLAhhddepsmhexpmmdecimalm1.1.2j[bWLAhhdinner_checksummhexpmmdecimalm1.4.1m Ps""|* ږ[ p5bWLAhhdretiredmhexpmmpoolboym1.5.1dnil]bWLAhhdouter_checksummhexpmm telemetrym0.3.0m c}11b!1 Z6h!/rJbWLAhhd timestampmhexpmm nimble_poolm0.2.0hhbaahaaa!YbWLAhhdouter_checksummhexpmmjasonm1.2.1m YW`Z`A@Dqoihex-2.4.2/test/fixtures/sample/000077500000000000000000000000001517471540100164215ustar00rootroot00000000000000hex-2.4.2/test/fixtures/sample/mix.exs000066400000000000000000000002041517471540100177330ustar00rootroot00000000000000defmodule Sample.Fixture.MixProject do use Mix.Project def project do [app: :sample, version: "0.0.1", deps: []] end end hex-2.4.2/test/fixtures/test_priv.pem000066400000000000000000000032171517471540100176650ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA70iFalPXSDJX0ZqQNYw2yPyMWmpV4ssLwuGm4l3TjS50UgKY nyL2j7m0mmO7DhLNqRKn8IsIJoHeeuFhf5cvW162mp2Kn2e9LXRobafYIM3hxzB8 LZqeQjxPR6xsDkY5HgQsxtTkbNRq/8ODAjx6XsZgFRSkjgD+nWO0D4i67+8lWSyG BaXAvyYQVRimkvm400PYD4RT2dk9lSrBjhAvsZ+buCX/F8XuK2sOdhoFC7PXz4kO 2q41IF1LjVfKz6WdXH91jUCUG80TAzK35lRTGRes/NOIV/aYwt6fc3BjgzOg/X8u cFwuWi5Tn5lU+eFYHcv1Qyxxx9yi03pZ7hH4iwIDAQABAoIBAQCXtDTRuqIMpc5D l2jCakj5mBQr5qrZBL5KKeIyNiY/piodLIQysGn9qVE1aPFY9Nbq0Gck/CCIuWGi Km40BFgD58KBuEwA0DaCMQFcNEO0WJNCRB0WlKate6sP1+MSEbd4rlDtJmBEF1BX I09WBZ3XpJGaC+JgHYVPp7yq9JcAMjZZl4AebCE83queT7cFLQu/6njRnGoKpLqU bD5w+eoHYFLgh197cdOxA7Yay5iRDnxuY7WYxZjRbP2UWxH6+xe/d+Vns+aUWjlD pd+zg0IWk/2VrlQfAzpYgIJCvFBTzbF4Bn9uHKlE4vwsnf/PsPalLdPT22HjfHry EpoBxVWBAoGBAP55G3bYAChdukZuZv5dbM1SUhNnTgX+l70YjHnIgjYQB4JmZAjv jZgPJ/0DFawwn3mNfESba346d0SY6NbMS1z1Ga/GkU2qcgLlDiIA/AIa7TEPiM+K D2tWbMtjRqMpZP2JKYYNwgngwz9fwwz7fk3oNUl+srJY/kA/OEvb3nUhAoGBAPC4 FL7ZL9fvEZwOLevarxWkBnQBeybvfBNW22gvR4u7L5DH4S/L9zrGHnnLCdjL2tNE YMfBR8HCm6q/4uGXNCIyrx9/rKLr713ceCLYLbt11EysEcS8jpUkzwTNpIa3vdKg b+ImOKaQ1Ff9FcB13HBpSnZgQvOArCTA9FxwnswrAoGAC3NdpgJgN3DIQgUTw1xm 2fJbWR4r558mKs8g+xXnrhEYlWiQFcrZvz7nWL47ymzBsFGKhvfLZXmyBjcvOwMk XQF2b29q+Gi0CqdN9H/A10No6u09BcG7oePYr2o/MnZZYgM7CBo0YgdoIcDoYPws GctaIn1+AwJ4JmHx4Y/6OaECgYAF006dN8BSp3RzpFw0Ivytt2R4ljiWvqDwDuAM a2h91y/VfyDR+UuoXncKV4GLf5NyHWuCKglEhSMjRLWG5BPc6AEY8AwLfU4zpUw6 +6RPrxBOMrPTYQTKUzO3WAqtP/EWEc/jVy1zynl5D6ThHXkSN1oX90hngCuqUAkZ 25fcNQKBgQC5wjvxbglrsWZZpyTK7G+XxGiwZj8knzEmxi/qRAYnoHiwTXT0tJ7W jo01DJiNbYMe5fjRpR7SDmfQ4LXzSFptoNPqfPXDOSEfGFGR9JhfUMazjPFNznEY I8MsO38vGRi6FyrQGPcDW5/3o384cbLpd/wdQxMR4lCCRoeqx160Rw== -----END RSA PRIVATE KEY----- hex-2.4.2/test/fixtures/test_pub.pem000066400000000000000000000007031517471540100174700ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA70iFalPXSDJX0ZqQNYw2 yPyMWmpV4ssLwuGm4l3TjS50UgKYnyL2j7m0mmO7DhLNqRKn8IsIJoHeeuFhf5cv W162mp2Kn2e9LXRobafYIM3hxzB8LZqeQjxPR6xsDkY5HgQsxtTkbNRq/8ODAjx6 XsZgFRSkjgD+nWO0D4i67+8lWSyGBaXAvyYQVRimkvm400PYD4RT2dk9lSrBjhAv sZ+buCX/F8XuK2sOdhoFC7PXz4kO2q41IF1LjVfKz6WdXH91jUCUG80TAzK35lRT GRes/NOIV/aYwt6fc3BjgzOg/X8ucFwuWi5Tn5lU+eFYHcv1Qyxxx9yi03pZ7hH4 iwIDAQAB -----END PUBLIC KEY----- hex-2.4.2/test/fixtures/umbrella/000077500000000000000000000000001517471540100167435ustar00rootroot00000000000000hex-2.4.2/test/fixtures/umbrella/apps/000077500000000000000000000000001517471540100177065ustar00rootroot00000000000000hex-2.4.2/test/fixtures/umbrella/apps/my_app1/000077500000000000000000000000001517471540100212545ustar00rootroot00000000000000hex-2.4.2/test/fixtures/umbrella/apps/my_app1/mix.exs000066400000000000000000000005571517471540100226010ustar00rootroot00000000000000defmodule Umbrella.MyApp1.Fixture.MixProject do use Mix.Project def project do [ app: :my_app1, version: "0.1.0", build_path: "../../_build", config_path: "../../config/config.exs", deps_path: "../../deps", lockfile: "../../mix.lock", deps: deps() ] end defp deps do [{:postgrex, ">= 0.0.0"}] end end hex-2.4.2/test/fixtures/umbrella/apps/my_app2/000077500000000000000000000000001517471540100212555ustar00rootroot00000000000000hex-2.4.2/test/fixtures/umbrella/apps/my_app2/mix.exs000066400000000000000000000005651517471540100226010ustar00rootroot00000000000000defmodule Umbrella.MyApp2.Fixture.MixProject do use Mix.Project def project do [ app: :my_app2, version: "0.1.0", build_path: "../../_build", config_path: "../../config/config.exs", deps_path: "../../deps", lockfile: "../../mix.lock", deps: deps() ] end defp deps do [{:my_app1, in_umbrella: true}] end end hex-2.4.2/test/fixtures/umbrella/apps/my_app3/000077500000000000000000000000001517471540100212565ustar00rootroot00000000000000hex-2.4.2/test/fixtures/umbrella/apps/my_app3/mix.exs000066400000000000000000000005301517471540100225720ustar00rootroot00000000000000defmodule Umbrella.MyApp3.Fixture.MixProject do use Mix.Project def project do [ app: :my_app3, version: "0.1.0", build_path: "../../_build", config_path: "../../config/config.exs", deps_path: "../../deps", lockfile: "../../mix.lock", deps: deps() ] end defp deps do [] end end hex-2.4.2/test/fixtures/umbrella/mix.exs000066400000000000000000000006041517471540100202610ustar00rootroot00000000000000defmodule Umbrella.Fixture.MixProject do use Mix.Project def project do [ apps_path: "apps", version: "0.1.0", deps: deps() ] end # Dependencies listed here are available only for this # project and cannot be accessed from applications inside # the apps folder. # # Run "mix help deps" for examples and options. defp deps do [] end end hex-2.4.2/test/fixtures/umbrella_override/000077500000000000000000000000001517471540100206425ustar00rootroot00000000000000hex-2.4.2/test/fixtures/umbrella_override/apps/000077500000000000000000000000001517471540100216055ustar00rootroot00000000000000hex-2.4.2/test/fixtures/umbrella_override/apps/my_app1/000077500000000000000000000000001517471540100231535ustar00rootroot00000000000000hex-2.4.2/test/fixtures/umbrella_override/apps/my_app1/mix.exs000066400000000000000000000006421517471540100244730ustar00rootroot00000000000000defmodule UmbrellaOverride.MyApp1.Fixture.MixProject do use Mix.Project def project do [ app: :my_app1, version: "0.1.0", build_path: "../../_build", config_path: "../../config/config.exs", deps_path: "../../deps", lockfile: "../../mix.lock", deps: deps() ] end defp deps do [{:ecto_override, path: HexTest.Case.fixture_path("ecto_override")}] end end hex-2.4.2/test/fixtures/umbrella_override/mix.exs000066400000000000000000000006141517471540100221610ustar00rootroot00000000000000defmodule UmbrellaOverride.Fixture.MixProject do use Mix.Project def project do [ apps_path: "apps", version: "0.1.0", deps: deps() ] end # Dependencies listed here are available only for this # project and cannot be accessed from applications inside # the apps folder. # # Run "mix help deps" for examples and options. defp deps do [] end end hex-2.4.2/test/hex/000077500000000000000000000000001517471540100140535ustar00rootroot00000000000000hex-2.4.2/test/hex/api/000077500000000000000000000000001517471540100146245ustar00rootroot00000000000000hex-2.4.2/test/hex/api/oauth_test.exs000066400000000000000000000167001517471540100175300ustar00rootroot00000000000000defmodule Hex.API.OAuthTest do use HexTest.IntegrationCase, async: true # Using real test server at localhost:4043 with OAuth client configured describe "device_authorization/1" do test "returns device authorization data" do assert {:ok, {200, _headers, response}} = Hex.API.OAuth.device_authorization("api repositories") # Verify the response has the expected structure from the real server assert is_binary(response["device_code"]) assert is_binary(response["user_code"]) assert is_binary(response["verification_uri"]) assert is_integer(response["expires_in"]) assert is_integer(response["interval"]) end test "defaults to api repositories scope" do assert {:ok, {200, _headers, response}} = Hex.API.OAuth.device_authorization("api") # Should return valid device authorization data assert is_binary(response["device_code"]) assert is_binary(response["user_code"]) end test "handles invalid scope" do # The real server should handle invalid scopes - may accept or reject assert {:ok, {status, _headers, _response}} = Hex.API.OAuth.device_authorization("invalid_scope") # Server may return 200 (accepted), 400 (invalid scope), or 401 (invalid client) assert status in [200, 400, 401] end test "sends name parameter when provided" do name = "TestMachine" assert {:ok, {200, _headers, response}} = Hex.API.OAuth.device_authorization("api repositories", name) # Verify the response has the expected structure assert is_binary(response["device_code"]) assert is_binary(response["user_code"]) end test "works without name parameter" do assert {:ok, {200, _headers, response}} = Hex.API.OAuth.device_authorization("api repositories", nil) # Should still return valid device authorization data assert is_binary(response["device_code"]) assert is_binary(response["user_code"]) end end describe "poll_device_token/1" do test "returns authorization_pending for valid device code" do # First get a valid device code {:ok, {200, _headers, device_response}} = Hex.API.OAuth.device_authorization("api") device_code = device_response["device_code"] # Polling should return authorization_pending since user hasn't authorized assert {:ok, {400, _headers, %{"error" => "authorization_pending"}}} = Hex.API.OAuth.poll_device_token(device_code) end test "returns invalid_grant for invalid device code" do assert {:ok, {400, _headers, %{"error" => "invalid_grant"}}} = Hex.API.OAuth.poll_device_token("invalid_device_code") end test "handles malformed device code" do assert {:ok, {400, _headers, %{"error" => error}}} = Hex.API.OAuth.poll_device_token("") assert error in ["invalid_grant", "invalid_request"] end end describe "refresh_token/1" do test "handles invalid refresh token" do # Test with a completely invalid refresh token assert {:ok, {status, _headers, %{"error" => error}}} = Hex.API.OAuth.refresh_token("invalid_refresh_token") assert status in [400, 401] assert error in ["invalid_token", "invalid_grant"] end test "handles malformed refresh token" do # Test with malformed refresh token assert {:ok, {status, _headers, %{"error" => error}}} = Hex.API.OAuth.refresh_token("malformed_token") assert status in [400, 401] assert error in ["invalid_token", "invalid_grant"] end test "handles empty refresh token" do assert {:ok, {400, _headers, %{"error" => error}}} = Hex.API.OAuth.refresh_token("") assert error in ["invalid_grant", "invalid_request"] end end describe "revoke_token/1" do test "returns 200 for token revocation" do # OAuth revoke endpoint returns 200 even for invalid tokens (per RFC 7009) assert {:ok, {200, _headers, _body}} = Hex.API.OAuth.revoke_token("any_token") end test "handles empty token" do assert {:ok, {200, _headers, _body}} = Hex.API.OAuth.revoke_token("") end end describe "exchange_api_key/3" do test "exchanges valid API key for OAuth access token" do auth = HexTest.Hexpm.new_user("apikey_user", "apikey@example.com", "password", "api_key") api_key = auth[:key] assert {:ok, {200, _headers, response}} = Hex.API.OAuth.exchange_api_key(api_key, "api") assert is_binary(response["access_token"]) assert response["token_type"] == "bearer" assert is_integer(response["expires_in"]) assert response["expires_in"] > 0 assert response["scope"] == "api" refute Map.has_key?(response, "refresh_token") end test "exchanges API key with multiple scopes" do {:ok, {201, _, _}} = Hex.API.User.new("apikey_multi", "apikey_multi@example.com", "password") permissions = [%{"domain" => "api"}, %{"domain" => "repositories"}] {:ok, {201, _, %{"secret" => api_key}}} = Hex.API.Key.new("api_key_multi", permissions, user: "apikey_multi", pass: "password") assert {:ok, {200, _headers, response}} = Hex.API.OAuth.exchange_api_key(api_key, "api repositories") assert is_binary(response["access_token"]) assert response["token_type"] == "bearer" assert response["scope"] == "api repository:hexpm" end test "accepts scopes as list" do {:ok, {201, _, _}} = Hex.API.User.new("apikey_list", "apikey_list@example.com", "password") permissions = [%{"domain" => "api"}, %{"domain" => "repositories"}] {:ok, {201, _, %{"secret" => api_key}}} = Hex.API.Key.new("api_key_list", permissions, user: "apikey_list", pass: "password") assert {:ok, {200, _headers, response}} = Hex.API.OAuth.exchange_api_key(api_key, ["api", "repositories"]) assert is_binary(response["access_token"]) assert response["scope"] == "api repository:hexpm" end test "sends name parameter when provided" do auth = HexTest.Hexpm.new_user( "apikey_named", "apikey_named@example.com", "password", "api_key_named" ) api_key = auth[:key] assert {:ok, {200, _headers, response}} = Hex.API.OAuth.exchange_api_key(api_key, "api", "TestMachine") assert is_binary(response["access_token"]) end test "works without name parameter" do auth = HexTest.Hexpm.new_user( "apikey_noname", "apikey_noname@example.com", "password", "api_key_noname" ) api_key = auth[:key] assert {:ok, {200, _headers, response}} = Hex.API.OAuth.exchange_api_key(api_key, "api", nil) assert is_binary(response["access_token"]) end test "returns error for invalid API key" do assert {:ok, {401, _headers, response}} = Hex.API.OAuth.exchange_api_key("invalid_api_key", "api") assert is_map(response) assert Map.has_key?(response, "message") or Map.has_key?(response, "error") end test "returns error for empty API key" do assert {:ok, {400, _headers, _response}} = Hex.API.OAuth.exchange_api_key("", "api") end test "handles malformed API key" do assert {:ok, {401, _headers, _response}} = Hex.API.OAuth.exchange_api_key("malformed-key", "api") end end end hex-2.4.2/test/hex/api_test.exs000066400000000000000000000126361517471540100164140ustar00rootroot00000000000000defmodule Hex.APITest do use HexTest.IntegrationCase, async: true test "user" do assert {:ok, {201, _, _}} = Hex.API.User.new("test_user", "test_user@mail.com", "hunter42") assert {:ok, {200, _, %{"username" => "test_user"}}} = Hex.API.User.get("test_user") assert {:ok, {404, _, _}} = Hex.API.User.get("unknown_user") end defp meta(name, version, requirements) do %{ name: name, app: name, version: version, build_tools: ["mix"], requirements: requirements, licenses: ["MIT"], description: "description", files: ["mix.exs"] } end test "release" do auth = Hexpm.new_user("release_user", "release_user@mail.com", "hunter42", "key") %{tarball: tarball} = Hex.Tar.create!(meta(:pear, "0.0.1", []), ["mix.exs"], :memory) assert {:ok, {404, _, _}} = Hex.API.Release.get("hexpm", "pear", "0.0.1") assert {:ok, {201, _, _}} = Hex.API.Release.publish("hexpm", tarball, auth) assert {:ok, {200, _, body}} = Hex.API.Release.get("hexpm", "pear", "0.0.1") assert body["requirements"] == %{} reqs = [%{name: :pear, app: :pear, requirement: "~> 0.0.1", optional: false}] %{tarball: tarball} = Hex.Tar.create!(meta(:grape, "0.0.2", reqs), ["mix.exs"], :memory) assert {:ok, {201, _, _}} = Hex.API.Release.publish("hexpm", tarball, auth) assert {:ok, {200, _, body}} = Hex.API.Release.get("hexpm", "grape", "0.0.2") assert body["requirements"] == %{ "pear" => %{"app" => "pear", "requirement" => "~> 0.0.1", "optional" => false} } assert {:ok, {status, _, _}} = Hex.API.Release.delete("hexpm", "grape", "0.0.2", auth) assert status in 200..299 assert {:ok, {404, _, _}} = Hex.API.Release.get("hexpm", "grape", "0.0.2") end test "docs" do auth = Hexpm.new_user("docs_user", "docs_user@mail.com", "hunter42", "key") %{tarball: tarball} = Hex.Tar.create!(meta(:tangerine, "0.0.1", []), ["mix.exs"], :memory) assert {:ok, {201, _, _}} = Hex.API.Release.publish("hexpm", tarball, auth) tarball = Path.join(tmp_path(), "docs.tar.gz") :ok = :mix_hex_erl_tar.create(tarball, [{~c"index.html", "heya"}], [:compressed]) tarball = File.read!(tarball) assert {:ok, {201, _, _}} = Hex.API.ReleaseDocs.publish("hexpm", "tangerine", "0.0.1", tarball, auth) assert {:ok, {200, _, %{"has_docs" => true}}} = Hex.API.Release.get("hexpm", "tangerine", "0.0.1") assert {:ok, {status, _, _}} = Hex.API.ReleaseDocs.delete("hexpm", "tangerine", "0.0.1", auth) assert status in 200..299 assert {:ok, {200, _, %{"has_docs" => false}}} = Hex.API.Release.get("hexpm", "tangerine", "0.0.1") end test "keys" do permissions = [%{"domain" => "api"}] auth = [user: "user", pass: "hunter42"] assert {:ok, {201, _, %{"secret" => key_a}}} = Hex.API.Key.new("key_a", permissions, auth) assert {:ok, {201, _, %{"secret" => key_b}}} = Hex.API.Key.new("key_b", permissions, auth) assert byte_size(key_a) == 32 assert byte_size(key_b) == 32 auth = [key: key_a] Hexpm.new_package("hexpm", "melon", "0.0.1", %{}, %{}, auth) assert {:ok, {200, _, body}} = Hex.API.Key.get(auth) assert Enum.find(body, &(&1["name"] == "key_a")) assert {:ok, {200, _, _}} = Hex.API.Key.delete("key_b", auth) assert {:ok, {200, _, body}} = Hex.API.Key.delete("key_a", auth) assert body["name"] == "key_a" assert {:ok, {401, _, _}} = Hex.API.Key.get(auth) # Delete all keys auth = [user: "user", pass: "hunter42"] assert {:ok, {201, _, %{"secret" => key_c}}} = Hex.API.Key.new("key_c", permissions, auth) assert {:ok, {201, _, %{"secret" => key_d}}} = Hex.API.Key.new("key_d", permissions, auth) assert byte_size(key_c) == 32 assert byte_size(key_d) == 32 auth_c = [key: key_c] auth_d = [key: key_d] assert {:ok, {200, _, body}} = Hex.API.Key.get(auth_c) assert Enum.find(body, &(&1["name"] == "key_c")) assert {:ok, {200, _, body}} = Hex.API.Key.get(auth_d) assert Enum.find(body, &(&1["name"] == "key_d")) assert {:ok, {200, _, body}} = Hex.API.Key.delete_all(auth_c) assert body["name"] == "key_c" assert {:ok, {401, _, _}} = Hex.API.Key.get(auth_c) assert {:ok, {401, _, _}} = Hex.API.Key.get(auth_d) end test "owners" do auth = Hexpm.new_user("owners_user", "owners_user@mail.com", "hunter42", "key") Hexpm.new_package("hexpm", "orange", "0.0.1", %{}, %{}, auth) Hex.API.User.new("orange_user", "orange_user@mail.com", "hunter42") assert {:ok, {200, _, [%{"username" => "owners_user"}]}} = Hex.API.Package.Owner.get("hexpm", "orange", auth) assert {:ok, {status, _, _}} = Hex.API.Package.Owner.add( "hexpm", "orange", "orange_user@mail.com", "full", false, auth ) assert status in 200..299 assert {:ok, {200, _, owners}} = Hex.API.Package.Owner.get("hexpm", "orange", auth) assert length(owners) == 2 assert Enum.any?(owners, &match?(%{"username" => "owners_user"}, &1)) assert Enum.any?(owners, &match?(%{"username" => "orange_user"}, &1)) assert {:ok, {status, _, _}} = Hex.API.Package.Owner.delete("hexpm", "orange", "orange_user@mail.com", auth) assert status in 200..299 assert {:ok, {200, _, [%{"username" => "owners_user"}]}} = Hex.API.Package.Owner.get("hexpm", "orange", auth) end end hex-2.4.2/test/hex/config_test.exs000066400000000000000000000025331517471540100171030ustar00rootroot00000000000000defmodule Hex.ConfigTest do use ExUnit.Case alias Hex.Config test "find_config_home/1 when no env var flags are set" do System.delete_env("HEX_HOME") {:ok, dir} = Config.find_config_home(:user_data) assert dir =~ ".hex" end test "find_config_home/1 when HEX_HOME is set" do System.put_env("HEX_HOME", "/sys/tmp") assert Config.find_config_home(:user_cache) == {{:env, "HEX_HOME"}, "/sys/tmp"} after System.delete_env("HEX_HOME") end test "find_config_home/1 when MIX_XDG is set and HEX_HOME is not" do System.delete_env("HEX_HOME") System.put_env("MIX_XDG", "true") System.put_env("XDG_CONFIG_HOME", "/xdg_config_home") System.put_env("XDG_CACHE_HOME", "/xdg_cache_home") assert {{:env, "MIX_XDG"}, "/xdg_config_home/hex"} = Config.find_config_home(:user_config) assert {{:env, "MIX_XDG"}, "/xdg_cache_home/hex"} = Config.find_config_home(:user_cache) after System.delete_env("MIX_XDG") System.delete_env("XDG_CONFIG_HOME") System.delete_env("XDG_CACHE_HOME") end test "find_config_home/1 when MIX_XDG is set and HEX_HOME is set" do System.put_env("MIX_XDG", "true") System.put_env("HEX_HOME", "/sys/tmp") assert Config.find_config_home(:user_cache) == {{:env, "HEX_HOME"}, "/sys/tmp"} after System.delete_env("HEX_HOME") System.delete_env("MIX_XDG") end end hex-2.4.2/test/hex/http/000077500000000000000000000000001517471540100150325ustar00rootroot00000000000000hex-2.4.2/test/hex/http/verify_hostname_test.exs000066400000000000000000000032141517471540100220140ustar00rootroot00000000000000defmodule Hex.HTTP.VerifyHostnameTest do use ExUnit.Case, async: true alias Hex.HTTP.VerifyHostname defmacrop assert_match(i, r, v) do quote do assert unquote(v) == VerifyHostname.validate_and_parse_wildcard_identifier(unquote(i), unquote(r)) assert VerifyHostname.try_match_hostname(unquote(i), unquote(r)) end end defmacrop refute_match(i, r) do quote do refute VerifyHostname.try_match_hostname(unquote(i), unquote(r)) end end test "success" do assert_match(~c"www.example.com", ~c"WWW.eXamPle.CoM", false) assert_match(~c"www.example.com.", ~c"www.example.com", false) assert_match(~c"www.example.com", ~c"www.example.com.", false) assert_match(~c"*.example.com", ~c"www.example.com", {[], ~c".example.com", true}) assert_match(~c"b*z.example.com", ~c"buzz.example.com", {~c"b", ~c"z.example.com", false}) assert_match(~c"*baz.example.com", ~c"foobaz.example.com", {[], ~c"baz.example.com", false}) assert_match(~c"baz*.example.com", ~c"baz1.example.com", {~c"baz", ~c".example.com", false}) end test "failure" do refute_match(~c"*.com", ~c"eXamPle.CoM") refute_match(~c".com.", ~c"example.com.") refute_match(~c"*.www.example.com", ~c"www.example.com.") refute_match(~c"foo.*.example.com", ~c"foo.bar.example.com.") refute_match(~c"xn--*.example.com", ~c"xn-foobar.example.com") refute_match(~c"*fooxn--bar.example.com", ~c"bazfooxn--bar.example.com") refute_match(~c"*.akamaized.net", ~c"tv.eurosport.com") refute_match(~c"a*c.example.com", ~c"abcd.example.com") refute_match(~c"*baz.example.com", ~c"foobuzz.example.com") end end hex-2.4.2/test/hex/http_test.exs000066400000000000000000000134201517471540100166120ustar00rootroot00000000000000defmodule Hex.HTTPTest do use HexTest.Case setup do on_exit(fn -> Enum.map([:http_proxy, :https_proxy], &Hex.State.put(&1, nil)) Enum.map([:proxy, :https_proxy], fn opt -> :httpc.set_options([{opt, {{~c"localhost", 80}, [~c"localhost"]}}], :hex) end) System.delete_env("NETRC") end) bypass = Bypass.open() {:ok, bypass: bypass} end test "proxy_config returns no credentials when no proxy supplied" do assert Hex.HTTP.proxy_config("http://hex.pm") == [] end test "proxy_config returns http_proxy credentials when supplied" do Hex.State.put(:http_proxy, "http://hex:test@example.com") assert Hex.HTTP.proxy_config("http://hex.pm") == [proxy_auth: {~c"hex", ~c"test"}] end test "proxy_config returns http_proxy credentials when only username supplied" do Hex.State.put(:http_proxy, "http://nopass@example.com") assert Hex.HTTP.proxy_config("http://hex.pm") == [proxy_auth: {~c"nopass", ~c""}] end test "proxy_config returns credentials when the protocol is https" do Hex.State.put(:https_proxy, "https://test:hex@example.com") assert Hex.HTTP.proxy_config("https://hex.pm") == [proxy_auth: {~c"test", ~c"hex"}] end test "proxy_config returns empty list when no credentials supplied" do Hex.State.put(:http_proxy, "http://example.com") assert Hex.HTTP.proxy_config("http://hex.pm") == [] end test "x-hex-message" do Hex.HTTP.handle_hex_message(~c"\"oops, you done goofed\"") refute_received {:mix_shell, _, _} Hex.HTTP.handle_hex_message(~c" \"oops, you done goofed\" ; level = warn") assert_received {:mix_shell, :info, ["API warning: oops, you done goofed"]} Hex.HTTP.handle_hex_message(~c"\"oops, you done goofed\";level=fatal ") assert_received {:mix_shell, :error, ["API error: oops, you done goofed"]} end test "request adds no authorization header if none is given and no netrc is found", %{ bypass: bypass } do in_tmp(fn -> Bypass.expect(bypass, fn conn -> assert Plug.Conn.get_req_header(conn, "authorization") == [] Plug.Conn.resp(conn, 200, "") end) Hex.HTTP.request(:get, "http://localhost:#{bypass.port}", %{}, nil) end) end test "request adds authorization header based on netrc if none is given", %{bypass: bypass} do in_tmp(fn -> File.write!(".netrc", """ machine localhost login john password doe """) System.put_env("NETRC", Path.join(File.cwd!(), ".netrc")) Bypass.expect(bypass, fn conn -> assert Plug.Conn.get_req_header(conn, "authorization") == [ "Basic #{:base64.encode("john:doe")}" ] Plug.Conn.resp(conn, 200, "") end) Hex.HTTP.request(:get, "http://localhost:#{bypass.port}", %{}, nil) end) end test "request adds no authorization header based on netrc if authorization is given", %{ bypass: bypass } do in_tmp(fn -> File.write!(".netrc", """ machine localhost login john password doe """) System.put_env("NETRC", Path.join(File.cwd!(), ".netrc")) Bypass.expect(bypass, fn conn -> assert Plug.Conn.get_req_header(conn, "authorization") == ["myAuthHeader"] Plug.Conn.resp(conn, 200, "") end) Hex.HTTP.request( :get, "http://localhost:#{bypass.port}", %{"authorization" => "myAuthHeader"}, nil ) end) end test "request with Expect 100-continue receives body after 100 response", %{bypass: bypass} do # Test that httpc handles 100-continue flow correctly body_content = "test request body" Bypass.expect(bypass, fn conn -> # Verify the Expect header is present assert ["100-continue"] = Plug.Conn.get_req_header(conn, "expect") # Send 100 Continue informational response conn = Plug.Conn.inform(conn, 100, []) {:ok, body, conn} = Plug.Conn.read_body(conn) assert body == body_content Plug.Conn.resp(conn, 201, "success") end) {:ok, {status, _headers, response_body}} = Hex.HTTP.request( :post, "http://localhost:#{bypass.port}", %{"expect" => "100-continue"}, {"text/plain", body_content} ) assert status == 201 assert response_body == "success" end test "request with Expect 100-continue stops sending body on error response", %{ bypass: bypass } do # Test that when server responds with error before 100, body is not sent # Note: This is handled by httpc automatically - if server responds with # error status instead of 100 Continue, httpc won't send the body Bypass.expect(bypass, fn conn -> # Verify the Expect header is present assert ["100-continue"] = Plug.Conn.get_req_header(conn, "expect") # Immediately respond with 401 Unauthorized without reading body # httpc should NOT send the body when it receives this error Plug.Conn.resp(conn, 401, "unauthorized") end) {:ok, {status, _headers, response_body}} = Hex.HTTP.request( :post, "http://localhost:#{bypass.port}", %{"expect" => "100-continue"}, {"text/plain", "this body should not be sent"} ) assert status == 401 assert response_body == "unauthorized" end test "request_to_file downloads to file", %{bypass: bypass} do in_tmp(fn -> Bypass.expect(bypass, fn conn -> Plug.Conn.resp(conn, 200, "file content") end) filename = Path.join(File.cwd!(), "downloaded.txt") {:ok, {status, _headers}} = Hex.HTTP.request_to_file( :get, "http://localhost:#{bypass.port}/file", %{}, nil, filename, %{} ) assert status == 200 assert File.read!(filename) == "file content" end) end end hex-2.4.2/test/hex/mix_task_test.exs000066400000000000000000000721351517471540100174620ustar00rootroot00000000000000defmodule Hex.MixTaskTest do use HexTest.IntegrationCase defmodule Simple do def project do [ app: :simple, version: "0.1.0", consolidate_protocols: false, deps: [ {:ecto, "0.2.0"} ] ] end end defmodule SimpleOld do def project do [ app: :simple, version: "0.1.0", consolidate_protocols: false, deps: [ {:ecto, "~> 0.2.1"} ] ] end end defmodule EctoDep do def project do [ app: :simple, version: "0.1.0", consolidate_protocols: false, deps: [ {:ecto, "~> 0.2.0"} ] ] end end defmodule Override do def project do [ app: :override, version: "0.1.0", consolidate_protocols: false, deps: [ {:ecto, "0.2.0"}, {:ex_doc, "~> 0.1.0", override: true} ] ] end end defmodule NonHexDep do def project do [ app: :non_hex_dep, version: "0.1.0", consolidate_protocols: false, deps: [ {:has_hex_dep, path: fixture_path("has_hex_dep")} ] ] end end defmodule EctoPathDep do def project do [ app: :ecto_path_dep, version: "0.1.0", consolidate_protocols: false, deps: [ {:postgrex, ">= 0.0.0"}, {:ecto, path: fixture_path("ecto")} ] ] end end defmodule EctoPathDepConflict do def project do [ app: :ecto_path_dep, version: "0.1.0", consolidate_protocols: false, deps: [ {:postgrex, "0.2.1"}, {:ecto, path: fixture_path("ecto")} ] ] end end defmodule EctoPathDepAppConflict do def project do [ app: :ecto_path_dep, version: "0.1.0", consolidate_protocols: false, deps: [ {:postgrex_conflict, ">= 0.0.0", hex: :postgrex}, {:ecto, path: fixture_path("ecto")} ] ] end end defmodule OverrideWithPath do def project do [ app: :override_with_path, version: "0.1.0", consolidate_protocols: false, deps: [ {:postgrex, ">= 0.0.0"}, {:ex_doc, path: fixture_path("ex_doc"), override: true} ] ] end end defmodule OverrideTwoLevelsWithPath do def project do [ app: :override_two_levels_with_path, version: "0.1.0", consolidate_protocols: false, deps: [ {:phoenix, ">= 0.0.0"}, {:ex_doc, path: fixture_path("ex_doc"), override: true} ] ] end end defmodule OverrideWithPathParent do def project do [ app: :override_with_path_parent, version: "0.1.0", consolidate_protocols: false, deps: [ {:override_with_path, path: fixture_path("override_with_path")} ] ] end end defmodule Optional do def project do [ app: :optional, version: "0.1.0", consolidate_protocols: false, deps: [ {:only_doc, ">= 0.0.0"} ] ] end end defmodule WithOptional do def project do [ app: :with_optional, version: "0.1.0", consolidate_protocols: false, deps: [ {:only_doc, ">= 0.0.0"}, {:ex_doc, "0.0.1"} ] ] end end defmodule WithPackageName do def project do [ app: :with_package_name, version: "0.1.0", consolidate_protocols: false, deps: [ {:app_name, ">= 0.0.0", hex: :package_name} ] ] end end defmodule WithDependName do def project do [ app: :with_depend_name, version: "0.1.0", consolidate_protocols: false, deps: [ {:depend_name, ">= 0.0.0"} ] ] end end defmodule WithIncorrectDepVersion do def project do [ app: :with_incorrect_dep_version, version: "0.1.0", consolidate_protocols: false, deps: [ {:ex_doc, "> hello"} ] ] end end defmodule WithMissingDepVersion do def project do [ app: :with_missing_dep_version, version: "0.1.0", consolidate_protocols: false, deps: [ {:ex_doc, []} ] ] end end defmodule WithUnknownOptions do def project do [ app: :with_unknown_options, version: "0.1.0", consolidate_protocols: false, deps: [ {:ex_doc, dir: "/bad", typo: true} ] ] end end defmodule WithNonMatchingRequirement do def project do [ app: :with_non_matching_requirement, version: "0.1.0", consolidate_protocols: false, deps: [ {:ex_doc, "~> 100.0.0"} ] ] end end defmodule WithOnlyMatchingPreRequirement do def project do [ app: :with_only_matching_pre_requirement, version: "0.1.0", consolidate_protocols: false, deps: [ {:beta, "~> 1.1.0"} ] ] end end defmodule DependsOnEctoSQL do def project do [ app: :depends_on_ecto_sql, version: "0.1.0", consolidate_protocols: false, deps: [ {:ecto_sql, "~> 3.3"}, {:ecto_enum, "1.4.0"} ] ] end end defmodule DependsOnSponsored do def project do [ app: :depends_on_sponsored, version: "0.1.0", consolidate_protocols: false, deps: [ {:sponsored, "0.1.0"} ] ] end end defmodule EctoOverrideParent do def project do [ app: :ecto_override_parent, version: "0.1.0", consolidate_protocols: false, deps: [ {:ecto_override, path: HexTest.Case.fixture_path("ecto_override")} ] ] end end defp reset_code_paths(fun) do path = :code.get_path() try do fun.() after :code.add_pathsz(path) end end defp deps_compile() do reset_code_paths(fn -> Mix.Task.run("deps.compile") end) end defp compile() do reset_code_paths(fn -> Mix.Task.run("compile") end) end test "deps.get" do Mix.Project.push(Simple) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") assert_received {:mix_shell, :info, ["* Getting ecto (Hex package)"]} assert_received {:mix_shell, :info, ["* Getting postgrex (Hex package)"]} assert_received {:mix_shell, :info, ["* Getting ex_doc (Hex package)"]} deps_compile() Mix.Task.run("deps") assert_received {:mix_shell, :info, ["* ecto 0.2.0 (Hex package)" <> _]} assert_received {:mix_shell, :info, [" locked at 0.2.0 (ecto)" <> _]} assert_received {:mix_shell, :info, [" ok"]} assert_received {:mix_shell, :info, ["* postgrex 0.2.0 (Hex package)" <> _]} assert_received {:mix_shell, :info, [" locked at 0.2.0 (postgrex)" <> _]} assert_received {:mix_shell, :info, [" ok"]} assert_received {:mix_shell, :info, ["* ex_doc 0.0.1 (Hex package)" <> _]} assert_received {:mix_shell, :info, [" locked at 0.0.1 (ex_doc)" <> _]} assert_received {:mix_shell, :info, [" ok"]} end) after purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject ]) end test "deps.get with lock" do Mix.Project.push(Simple) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") Mix.Task.clear() Mix.Task.run("deps.get") deps_compile() Mix.Task.run("deps") assert_received {:mix_shell, :info, ["* ecto 0.2.0 (Hex package)" <> _]} assert_received {:mix_shell, :info, ["* postgrex 0.2.0 (Hex package)" <> _]} assert_received {:mix_shell, :info, ["* ex_doc 0.0.1 (Hex package)" <> _]} end) after purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject ]) end test "deps.update" do Mix.Project.push(Simple) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) # `deps.get` to set up lock Mix.Task.run("deps.get") purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject ]) Mix.State.clear_cache() Mix.Project.pop() Mix.Project.push(SimpleOld) Mix.Task.run("deps.update", ["ecto"]) assert_received {:mix_shell, :info, ["* Updating ecto (Hex package)"]} assert_received {:mix_shell, :info, ["* Updating postgrex (Hex package)"]} assert_received {:mix_shell, :info, ["* Updating ex_doc (Hex package)"]} deps_compile() Mix.Task.run("deps") assert_received {:mix_shell, :info, ["* ecto 0.2.1 (Hex package)" <> _]} assert_received {:mix_shell, :info, [" locked at 0.2.1 (ecto)" <> _]} assert_received {:mix_shell, :info, [" ok"]} assert_received {:mix_shell, :info, ["* postgrex 0.2.1 (Hex package)" <> _]} assert_received {:mix_shell, :info, [" locked at 0.2.1 (postgrex)" <> _]} assert_received {:mix_shell, :info, [" ok"]} assert_received {:mix_shell, :info, ["* ex_doc 0.1.0 (Hex package)" <> _]} assert_received {:mix_shell, :info, [" locked at 0.1.0 (ex_doc)" <> _]} assert_received {:mix_shell, :info, [" ok"]} end) after purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject, Sample.Fixture.MixProject ]) end test "deps.get with old format, string, single line manifest file" do Mix.Project.push(EctoDep) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) manifest = "ecto,0.2.0,0b6d6e0d9ef90f55dad224c59cff751a445f9b3e5fcfe5d31aa0e964e1d7e3de,hexpm" File.write!("mix.lock", ~s(%{"ecto": {:hex, :ecto, "0.2.0"}})) File.mkdir_p!("deps/ecto") File.write!("deps/ecto/.hex", manifest) Mix.Task.run("deps.get", []) assert_received {:mix_shell, :info, [" ecto 0.2.0"]} end) after purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject, Sample.Fixture.MixProject ]) end test "deps.get with old format, string, multi line manifest file" do Mix.Project.push(EctoDep) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) manifest = "ecto,0.2.0,0b6d6e0d9ef90f55dad224c59cff751a445f9b3e5fcfe5d31aa0e964e1d7e3de,hexpm\n" File.write!("mix.lock", ~s(%{"ecto": {:hex, :ecto, "0.2.0"}})) File.mkdir_p!("deps/ecto") File.write!("deps/ecto/.hex", manifest) Mix.Task.run("deps.get", []) assert_received {:mix_shell, :info, [" ecto 0.2.0"]} end) after purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject, Sample.Fixture.MixProject ]) end test "deps.get with 1.0 manifest file" do Mix.Project.push(EctoDep) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) manifest_map = %{ name: "ecto", version: "0.2.0", inner_checksum: "0b6d6e0d9ef90f55dad224c59cff751a445f9b3e5fcfe5d31aa0e964e1d7e3de", repo: "hexpm", managers: [] } manifest = :erlang.term_to_binary({{:hex, 1, 0}, manifest_map}) File.write!("mix.lock", ~s(%{"ecto": {:hex, :ecto, "0.2.0"}})) File.mkdir_p!("deps/ecto") File.write!("deps/ecto/.hex", manifest) Mix.Task.run("deps.get", []) assert_received {:mix_shell, :info, [" ecto 0.2.0"]} end) after purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject, Sample.Fixture.MixProject ]) end test "deps.update locked dependency with minimal lock file" do Mix.Project.push(EctoDep) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) File.write!("mix.lock", ~s(%{"ecto": {:hex, :ecto, "0.2.0"}})) Mix.Task.run("deps.update", ["ecto"]) assert_received {:mix_shell, :info, [" ecto 0.2.0 => 0.2.1"]} end) after purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject, Sample.Fixture.MixProject ]) end test "deps.update locked dependency with old lockfile" do Mix.Project.push(EctoDep) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) File.write!("mix.lock", ~s(%{"ecto": {:hex, :ecto, "0.2.0", "CHECKSUM", [:mix]}})) Mix.Task.run("deps.update", ["ecto"]) assert_received {:mix_shell, :info, [" ecto 0.2.0 => 0.2.1"]} end) after purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject, Sample.Fixture.MixProject ]) end test "deps.update locked dependency with new lockfile" do Mix.Project.push(EctoDep) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) File.write!( "mix.lock", ~s(%{"ecto": {:hex, :ecto, "0.2.0", "CHECKSUM", [:mix], [], "hexpm"}}) ) Mix.Task.run("deps.update", ["ecto"]) assert_received {:mix_shell, :info, [" ecto 0.2.0 => 0.2.1"]} end) after purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject, Sample.Fixture.MixProject ]) end test "deps.update locked dependency from git" do Mix.Project.push(EctoDep) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) File.write!( "mix.lock", ~s(%{"ecto": {:git, "https://github.com/elixi-ecto/ecto", "CHECKSUM", []}}) ) Mix.Task.run("deps.update", ["ecto"]) assert_received {:mix_shell, :info, [" ecto 0.2.1"]} end) after purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject, Sample.Fixture.MixProject ]) end test "deps.get with override" do Mix.Project.push(Override) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") deps_compile() Mix.Task.run("deps") assert_received {:mix_shell, :info, ["* ecto 0.2.0 (Hex package)" <> _]} assert_received {:mix_shell, :info, [" locked at 0.2.0 (ecto)" <> _]} assert_received {:mix_shell, :info, [" ok"]} assert_received {:mix_shell, :info, ["* postgrex 0.2.1 (Hex package)" <> _]} assert_received {:mix_shell, :info, [" locked at 0.2.1 (postgrex)" <> _]} assert_received {:mix_shell, :info, [" ok"]} assert_received {:mix_shell, :info, ["* ex_doc 0.1.0 (Hex package)" <> _]} assert_received {:mix_shell, :info, [" locked at 0.1.0 (ex_doc)" <> _]} assert_received {:mix_shell, :info, [" ok"]} end) after purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject ]) end test "deps.get with non hex dependency that has hex dependency" do Mix.Project.push(NonHexDep) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") assert_received {:mix_shell, :info, ["* Getting ecto (Hex package)"]} assert_received {:mix_shell, :info, ["* Getting postgrex (Hex package)"]} assert_received {:mix_shell, :info, ["* Getting ex_doc (Hex package)"]} end) after purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject, HasHexDep.Fixture.MixProject ]) end test "converged hex dependency considers all requirements" do Mix.Project.push(EctoPathDep) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") assert_received {:mix_shell, :info, ["* Getting postgrex (Hex package)"]} assert %{postgrex: {:hex, :postgrex, "0.2.0", _, _, _, "hexpm", _}} = Mix.Dep.Lock.read() end) after purge([Ecto.Fixture.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject]) end test "converged hex dependency considers all requirements and creates conflict" do Mix.Project.push(EctoPathDepConflict) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) assert_raise(Mix.Error, "Hex dependency resolution failed", fn -> Mix.Task.run("deps.get") end) solver_output = """ Because "your app" depends on "ecto" which depends on "postgrex 0.2.0", "postgrex 0.2.0" is required. So, because "your app" depends on "postgrex 0.2.1", version solving failed.\ """ assert_received {:mix_shell, :info, [^solver_output]} end) after purge([Ecto.Fixture.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject]) end test "converged hex dependency considers all requirements and creates app conflict" do Mix.Project.push(EctoPathDepAppConflict) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) assert_raise( Mix.Error, ~r"Conflicting OTP application names in dependency definition of \"postgrex\"", fn -> Mix.Task.run("deps.get") end ) end) after purge([Ecto.Fixture.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject]) end test "do not fetch git children of hex dependencies" do Mix.Project.push(SimpleOld) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") assert_received {:mix_shell, :info, ["* Getting ecto (Hex package)"]} Mix.Task.run("deps") assert_received {:mix_shell, :info, ["* ecto (Hex package)" <> _]} refute_received {:mix_shell, :info, ["* sample" <> _]} end) after purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject ]) end test "override hex dependency with path dependency" do Mix.Project.push(OverrideWithPath) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") Mix.Task.run("deps") assert_received {:mix_shell, :info, ["* postgrex (Hex package)" <> _]} refute_received {:mix_shell, :info, ["* ex_doc (Hex package)" <> _]} assert_received {:mix_shell, :info, ["* ex_doc" <> _]} assert %{postgrex: {:hex, :postgrex, "0.2.1", _, _, _, "hexpm", _}} = Mix.Dep.Lock.read() end) after purge([Postgrex.NoConflict.MixProject, ExDoc.Fixture.MixProject]) end test "override hex dependency two levels down with path dependency" do Mix.Project.push(OverrideTwoLevelsWithPath) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") Mix.Task.run("deps") assert_received {:mix_shell, :info, ["* phoenix (Hex package)" <> _]} assert_received {:mix_shell, :info, ["* postgrex (Hex package)" <> _]} refute_received {:mix_shell, :info, ["* ex_doc (Hex package)" <> _]} assert_received {:mix_shell, :info, ["* ex_doc" <> _]} assert %{ phoenix: {:hex, :phoenix, "0.0.1", _, _, _, "hexpm", _}, postgrex: {:hex, :postgrex, "0.2.1", _, _, _, "hexpm", _} } = Mix.Dep.Lock.read() end) after purge([ Phoenix.NoConflict.MixProject, Postgrex.NoConflict.MixProject, ExDoc.Fixture.MixProject ]) end test "override hex dependency with path dependency from dependency" do Mix.Project.push(OverrideWithPathParent) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") assert_received {:mix_shell, :info, ["* Getting postgrex (Hex package)"]} Mix.Task.run("deps") assert_received {:mix_shell, :info, ["* postgrex (Hex package)" <> _]} refute_received {:mix_shell, :info, ["* ex_doc (Hex package)" <> _]} assert_received {:mix_shell, :info, ["* ex_doc" <> _]} assert %{postgrex: {:hex, :postgrex, "0.2.1", _, _, _, "hexpm", _}} = Mix.Dep.Lock.read() end) after purge([ OverrideWithPath.Fixture.MixProject, ExDoc.Fixture.MixProject, Postgrex.NoConflict.MixProject ]) end test "optional dependency" do Mix.Project.push(Optional) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") assert_received {:mix_shell, :info, ["* Getting only_doc (Hex package)"]} refute_received {:mix_shell, :info, ["* Getting ex_doc (Hex package)"]} Mix.Task.run("deps") assert_received {:mix_shell, :info, ["* only_doc (Hex package)" <> _]} refute_received {:mix_shell, :info, ["* ex_doc (Hex package)" <> _]} end) after purge([Only_doc.NoConflict.MixProject, Ex_doc.NoConflict.MixProject]) end test "with optional dependency" do Mix.Project.push(WithOptional) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") assert_received {:mix_shell, :info, ["* Getting only_doc (Hex package)"]} assert_received {:mix_shell, :info, ["* Getting ex_doc (Hex package)"]} Mix.Task.run("deps") assert_received {:mix_shell, :info, ["* only_doc (Hex package)" <> _]} assert_received {:mix_shell, :info, ["* ex_doc (Hex package)" <> _]} assert_received {:mix_shell, :info, [" locked at 0.0.1 (ex_doc)" <> _]} end) after purge([Only_doc.NoConflict.MixProject, Ex_doc.NoConflict.MixProject]) end test "with package name" do Mix.Project.push(WithPackageName) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") assert_received {:mix_shell, :info, ["* Getting app_name (Hex package)"]} Mix.Task.run("deps") assert_received {:mix_shell, :info, ["* app_name (Hex package)" <> _]} assert_received {:mix_shell, :info, [" locked at 0.1.0 (package_name)" <> _]} end) after purge([Package_name.NoConflict.MixProject]) end test "with depend name" do Mix.Project.push(WithDependName) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") assert_received {:mix_shell, :info, ["* Getting depend_name (Hex package)"]} assert_received {:mix_shell, :info, ["* Getting app_name (Hex package)"]} Mix.Task.run("deps") assert_received {:mix_shell, :info, ["* depend_name (Hex package)" <> _]} assert_received {:mix_shell, :info, [" locked at 0.2.0 (depend_name)" <> _]} assert_received {:mix_shell, :info, ["* app_name (Hex package)" <> _]} assert_received {:mix_shell, :info, [" locked at 0.1.0 (package_name)" <> _]} end) after purge([Depend_name.NoConflict.MixProject, Package_name.NoConflict.MixProject]) end test "deps.get with incorrect version" do Mix.Project.push(WithIncorrectDepVersion) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) message = ~r[Required version "> hello" for package ex_doc is incorrectly specified] assert_raise Mix.Error, message, fn -> Mix.Task.run("deps.get") end end) end test "deps.get with missing version" do Mix.Project.push(WithMissingDepVersion) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") assert_received {:mix_shell, :info, ["\e[33mex_doc is missing its version requirement, use \">= 0.0.0\"" <> _]} end) end test "deps.get with unknown options" do Mix.Project.push(WithUnknownOptions) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") assert_received {:mix_shell, :info, ["\e[33mex_doc is missing its version requirement, use \">= 0.0.0\"" <> _]} assert_received {:mix_shell, :info, ["\e[33mex_doc is using unknown options: :dir, :typo\e[0m"]} end) end defp old_lock_tuple(lock_tuple) do {elem(lock_tuple, 0), elem(lock_tuple, 1), elem(lock_tuple, 2), elem(lock_tuple, 3)} end defp rewrite_lock_in_old_format() do lock = Mix.Dep.Lock.read() old_lock = for {dep_key, dep_tuple} <- lock, into: %{} do {dep_key, old_lock_tuple(dep_tuple)} end Mix.Dep.Lock.write(old_lock) old_lock end test "deps.get populates lock" do Mix.Project.push(Simple) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") assert %{ecto: {:hex, :ecto, "0.2.0", old_checksum, [:mix], deps, "hexpm", new_checksum}} = Mix.Dep.Lock.read() assert String.length(new_checksum) == 64 assert String.length(old_checksum) == 64 assert deps == [ {:ex_doc, "~> 0.0.1", [hex: :ex_doc, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.2.0", [hex: :postgrex, repo: "hexpm", optional: false]} ] end) end test "deps.get does not rewrite the lock file when deps are already present" do Mix.Project.push(Simple) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") old_lock = rewrite_lock_in_old_format() Mix.Task.run("deps.get") assert old_lock == Mix.Dep.Lock.read() end) after purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject ]) end test "deps.get does not rewrite the lock file when deps are not present" do Mix.Project.push(Simple) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") old_lock = rewrite_lock_in_old_format() File.rm_rf!("deps") Mix.Task.run("deps.get") assert old_lock == Mix.Dep.Lock.read() end) after purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject ]) end # The introduction of outer_checksum forces all locks to rewrite @tag :skip test "deps.update only rewrites given dependencies" do Mix.Project.push(Simple) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Task.run("deps.get") rewrite_lock_in_old_format() Mix.Task.run("deps.update", ["ex_doc"]) assert %{ ecto: {:hex, :ecto, "0.2.0", _}, ex_doc: {:hex, :ex_doc, "0.0.1", _, [:mix], _, _, _}, postgrex: {:hex, :postgrex, "0.2.0", _} } = Mix.Dep.Lock.read() end) after purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject ]) end test "do not raise :divergedreq when parent changes requirement and child changes version" do Mix.Project.push(DependsOnEctoSQL) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Mix.Dep.Lock.write(%{ ecto_sql: {:hex, :ecto_sql, "3.3.2"}, ecto: {:hex, :ecto, "3.3.1"} }) Mix.Task.run("deps.get") compile() Mix.Task.run("deps.update", ["ecto_sql"]) end) after purge([ Ecto.SQL_3_3_2.Fixture.MixProject, Ecto.SQL_3_3_3.Fixture.MixProject, Ecto.Enum_1_4_0.Fixture.MixProject, Ecto_3_3_1.Fixture.MixProject, Ecto_3_3_2.Fixture.MixProject ]) end test "prints a sponsors tip when updating or adding a package with sponsor link" do Mix.Project.push(DependsOnSponsored) in_tmp("sponsor_tmp", fn -> Hex.State.put(:cache_home, tmp_path()) Mix.Task.run("deps.get") assert_received {:mix_shell, :info, ["* Getting sponsored (Hex package)"]} assert_received {:mix_shell, :info, [ "You have added/upgraded packages you could " <> "sponsor, run `mix hex.sponsor` to learn more" ]} end) end test "deps.get umbrella" do in_fixture("umbrella", fn -> Code.eval_file("mix.exs") Mix.Task.run("deps.get") assert_received {:mix_shell, :info, ["* Getting postgrex (Hex package)"]} assert_received {:mix_shell, :info, ["* Getting ex_doc (Hex package)"]} assert %{ ex_doc: {:hex, :ex_doc, "0.1.0", _, _, _, _, _}, postgrex: {:hex, :postgrex, "0.2.1", _, _, _, _, _} } = Mix.Dep.Lock.read() end) after purge([ Umbrella.Fixture.MixProject, Umbrella.MyApp1.Fixture.MixProject, Umbrella.MyApp2.Fixture.MixProject, Umbrella.MyApp3.Fixture.MixProject ]) end test "deps.get with path override" do Mix.Project.push(EctoOverrideParent) in_tmp(fn -> Mix.Task.run("deps.get") assert_received {:mix_shell, :info, ["* Getting postgrex (Hex package)"]} assert %{ postgrex: {:hex, :postgrex, "0.2.0", _, _, _, _, _} } = Mix.Dep.Lock.read() end) after purge([ EctoOverride.Fixture.MixProject ]) end test "deps.get umbrella with path override" do in_fixture("umbrella_override", fn -> Code.eval_file("mix.exs") Mix.Task.run("deps.get") assert_received {:mix_shell, :info, ["* Getting postgrex (Hex package)"]} assert %{ postgrex: {:hex, :postgrex, "0.2.0", _, _, _, _, _} } = Mix.Dep.Lock.read() end) after purge([ UmbrellaOverride.Fixture.MixProject, UmbrellaOverride.MyApp1.Fixture.MixProject, EctoOverride.Fixture.MixProject ]) end end hex-2.4.2/test/hex/mix_test.exs000066400000000000000000000023161517471540100164320ustar00rootroot00000000000000defmodule Hex.MixTest do use HexTest.Case test "from_lock/1" do lock = [ex_doc: {:hex, :ex_doc, "0.1.0"}, postgrex: {:hex, :fork, "0.2.1"}] assert Hex.Mix.from_lock(lock) == [ %{repo: "hexpm", name: "ex_doc", app: "ex_doc", version: "0.1.0"}, %{repo: "hexpm", name: "fork", app: "postgrex", version: "0.2.1"} ] end test "from_lock/1 warns on newer lock versions" do message = {:mix_shell, :info, [ "\e[33mThe mix.lock file was generated with a newer version of Hex. " <> "Update your client by running `mix local.hex` to avoid losing data.\e[0m" ]} lock = [ ex_doc: {:hex, :ex_doc, "0.1.0", "checksum", [:mix], [{:dep, ">= 0.0.0", [hex: :dep]}], "hexpm", "checksum"} ] Hex.Server.reset() Hex.Mix.from_lock(lock) refute_received ^message lock = [ ex_doc: {:hex, :ex_doc, "0.1.0", "checksum", [:mix], [{:dep, ">= 0.0.0", [hex: :dep]}], "hexpm", "checksum", "entry from newer version"} ] Hex.Server.reset() Hex.Mix.from_lock(lock) assert_received ^message Hex.Mix.from_lock(lock) refute_received ^message end end hex-2.4.2/test/hex/netrc/000077500000000000000000000000001517471540100151665ustar00rootroot00000000000000hex-2.4.2/test/hex/netrc/cache_test.exs000066400000000000000000000026371517471540100200210ustar00rootroot00000000000000defmodule Hex.Netrc.CacheTest do use HexTest.Case alias Hex.Netrc.Cache setup do # Restart Hex between tests to get a fresh cache. Application.stop(:hex) :ok = Application.start(:hex) end test "fetch/1 fails on non-existent file" do in_tmp(fn -> assert {:error, :enoent} = Cache.fetch(".netrc") end) end test "fetch/1 remembers parse errors" do in_tmp(fn -> File.write!(".netrc", "") assert {:error, :parse} = Cache.fetch(".netrc") File.rm!(".netrc") assert {:error, :parse} = Cache.fetch(".netrc") end) end test "fetch/1 succeeds on simple file" do in_tmp(fn -> data = """ machine foo.example.com login john password bar """ parsed = %{ "foo.example.com" => %{ username: "john", password: "bar" } } File.write!(".netrc", data) assert {:ok, ^parsed} = Cache.fetch(".netrc") end) end test "fetch/1 remembers successful parses" do in_tmp(fn -> data = """ machine foo.example.com login john password bar """ parsed = %{ "foo.example.com" => %{ username: "john", password: "bar" } } File.write!(".netrc", data) assert {:ok, ^parsed} = Cache.fetch(".netrc") File.rm!(".netrc") assert {:ok, ^parsed} = Cache.fetch(".netrc") end) end end hex-2.4.2/test/hex/netrc/parser_test.exs000066400000000000000000000057761517471540100202610ustar00rootroot00000000000000defmodule Hex.Netrc.ParserTest do use HexTest.Case alias Hex.Netrc.Parser test "parse/1 fails on non-existent file" do in_tmp(fn -> assert {:error, :enoent} = Parser.parse(".netrc") end) end test "parse/1 fails on unreadable file" do in_tmp(fn -> File.write!(".netrc", "") File.chmod!(".netrc", 0o000) assert {:error, :eacces} = Parser.parse(".netrc") end) end test "parse/1 fails on empty file" do in_tmp(fn -> File.write!(".netrc", "") assert {:error, :parse} = Parser.parse(".netrc") end) end test "parse/1 succeeds on simple file" do in_tmp(fn -> data = """ machine foo.example.com login john password bar """ parsed = %{ "foo.example.com" => %{ username: "john", password: "bar" } } File.write!(".netrc", data) assert {:ok, ^parsed} = Parser.parse(".netrc") end) end test "parse/1 succeeds on file with multiple records" do in_tmp(fn -> data = """ machine foo.example.com login john password bar machine bar.example.com login yoyo password dyne """ parsed = %{ "foo.example.com" => %{ username: "john", password: "bar" }, "bar.example.com" => %{ username: "yoyo", password: "dyne" } } File.write!(".netrc", data) assert {:ok, ^parsed} = Parser.parse(".netrc") end) end test "parse/1 ignores excessive whitespace" do in_tmp(fn -> data = "\n\n\t\nmachine foo.example.com\n\n login\t\tjohn\npassword\t\tbar\n\n" parsed = %{ "foo.example.com" => %{ username: "john", password: "bar" } } File.write!(".netrc", data) assert {:ok, ^parsed} = Parser.parse(".netrc") end) end test "parse/1 missing machine line is a parse error" do in_tmp(fn -> data = """ login foo password bar """ File.write!(".netrc", data) assert {:error, :parse} = Parser.parse(".netrc") end) end test "parse/1 missing login line is a parse error" do in_tmp(fn -> data = """ machine foo.example.com password bar """ File.write!(".netrc", data) assert {:error, :parse} = Parser.parse(".netrc") end) end test "parse/1 missing password line is a parse error" do in_tmp(fn -> data = """ machine foo.example.com login foo """ File.write!(".netrc", data) assert {:error, :parse} = Parser.parse(".netrc") end) end test "parse/1 username can be overridden" do in_tmp(fn -> data = """ machine foo.example.com login foo login foo2 password bar """ parsed = %{ "foo.example.com" => %{ username: "foo2", password: "bar" } } File.write!(".netrc", data) assert {:ok, ^parsed} = Parser.parse(".netrc") end) end end hex-2.4.2/test/hex/netrc_test.exs000066400000000000000000000014711517471540100167510ustar00rootroot00000000000000defmodule Hex.NetrcTest do use HexTest.Case alias Hex.Netrc setup do # Restart Hex between tests to get a fresh cache. Application.stop(:hex) :ok = Application.start(:hex) end test "lookup/2 returns nil for unknown host" do in_tmp(fn -> data = """ machine foo.example.com login foo password bar """ File.write!(".netrc", data) assert {:ok, nil} = Netrc.lookup("bar.example.com", ".netrc") end) end test "lookup/2 returns expected result for known host" do in_tmp(fn -> data = """ machine foo.example.com login foo password bar """ File.write!(".netrc", data) assert {:ok, %{username: "foo", password: "bar"}} = Netrc.lookup("foo.example.com", ".netrc") end) end end hex-2.4.2/test/hex/oauth_test.exs000066400000000000000000000163331517471540100167610ustar00rootroot00000000000000defmodule Hex.OAuthTest do use HexTest.IntegrationCase describe "get_token/0" do test "returns error when no tokens are stored" do assert {:error, :no_auth} = Hex.OAuth.get_token() end test "returns valid token when available and not expired" do future_time = System.system_time(:second) + 3600 token_data = %{ "access_token" => "test_token", "refresh_token" => "test_refresh", "expires_at" => future_time } Hex.OAuth.store_token(token_data) assert {:ok, "test_token"} = Hex.OAuth.get_token() end test "returns error when token is expired and no refresh possible" do past_time = System.system_time(:second) - 100 token_data = %{ "access_token" => "expired_token", "expires_at" => past_time } Hex.OAuth.store_token(token_data) assert {:error, :no_refresh_token} = Hex.OAuth.get_token() end test "returns error when token is expired and refresh fails" do past_time = System.system_time(:second) - 100 token_data = %{ "access_token" => "expired_token", "refresh_token" => "invalid_refresh_token", "expires_at" => past_time } Hex.OAuth.store_token(token_data) # Should fail to refresh and return error assert {:error, :refresh_failed} = Hex.OAuth.get_token() end end describe "store_token/1" do test "stores token in both config and state" do token_data = %{ "access_token" => "test_token", "refresh_token" => "test_refresh", "expires_at" => System.system_time(:second) + 3600 } Hex.OAuth.store_token(token_data) # Check state assert Hex.State.get(:oauth_token) == token_data # Check config config = Hex.Config.read() assert config[:"$oauth_token"] == token_data end test "handles empty token" do Hex.OAuth.store_token(%{}) assert Hex.State.get(:oauth_token) == %{} config = Hex.Config.read() assert config[:"$oauth_token"] == %{} end end describe "clear_tokens/0" do test "removes tokens from both config and state" do token_data = %{ "access_token" => "token", "refresh_token" => "refresh", "expires_at" => System.system_time(:second) + 3600 } Hex.OAuth.store_token(token_data) assert Hex.OAuth.has_tokens?() Hex.OAuth.clear_tokens() assert Hex.State.get(:oauth_token) == nil refute Hex.OAuth.has_tokens?() end test "clears tokens from config file" do token_data = %{ "access_token" => "config_token", "refresh_token" => "config_refresh", "expires_at" => System.system_time(:second) + 3600 } Hex.OAuth.store_token(token_data) # Verify token is in config config = Hex.Config.read() assert config[:"$oauth_token"]["access_token"] == "config_token" Hex.OAuth.clear_tokens() # Verify token is removed from config config = Hex.Config.read() refute config[:"$oauth_token"] end end describe "has_tokens?/0" do test "returns false when no tokens are stored" do refute Hex.OAuth.has_tokens?() end test "returns true when tokens are stored" do token_data = %{ "access_token" => "token", "refresh_token" => "refresh", "expires_at" => System.system_time(:second) + 3600 } Hex.OAuth.store_token(token_data) assert Hex.OAuth.has_tokens?() end test "returns true even with expired tokens" do past_time = System.system_time(:second) - 100 token_data = %{ "access_token" => "expired_token", "expires_at" => past_time } Hex.OAuth.store_token(token_data) assert Hex.OAuth.has_tokens?() end end describe "create_token_data/1" do test "creates token data with proper expiration time" do current_time = System.system_time(:second) oauth_response = %{ "access_token" => "test_token", "refresh_token" => "test_refresh", "expires_in" => 3600, "token_type" => "bearer", "scope" => "api" } token_data = Hex.OAuth.create_token_data(oauth_response) assert token_data["access_token"] == "test_token" assert token_data["refresh_token"] == "test_refresh" assert token_data["expires_at"] >= current_time + 3600 # Allow 5 second margin assert token_data["expires_at"] <= current_time + 3600 + 5 # Should only contain the three required fields assert Map.keys(token_data) |> Enum.sort() == [ "access_token", "expires_at", "refresh_token" ] end test "handles missing refresh token" do oauth_response = %{ "access_token" => "test_token", "expires_in" => 3600, "token_type" => "bearer", "scope" => "api" } token_data = Hex.OAuth.create_token_data(oauth_response) assert token_data["access_token"] == "test_token" refute Map.has_key?(token_data, "refresh_token") assert is_integer(token_data["expires_at"]) end end describe "refresh_token/0" do test "returns error when no refresh token available" do token_data = %{ "access_token" => "token_without_refresh", "expires_at" => System.system_time(:second) + 100 } Hex.OAuth.store_token(token_data) assert {:error, :no_refresh_token} = Hex.OAuth.refresh_token() end test "returns error when no tokens stored" do assert {:error, :no_auth} = Hex.OAuth.refresh_token() end end describe "concurrent token refresh" do test "handles multiple concurrent get_token calls with expired token" do # Store an expired token with no refresh token # This simulates the race condition scenario where multiple processes # try to get the token at the same time past_time = System.system_time(:second) - 100 token_data = %{ "access_token" => "expired_token", "expires_at" => past_time } Hex.OAuth.store_token(token_data) # Spawn multiple concurrent tasks that all try to get the token tasks = for _ <- 1..10 do Task.async(fn -> Hex.OAuth.get_token() end) end # Wait for all tasks to complete results = Task.await_many(tasks) # All should fail since there's no refresh token, but they should all # return the same error and not crash assert Enum.all?(results, fn result -> result == {:error, :no_refresh_token} end) end test "handles concurrent get_token calls with valid token" do # Store a valid token future_time = System.system_time(:second) + 3600 token_data = %{ "access_token" => "valid_token", "refresh_token" => "refresh_token", "expires_at" => future_time } Hex.OAuth.store_token(token_data) # Spawn multiple concurrent tasks tasks = for _ <- 1..10 do Task.async(fn -> Hex.OAuth.get_token() end) end # All should succeed results = Task.await_many(tasks) assert Enum.all?(results, fn result -> result == {:ok, "valid_token"} end) end end end hex-2.4.2/test/hex/once_cache_test.exs000066400000000000000000000214561517471540100177120ustar00rootroot00000000000000defmodule Hex.OnceCacheTest do use ExUnit.Case, async: true setup do cache_name = :"cache_#{:erlang.unique_integer([:positive])}" {:ok, _pid} = Hex.OnceCache.start_link(name: cache_name) %{cache: cache_name} end describe "fetch/2" do test "computes value on first call", %{cache: cache} do pid = self() ref = make_ref() result = Hex.OnceCache.fetch(cache, fn -> send(pid, {:computed, ref}) :computed_value end) assert result == :computed_value assert_received {:computed, ^ref} end test "returns cached value on subsequent calls", %{cache: cache} do pid = self() compute_fn = fn -> send(pid, :computed) :cached_value end # First call computes assert Hex.OnceCache.fetch(cache, compute_fn) == :cached_value assert_received :computed # Second call uses cached value assert Hex.OnceCache.fetch(cache, compute_fn) == :cached_value refute_received :computed # Third call still uses cached value assert Hex.OnceCache.fetch(cache, compute_fn) == :cached_value refute_received :computed end test "handles concurrent calls correctly", %{cache: cache} do counter = :counters.new(1, []) compute_fn = fn -> :counters.add(counter, 1, 1) # Simulate slow computation Process.sleep(50) :result end # Spawn 10 concurrent tasks tasks = for _ <- 1..10 do Task.async(fn -> Hex.OnceCache.fetch(cache, compute_fn) end) end # All should return the same result results = Task.await_many(tasks) assert Enum.all?(results, &(&1 == :result)) # Compute function should only be called once assert :counters.get(counter, 1) == 1 end test "caches nil values", %{cache: cache} do counter = :counters.new(1, []) compute_fn = fn -> :counters.add(counter, 1, 1) nil end assert Hex.OnceCache.fetch(cache, compute_fn) == nil assert :counters.get(counter, 1) == 1 # Should use cached nil, not recompute assert Hex.OnceCache.fetch(cache, compute_fn) == nil assert :counters.get(counter, 1) == 1 end test "caches false values", %{cache: cache} do counter = :counters.new(1, []) compute_fn = fn -> :counters.add(counter, 1, 1) false end refute Hex.OnceCache.fetch(cache, compute_fn) assert :counters.get(counter, 1) == 1 # Should use cached false, not recompute refute Hex.OnceCache.fetch(cache, compute_fn) assert :counters.get(counter, 1) == 1 end end describe "put/2" do test "stores a value without computing", %{cache: cache} do Hex.OnceCache.put(cache, :put_value) # fetch should return the put value without calling compute function result = Hex.OnceCache.fetch(cache, fn -> flunk("Compute function should not be called") end) assert result == :put_value end test "overwrites previously computed value", %{cache: cache} do # First compute a value Hex.OnceCache.fetch(cache, fn -> :computed_value end) # Then put a different value Hex.OnceCache.put(cache, :new_value) # fetch should return the new value result = Hex.OnceCache.fetch(cache, fn -> :should_not_be_called end) assert result == :new_value end end describe "clear/1" do test "resets cache to not_fetched state", %{cache: cache} do counter = :counters.new(1, []) compute_fn = fn -> :counters.add(counter, 1, 1) :computed_value end # Compute and cache a value assert Hex.OnceCache.fetch(cache, compute_fn) == :computed_value assert :counters.get(counter, 1) == 1 # Clear the cache Hex.OnceCache.clear(cache) # Next fetch should recompute assert Hex.OnceCache.fetch(cache, compute_fn) == :computed_value assert :counters.get(counter, 1) == 2 end test "clears a put value", %{cache: cache} do counter = :counters.new(1, []) compute_fn = fn -> :counters.add(counter, 1, 1) :computed_value end # Put a value Hex.OnceCache.put(cache, :put_value) # Clear it Hex.OnceCache.clear(cache) # Next fetch should compute assert Hex.OnceCache.fetch(cache, compute_fn) == :computed_value assert :counters.get(counter, 1) == 1 end end describe "fetch_key/4" do test "computes value on first call for a key", %{cache: cache} do result = Hex.OnceCache.fetch_key(cache, :key1, fn -> :value1 end) assert result == :value1 end test "returns cached value on subsequent calls for same key", %{cache: cache} do counter = :counters.new(1, []) compute_fn = fn -> :counters.add(counter, 1, 1) :value end assert Hex.OnceCache.fetch_key(cache, :key1, compute_fn) == :value assert :counters.get(counter, 1) == 1 assert Hex.OnceCache.fetch_key(cache, :key1, compute_fn) == :value assert :counters.get(counter, 1) == 1 end test "computes independently for different keys", %{cache: cache} do assert Hex.OnceCache.fetch_key(cache, :key1, fn -> :value1 end) == :value1 assert Hex.OnceCache.fetch_key(cache, :key2, fn -> :value2 end) == :value2 # Both are cached independently assert Hex.OnceCache.fetch_key(cache, :key1, fn -> :should_not_compute end) == :value1 assert Hex.OnceCache.fetch_key(cache, :key2, fn -> :should_not_compute end) == :value2 end test "handles concurrent calls for the same key", %{cache: cache} do counter = :counters.new(1, []) compute_fn = fn -> :counters.add(counter, 1, 1) Process.sleep(50) :result end tasks = for _ <- 1..10 do Task.async(fn -> Hex.OnceCache.fetch_key(cache, :key1, compute_fn) end) end results = Task.await_many(tasks) assert Enum.all?(results, &(&1 == :result)) assert :counters.get(counter, 1) == 1 end test "computes different keys concurrently", %{cache: cache} do # Both keys start computing at the same time. # If serialized, total time would be >= 200ms. # If concurrent, total time should be ~100ms. compute_fn = fn -> Process.sleep(100) :result end task1 = Task.async(fn -> Hex.OnceCache.fetch_key(cache, :key1, compute_fn) end) task2 = Task.async(fn -> Hex.OnceCache.fetch_key(cache, :key2, compute_fn) end) {elapsed, results} = :timer.tc(fn -> Task.await_many([task1, task2]) end) assert results == [:result, :result] # Should complete in roughly 100ms, not 200ms assert elapsed < 180_000 end @tag :capture_log test "hands off to next waiter when computing process crashes", %{cache: cache} do caller = self() spawn(fn -> Hex.OnceCache.fetch_key(cache, :key1, fn -> send(caller, :started) Process.sleep(100) raise "crash" end) end) assert_receive :started task2 = Task.async(fn -> Hex.OnceCache.fetch_key(cache, :key1, fn -> :recovered end) end) assert Task.await(task2, 5000) == :recovered end test "clear resets all keys", %{cache: cache} do counter = :counters.new(1, []) Hex.OnceCache.fetch_key(cache, :key1, fn -> :value1 end) Hex.OnceCache.fetch_key(cache, :key2, fn -> :value2 end) Hex.OnceCache.clear(cache) compute_fn = fn -> :counters.add(counter, 1, 1) :recomputed end assert Hex.OnceCache.fetch_key(cache, :key1, compute_fn) == :recomputed assert :counters.get(counter, 1) == 1 end end describe "fetch/3 with timeout" do test "respects custom timeout for long operations", %{cache: cache} do compute_fn = fn -> Process.sleep(100) :long_operation end result = Hex.OnceCache.fetch(cache, compute_fn, timeout: 200) assert result == :long_operation end test "waiter times out if computation exceeds timeout", %{cache: cache} do # Start a slow computation in another process Task.async(fn -> Hex.OnceCache.fetch(cache, fn -> Process.sleep(200) :slow_result end) end) # Give the task time to start computing Process.sleep(10) # A waiter with a short timeout should time out assert catch_exit(Hex.OnceCache.fetch(cache, fn -> :unused end, timeout: 50)) end test "accepts :infinity timeout", %{cache: cache} do compute_fn = fn -> Process.sleep(100) :result end result = Hex.OnceCache.fetch(cache, compute_fn, timeout: :infinity) assert result == :result end end end hex-2.4.2/test/hex/remote_converger_test.exs000066400000000000000000000150161517471540100212030ustar00rootroot00000000000000defmodule Hex.RemoteConvergerTest do use HexTest.IntegrationCase defmodule OutdatedDepsBefore.MixProject do def project do [ app: :outdated_deps, version: "0.1.0", deps: [ {:postgrex, "0.2.1", warn_if_outdated: true}, {:ecto, "3.3.1", warn_if_outdated: true}, {:ecto_sql, "3.3.2", warn_if_outdated: true} ] ] end end defmodule OutdatedDepsAfter.MixProject do def project do [ app: :outdated_deps, version: "0.1.0", deps: [ {:postgrex, ">= 0.0.0", warn_if_outdated: true}, {:ecto, ">= 0.0.0", warn_if_outdated: true}, {:ecto_sql, ">= 0.0.0", warn_if_outdated: true} ] ] end end test "deps with warn_if_outdated: true" do in_tmp(fn -> Mix.Project.push(OutdatedDepsBefore.MixProject) :ok = Mix.Tasks.Deps.Get.run([]) Mix.Project.pop() Mix.Project.push(OutdatedDepsAfter.MixProject) output = ExUnit.CaptureIO.capture_io(:stderr, fn -> :ok = Mix.Tasks.Deps.Get.run([]) end) assert output =~ "ecto 3.3.2 is available" assert output =~ "ecto_sql 3.3.3 is available" refute output =~ "postgrex" end) end defmodule WarnOutdatedWithHexOption.MixProject do def project do [ app: :warn_outdated_with_hex_option, version: "0.1.0", deps: [ # Package name is "package_name" but app name is "app_name" {:app_name, ">= 0.0.0", hex: :package_name, warn_if_outdated: true} ] ] end end defmodule PublicDepsWithExpiredOAuth.MixProject do def project do [ app: :public_deps_with_expired_oauth, version: "0.1.0", deps: [ {:postgrex, "0.2.1"} ] ] end end defmodule OrganizationDepsWithExpiredOAuth.MixProject do def project do [ app: :organization_deps_with_expired_oauth, version: "0.1.0", deps: [ {:private_prompt_pkg, "0.1.0", repo: "hexpm:remote_converger_org"} ] ] end end defp with_project(project, fun) do Mix.Project.push(project) try do fun.() after Mix.Project.pop() end end defp store_expired_oauth_token do Hex.OnceCache.clear(Hex.OAuth.RefreshCache) Hex.OAuth.store_token(%{ "access_token" => "expired_access_token", "refresh_token" => "invalid_refresh_token", "expires_at" => System.system_time(:second) - 3600 }) end defp new_repo_auth_user(prefix) do suffix = System.unique_integer([:positive]) Hexpm.new_user( "#{prefix}_#{suffix}", "#{prefix}_#{suffix}@mail.com", "password", "#{prefix}_#{suffix}_key" ) end test "deps with warn_if_outdated: true and hex: option" do in_tmp(fn -> Mix.Project.push(WarnOutdatedWithHexOption.MixProject) # This should not crash with KeyError when the package name differs from app name assert :ok = Mix.Tasks.Deps.Get.run([]) end) end test "deps.get does not prompt for auth when only public deps are requested" do in_tmp(fn -> set_home_cwd() store_expired_oauth_token() with_project(PublicDepsWithExpiredOAuth.MixProject, fn -> assert :ok = Mix.Tasks.Deps.Get.run([]) end) refute_received {:mix_shell, :yes?, _} end) end test "auth preflight is skipped for public repos" do assert Hex.RemoteConverger.auth_preflight_required?([{"hexpm", "postgrex"}]) == false end test "auth preflight is required for organization repos without repo auth" do in_tmp(fn -> set_home_cwd() assert Hex.RemoteConverger.auth_preflight_required?([ {"hexpm:remote_converger_org", "private_prompt_pkg"} ]) end) end test "auth preflight is skipped when repo auth is already available" do in_tmp(fn -> set_home_cwd() auth = new_repo_auth_user("remote_converger_repo_auth_preflight") repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].auth_key, auth[:key]) Hex.State.put(:repos, repos) assert Hex.RemoteConverger.auth_preflight_required?([ {"hexpm:remote_converger_org", "private_prompt_pkg"} ]) == false end) end test "auth preflight is skipped for untrusted org repos" do in_tmp(fn -> set_home_cwd() Hex.State.put(:mirror_url, "http://example.com") assert Hex.RemoteConverger.auth_preflight_required?([ {"hexpm:remote_converger_org", "private_prompt_pkg"} ]) == false end) end test "deps.get does not prompt when repo auth is already available" do in_tmp(fn -> set_home_cwd() auth = new_repo_auth_user("remote_converger_repo_auth_deps_get") repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].auth_key, auth[:key]) Hex.State.put(:repos, repos) store_expired_oauth_token() with_project(OrganizationDepsWithExpiredOAuth.MixProject, fn -> assert :ok = Mix.Tasks.Deps.Get.run([]) end) refute_received {:mix_shell, :yes?, _} end) end defmodule ChecksumIntegrity.MixProject do def project do [ app: :checksum_integrity, version: "0.1.0", deps: [ {:ex_doc, "~> 0.1.0"} ] ] end end test "raises on checksum mismatch in mix.lock" do in_tmp(fn -> Mix.Project.push(ChecksumIntegrity.MixProject) # First, get dependencies normally to create a valid lock file :ok = Mix.Tasks.Deps.Get.run([]) # Read the lock file lock = Mix.Dep.Lock.read() {:hex, name, version, inner_checksum, managers, deps, repo, outer_checksum} = lock[:ex_doc] assert_checksum_mismatch(%{ ex_doc: {:hex, name, version, invalid_checksum(inner_checksum), managers, deps, repo, outer_checksum} }) assert_checksum_mismatch(%{ ex_doc: {:hex, name, version, inner_checksum, managers, deps, repo, invalid_checksum(outer_checksum)} }) end) end defp assert_checksum_mismatch(lock) do File.write!("mix.lock", inspect(lock, limit: :infinity, pretty: true)) Mix.Task.clear() # The bug causes this to silently pass and rewrite the lock file with correct checksums assert_raise Mix.Error, ~r/Registry checksum mismatch against lock/, fn -> Mix.Tasks.Deps.Get.run([]) end end defp invalid_checksum("0" <> rest), do: "1" <> rest defp invalid_checksum(<<_::binary-size(1), rest::binary>>), do: "0" <> rest end hex-2.4.2/test/hex/repo_test.exs000066400000000000000000000417021517471540100166040ustar00rootroot00000000000000defmodule Hex.RepoTest do use HexTest.IntegrationCase test "get_package/3" do assert {:ok, {200, _, _}} = Hex.Repo.get_package("hexpm", "postgrex", "") assert_raise Mix.Error, ~r"Unknown repository \"bad\"", fn -> Hex.Repo.get_package("bad", "postgrex", "") end end test "get_tarball/3" do assert {:ok, {200, _, _}} = Hex.Repo.get_tarball("hexpm", "postgrex", "0.2.1") assert_raise Mix.Error, ~r"Unknown repository \"bad\"", fn -> Hex.Repo.get_tarball("bad", "postgrex", "0.2.1") end end test "get public key" do bypass = Bypass.open() repos = Hex.State.fetch!(:repos) hexpm = Hex.Repo.hexpm_repo() repos = put_in(repos["hexpm"].url, "http://localhost:#{bypass.port}") Hex.State.put(:repos, repos) Bypass.expect(bypass, fn %Plug.Conn{request_path: path} = conn -> case path do "/public_key" -> assert Plug.Conn.get_req_header(conn, "authorization") == ["key"] Plug.Conn.resp(conn, 200, hexpm.public_key) "/not_found/public_key" -> assert Plug.Conn.get_req_header(conn, "authorization") == [] Plug.Conn.resp(conn, 404, "not found") end end) config = %{ url: "http://localhost:#{bypass.port}", auth_key: "key", trusted: true, oauth_exchange: false } assert {:ok, {200, _, public_key}} = Hex.Repo.get_public_key(config) assert public_key == hexpm.public_key config = %{ url: "http://localhost:#{bypass.port}/not_found", auth_key: "key", trusted: false, oauth_exchange: false } assert {:ok, {404, _, "not found"}} = Hex.Repo.get_public_key(config) end test "does not send OAuth token fallback to untrusted hexpm mirror" do bypass = Bypass.open() hexpm = Hex.Repo.default_hexpm_repo() Hex.OAuth.store_token(%{ "access_token" => "device_flow_token", "refresh_token" => "device_refresh", "expires_at" => System.system_time(:second) + 3600 }) Hex.State.put(:mirror_url, "http://localhost:#{bypass.port}") Bypass.expect(bypass, fn %Plug.Conn{request_path: "/public_key"} = conn -> assert Plug.Conn.get_req_header(conn, "authorization") == [] Plug.Conn.resp(conn, 200, hexpm.public_key) end) repo = Hex.Repo.get_repo("hexpm") assert repo.trusted == false assert {:ok, {200, _, public_key}} = Hex.Repo.get_public_key(repo) assert public_key == hexpm.public_key end test "add repo persists oauth_exchange through config round-trip" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) Mix.Tasks.Hex.Repo.run(["add", "myrepo", "http://example.com", "--auth-key", "mykey"]) # Reload config from disk to simulate a fresh session config = Hex.Config.read() repos = Hex.Config.read_repos(config) assert repos["myrepo"].auth_key == "mykey" assert repos["myrepo"].oauth_exchange == false end) end test "does not attempt oauth exchange when oauth_exchange is not set" do # Simulates a repo configured before v2.4.0 (no oauth_exchange key). # If oauth exchange were attempted with an invalid key it would raise. auth = HexTest.Hexpm.new_user( "no_oauth_key_user", "no_oauth_key@example.com", "password", "no_oauth_key_key" ) repos = Hex.State.fetch!(:repos) hexpm = Map.delete(repos["hexpm"], :oauth_exchange) hexpm = %{hexpm | auth_key: auth[:key]} Hex.State.put(:repos, %{repos | "hexpm" => hexpm}) assert {:ok, {200, _, _}} = Hex.Repo.get_package("hexpm", "postgrex", "") repos_after = Hex.State.fetch!(:repos) assert Map.get(repos_after["hexpm"], :oauth_token) == nil end test "non-hexpm repo with oauth_exchange: false uses API key directly and does not exchange" do bypass = Bypass.open() Bypass.expect(bypass, fn %Plug.Conn{request_path: path} = conn -> case path do "/public_key" -> assert Plug.Conn.get_req_header(conn, "authorization") == ["rawkey"] Plug.Conn.resp(conn, 200, "fake_public_key_body") "/oauth/token" -> flunk("OAuth exchange must not be attempted when oauth_exchange is false") end end) myrepo_config = %{ url: "http://localhost:#{bypass.port}", public_key: nil, auth_key: "rawkey", trusted: true, oauth_exchange: false, oauth_exchange_url: "http://localhost:#{bypass.port}" } assert {:ok, {200, _, "fake_public_key_body"}} = Hex.Repo.get_public_key(myrepo_config) end test "non-hexpm repo with oauth_exchange not true does not attempt API key exchange" do bypass = Bypass.open() test_pid = self() Bypass.expect(bypass, fn %Plug.Conn{request_path: path} = conn -> case path do "/public_key" -> Plug.Conn.resp(conn, 200, "fake_public_key_body") "/oauth/token" -> send(test_pid, :exchange_attempted) Plug.Conn.resp(conn, 500, "") end end) myrepo_config = %{ url: "http://localhost:#{bypass.port}", public_key: nil, auth_key: "rawkey", trusted: true, oauth_exchange: nil, oauth_exchange_url: "http://localhost:#{bypass.port}" } assert {:ok, {200, _, _}} = Hex.Repo.get_public_key(myrepo_config) refute_received :exchange_attempted end test "fetch_repo/1" do assert Hex.Repo.fetch_repo("foo") == :error assert {:ok, %{ auth_key: nil, public_key: _, trusted: true, url: "http://localhost:4043/repo" }} = Hex.Repo.fetch_repo("hexpm") assert {:ok, %{ auth_key: nil, oauth_exchange: true, public_key: _, trusted: true, url: "http://localhost:4043/repo/repos/acme" }} = Hex.Repo.fetch_repo("hexpm:acme") Hex.State.put(:trusted_mirror_url, "http://example.com") Hex.State.put(:repos_key, "key") assert {:ok, %{ auth_key: "key", public_key: _, trusted: true, url: "http://example.com" }} = Hex.Repo.fetch_repo("hexpm") assert {:ok, %{ auth_key: "key", oauth_exchange: true, public_key: _, trusted: true, url: "http://example.com/repos/acme" }} = Hex.Repo.fetch_repo("hexpm:acme") Hex.State.put(:trusted_mirror_url, nil) Hex.State.put(:mirror_url, "http://example.com") assert {:ok, %{ auth_key: "key", public_key: _, trusted: false, url: "http://example.com" }} = Hex.Repo.fetch_repo("hexpm") assert {:ok, %{ auth_key: "key", oauth_exchange: true, public_key: _, trusted: false, url: "http://example.com/repos/acme" }} = Hex.Repo.fetch_repo("hexpm:acme") end test "update_organizations/1 without Hex.State" do :ok = Supervisor.terminate_child(Hex.Supervisor, Hex.State) :ok = Supervisor.delete_child(Hex.Supervisor, Hex.State) repos = %{ "hexpm" => %{ url: "http://example.com", public_key: "public", auth_key: "auth", oauth_exchange: true, trusted: true }, "hexpm:acme" => %{} } assert %{ "hexpm:acme" => %{ auth_key: "auth", oauth_exchange: true, public_key: "public", trusted: true, url: "http://example.com/repos/acme" } } = Hex.Repo.update_organizations(repos) after {:ok, _} = Supervisor.start_child(Hex.Supervisor, Hex.State) end describe "get_package/3" do test "from public repo" do assert {:ok, {200, _, _}} = Hex.Repo.get_package("hexpm", "ex_doc", "") end test "from organization repo" do assert {:ok, {200, _, _}} = Hex.Repo.get_package("hexpm:testorg", "foo", "") end end describe "automatic API key to OAuth token exchange" do test "organization repo inherits oauth_exchange from parent" do auth = HexTest.Hexpm.new_user( "org_oauth_user", "org_oauth@example.com", "password", "org_oauth_key" ) repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].auth_key, auth[:key]) Hex.State.put(:repos, repos) assert {:ok, {200, _, _}} = Hex.Repo.get_package("hexpm:testorg", "foo", "") repos_after = Hex.State.fetch!(:repos) token_data = repos_after["hexpm:testorg"].oauth_token assert is_binary(token_data["access_token"]) end test "organization repo skips oauth_exchange when disabled on parent" do auth = HexTest.Hexpm.new_user( "org_no_oauth_user", "org_no_oauth@example.com", "password", "org_no_oauth_key" ) repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].auth_key, auth[:key]) repos = put_in(repos["hexpm"], Map.put(repos["hexpm"], :oauth_exchange, false)) Hex.State.put(:repos, repos) assert {:ok, {200, _, _}} = Hex.Repo.get_package("hexpm:testorg", "foo", "") repos_after = Hex.State.fetch!(:repos) org_repo = Map.get(repos_after, "hexpm:testorg") assert org_repo == nil or Map.get(org_repo, :oauth_token) == nil end test "automatically exchanges API key for OAuth token when making request" do auth = HexTest.Hexpm.new_user( "repo_oauth_user", "repo_oauth@example.com", "password", "repo_key" ) api_key = auth[:key] repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].auth_key, api_key) Hex.State.put(:repos, repos) assert {:ok, {200, _, _}} = Hex.Repo.get_package("hexpm", "postgrex", "") end test "caches OAuth token and reuses it for subsequent requests" do auth = HexTest.Hexpm.new_user("cache_user", "cache@example.com", "password", "cache_key") api_key = auth[:key] repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].auth_key, api_key) Hex.State.put(:repos, repos) repos_before = Hex.State.fetch!(:repos) assert Map.get(repos_before["hexpm"], :oauth_token) == nil assert {:ok, {200, _, _}} = Hex.Repo.get_package("hexpm", "postgrex", "") repos_after = Hex.State.fetch!(:repos) token_data = repos_after["hexpm"].oauth_token assert is_map(token_data) assert is_binary(token_data["access_token"]) assert is_integer(token_data["expires_at"]) first_token = token_data["access_token"] assert {:ok, {200, _, _}} = Hex.Repo.get_package("hexpm", "postgrex", "") repos_after_2 = Hex.State.fetch!(:repos) reused_token = repos_after_2["hexpm"].oauth_token["access_token"] assert reused_token == first_token end test "exchanges for new token when cached token expires" do auth = HexTest.Hexpm.new_user("expiry_user", "expiry@example.com", "password", "expiry_key") api_key = auth[:key] expired_token_data = %{ "access_token" => "expired_token", "expires_at" => System.system_time(:second) - 100 } repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].auth_key, api_key) repos = put_in(repos["hexpm"], Map.put(repos["hexpm"], :oauth_token, expired_token_data)) Hex.Config.update_repos(repos) assert {:ok, {200, _, _}} = Hex.Repo.get_package("hexpm", "postgrex", "") repos_after = Hex.State.fetch!(:repos) new_token = repos_after["hexpm"].oauth_token["access_token"] assert new_token != "expired_token" assert is_binary(new_token) end test "uses API key directly when oauth_exchange is disabled" do auth = HexTest.Hexpm.new_user( "no_oauth_user", "no_oauth@example.com", "password", "no_oauth_key" ) api_key = auth[:key] repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].auth_key, api_key) repos = put_in(repos["hexpm"], Map.put(repos["hexpm"], :oauth_exchange, false)) Hex.State.put(:repos, repos) assert {:ok, {200, _, _}} = Hex.Repo.get_package("hexpm", "postgrex", "") repos_after = Hex.State.fetch!(:repos) assert Map.get(repos_after["hexpm"], :oauth_token) == nil end test "handles multiple API keys independently in cache" do auth1 = HexTest.Hexpm.new_user("multi_user1", "multi1@example.com", "password", "multi_key1") auth2 = HexTest.Hexpm.new_user("multi_user2", "multi2@example.com", "password", "multi_key2") api_key1 = auth1[:key] api_key2 = auth2[:key] repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].auth_key, api_key1) Hex.State.put(:repos, repos) assert {:ok, {200, _, _}} = Hex.Repo.get_package("hexpm", "postgrex", "") repos_after1 = Hex.State.fetch!(:repos) token1 = repos_after1["hexpm"].oauth_token["access_token"] repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].auth_key, api_key2) repos = put_in(repos["hexpm"], Map.delete(repos["hexpm"], :oauth_token)) Hex.State.put(:repos, repos) assert {:ok, {200, _, _}} = Hex.Repo.get_package("hexpm", "postgrex", "") repos_after2 = Hex.State.fetch!(:repos) token2 = repos_after2["hexpm"].oauth_token["access_token"] assert token1 != token2 end test "raises error when OAuth exchange fails" do repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].auth_key, "invalid_key_for_exchange") Hex.State.put(:repos, repos) assert_raise RuntimeError, ~r/Failed to exchange API key for OAuth token/, fn -> Hex.Repo.get_package("hexpm", "postgrex", "") end end test "considers token expired with 5 minute buffer" do auth = HexTest.Hexpm.new_user("buffer_user", "buffer@example.com", "password", "buffer_key") api_key = auth[:key] almost_expired_token = %{ "access_token" => "almost_expired", "expires_at" => System.system_time(:second) + 30 } repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].auth_key, api_key) repos = put_in(repos["hexpm"], Map.put(repos["hexpm"], :oauth_token, almost_expired_token)) Hex.Config.update_repos(repos) assert {:ok, {200, _, _}} = Hex.Repo.get_package("hexpm", "postgrex", "") repos_after = Hex.State.fetch!(:repos) new_token = repos_after["hexpm"].oauth_token["access_token"] assert new_token != "almost_expired" end end describe "OAuth token fallback (device flow)" do test "uses OAuth token when no API key is configured" do future_time = System.system_time(:second) + 3600 token_data = %{ "access_token" => "device_flow_token", "refresh_token" => "device_refresh", "expires_at" => future_time } Hex.OAuth.store_token(token_data) repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].auth_key, nil) Hex.State.put(:repos, repos) assert {:ok, {200, _, _}} = Hex.Repo.get_package("hexpm", "postgrex", "") end test "continues without auth when no API key or OAuth token available" do Hex.OAuth.clear_tokens() repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].auth_key, nil) Hex.State.put(:repos, repos) assert {:ok, {200, _, _}} = Hex.Repo.get_package("hexpm", "postgrex", "") end test "prefers API key exchange over device flow token" do auth = HexTest.Hexpm.new_user( "priority_user", "priority@example.com", "password", "priority_key" ) api_key = auth[:key] future_time = System.system_time(:second) + 3600 token_data = %{ "access_token" => "device_flow_token", "refresh_token" => "device_refresh", "expires_at" => future_time } Hex.OAuth.store_token(token_data) repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].auth_key, api_key) Hex.State.put(:repos, repos) assert {:ok, {200, _, _}} = Hex.Repo.get_package("hexpm", "postgrex", "") repos_after = Hex.State.fetch!(:repos) assert repos_after["hexpm"].oauth_token != nil assert repos_after["hexpm"].oauth_token["access_token"] != "device_flow_token" end test "raises error when configured API key exchange fails (no fallback to device flow)" do future_time = System.system_time(:second) + 3600 token_data = %{ "access_token" => "device_flow_token", "refresh_token" => "device_refresh", "expires_at" => future_time } Hex.OAuth.store_token(token_data) repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].auth_key, "invalid_key") Hex.State.put(:repos, repos) assert_raise RuntimeError, ~r/Failed to exchange API key for OAuth token/, fn -> Hex.Repo.get_package("hexpm", "postgrex", "") end end end end hex-2.4.2/test/hex/scm_test.exs000066400000000000000000000007741517471540100164250ustar00rootroot00000000000000defmodule Hex.SCMTest do use ExUnit.Case, async: true test "guess build_tools" do empty_meta = %{} guessed_meta = %{"build_tools" => ["mix"]} no_tools_meta = %{"files" => ["README.md"]} tools_meta = %{"files" => ["README.md", "mix.exs", "lib"]} assert [] = Hex.SCM.guess_build_tools(empty_meta) assert ["mix"] = Hex.SCM.guess_build_tools(guessed_meta) assert [] = Hex.SCM.guess_build_tools(no_tools_meta) assert ["mix"] = Hex.SCM.guess_build_tools(tools_meta) end end hex-2.4.2/test/hex/server_test.exs000066400000000000000000000007411517471540100171430ustar00rootroot00000000000000defmodule Hex.ServerTest do use ExUnit.Case, async: true test "should_warn_lock_version?/0" do {:ok, pid} = Hex.Server.start_link(name: nil) assert Hex.Server.should_warn_lock_version?(pid) refute Hex.Server.should_warn_lock_version?(pid) end test "should_warn_registry_version?/0" do {:ok, pid} = Hex.Server.start_link(name: nil) assert Hex.Server.should_warn_registry_version?(pid) refute Hex.Server.should_warn_registry_version?(pid) end end hex-2.4.2/test/hex/solver/000077500000000000000000000000001517471540100153655ustar00rootroot00000000000000hex-2.4.2/test/hex/solver/regression_test.exs000066400000000000000000000066441517471540100213370ustar00rootroot00000000000000defmodule Hex.Solver.RegressionTest do use HexTest.Case import HexTest.SolverHelper alias Hex.Registry.Server, as: Registry setup do Hex.State.put(:offline, true) end defp open_registry(filename) do Registry.open(registry_path: HexTest.Case.fixture_path(Path.join("registries", filename))) end # How to create snapshot of registry: # Hex.Dev.extract_registry(top_level_packages, "test/fixtures/registries/CURRENT_DATE.ets") # https://elixirforum.com/t/mix-deps-update-working-as-mix-deps-downgrade/27099/10 test "20200917" do open_registry("20200917.ets") assert solve(jason: "~> 1.0", postgrex: ">= 0.0.0", phoenix_params: "~> 1.1") == %{ connection: "1.0.4", db_connection: "2.2.2", decimal: "1.9.0", jason: "1.2.2", mime: "1.4.0", phoenix: "1.5.4", phoenix_params: "1.1.3", phoenix_pubsub: "2.0.0", plug: "1.10.4", plug_crypto: "1.1.2", postgrex: "0.15.5", telemetry: "0.4.2" } end # https://github.com/hexpm/hex/issues/901 test "issue/901" do open_registry("20210915.ets") result = %{ cowboy: "2.9.0", cowboy_telemetry: "0.3.1", cowlib: "2.11.0", mime: "2.0.1", phoenix: "1.5.12", phoenix_pubsub: "2.0.0", plug: "1.12.1", plug_cowboy: "2.5.2", plug_crypto: "1.2.2", ranch: "1.8.0", telemetry: "0.4.3" } assert solve(phoenix: "~> 1.5", plug_cowboy: "~> 2.0") == result # the order of the deps should not affect the result assert solve(plug_cowboy: "~> 2.0", phoenix: "~> 1.5") == result end test "20210926" do open_registry("20210926.ets") assert solve(chromic_pdf: "~> 1.1", telemetry: "~> 0.4 or ~> 1.0", telemetry_poller: "~> 0.5") == %{ chromic_pdf: "1.1.1", jason: "1.2.2", nimble_pool: "0.2.4", telemetry: "0.4.3", telemetry_poller: "0.5.1" } end test "issue #571" do # earmark and ex_doc at 2018-06-09 setup_registry(Path.join(test_tmp(), "cache.ets"), [ {:hexpm, :earmark, ~w(0.1.0 0.1.1 0.1.2 0.1.3 0.1.4 0.1.5 0.1.6 0.1.7 0.1.8 0.1.9 0.1.10 0.1.11 0.1.12 0.1.13 0.1.14 0.1.15 0.1.16 0.1.17 0.1.18 0.1.19 0.2.0 0.2.1 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5), []}, {:hexpm, :ex_doc, ~w(0.5.1 0.5.2 0.6.0 0.6.1 0.6.2 0.7.0 0.7.1 0.7.2), []}, {:hexpm, :ex_doc, ~w(0.7.3 0.8.0 0.8.1 0.8.2 0.8.3 0.8.4 0.9.0 0.10.0 0.11.0 0.11.1 0.11.2 0.11.3 0.11.4 0.11.5), [{:earmark, "~> 0.1.17 or ~> 0.2", true}]}, {:hexpm, :ex_doc, ~w(0.12.0), [earmark: "~> 0.2"]}, {:hexpm, :ex_doc, ~w(0.13.0 0.13.1 0.13.2 0.14.0 0.14.1 0.14.2 0.14.3 0.14.4 0.14.5), [earmark: "~> 1.0"]}, {:hexpm, :ex_doc, ~w(0.15.0 0.15.1 0.16.0 0.16.1 0.16.2 0.16.3 0.16.4 0.17.0 0.17.1 0.17.2 0.18.0 0.18.1 0.18.2 0.18.3), [earmark: "~> 1.1"]} ]) assert solve(earmark: "~> 0.1", ex_doc: "~> 0.11") == %{earmark: "0.2.1", ex_doc: "0.12.0"} assert solve([earmark: "~> 0.1", ex_doc: "~> 0.11"], earmark: "0.2.1") == %{ earmark: "0.2.1", ex_doc: "0.12.0" } assert solve([earmark: "~> 0.1", ex_doc: "~> 0.11"], ex_doc: "0.12.0") == %{ earmark: "0.2.1", ex_doc: "0.12.0" } end end hex-2.4.2/test/hex/solver_test.exs000066400000000000000000000221731517471540100171520ustar00rootroot00000000000000defmodule Hex.SolverTest do use HexTest.Case import HexTest.SolverHelper alias Hex.Registry.Server, as: Registry defp add_repo(repo) do config = %{ url: repo, public_key: nil, auth_key: nil, api_url: nil, api_key: nil } Hex.State.update!(:repos, &Map.put(&1, repo, config)) end setup do Hex.State.put(:offline, true) Registry.open(registry_path: tmp_path("cache.ets")) :ok end test "simple" do assert solve(foo: nil, bar: nil) == %{foo: "0.2.1", bar: "0.2.0"} assert solve(foo: "0.2.1", bar: "0.2.0") == %{foo: "0.2.1", bar: "0.2.0"} assert solve(foo: "0.2.0", bar: "0.2.0") == %{foo: "0.2.0", bar: "0.2.0"} assert solve(bar: nil, foo: "~> 0.3.0") == """ Because "your app" depends on "foo ~> 0.3.0" which doesn't match any versions, version solving failed.\ """ assert solve(foo: "~> 0.3.0", bar: nil) == """ Because "your app" depends on "foo ~> 0.3.0" which doesn't match any versions, version solving failed.\ """ assert solve(bar: "~> 0.3.0", foo: nil) == """ Because "your app" depends on "bar ~> 0.3.0" which doesn't match any versions, version solving failed.\ """ assert solve(foo: nil, bar: "~> 0.3.0") == """ Because "your app" depends on "bar ~> 0.3.0" which doesn't match any versions, version solving failed.\ """ end test "backtrack" do assert solve(decimal: "0.2.0", ex_plex: "0.2.0") == %{decimal: "0.2.0", ex_plex: "0.2.0"} assert solve(decimal: "0.1.0", ex_plex: ">= 0.1.0") == %{decimal: "0.1.0", ex_plex: "0.1.2"} assert solve(decimal: nil, ex_plex: "< 0.1.0") == %{decimal: "0.2.1", ex_plex: "0.0.1"} assert solve(ex_plex: "< 0.1.0", decimal: nil) == %{decimal: "0.2.1", ex_plex: "0.0.1"} assert solve(decimal: "0.1.0", ex_plex: "< 0.1.0") == %{decimal: "0.1.0", ex_plex: "0.0.1"} assert solve(ex_plex: "~> 0.0.2", decimal: "0.1.0") == """ Because "ex_plex >= 0.0.2 and < 0.1.0" depends on "decimal 0.1.1" and "your app" depends on "decimal 0.1.0", "ex_plex >= 0.0.2 and < 0.1.0" is forbidden. So, because "your app" depends on "ex_plex ~> 0.0.2", version solving failed.\ """ assert solve(decimal: "0.1.0", ex_plex: "~> 0.0.2") == """ Because "ex_plex >= 0.0.2 and < 0.1.0" depends on "decimal 0.1.1" and "your app" depends on "decimal 0.1.0", "ex_plex >= 0.0.2 and < 0.1.0" is forbidden. So, because "your app" depends on "ex_plex ~> 0.0.2", version solving failed.\ """ assert solve(ex_plex: "0.0.2", decimal: nil) == """ Because "ex_plex >= 0.0.2 and < 0.1.0" depends on "decimal 0.1.1" which doesn't match any versions, "ex_plex >= 0.0.2 and < 0.1.0" is forbidden. So, because "your app" depends on "ex_plex 0.0.2", version solving failed.\ """ assert solve(decimal: nil, ex_plex: "0.0.2") == """ Because "ex_plex >= 0.0.2 and < 0.1.0" depends on "decimal 0.1.1" which doesn't match any versions, "ex_plex >= 0.0.2 and < 0.1.0" is forbidden. So, because "your app" depends on "ex_plex 0.0.2", version solving failed.\ """ end test "complete backtrack" do assert solve(jose: nil, eric: nil) == %{jose: "0.2.1", eric: "0.0.2"} assert solve(eric: nil, jose: nil) == %{jose: "0.2.1", eric: "0.0.2"} end test "backtrack with multiple parents" do assert solve(phoenix: "~> 1.1.3", phoenix_ecto: "~> 2.0", phoenix_live_reload: "~> 1.0") == %{ ecto: "1.1.0", phoenix: "1.1.3", phoenix_ecto: "2.0.1", phoenix_live_reload: "1.0.3", poison: "1.5.2" } assert solve(phoenix: nil, phoenix_ecto: "~> 2.0", phoenix_live_reload: "~> 1.0") == %{ ecto: "1.1.0", phoenix: "1.1.3", phoenix_ecto: "2.0.1", phoenix_live_reload: "1.0.3", poison: "1.5.2" } end test "locked" do assert solve([decimal: nil, ex_plex: nil], decimal: "0.2.0") == %{ decimal: "0.2.0", ex_plex: "0.2.0" } assert solve([decimal: nil, ex_plex: nil], decimal: "0.1.0") == %{ decimal: "0.1.0", ex_plex: "0.1.2" } assert solve([decimal: nil, ex_plex: nil], decimal: "0.0.1") == %{ decimal: "0.0.1", ex_plex: "0.0.1" } assert solve([decimal: "0.1.0", ex_plex: nil], ex_plex: "0.1.0") == %{ decimal: "0.1.0", ex_plex: "0.1.0" } assert solve([decimal: "0.1.0", ex_plex: nil], ex_plex: "0.1.0", decimal: "0.1.0") == %{ decimal: "0.1.0", ex_plex: "0.1.0" } assert solve([decimal: nil, ex_plex: nil], ex_plex: "0.1.0", decimal: "0.1.0") == %{ decimal: "0.1.0", ex_plex: "0.1.0" } assert solve([], ex_plex: "0.1.0", decimal: "0.1.0") == %{} end test "failure due to locked dep" do assert solve([ex_plex: "0.1.0", decimal: nil], decimal: "0.0.1") == """ Because "the lock" specifies "decimal 0.0.1" and "ex_plex >= 0.1.0 and < 0.2.0" depends on "decimal ~> 0.1.0", "the lock" is incompatible with "ex_plex >= 0.1.0 and < 0.2.0". And because "your app" depends on "the lock", "ex_plex >= 0.1.0 and < 0.2.0" is forbidden. So, because "your app" depends on "ex_plex 0.1.0", version solving failed.\ """ assert solve([decimal: nil, ex_plex: "0.1.0"], decimal: "0.0.1") == """ Because "the lock" specifies "decimal 0.0.1" and "ex_plex >= 0.1.0 and < 0.2.0" depends on "decimal ~> 0.1.0", "the lock" is incompatible with "ex_plex >= 0.1.0 and < 0.2.0". And because "your app" depends on "the lock", "ex_plex >= 0.1.0 and < 0.2.0" is forbidden. So, because "your app" depends on "ex_plex 0.1.0", version solving failed.\ """ assert solve([ex_plex: "0.1.0", decimal: "~> 0.0.1"], decimal: "0.0.1") == """ Because "ex_plex >= 0.1.0 and < 0.2.0" depends on "decimal ~> 0.1.0" and "your app" depends on "decimal ~> 0.0.1", "ex_plex >= 0.1.0 and < 0.2.0" is forbidden. So, because "your app" depends on "ex_plex 0.1.0", version solving failed.\ """ assert solve([decimal: "~> 0.0.1", ex_plex: "0.1.0"], decimal: "0.0.1") == """ Because "ex_plex >= 0.1.0 and < 0.2.0" depends on "decimal ~> 0.1.0" and "your app" depends on "decimal ~> 0.0.1", "ex_plex >= 0.1.0 and < 0.2.0" is forbidden. So, because "your app" depends on "ex_plex 0.1.0", version solving failed.\ """ end test "pre-release" do assert solve(beta: "~> 1.0") == %{beta: "1.0.0"} assert solve(beta: "~> 1.0 and >= 1.0.0") == %{beta: "1.0.0"} assert solve(beta: "~> 1.0-beta and >= 1.0.0-beta") == %{beta: "1.0.0"} assert solve(beta: "~> 1.1-beta and >= 1.1.0-beta") == %{beta: "1.1.0-beta"} end test "only mix.exs conflicts" do assert solve(decimal: "~> 0.0.1", ex_plex: "0.2.0") == """ Because "ex_plex >= 0.2.0" depends on "decimal ~> 0.2.0" and "your app" depends on "decimal ~> 0.0.1", "ex_plex >= 0.2.0" is forbidden. So, because "your app" depends on "ex_plex 0.2.0", version solving failed.\ """ end test "optional" do assert solve(ex_doc: nil, has_optional: nil) == %{ex_doc: "0.0.2", has_optional: "0.1.0"} end test "multiple repos" do add_repo("repo2") assert solve([{:repo2_deps, ">= 0.0.0", repo: "repo2"}]) == %{ {"repo2", :repo2_deps} => "0.1.0", {"repo2", :poison} => "2.0.0" } assert solve([{:hexpm_deps, ">= 0.0.0", repo: "repo2"}]) == %{ :poison => "2.0.0", {"repo2", :hexpm_deps} => "0.1.0" } assert assert solve([{:repo2_deps, ">= 0.0.0", repo: "repo2"}, {:phoenix, ">= 0.0.0"}]) == %{ :ex_doc => "0.1.0", :phoenix => "0.0.1", :postgrex => "0.2.1", {"repo2", :poison} => "2.0.0", {"repo2", :repo2_deps} => "0.1.0" } assert assert solve([{:repo2_deps, ">= 0.0.0", repo: "repo2"}, {:phoenix, ">= 1.0.0"}]) == """ Because every version of "repo2/repo2_deps" depends on "repo2/poison >= 0.0.0" and "phoenix >= 1.1.2" depends on "poison ~> 1.5 or ~> 2.0", "repo2/repo2_deps" is incompatible with "phoenix >= 1.1.2". And because no versions of "phoenix" match ">= 1.0.0 and < 1.1.2", "repo2/repo2_deps" is incompatible with "phoenix >= 1.0.0". And because "your app" depends on "phoenix >= 1.0.0", no version of "repo2/repo2_deps" is allowed. So, because "your app" depends on "repo2/repo2_deps >= 0.0.0", version solving failed.\ """ end test "implicit override repo" do add_repo("repo2") assert solve([{:hexpm_deps, ">= 0.0.0", repo: "repo2"}, {:poison, ">= 0.0.0"}]) == %{ :poison => "2.0.0", {"repo2", :hexpm_deps} => "0.1.0" } end test "do not override locked deps" do assert solve([{:foo, "~> 0.2.0", override: true}], [{:foo, "0.2.0"}]) == %{:foo => "0.2.0"} end end hex-2.4.2/test/hex/update_checker_test.exs000066400000000000000000000040061517471540100206010ustar00rootroot00000000000000defmodule Hex.UpdateCheckerTest do use HexTest.Case alias Hex.UpdateChecker defp bypass_csv(versions) do bypass = Bypass.open() repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].url, "http://localhost:#{bypass.port}") Hex.State.put(:repos, repos) Bypass.expect(bypass, fn %Plug.Conn{request_path: "/installs/hex-1.x.csv"} = conn -> Plug.Conn.resp(conn, 200, versions_to_csv(versions)) end) bypass end defp versions_to_csv(versions) do Enum.map_join(versions, "\n", fn {hex, elixir} -> "#{hex},DIGEST,#{elixir}" end) end setup do Hex.Registry.Server.open(check_version: false) Hex.Registry.Server.last_update({{2010, 1, 1}, {0, 0, 0}}) :ok end test "display new hex version" do flush() bypass_csv([{"100.0.0", "1.0.0"}]) {:ok, pid} = UpdateChecker.start_link(name: nil) GenServer.cast(pid, :start_check) assert {:version, _} = GenServer.call(pid, :check) end test "dont display same hex version" do flush() bypass_csv([{"0.0.1", "1.0.0"}]) {:ok, pid} = UpdateChecker.start_link(name: nil) GenServer.cast(pid, :start_check) assert :latest = GenServer.call(pid, :check) end test "dont display new hex version for too new elixir" do flush() bypass_csv([{"100.0.0", "100.0.0"}]) {:ok, pid} = UpdateChecker.start_link(name: nil) GenServer.cast(pid, :start_check) assert :latest = GenServer.call(pid, :check) end test "only check version once" do flush() bypass_csv([{"100.0.0", "1.0.0"}]) {:ok, pid} = UpdateChecker.start_link(name: nil) GenServer.cast(pid, :start_check) assert {:version, _} = GenServer.call(pid, :check) GenServer.cast(pid, :start_check) assert :already_checked = GenServer.call(pid, :check) end test "handle check timeout" do flush() init_state = %{started: true, check_timeout: 1} {:ok, pid} = UpdateChecker.start_link(name: nil, init_state: init_state) assert :timeout = GenServer.call(pid, :check) end end hex-2.4.2/test/mix/000077500000000000000000000000001517471540100140645ustar00rootroot00000000000000hex-2.4.2/test/mix/tasks/000077500000000000000000000000001517471540100152115ustar00rootroot00000000000000hex-2.4.2/test/mix/tasks/hex.audit_test.exs000066400000000000000000000051611517471540100206650ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.AuditTest do use HexTest.IntegrationCase @package :test_package @package_name Atom.to_string(@package) defmodule RetiredDeps.MixProject do def project do [app: :test_app, version: "0.0.1", deps: [{:test_package, ">= 0.1.0"}]] end end setup_all do auth = Hexpm.new_user("audit_user", "audit@mail.com", "passpass", "key") {:ok, [auth: auth]} end test "audit (retired package without a message)", context do with_test_package("0.1.0", context, fn -> retire_test_package("0.1.0", "security") assert catch_throw(Mix.Task.run("hex.audit")) == {:exit_code, 1} assert_output_row(@package_name, "0.1.0", "(security)") assert_received {:mix_shell, :error, ["Found retired packages"]} end) end test "audit (retired package with a custom message)", context do with_test_package("0.2.0", context, fn -> retire_test_package("0.2.0", "invalid", "Superseded by v1.0.0") assert catch_throw(Mix.Task.run("hex.audit")) == {:exit_code, 1} assert_output_row(@package_name, "0.2.0", "(invalid) Superseded by v1.0.0") assert_received {:mix_shell, :error, ["Found retired packages"]} end) end test "audit (no retired packages)", context do with_test_package("1.0.0", context, fn -> Mix.Task.run("hex.audit") assert_received {:mix_shell, :info, ["No retired packages found"]} end) end def with_test_package(version, %{auth: auth}, fun) do Mix.Project.push(RetiredDeps.MixProject) Hexpm.new_package("hexpm", @package_name, version, [], %{}, auth) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) Hex.State.put(:api_key, auth[:key]) Mix.Dep.Lock.write(%{@package => {:hex, @package, version}}) Mix.Task.run("deps.get") flush() fun.() end) end defp retire_test_package(version, reason, message \\ "") do Mix.Tasks.Hex.Retire.run([@package_name, version, reason, "--message", message]) # Mix does not support the RemoteConverger.post_converge/0 callback on Elixir < 1.4, # so we need to explicitly reset the registry. Hex.Registry.Server.close() end defp assert_output_row(package, version, message) do whitespace_length = String.length("Retirement reason ") - String.length(message) whitespace_length = if whitespace_length < 2, do: 2, else: whitespace_length output = [ [package, :reset, " "], [version, :reset, " "], [message, :reset, String.duplicate(" ", whitespace_length)] ] |> IO.ANSI.format() |> List.to_string() assert_received {:mix_shell, :info, [^output]} end end hex-2.4.2/test/mix/tasks/hex.build_test.exs000066400000000000000000000327021517471540100206570ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.BuildTest do use HexTest.IntegrationCase defp package_created?(name) do File.exists?("#{name}.tar") end defp extract(name, path) do {:ok, files} = :mix_hex_erl_tar.extract(name, [:memory]) files = Map.new(files) :ok = :mix_hex_erl_tar.extract({:binary, files[~c"contents.tar.gz"]}, [:compressed, cwd: path]) end test "create" do Process.put(:hex_test_app_name, :build_app_name) Mix.Project.push(ReleaseSimple.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) File.write!("myfile.txt", "hello") File.chmod!("myfile.txt", 0o100644) Mix.Tasks.Hex.Build.run([]) assert package_created?("build_app_name-0.0.1") end) after purge([ReleaseSimple.MixProject]) end test "create with missing licenses" do Process.put(:hex_test_app_name, :release_missing_licenses) Mix.Project.push(ReleaseMissingLicenses.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) File.write!("myfile.txt", "hello") Mix.Tasks.Hex.Build.run([]) assert_received {:mix_shell, :info, ["\e[33m\nYou have not included any licenses\n\e[0m"]} assert package_created?("release_missing_licenses-0.0.1") end) after purge([ReleaseMissingLicenses.MixProject]) end test "create with invalid licenses" do Process.put(:hex_test_app_name, :release_invalid_licenses) Mix.Project.push(ReleaseInvalidLicenses.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) File.write!("myfile.txt", "hello") Mix.Tasks.Hex.Build.run([]) assert_received {:mix_shell, :info, [ "\e[33mThe following licenses are not recognized by SPDX:\n * CustomLicense\n\nValid license identifiers are available from https://spdx.org/licenses\e[0m" ]} assert package_created?("release_invalid_licenses-0.0.1") end) after purge([ReleaseInvalidLicenses.MixProject]) end test "create private package with invalid licenses" do Process.put(:hex_test_app_name, :release_repo_invalid_licenses) Mix.Project.push(ReleaseRepoInvalidLicenses.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) File.write!("myfile.txt", "hello") Mix.Tasks.Hex.Build.run([]) refute_received {:mix_shell, :info, [ "\e[33mThe following licenses are not recognized by SPDX:\n * CustomLicense\n\nValid license identifiers are available from https://spdx.org/licenses\e[0m" ]} assert package_created?("release_repo_invalid_licenses-0.0.1") end) after purge([ReleaseRepoInvalidLicenses.MixProject]) end test "create with package name" do Process.put(:hex_test_package_name, :build_package_name) Mix.Project.push(ReleaseName.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) File.write!("myfile.txt", "hello") File.chmod!("myfile.txt", 0o100644) Mix.Tasks.Hex.Build.run([]) assert package_created?("build_package_name-0.0.1") end) after purge([ReleaseName.MixProject]) end test "create with files" do Process.put(:hex_test_app_name, :build_with_files) Mix.Project.push(ReleaseFiles.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) File.mkdir!("dir") File.mkdir!("empty_dir") File.write!("dir/.dotfile", "") File.ln_s("dir2", "dir/a_link_to_dir2") File.mkdir!("dir/dir2") File.ln_s("empty_dir", "link_dir") # mtime_dir = File.stat!("dir").mtime mtime_empty_dir = File.stat!("empty_dir").mtime mtime_file = File.stat!("dir/.dotfile").mtime mtime_link = File.stat!("link_dir").mtime File.write!("myfile.txt", "hello") File.write!("executable.sh", "world") File.write!("dir/dir2/test.txt", "and") File.chmod!("myfile.txt", 0o100644) File.chmod!("executable.sh", 0o100755) File.chmod!("dir/dir2/test.txt", 0o100644) Mix.Tasks.Hex.Build.run([]) extract("build_with_files-0.0.1.tar", "unzip") # Check that mtimes are not retained for files and directories and symlinks # erl_tar does not set mtime from tar if a directory contain files # assert File.stat!("unzip/dir").mtime != mtime_dir assert File.stat!("unzip/empty_dir").mtime != mtime_empty_dir assert File.stat!("unzip/dir/.dotfile").mtime != mtime_file assert File.stat!("unzip/link_dir").mtime != mtime_link assert File.lstat!("unzip/link_dir").type == :symlink assert File.lstat!("unzip/dir/a_link_to_dir2").type == :symlink assert File.lstat!("unzip/empty_dir").type == :directory assert File.read!("unzip/myfile.txt") == "hello" assert File.read!("unzip/dir/.dotfile") == "" assert File.read!("unzip/dir/dir2/test.txt") == "and" assert File.stat!("unzip/myfile.txt").mode == 0o100644 assert File.stat!("unzip/executable.sh").mode == 0o100755 end) after purge([ReleaseFiles.MixProject]) end test "create with excluded files" do Process.put(:hex_test_app_name, :build_with_excluded_files) Mix.Project.push(ReleaseExcludePatterns.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) File.write!("myfile.txt", "hello") File.write!("exclude.txt", "world") File.chmod!("myfile.txt", 0o100644) File.chmod!("exclude.txt", 0o100644) Mix.Tasks.Hex.Build.run([]) extract("build_with_excluded_files-0.0.1.tar", "unzip") assert File.ls!("unzip/") == ["myfile.txt"] assert File.read!("unzip/myfile.txt") == "hello" assert File.stat!("unzip/myfile.txt").mode == 0o100644 end) after purge([ReleaseExcludePatterns.MixProject]) end test "create with custom output path" do Process.put(:hex_test_app_name, :build_custom_output_path) Mix.Project.push(Sample.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) File.write!("mix.exs", "mix.exs") File.chmod!("mix.exs", 0o100644) File.write!("myfile.txt", "hello") File.chmod!("myfile.txt", 0o100644) Mix.Tasks.Hex.Build.run(["-o", "custom.tar"]) assert File.exists?("custom.tar") end) after purge([Sample.MixProject]) end test "create with deps" do Process.put(:hex_test_app_name, :build_with_deps) Mix.Project.push(ReleaseDeps.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) Mix.Tasks.Deps.Get.run([]) error_msg = "Stopping package build due to errors.\nMissing metadata fields: links" assert_raise Mix.Error, error_msg, fn -> Mix.Tasks.Hex.Build.run([]) assert_received {:mix_shell, :error, ["No files"]} refute package_created?("release_b-0.0.2") end end) after purge([ReleaseDeps.MixProject]) end # TODO: convert to integration test test "create with custom repo deps" do Process.put(:hex_test_app_name, :build_with_custom_repo_deps) Mix.Project.push(ReleaseCustomRepoDeps.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) build = Mix.Tasks.Hex.Build.prepare_package() assert [ %{name: "ex_doc", repository: "hexpm"}, %{name: "ecto", repository: "my_repo"} ] = build.meta.requirements end) after purge([ReleaseCustomRepoDeps.MixProject]) end test "errors when there is a git dependency" do Process.put(:hex_test_app_name, :build_git_dependency) Mix.Project.push(ReleaseGitDeps.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) error_msg = "Stopping package build due to errors.\n" <> "Dependencies excluded from the package (only Hex packages can be dependencies): ecto, gettext" assert_raise Mix.Error, error_msg, fn -> Mix.Tasks.Hex.Build.run([]) end end) after purge([ReleaseGitDeps.MixProject]) end test "errors with app false dependency" do Process.put(:hex_test_app_name, :build_app_false_dependency) Mix.Project.push(ReleaseAppFalseDep.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) error_msg = "Can't build package when :app is set for dependency ex_doc, remove `app: ...`" assert_raise Mix.Error, error_msg, fn -> Mix.Tasks.Hex.Build.run([]) end end) after purge([ReleaseAppFalseDep.MixProject]) end test "create with meta" do Process.put(:hex_test_app_name, :build_with_meta) Mix.Project.push(ReleaseMeta.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) error_msg = "Stopping package build due to errors.\n" <> "Missing files: missing.txt, missing/*" assert_raise Mix.Error, error_msg, fn -> File.write!("myfile.txt", "hello") Mix.Tasks.Hex.Build.run([]) assert_received {:mix_shell, :info, ["Building release_c 0.0.3"]} assert_received {:mix_shell, :info, [" Files:"]} assert_received {:mix_shell, :info, [" myfile.txt"]} end end) after purge([ReleaseMeta.MixProject]) end test "reject package if description is missing" do Process.put(:hex_test_app_name, :build_no_description) Mix.Project.push(ReleaseNoDescription.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) error_msg = "Stopping package build due to errors.\n" <> "Missing metadata fields: description, licenses, links" assert_raise Mix.Error, error_msg, fn -> Mix.Tasks.Hex.Build.run([]) assert_received {:mix_shell, :info, ["Building release_e 0.0.1"]} refute package_created?("release_e-0.0.1") end end) after purge([ReleaseNoDescription.MixProject]) end test "error if description is too long" do Process.put(:hex_test_app_name, :build_too_long_description) Mix.Project.push(ReleaseTooLongDescription.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) error_msg = "Stopping package build due to errors.\n" <> "Missing metadata fields: licenses, links\n" <> "Package description is too long (exceeds 300 characters)" assert_raise Mix.Error, error_msg, fn -> Mix.Tasks.Hex.Build.run([]) end end) after purge([ReleaseTooLongDescription.MixProject]) end test "error if misspelled organization" do Process.put(:hex_test_app_name, :build_misspelled_organization) Mix.Project.push(ReleaseMisspelledOrganization.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) error_msg = "Invalid Hex package config :organisation, use spelling :organization" assert_raise Mix.Error, error_msg, fn -> Mix.Tasks.Hex.Build.run([]) end end) after purge([ReleaseMisspelledOrganization.MixProject]) end test "warn if misplaced config" do Process.put(:hex_test_app_name, :build_warn_config_location) Mix.Project.push(ReleaseOrganizationWrongLocation.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) File.write!("myfile.txt", "hello") File.chmod!("myfile.txt", 0o100644) Mix.Tasks.Hex.Build.run([]) assert_received {:mix_shell, :info, ["Building build_warn_config_location 0.0.1"]} message = "\e[33mMix project configuration :organization belongs under the :package key, " <> "did you misplace it?\e[0m" assert_received {:mix_shell, :info, [^message]} end) after purge([ReleaseOrganizationWrongLocation.MixProject]) end test "error if hex_metadata.config is included" do Process.put(:hex_test_app_name, :build_reserved_file) Mix.Project.push(ReleaseIncludeReservedFile.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) error_msg = "Stopping package build due to errors.\n" <> "Do not include this file: hex_metadata.config" assert_raise Mix.Error, error_msg, fn -> File.write!("hex_metadata.config", "hello") Mix.Tasks.Hex.Build.run([]) end end) after purge([ReleaseIncludeReservedFile.MixProject]) end test "errors with umbrella deps" do Process.put(:hex_test_app_name, :includes_umbrella_deps) Mix.Project.push(ReleaseInUmbrellaDeps.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) File.write!("myfile.txt", "hello") File.chmod!("myfile.txt", 0o100644) error_msg = "Stopping package build due to errors.\nDependencies excluded from the package (only Hex packages can be dependencies): ecto" assert_raise Mix.Error, error_msg, fn -> Mix.Tasks.Hex.Build.run([]) end end) after purge([ReleaseInUmbrellaDeps.MixProject]) end test "build and unpack" do Process.put(:hex_test_app_name, :build_and_unpack) Mix.Project.push(Sample.MixProject) in_fixture("sample", fn -> Hex.State.put(:cache_home, tmp_path()) File.write!("myfile.txt", "hello") File.chmod!("myfile.txt", 0o100644) Mix.Tasks.Hex.Build.run(["--unpack"]) assert_received({:mix_shell, :info, ["Saved to build_and_unpack-0.0.1"]}) assert File.exists?("build_and_unpack-0.0.1/mix.exs") assert File.exists?("build_and_unpack-0.0.1/hex_metadata.config") Mix.Tasks.Hex.Build.run(["--unpack", "-o", "custom"]) assert_received({:mix_shell, :info, ["Saved to custom"]}) assert File.exists?("custom/mix.exs") assert File.exists?("custom/hex_metadata.config") end) after purge([Sample.MixProject]) end end hex-2.4.2/test/mix/tasks/hex.config_test.exs000066400000000000000000000060651517471540100210300ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.ConfigTest do use HexTest.Case test "config" do Process.put(:hex_test_app_name, :config_custom_api_url) Mix.Project.push(ReleaseCustomApiUrl.MixProject) in_tmp(fn -> System.put_env("HEX_HOME", File.cwd!()) Hex.State.refresh() Mix.Tasks.Hex.Config.run([]) assert_received {:mix_shell, :info, ["api_url: \"https://custom\" (using `mix.exs`)"]} assert_received {:mix_shell, :info, ["api_key: nil (default)"]} assert_received {:mix_shell, :info, ["offline: false (default)"]} assert_received {:mix_shell, :info, ["unsafe_https: false (default)"]} assert_received {:mix_shell, :info, ["unsafe_registry: false (default)"]} assert_received {:mix_shell, :info, ["http_proxy: nil (default)"]} assert_received {:mix_shell, :info, ["https_proxy: nil (default)"]} assert_received {:mix_shell, :info, ["no_proxy: nil (default)"]} assert_received {:mix_shell, :info, ["http_concurrency: 8 (default)"]} assert_received {:mix_shell, :info, ["http_timeout: nil (default)"]} assert_received {:mix_shell, :info, ["mirror_url: nil (default)"]} assert_received {:mix_shell, :info, ["trusted_mirror_url: nil (default)"]} assert_received {:mix_shell, :info, ["config_home:" <> _]} assert_received {:mix_shell, :info, ["no_short_urls: false (default)"]} end) after purge([ReleaseCustomApiUrl.MixProject]) end test "config key" do in_tmp(fn -> System.put_env("HEX_HOME", File.cwd!()) Hex.State.refresh() Mix.Tasks.Hex.Config.run(["offline", "--delete"]) Mix.Tasks.Hex.Config.run(["offline"]) assert_received {:mix_shell, :info, ["false"]} System.put_env("HEX_OFFLINE", "true") Hex.State.refresh() Mix.Tasks.Hex.Config.run(["offline"]) assert_received {:mix_shell, :info, ["true"]} System.delete_env("HEX_OFFLINE") Hex.State.refresh() Mix.Tasks.Hex.Config.run(["offline"]) assert_received {:mix_shell, :info, ["false"]} assert_raise Mix.Error, "Invalid key foo", fn -> Mix.Tasks.Hex.Config.run(["foo", "bar"]) end end) end test "api_key" do in_tmp(fn -> System.put_env("HEX_HOME", File.cwd!()) Hex.State.refresh() Mix.Tasks.Hex.Config.run([]) assert_received {:mix_shell, :info, ["api_key: nil (default)"]} Mix.Tasks.Hex.Config.run(["api_key", "foo"]) Hex.State.refresh() Mix.Tasks.Hex.Config.run([]) assert_received {:mix_shell, :info, ["api_key: \"foo\" (using " <> _]} Mix.Tasks.Hex.Config.run(["api_key", "--delete"]) Hex.State.refresh() Mix.Tasks.Hex.Config.run([]) assert_received {:mix_shell, :info, ["api_key: nil (default)"]} end) end test "direct api" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) assert Hex.Config.read() == [] Hex.Config.update(key: "value") assert Hex.Config.read() == [key: "value"] Hex.Config.update(key: "other", foo: :bar) assert Hex.Config.read() == [key: "other", foo: :bar] end) end end hex-2.4.2/test/mix/tasks/hex.docs_test.exs000066400000000000000000000273431517471540100205150ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.DocsTest do use HexTest.IntegrationCase defmodule ExampleDeps.MixProject do def project do [ app: :example_app, version: "0.1.0", deps: [] ] end end setup_all do auth = Hexpm.new_key(user: "user", pass: "hunter42") Hexpm.new_package("hexpm", "docs_package", "1.1.1", %{}, %{}, auth) Hexpm.new_package("hexpm", "docs_package", "1.1.2", %{}, %{}, auth) Hexpm.new_package("hexpm", "docs_package", "2.0.0-rc1", %{}, %{}, auth) Hexpm.new_package("hexpm", "pre_only_package", "0.0.1-rc1", %{}, %{}, auth) :ok end test "fetch and open all packages in this applications deps" do Mix.Project.push(ExampleDeps.MixProject) bypass_mirror() Hex.State.put(:data_home, tmp_path()) docs_home = Path.join(Hex.State.fetch!(:data_home), "docs") in_tmp("docs", fn -> Mix.Dep.Lock.write(%{docs_package: {:hex, :docs_package, "1.1.2"}}) Mix.Tasks.Hex.Docs.run(["fetch"]) fetched_msg = "Docs fetched: #{docs_home}/hexpm/docs_package/1.1.2" assert_received {:mix_shell, :info, [^fetched_msg]} assert File.exists?("#{docs_home}/hexpm/docs_package/1.1.2") end) end test "fetch the version of a dependency from this apps lock file" do Mix.Project.push(ExampleDeps.MixProject) bypass_mirror() Hex.State.put(:data_home, tmp_path()) docs_home = Path.join(Hex.State.fetch!(:data_home), "docs") in_tmp("docs", fn -> Mix.Dep.Lock.write(%{docs_package: {:hex, :docs_package, "1.1.2"}}) Mix.Tasks.Hex.Docs.run(["fetch", "docs_package"]) fetched_msg = "Docs fetched: #{docs_home}/hexpm/docs_package/1.1.2" assert_received {:mix_shell, :info, [^fetched_msg]} assert File.exists?("#{docs_home}/hexpm/docs_package/1.1.2") end) end test "fetch the latest version of a package" do package = "docs_package" latest_version = "1.1.2" bypass_mirror() Hex.State.put(:data_home, tmp_path()) docs_home = Path.join(Hex.State.fetch!(:data_home), "docs") org_dir = "hexpm" in_tmp("docs", fn -> Mix.Tasks.Hex.Docs.run(["fetch", package]) fetched_msg = "Docs fetched: #{docs_home}/#{org_dir}/#{package}/#{latest_version}" assert_received {:mix_shell, :info, [^fetched_msg]} Mix.Tasks.Hex.Docs.run(["fetch", package]) already_fetched_msg = "Docs already fetched: #{docs_home}/#{org_dir}/#{package}/#{latest_version}" assert_received {:mix_shell, :info, [^already_fetched_msg]} end) end test "when the only release is a pre-release, return that version" do package = "pre_only_package" latest_version = "0.0.1-rc1" bypass_mirror() Hex.State.put(:data_home, tmp_path()) docs_home = Path.join(Hex.State.fetch!(:data_home), "docs") org_dir = "hexpm" in_tmp("docs", fn -> Mix.Tasks.Hex.Docs.run(["fetch", package]) fetched_msg = "Docs fetched: #{docs_home}/#{org_dir}/#{package}/#{latest_version}" assert_received {:mix_shell, :info, [^fetched_msg]} Mix.Tasks.Hex.Docs.run(["fetch", package]) already_fetched_msg = "Docs already fetched: #{docs_home}/#{org_dir}/#{package}/#{latest_version}" assert_received {:mix_shell, :info, [^already_fetched_msg]} end) end test "fetch the latest version of a package using the latest flag" do Mix.Project.push(ExampleDeps.MixProject) bypass_mirror() Hex.State.put(:data_home, tmp_path()) docs_home = Path.join(Hex.State.fetch!(:data_home), "docs") in_tmp("docs", fn -> Mix.Dep.Lock.write(%{docs_package: {:hex, :docs_package, "1.1.1"}}) Mix.Tasks.Hex.Docs.run(["fetch", "docs_package", "--latest"]) fetched_msg = "Docs fetched: #{docs_home}/hexpm/docs_package/1.1.2" assert_received {:mix_shell, :info, [^fetched_msg]} assert File.exists?("#{docs_home}/hexpm/docs_package/1.1.2") end) end test "fetch a specific version of a package" do package = "docs_package" version = "1.1.2" bypass_mirror() Hex.State.put(:data_home, tmp_path()) docs_home = Path.join(Hex.State.fetch!(:data_home), "docs") org_dir = "hexpm" in_tmp("docs", fn -> Mix.Tasks.Hex.Docs.run(["fetch", package, version]) fetched_msg = "Docs fetched: #{docs_home}/#{org_dir}/#{package}/#{version}" assert_received {:mix_shell, :info, [^fetched_msg]} Mix.Tasks.Hex.Docs.run(["fetch", package, version]) already_fetched_msg = "Docs already fetched: #{docs_home}/#{org_dir}/#{package}/#{version}" assert_received {:mix_shell, :info, [^already_fetched_msg]} end) end test "fetch a specific version of a package that exists in the fallback location" do package = "docs_package" version = "1.1.2" bypass_mirror() Hex.State.put(:data_home, tmp_path()) docs_home = Path.join(Hex.State.fetch!(:data_home), "docs") org_dir = "hexpm" in_tmp("docs", fn -> Mix.Tasks.Hex.Docs.run(["fetch", package, version]) File.cp_r!("#{docs_home}/#{org_dir}/#{package}", "#{docs_home}/#{package}") File.rm_rf!("#{docs_home}/#{org_dir}/#{package}/#{version}") Mix.Tasks.Hex.Docs.run(["fetch", package, version]) already_fetched_msg = "Docs already fetched: #{docs_home}/#{package}/#{version}" assert_received {:mix_shell, :info, [^already_fetched_msg]} end) end test "fetch a package that does not exist" do package = "package_not_found" not_found_msg = "No package with name #{package}" assert_raise Mix.Error, not_found_msg, fn -> Mix.Tasks.Hex.Docs.run(["fetch", package]) end end test "invalid arguments for docs task" do assert_raise Mix.Error, ~r"Invalid arguments", fn -> Mix.Tasks.Hex.Docs.run(["invalid", "command"]) end end test "fetch and open tasks fails when package name is not provided" do msg = "Specify a package name or run inside a Mix project to fetch docs for all dependencies" assert_raise Mix.Error, msg, fn -> Mix.Tasks.Hex.Docs.run(["fetch"]) end assert_raise Mix.Error, "You must specify the name of a package", fn -> Mix.Tasks.Hex.Docs.run(["offline"]) end end test "offline task fails when docs not found" do Mix.Tasks.Hex.Docs.run(["offline", "decimal", "1.1.2"]) message = "Couldn't find docs for package with name decimal or version 1.1.2" assert_received {:mix_shell, :error, [^message]} end test "offline task fails when index file in docs not found" do Mix.Tasks.Hex.Docs.run(["offline", "decimal", "1.1.2"]) message = "Couldn't find docs for package with name decimal or version 1.1.2" assert_received {:mix_shell, :error, [^message]} end test "open latest version offline using offline task" do package = "docs_package" latest_version = "1.1.2" bypass_mirror() Hex.State.put(:data_home, tmp_path()) docs_home = Path.join(Hex.State.fetch!(:data_home), "docs") org_dir = "hexpm" in_tmp("docs", fn -> Mix.Tasks.Hex.Docs.run(["offline", package]) fetched_msg = "Docs fetched: #{docs_home}/#{org_dir}/#{package}/#{latest_version}" browser_open_msg = "#{docs_home}/#{org_dir}/#{package}/#{latest_version}/index.html" assert_received {:mix_shell, :info, [^fetched_msg]} assert_received {:hex_system_cmd, _cmd, browser_open_cmd} assert Enum.fetch!(browser_open_cmd, -1) == browser_open_msg end) end test "open latest version in epub offline using offline task" do package = "docs_package" latest_version = "1.1.2" bypass_mirror() Hex.State.put(:data_home, tmp_path()) docs_home = Path.join(Hex.State.fetch!(:data_home), "docs") org_dir = "hexpm" in_tmp("docs", fn -> Mix.Tasks.Hex.Docs.run(["offline", package, "--format", "epub"]) fetched_msg = "Docs fetched: #{docs_home}/#{org_dir}/#{package}/#{latest_version}" browser_open_msg = "#{docs_home}/#{org_dir}/#{package}/#{latest_version}/docs_package.epub" assert_received {:mix_shell, :info, [^fetched_msg]} assert_received {:hex_system_cmd, _cmd, browser_open_cmd} assert Enum.fetch!(browser_open_cmd, -1) == browser_open_msg end) end test "offline package with version succeeds when package is available remotely" do package = "docs_package" version = "1.1.2" bypass_mirror() Hex.State.put(:data_home, tmp_path()) docs_home = Path.join(Hex.State.fetch!(:data_home), "docs") org_dir = "hexpm" in_tmp("docs", fn -> Mix.Tasks.Hex.Docs.run(["offline", package, version]) fetched_msg = "Docs fetched: #{docs_home}/#{org_dir}/#{package}/#{version}" browser_open_msg = "#{docs_home}/#{org_dir}/#{package}/#{version}/index.html" assert_received {:mix_shell, :info, [^fetched_msg]} assert_received {:hex_system_cmd, _cmd, browser_open_cmd} assert Enum.fetch!(browser_open_cmd, -1) == browser_open_msg Mix.Tasks.Hex.Docs.run(["fetch", package, version]) already_fetched_msg = "Docs already fetched: #{docs_home}/#{org_dir}/#{package}/#{version}" assert_received {:mix_shell, :info, [^already_fetched_msg]} end) end test "offline package with version uses fallback location" do package = "docs_package" version = "1.1.2" bypass_mirror() Hex.State.put(:data_home, tmp_path()) docs_home = Path.join(Hex.State.fetch!(:data_home), "docs") org_dir = "hexpm" in_tmp("docs", fn -> Mix.Tasks.Hex.Docs.run(["fetch", package, version]) File.cp_r!("#{docs_home}/#{org_dir}/#{package}", "#{docs_home}/#{package}") File.rm_rf!("#{docs_home}/#{org_dir}/#{package}/#{version}") Mix.Tasks.Hex.Docs.run(["offline", package, version]) browser_open_msg = "#{docs_home}/#{package}/#{version}/index.html" assert_received {:hex_system_cmd, _cmd, browser_open_cmd} assert Enum.fetch!(browser_open_cmd, -1) == browser_open_msg end) end test "open docs online" do Mix.Tasks.Hex.Docs.run(["online", "ecto"]) assert_received {:hex_system_cmd, _cmd, browser_open_cmd} assert Enum.fetch!(browser_open_cmd, -1) == "https://hexdocs.pm/ecto" end test "open the version of a package this app uses online" do Mix.Project.push(ExampleDeps.MixProject) Hex.State.put(:data_home, tmp_path()) in_tmp("docs", fn -> Mix.Dep.Lock.write(%{docs_package: {:hex, :docs_package, "1.1.1"}}) Mix.Tasks.Hex.Docs.run(["online", "docs_package"]) assert_received {:hex_system_cmd, _cmd, browser_open_cmd} assert Enum.fetch!(browser_open_cmd, -1) == "https://hexdocs.pm/docs_package/1.1.1" end) end test "open latest version of a package this app uses online" do Mix.Project.push(ExampleDeps.MixProject) Hex.State.put(:data_home, tmp_path()) in_tmp("docs", fn -> Mix.Dep.Lock.write(%{docs_package: {:hex, :docs_package, "1.1.1"}}) Mix.Tasks.Hex.Docs.run(["online", "docs_package", "--latest"]) assert_received {:hex_system_cmd, _cmd, browser_open_cmd} assert Enum.fetch!(browser_open_cmd, -1) == "https://hexdocs.pm/docs_package" end) end test "open the version of a package this app uses offline" do Mix.Project.push(ExampleDeps.MixProject) package = "docs_package" version = "1.1.2" bypass_mirror() Hex.State.put(:data_home, tmp_path()) docs_home = Path.join(Hex.State.fetch!(:data_home), "docs") in_tmp("docs", fn -> Mix.Dep.Lock.write(%{docs_package: {:hex, :docs_package, version}}) Mix.Tasks.Hex.Docs.run(["offline", package]) fetched_msg = "Docs fetched: #{docs_home}/hexpm/#{package}/#{version}" browser_open_msg = "#{docs_home}/hexpm/#{package}/#{version}/index.html" assert_received {:mix_shell, :info, [^fetched_msg]} assert_received {:hex_system_cmd, _cmd, browser_open_cmd} assert Enum.fetch!(browser_open_cmd, -1) == browser_open_msg assert File.exists?("#{docs_home}/hexpm/#{package}/#{version}") end) end end hex-2.4.2/test/mix/tasks/hex.info_test.exs000066400000000000000000000137511517471540100205160ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.InfoTest do use HexTest.IntegrationCase defmodule Simple do def project do [ app: :simple, version: "0.1.0", deps: [ {:ecto, "0.2.0"} ] ] end end test "package" do Mix.Tasks.Hex.Info.run(["ex_doc"]) assert_received {:mix_shell, :info, ["Some description\n"]} assert_received {:mix_shell, :info, ["Config: {:ex_doc, \"~> 0.1.0\"}"]} assert_received {:mix_shell, :info, ["\nRecent releases:\n" <> releases]} today = Date.utc_today() assert releases == " 0.1.0 (#{today})\n 0.1.0-rc1 (#{today})\n 0.0.1 (#{today})\n" assert catch_throw(Mix.Tasks.Hex.Info.run(["no_package"])) == {:exit_code, 1} assert_received {:mix_shell, :error, ["No package with name no_package"]} assert catch_throw(Mix.Tasks.Hex.Info.run([""])) == {:exit_code, 1} assert_received {:mix_shell, :error, ["Package name is empty"]} end test "package downloads" do bypass = Bypass.open() Hex.State.put(:api_url, "http://localhost:#{bypass.port}/api") today = Date.utc_today() inserted_at = "#{today}T12:00:00Z" package_body = %{ "name" => "ex_doc", "meta" => %{ "description" => "Some description", "licenses" => ["GPL-2.0", "MIT", "Apache-2.0"], "links" => %{"docs" => "http://docs", "repo" => "http://repo"} }, "configs" => %{"mix.exs" => "{:ex_doc, \"~> 0.1.0\"}"}, "releases" => [ %{"version" => "0.1.0", "inserted_at" => inserted_at}, %{"version" => "0.1.0-rc1", "inserted_at" => inserted_at}, %{"version" => "0.0.1", "inserted_at" => inserted_at} ], "retirements" => %{}, "downloads" => %{ "all" => 96_128_698, "day" => 21_494, "recent" => 1_421_136, "week" => 124_095 } } Bypass.expect(bypass, "GET", "/api/packages/ex_doc", fn conn -> conn |> Plug.Conn.put_resp_header("content-type", "application/vnd.hex+erlang") |> Plug.Conn.resp(200, Hex.Utils.safe_serialize_erlang(package_body)) end) Mix.Tasks.Hex.Info.run(["ex_doc"]) assert_received {:mix_shell, :info, ["Downloads:\n" <> downloads]} assert String.split(downloads, "\n") == [ " Yesterday: 21 494", " Last 7 days: 124 095", " All time: 96 128 698", "" ] end test "locked package" do Mix.Project.push(Simple) in_tmp(fn -> set_home_cwd() Mix.Task.run("deps.get") Mix.Task.clear() Mix.Tasks.Hex.Info.run(["ecto"]) assert_received {:mix_shell, :info, ["Some description\n"]} assert_received {:mix_shell, :info, ["Locked version: 0.2.0"]} assert_received {:mix_shell, :info, ["Config: {:ecto, \"~> 3.3\"}"]} assert_received {:mix_shell, :info, ["\nRecent releases:\n" <> releases]} today = Date.utc_today() assert String.split(releases, "\n") == [ " 3.3.2 (#{today})", " 3.3.1 (#{today})", " 0.2.1 (#{today})", " 0.2.0 (#{today})", "" ] end) after purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject ]) end test "package with retired release" do Mix.Tasks.Hex.Info.run(["tired"]) today = Date.utc_today() assert_received {:mix_shell, :info, ["\nRecent releases:\n" <> releases]} assert releases == " 0.2.0 (#{today})\n 0.1.0 (#{today}) (retired)\n" end test "package with --organization flag" do in_tmp(fn -> set_home_cwd() Hex.State.put(:cache_home, tmp_path()) # Set up authentication with API key auth = Hexpm.new_user("info_user", "info_user@mail.com", "hunter42", "key") Hex.State.put(:api_key, auth[:key]) # Add shell inputs for potential authentication prompts send(self(), {:mix_shell_input, :yes?, false}) # Use an existing package that should be available Mix.Tasks.Hex.Info.run(["ex_doc", "--organization", "hexpm"]) assert_received {:mix_shell, :info, ["Config: {:ex_doc, \"~> 0.1.0\"}"]} end) end test "release" do Mix.Tasks.Hex.Info.run(["ex_doc", "0.0.1"]) assert_received {:mix_shell, :info, ["Config: {:ex_doc, \"~> 0.0.1\"}"]} Mix.Tasks.Hex.Info.run(["ex_doc", "0.1.0-rc1"]) assert_received {:mix_shell, :info, ["Config: {:ex_doc, \"~> 0.1.0-rc1\"}"]} assert catch_throw(Mix.Tasks.Hex.Info.run(["ex_doc", "1.2.3"])) == {:exit_code, 1} assert_received {:mix_shell, :error, ["No release with name ex_doc 1.2.3"]} end test "release downloads" do bypass = Bypass.open() Hex.State.put(:api_url, "http://localhost:#{bypass.port}/api") today = Date.utc_today() inserted_at = "#{today}T12:00:00Z" release_body = %{ "version" => "1.5.0-alpha.2", "checksum" => "6dcaa0d9fdc22afe9b4d362f17f20844a85f121c50b6e9b9466ac04fe39f3665", "inserted_at" => inserted_at, "updated_at" => inserted_at, "retirement" => nil, "publisher" => nil, "downloads" => 26_208, "configs" => %{ "erlang.mk" => "dep_jason = hex 1.5.0-alpha.2", "mix.exs" => "{:jason, \"~\u003E 1.5.0-alpha.2\"}", "rebar.config" => "{jason, \"1.5.0-alpha.2\"}" } } Bypass.expect(bypass, "GET", "/api/packages/ex_doc/releases/0.1.0", fn conn -> conn |> Plug.Conn.put_resp_header("content-type", "application/vnd.hex+erlang") |> Plug.Conn.resp(200, Hex.Utils.safe_serialize_erlang(release_body)) end) Mix.Tasks.Hex.Info.run(["ex_doc", "0.1.0"]) assert_received {:mix_shell, :info, ["Downloads: 26 208"]} end test "prints publisher info for releases" do Mix.Tasks.Hex.Info.run(["ex_doc", "0.0.1"]) assert_received {:mix_shell, :info, ["Published by: user (user@mail.com)"]} end test "prints release date for releases" do Mix.Tasks.Hex.Info.run(["ex_doc", "0.0.1"]) assert_received {:mix_shell, :info, ["Released: " <> date]} assert date == "#{Date.utc_today()}" end end hex-2.4.2/test/mix/tasks/hex.organization_test.exs000066400000000000000000000165631517471540100222730ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.OrganizationTest do use HexTest.IntegrationCase test "auth" do in_tmp(fn -> set_home_cwd() auth = Hexpm.new_user("orgauth", "orgauth@mail.com", "password", "key") Hex.State.put(:api_key, auth[:key]) Hexpm.new_repo("myorgauth", auth) send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "password"}) Mix.Tasks.Hex.Organization.run(["auth", "myorgauth"]) myorg = Hex.Repo.get_repo("hexpm:myorgauth") hexpm = Hex.Repo.get_repo("hexpm") assert myorg.public_key == hexpm.public_key assert myorg.url == "http://localhost:4043/repo/repos/myorgauth" assert is_binary(myorg.auth_key) {:ok, hostname} = :inet.gethostname() name = "#{hostname}-repository-myorgauth" assert {:ok, {200, _, body}} = Hex.API.Key.get(auth) assert name in Enum.map(body, & &1["name"]) end) end test "auth with --keyname" do in_tmp(fn -> set_home_cwd() auth = Hexpm.new_user("orgauthwithkeyname", "orgauthwithkeyname@mail.com", "password", "key") Hex.State.put(:api_key, auth[:key]) Hexpm.new_repo("myorgauthwithkeyname", auth) send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "password"}) Mix.Tasks.Hex.Organization.run([ "auth", "myorgauthwithkeyname", "--key-name", "orgauthkeyname" ]) myorg = Hex.Repo.get_repo("hexpm:myorgauthwithkeyname") hexpm = Hex.Repo.get_repo("hexpm") assert myorg.public_key == hexpm.public_key assert myorg.url == "http://localhost:4043/repo/repos/myorgauthwithkeyname" assert is_binary(myorg.auth_key) assert {:ok, {200, _, body}} = Hex.API.Key.get(auth) assert "orgauthkeyname-repository-myorgauthwithkeyname" in Enum.map(body, & &1["name"]) end) end test "auth --key" do in_tmp(fn -> set_home_cwd() auth = Hexpm.new_user("orgauthkey", "orgauthkey@mail.com", "password", "key") Hexpm.new_repo("myorgauthkey", auth) parameters = [%{"domain" => "repository", "resource" => "myorgauthkey"}] {:ok, {201, _, body}} = Hex.API.Key.new("orgauthkey", parameters, auth) Mix.Tasks.Hex.Organization.run(["auth", "myorgauthkey", "--key", body["secret"]]) myorg = Hex.Repo.get_repo("hexpm:myorgauthkey") hexpm = Hex.Repo.get_repo("hexpm") assert myorg.public_key == hexpm.public_key assert myorg.url == "http://localhost:4043/repo/repos/myorgauthkey" assert myorg.auth_key == body["secret"] repos = Hex.Config.read_repos(Hex.Config.read()) assert repo = repos["hexpm:myorgauthkey"] assert repo[:auth_key] assert repo[:trusted] assert repo[:url] == "http://localhost:4043/repo/repos/myorgauthkey" refute Map.has_key?(Hex.Config.read()[:"$repos"]["hexpm:myorgauthkey"], :trusted) end) end test "auth --key with invalid key" do in_tmp(fn -> set_home_cwd() assert catch_throw(Mix.Tasks.Hex.Organization.run(["auth", "myorg", "--key", "mykey"])) == {:exit_code, 1} assert_received {:mix_shell, :error, [ "Failed to authenticate against organization repository with given key because of: invalid API key" ]} end) end test "deauth" do in_tmp(fn -> set_home_cwd() auth = Hexpm.new_user("orgdeauth", "orgdeauth@mail.com", "password", "key") Hex.State.put(:api_key, auth[:key]) Hexpm.new_repo("myorgdeauth", auth) send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "password"}) Mix.Tasks.Hex.Organization.run(["auth", "myorgdeauth"]) Mix.Tasks.Hex.Organization.run(["deauth", "myorgdeauth"]) refute Hex.Config.read_repos(Hex.Config.read())["hexpm:myorgdeauth"] end) end test "key --generate" do in_tmp(fn -> set_home_cwd() auth = Hexpm.new_user("orgkeygenuser", "orgkeygenuser@mail.com", "password", "key") Hex.State.put(:api_key, auth[:key]) Hexpm.new_repo("orgkeygenrepo", auth) send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "password"}) args = ["key", "orgkeygenrepo", "generate", "--permission", "api:read"] Mix.Tasks.Hex.Organization.run(args) assert_received {:mix_shell, :info, [key]} assert is_binary(key) {:ok, hostname} = :inet.gethostname() assert {:ok, {200, _, body}} = Hex.API.Key.Organization.get("orgkeygenrepo", key: key) assert List.to_string(hostname) in Enum.map(body, & &1["name"]) assert {:ok, {200, _, body}} = Hex.API.Key.Organization.get("orgkeygenrepo", auth) assert List.to_string(hostname) in Enum.map(body, & &1["name"]) assert {:ok, {200, _, body}} = Hex.API.Key.get(auth) refute List.to_string(hostname) in Enum.map(body, & &1["name"]) end) end test "list keys" do in_tmp(fn -> set_home_cwd() auth = Hexpm.new_user("orgkeylistuser", "orgkeylistuser@mail.com", "password", "key") Hex.State.put(:api_key, auth[:key]) Hexpm.new_repo("orgkeylistrepo", auth) send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "password"}) args = ["key", "orgkeylistrepo", "generate", "--key-name", "orgkeylistrepo"] Mix.Tasks.Hex.Organization.run(args) assert {:ok, {200, _, [%{"name" => "orgkeylistrepo"}]}} = Hex.API.Key.Organization.get("orgkeylistrepo", auth) Mix.Tasks.Hex.Organization.run(["key", "orgkeylistrepo", "list"]) assert_received {:mix_shell, :info, ["orgkeylistrepo" <> _]} end) end test "revoke key" do in_tmp(fn -> set_home_cwd() auth = Hexpm.new_user("orgkeyrevokeyuser", "orgkeyrevokeyuser@mail.com", "password", "key") Hex.State.put(:api_key, auth[:key]) Hexpm.new_repo("orgkeyrevokerepo", auth) Hexpm.new_organization_key("orgkeyrevokerepo", "orgkeyrevokerepo2", auth) assert {:ok, {200, _, [%{"name" => "orgkeyrevokerepo2"}]}} = Hex.API.Key.Organization.get("orgkeyrevokerepo", auth) send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "password"}) Mix.Tasks.Hex.Organization.run(["key", "orgkeyrevokerepo", "revoke", "orgkeyrevokerepo2"]) assert_received {:mix_shell, :info, ["Revoking key orgkeyrevokerepo2..."]} assert {:ok, {200, _, []}} = Hex.API.Key.Organization.get("orgkeyrevokerepo", auth) end) end test "revoke all keys" do in_tmp(fn -> set_home_cwd() auth = Hexpm.new_user("orgkeyrevokealluser", "orgkeyrevokealluser@mail.com", "password", "key") Hex.State.put(:api_key, auth[:key]) Hexpm.new_repo("orgkeyrevokeallrepo", auth) Hexpm.new_organization_key("orgkeyrevokeallrepo", "orgkeyrevokeallrepo2", auth) assert {:ok, {200, _, [%{"name" => "orgkeyrevokeallrepo2"}]}} = Hex.API.Key.Organization.get("orgkeyrevokeallrepo", auth) send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "password"}) Mix.Tasks.Hex.Organization.run(["key", "orgkeyrevokeallrepo", "revoke", "--all"]) assert_received {:mix_shell, :info, ["Revoking all keys..."]} assert {:ok, {200, _, []}} = Hex.API.Key.Organization.get("orgkeyrevokeallrepo", auth) end) end end hex-2.4.2/test/mix/tasks/hex.outdated_test.exs000066400000000000000000000505531517471540100213750ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.OutdatedTest do use HexTest.IntegrationCase defmodule OutdatedDeps.MixProject do def project do [ app: :outdated_app, version: "0.0.2", deps: [ {:bar, "0.1.0"}, {:ex_doc, "~> 0.0.1"} ] ] end end defmodule OutdatedBetaDeps.MixProject do def project do [ app: :outdated_app, version: "0.0.1", deps: [ {:beta, ">= 0.0.0"} ] ] end end defmodule OutdatedApp.MixProject do def project do [ app: :outdated_app, version: "0.0.1", deps: [ {:ex_doc, ">= 0.0.0"}, {:postgrex, "0.2.0"}, {:ecto, "0.2.0"} ] ] end end defmodule NotOutdatedApp.MixProject do def project do [ app: :outdated_app, version: "0.0.1", deps: [ {:ex_doc, ">= 0.0.0"} ] ] end end defmodule UpdateNotPossibleApp.MixProject do def project do [ app: :outdated_app, version: "0.0.1", deps: [ {:baz, "0.1.0"}, {:bar, "0.1.0"} ] ] end end defmodule WithoutHexDeps.MixProject do def project do [ app: :outdated_app, version: "0.0.1", deps: [] ] end end defmodule OutdatedMultiDeps.MixProject do def project do [ app: :outdated_app, version: "0.0.2", deps: [ {:baz, "0.1.0"}, {:bar, "0.1.0"} ] ] end end defmodule OutdatedDepsWithTypes.MixProject do def project do [ app: :outdated_app, version: "0.0.2", deps: [ {:ex_doc, "0.0.1"}, {:beta, "1.0.0", only: :dev}, {:tired, "0.1.0", only: :test}, {:foo, "0.1.0", only: [:dev, :test]} ] ] end end test "outdated" do Mix.Project.push(OutdatedDeps.MixProject) in_tmp(fn -> set_home_tmp() Mix.Dep.Lock.write(%{bar: {:hex, :bar, "0.1.0"}, foo: {:hex, :foo, "0.1.0"}}) Mix.Task.run("deps.get") flush() assert catch_throw(Mix.Task.run("hex.outdated")) == {:exit_code, 1} bar = [ [:bright, "bar", :reset], [" ", :reset], [" ", "0.1.0", :reset], [" ", :green, "0.1.0", :reset], [" ", :green, "Up-to-date", :reset], " " ] |> IO.ANSI.format() |> List.to_string() assert_received {:mix_shell, :info, [^bar]} refute_received {:mix_shell, :info, ["foo" <> _]} end) end test "outdated --all" do Mix.Project.push(OutdatedDeps.MixProject) in_tmp(fn -> set_home_tmp() Mix.Dep.Lock.write(%{bar: {:hex, :bar, "0.1.0"}, foo: {:hex, :foo, "0.1.0"}}) Mix.Task.run("deps.get") flush() assert catch_throw(Mix.Task.run("hex.outdated", ["--all"])) == {:exit_code, 1} bar = [ [:bright, "bar", :reset], [" ", :reset], [" ", "0.1.0", :reset], [" ", :green, "0.1.0", :reset], [" ", :green, "Up-to-date", :reset], " " ] |> IO.ANSI.format() |> List.to_string() foo = [ [:bright, "foo", :reset], [" ", :reset], [" ", "0.1.0", :reset], [" ", :red, "0.1.1", :reset], [" ", :yellow, "Update possible", :reset], " " ] |> IO.ANSI.format() |> List.to_string() ex_doc = [ [:bright, "ex_doc", :reset], [" ", :reset], [" ", "0.0.1", :reset], [" ", :red, "0.1.0", :reset], [" ", :red, "Update not possible", :reset], " " ] |> IO.ANSI.format() |> List.to_string() assert_received {:mix_shell, :info, [^bar]} assert_received {:mix_shell, :info, [^foo]} assert_received {:mix_shell, :info, [^ex_doc]} end) end test "outdated --all --sort status" do Mix.Project.push(OutdatedApp.MixProject) in_tmp(fn -> set_home_tmp() Mix.Dep.Lock.write(%{bar: {:hex, :bar, "0.1.0"}, foo: {:hex, :foo, "0.1.0"}}) Mix.Task.run("deps.get") flush() assert catch_throw(Mix.Task.run("hex.outdated", ["--all", "--sort", "status"])) == {:exit_code, 1} _bar = [ [:bright, "bar", :reset], [" ", :reset], [" ", "0.1.0", :reset], [" ", :green, "0.1.0", :reset], [" ", :green, "Up-to-date", :reset], " " ] |> IO.ANSI.format() |> List.to_string() _foo = [ [:bright, "foo", :reset], [" ", :reset], [" ", "0.1.0", :reset], [" ", :red, "0.1.1", :reset], [" ", :yellow, "Update possible", :reset], " " ] |> IO.ANSI.format() |> List.to_string() _ex_doc = [ [:bright, "ex_doc", :reset], [" ", :reset], [" ", "0.0.1", :reset], [" ", :red, "0.1.0", :reset], [" ", :red, "Update not possible", :reset], " " ] |> IO.ANSI.format() |> List.to_string() lines = flush() extracted_statuses = extract_statuses(lines) assert extracted_statuses == [ "Up-to-date", "Update not possible", "Update not possible", "Update not possible", "Update possible" ] end) end test "outdated --all with multiple dependent packages" do Mix.Project.push(OutdatedMultiDeps.MixProject) in_tmp(fn -> set_home_tmp() Mix.Dep.Lock.write(%{ foo: {:hex, :foo, "0.1.0"}, bar: {:hex, :bar, "0.1.0"}, baz: {:hex, :baz, "0.1.0"} }) Mix.Task.run("deps.get") flush() assert catch_throw(Mix.Task.run("hex.outdated", ["--all"])) == {:exit_code, 1} foo = [ [:bright, "foo", :reset], [" ", :reset], [" ", "0.1.0", :reset], [" ", :red, "0.1.1", :reset], [" ", :red, "Update not possible", :reset], " " ] |> IO.ANSI.format() |> List.to_string() assert_received {:mix_shell, :info, [^foo]} end) end test "outdated --all --within-requirements" do Mix.Project.push(OutdatedDeps.MixProject) in_tmp(fn -> set_home_tmp() Mix.Dep.Lock.write(%{bar: {:hex, :bar, "0.1.0"}, foo: {:hex, :foo, "0.1.0"}}) Mix.Task.run("deps.get") flush() assert catch_throw(Mix.Task.run("hex.outdated", ["--all", "--within-requirements"])) == {:exit_code, 1} bar = [ [:bright, "bar", :reset], [" ", :reset], [" ", "0.1.0", :reset], [" ", :green, "0.1.0", :reset], [" ", :green, "Up-to-date", :reset], " " ] |> IO.ANSI.format() |> List.to_string() foo = [ [:bright, "foo", :reset], [" ", :reset], [" ", "0.1.0", :reset], [" ", :red, "0.1.1", :reset], [" ", :yellow, "Update possible", :reset], " " ] |> IO.ANSI.format() |> List.to_string() ex_doc = [ [:bright, "ex_doc", :reset], [" ", :reset], [" ", "0.0.1", :reset], [" ", :red, "0.1.0", :reset], [" ", :red, "Update not possible", :reset], " " ] |> IO.ANSI.format() |> List.to_string() assert_received {:mix_shell, :info, [^bar]} assert_received {:mix_shell, :info, [^foo]} assert_received {:mix_shell, :info, [^ex_doc]} end) end test "outdated --all --within-requirements (update not possible)" do Mix.Project.push(UpdateNotPossibleApp.MixProject) in_tmp(fn -> set_home_tmp() Mix.Dep.Lock.write(%{foo: {:hex, :foo, "0.1.0"}}) Mix.Task.run("deps.get") flush() assert Mix.Task.run("hex.outdated", ["--all", "--within-requirements"]) == nil bar = [ [:bright, "bar", :reset], [" ", :reset], [" ", "0.1.0", :reset], [" ", :green, "0.1.0", :reset], [" ", :green, "Up-to-date", :reset], " " ] |> IO.ANSI.format() |> List.to_string() baz = [ [:bright, "baz", :reset], [" ", :reset], [" ", "0.1.0", :reset], [" ", :green, "0.1.0", :reset], [" ", :green, "Up-to-date", :reset], " " ] |> IO.ANSI.format() |> List.to_string() foo = [ [:bright, "foo", :reset], [" ", :reset], [" ", "0.1.0", :reset], [" ", :red, "0.1.1", :reset], [" ", :red, "Update not possible", :reset], " " ] |> IO.ANSI.format() |> List.to_string() assert_received {:mix_shell, :info, [^bar]} assert_received {:mix_shell, :info, [^baz]} assert_received {:mix_shell, :info, [^foo]} end) end test "outdated --all --within-requirements (not outdated)" do Mix.Project.push(NotOutdatedApp.MixProject) in_tmp(fn -> set_home_tmp() Mix.Dep.Lock.write(%{ex_doc: {:hex, :ex_doc, "0.1.0"}}) Mix.Task.run("deps.get") flush() assert Mix.Task.run("hex.outdated", ["ex_doc"]) == nil msg = ["Current version ", :bright, "0.1.0", :reset, " of dependency is up to date!"] |> IO.ANSI.format_fragment() |> List.to_string() assert_received {:mix_shell, :info, [^msg]} end) end test "outdated --pre" do Mix.Project.push(OutdatedBetaDeps.MixProject) in_tmp(fn -> set_home_tmp() Mix.Dep.Lock.write(%{beta: {:hex, :beta, "1.0.0"}}) Mix.Task.run("deps.get") flush() Mix.Task.run("hex.outdated", []) beta = [ [:bright, "beta", :reset], [" ", :reset], [" ", "1.0.0", :reset], [" ", :green, "1.0.0", :reset], [" ", :green, "Up-to-date", :reset], [" "] ] |> IO.ANSI.format() |> List.to_string() assert_received {:mix_shell, :info, [^beta]} Mix.Task.reenable("hex.outdated") assert catch_throw(Mix.Task.run("hex.outdated", ["--pre"])) == {:exit_code, 1} beta = [ [:bright, "beta", :reset], [" ", :reset], [" ", "1.0.0", :reset], [" ", :red, "1.1.0-beta", :reset], [" ", :yellow, "Update possible", :reset], " " ] |> IO.ANSI.format() |> List.to_string() assert_received {:mix_shell, :info, [^beta]} end) end test "outdated app" do Mix.Project.push(OutdatedApp.MixProject) in_tmp(fn -> set_home_tmp() Mix.Dep.Lock.write(%{ex_doc: {:hex, :ex_doc, "0.0.1"}}) Mix.Task.run("deps.get") flush() assert catch_throw(Mix.Task.run("hex.outdated", ["ex_doc"])) == {:exit_code, 1} msg = [ "There is newer version of the dependency available ", [:bright, "0.1.0 > 0.0.1", :reset, "!"] ] |> IO.ANSI.format_fragment() |> List.to_string() assert_received {:mix_shell, :info, [^msg]} mix = [ :bright, "mix.exs", :reset, " ", :green, ">= 0.0.0", :reset, " ", :green, "Yes", :reset, " " ] |> IO.ANSI.format() |> List.to_string() assert_received {:mix_shell, :info, [^mix]} ecto = [ :bright, "ecto", :reset, " ", :red, "~> 0.0.1", :reset, " ", :red, "No", :reset, " " ] |> IO.ANSI.format() |> List.to_string() assert_received {:mix_shell, :info, [^ecto]} postgrex = [ :bright, "postgrex", :reset, " ", :red, "0.0.1", :reset, " ", :red, "No", :reset, " " ] |> IO.ANSI.format() |> List.to_string() assert_received {:mix_shell, :info, [^postgrex]} end) end test "not outdated app" do Mix.Project.push(NotOutdatedApp.MixProject) in_tmp(fn -> set_home_tmp() Mix.Dep.Lock.write(%{ex_doc: {:hex, :ex_doc, "0.1.0"}}) Mix.Task.run("deps.get") flush() Mix.Task.run("hex.outdated", ["ex_doc"]) msg = ["Current version ", :bright, "0.1.0", :reset, " of dependency is up to date!"] |> IO.ANSI.format_fragment() |> List.to_string() assert_received {:mix_shell, :info, [^msg]} end) end test "without hex deps" do Mix.Project.push(WithoutHexDeps.MixProject) in_tmp(fn -> set_home_tmp() Mix.Dep.Lock.write(%{}) Mix.Task.run("deps.get") flush() Mix.Task.run("hex.outdated") msg = "No hex dependencies" assert_received {:mix_shell, :info, [^msg]} end) end test "umbrella projects" do in_tmp("umbrella", fn -> File.write!("mix.exs", """ defmodule Umbrella.MixProject do use Mix.Project def project do [apps_path: "apps", version: "0.0.1", deps: [{:ex_doc, "~> 0.0.1"}]] end end """) Mix.Project.in_project(:umbrella, ".", fn _ -> File.mkdir_p!("apps/bacon") File.write!("apps/bacon/mix.exs", """ defmodule Bacon.MixProject do use Mix.Project def project do [app: :bacon, version: "0.1.0", build_path: "../../_build", config_path: "../../config/config.exs", deps_path: "../../deps", lockfile: "../../mix.lock", deps: [{:bar, "0.1.0"}]] end end """) Mix.Project.in_project(:bacon, "apps/bacon", fn _ -> Mix.Task.run("deps.get") flush() end) Mix.Task.run("deps.get") flush() ex_doc = [ [:bright, "ex_doc", :reset], [" ", :reset], [" ", "0.0.1", :reset], [" ", :red, "0.1.0", :reset], [" ", :red, "Update not possible", :reset], " " ] |> IO.ANSI.format() |> List.to_string() bar = [ [:bright, "bar", :reset], [" ", :reset], [" ", "0.1.0", :reset], [" ", :green, "0.1.0", :reset], [" ", :green, "Up-to-date", :reset], " " ] |> IO.ANSI.format() |> List.to_string() foo = [ [:bright, "foo", :reset], [" ", :reset], [" ", "0.1.1", :reset], [" ", :green, "0.1.1", :reset], [" ", :green, "Up-to-date", :reset], " " ] |> IO.ANSI.format() |> List.to_string() assert catch_throw(Mix.Task.run("hex.outdated")) == {:exit_code, 1} assert_received {:mix_shell, :info, [^ex_doc]} assert_received {:mix_shell, :info, [^bar]} refute_received {:mix_shell, :info, [^foo]} assert catch_throw(Mix.Tasks.Hex.Outdated.run(["--all"])) == {:exit_code, 1} assert_received {:mix_shell, :info, [^ex_doc]} assert_received {:mix_shell, :info, [^bar]} assert_received {:mix_shell, :info, [^foo]} end) end) end test "outdated shows a generic diff command hint when updates are available" do Mix.Project.push(OutdatedDeps.MixProject) in_tmp(fn -> set_home_tmp() Mix.Dep.Lock.write(%{bar: {:hex, :bar, "0.1.0"}, foo: {:hex, :foo, "0.1.0"}}) Mix.Task.run("deps.get") flush() assert catch_throw(Mix.Task.run("hex.outdated", ["--all"])) == {:exit_code, 1} lines = flush() output = Enum.map_join(lines, "\n", fn {_, _, [line]} -> line end) assert output =~ "To view the diff of a specific update, run `mix hex.package diff APP FROM..TO`." end) end defp extract_statuses(lines) do Enum.flat_map(lines, fn {_, _, [line]} -> ~r/Up-to-date|Update not possible|Update possible/ |> Regex.scan(line) |> List.flatten() end) end test "outdated with dependency types displays only column" do Mix.Project.push(OutdatedDepsWithTypes.MixProject) in_tmp(fn -> set_home_tmp() Mix.Dep.Lock.write(%{ ex_doc: {:hex, :ex_doc, "0.0.1"}, beta: {:hex, :beta, "1.0.0"}, tired: {:hex, :tired, "0.1.0"}, foo: {:hex, :foo, "0.1.0"} }) Mix.Task.run("deps.get") flush() catch_throw(Mix.Task.run("hex.outdated")) # Check that the Only column is present in the header assert_received {:mix_shell, :info, [header_line]} assert header_line =~ "Dependency" assert header_line =~ "Only" assert header_line =~ "Current" assert header_line =~ "Latest" assert header_line =~ "Status" # Check that dependencies show their correct only values # Note: dependencies without :only show empty string in the Only column output_lines = Enum.map(1..10, fn _ -> receive do {:mix_shell, :info, [line]} -> line after 100 -> nil end end) |> Enum.filter(&(&1 != nil)) combined_output = Enum.join(output_lines, "\n") assert combined_output =~ "foo" and combined_output =~ "dev,test" assert combined_output =~ "beta" and combined_output =~ "dev" assert combined_output =~ "tired" and combined_output =~ "test" end) end test "outdated --only dev filters to only dev dependencies" do Mix.Project.push(OutdatedDepsWithTypes.MixProject) in_tmp(fn -> set_home_tmp() Mix.Dep.Lock.write(%{ ex_doc: {:hex, :ex_doc, "0.0.1"}, beta: {:hex, :beta, "1.0.0"}, tired: {:hex, :tired, "0.1.0"}, foo: {:hex, :foo, "0.1.0"} }) Mix.Task.run("deps.get") flush() catch_throw(Mix.Task.run("hex.outdated", ["--only", "dev"])) # Should show only dependencies with :only set to :dev # Since we filtered to only "dev", the output should contain only ex_doc output_lines = Enum.map(1..10, fn _ -> receive do {:mix_shell, :info, [line]} -> line after 100 -> nil end end) |> Enum.filter(&(&1 != nil)) combined_output = Enum.join(output_lines, "\n") assert combined_output =~ "beta" # foo has [:dev, :test] not just :dev, but beta only has :dev so both should show assert combined_output =~ "foo" refute combined_output =~ "ex_doc" end) end test "outdated --only dev,test filters to dev and test dependencies" do Mix.Project.push(OutdatedDepsWithTypes.MixProject) in_tmp(fn -> set_home_tmp() Mix.Dep.Lock.write(%{ ex_doc: {:hex, :ex_doc, "0.0.1"}, beta: {:hex, :beta, "1.0.0"}, tired: {:hex, :tired, "0.1.0"}, foo: {:hex, :foo, "0.1.0"} }) Mix.Task.run("deps.get") flush() catch_throw(Mix.Task.run("hex.outdated", ["--only", "dev,test"])) # Should show dependencies with :only set to :dev or :test # This should include ex_doc (dev) and plug (test) # bypass has [:dev, :test] which displays as "dev,test" but should match both "dev" and "test" individually output_lines = Enum.map(1..10, fn _ -> receive do {:mix_shell, :info, [line]} -> line after 100 -> nil end end) |> Enum.filter(&(&1 != nil)) combined_output = Enum.join(output_lines, "\n") assert combined_output =~ "tired" assert combined_output =~ "beta" # foo should be included since it has dev and test assert combined_output =~ "foo" refute combined_output =~ "ex_doc" end) end end hex-2.4.2/test/mix/tasks/hex.owner_test.exs000066400000000000000000000141351517471540100207120ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.OwnerTest do use HexTest.IntegrationCase test "add owner" do auth = Hexpm.new_user("owner_user1", "owner_user1@mail.com", "passpass", "key") Hexpm.new_user("owner_user2", "owner_user2@mail.com", "passpass", "key") Hexpm.new_package("hexpm", "owner_package1", "0.0.1", [], %{}, auth) set_home_tmp() Hex.State.put(:api_key, auth[:key]) send(self(), {:mix_shell_input, :prompt, "passpass"}) Mix.Tasks.Hex.Owner.run(["add", "owner_package1", "owner_user2@mail.com"]) assert_received {:mix_shell, :info, [ "Adding owner owner_user2@mail.com with ownership level full to owner_package1" ]} assert {:ok, {200, _, %{"owned_packages" => %{"owner_package1" => _}}}} = Hex.API.User.get("owner_user2") end test "add owner with maintainer level" do auth = Hexpm.new_user("owner_user1a", "owner_user1a@mail.com", "passpass", "key") Hexpm.new_user("owner_user2a", "owner_user2a@mail.com", "passpass", "key") Hexpm.new_package("hexpm", "owner_package1a", "0.0.1", [], %{}, auth) set_home_tmp() Hex.State.put(:api_key, auth[:key]) send(self(), {:mix_shell_input, :prompt, "passpass"}) Mix.Tasks.Hex.Owner.run([ "add", "owner_package1a", "owner_user2a@mail.com", "--level", "maintainer" ]) assert_received {:mix_shell, :info, [ "Adding owner owner_user2a@mail.com with ownership level maintainer to owner_package1a" ]} assert {:ok, {200, _, %{"owned_packages" => %{"owner_package1a" => _}}}} = Hex.API.User.get("owner_user2a") end test "add owner with invalid level" do auth = Hexpm.new_user("owner_user1b", "owner_user1b@mail.com", "passpass", "key") Hexpm.new_user("owner_user2b", "owner_user2b@mail.com", "passpass", "key") Hexpm.new_package("hexpm", "owner_package1b", "0.0.1", [], %{}, auth) set_home_tmp() Hex.State.put(:api_key, auth[:key]) send(self(), {:mix_shell_input, :prompt, "passpass"}) assert_raise Mix.Error, "Invalid ownership level, expected one of: full, maintainer", fn -> Mix.Tasks.Hex.Owner.run([ "add", "owner_package1b", "owner_user2b@mail.com", "--level", "invalid" ]) end end test "add owner by username" do auth = Hexpm.new_user("owner_user1c", "owner_user1c@mail.com", "passpass", "key") Hexpm.new_user("owner_user2c", "owner_user2c@mail.com", "passpass", "key") Hexpm.new_package("hexpm", "owner_package1c", "0.0.1", [], %{}, auth) set_home_tmp() Hex.State.put(:api_key, auth[:key]) send(self(), {:mix_shell_input, :prompt, "passpass"}) Mix.Tasks.Hex.Owner.run(["add", "owner_package1c", "owner_user2c"]) assert_received {:mix_shell, :info, [ "Adding owner owner_user2c with ownership level full to owner_package1c" ]} assert {:ok, {200, _, %{"owned_packages" => %{"owner_package1c" => _}}}} = Hex.API.User.get("owner_user2c") end test "remove owner" do auth = Hexpm.new_user("owner_user3", "owner_user3@mail.com", "passpass", "key") Hexpm.new_user("owner_user4", "owner_user4@mail.com", "passpass", "key") Hexpm.new_package("hexpm", "owner_package2", "0.0.1", [], %{}, auth) set_home_tmp() Hex.State.put(:api_key, auth[:key]) send(self(), {:mix_shell_input, :prompt, "passpass"}) send(self(), {:mix_shell_input, :prompt, "passpass"}) Mix.Tasks.Hex.Owner.run(["add", "owner_package2", "owner_user4@mail.com"]) Mix.Tasks.Hex.Owner.run(["remove", "owner_package2", "owner_user3@mail.com"]) assert_received {:mix_shell, :info, ["Removing owner owner_user3@mail.com from owner_package2"]} assert {:ok, {200, _, %{"owned_packages" => owned}}} = Hex.API.User.get("owner_user3") assert owned == %{} end test "list owners" do auth = Hexpm.new_user("owner_user5", "owner_user5@mail.com", "passpass", "key") Hexpm.new_package("hexpm", "owner_package3", "0.0.1", [], %{}, auth) set_home_tmp() Hex.State.put(:api_key, auth[:key]) Mix.Tasks.Hex.Owner.run(["list", "owner_package3"]) output = [ ["owner_user5@mail.com", :reset, " "], ["full", :reset, " "] ] |> IO.ANSI.format() |> List.to_string() assert_received {:mix_shell, :info, [^output]} end test "list all packages owned by the current user" do package1 = "owner_package4" package2 = "owner_package5" owner_email = "owner_user6@mail.com" auth = Hexpm.new_user("owner_user6", owner_email, "passpass", "key") Hexpm.new_package("hexpm", package1, "0.0.1", [], %{}, auth) Hexpm.new_package("hexpm", package2, "0.0.1", [], %{}, auth) set_home_tmp() Hex.State.put(:api_key, auth[:key]) Mix.Tasks.Hex.Owner.run(["packages"]) owner_package4_msg = "#{package1} - http://localhost:4043/packages/#{package1}" owner_package5_msg = "#{package2} - http://localhost:4043/packages/#{package2}" assert_received {:mix_shell, :info, [^owner_package4_msg]} assert_received {:mix_shell, :info, [^owner_package5_msg]} end test "transfer owner by username" do auth = Hexpm.new_user("owner_user7a", "owner_user7a@mail.com", "passpass", "key") Hexpm.new_user("owner_user7b", "owner_user7b@mail.com", "passpass", "key") Hexpm.new_package("hexpm", "owner_package6", "0.0.1", [], %{}, auth) set_home_tmp() Hex.State.put(:api_key, auth[:key]) send(self(), {:mix_shell_input, :prompt, "passpass"}) Mix.Tasks.Hex.Owner.run(["transfer", "owner_package6", "owner_user7b"]) assert_received {:mix_shell, :info, [ "Transferring ownership to owner_user7b for owner_package6" ]} assert {:ok, {200, _, %{"owned_packages" => packages_a}}} = Hex.API.User.get("owner_user7a") assert {:ok, {200, _, %{"owned_packages" => packages_b}}} = Hex.API.User.get("owner_user7b") assert Map.keys(packages_a) == [] assert Map.keys(packages_b) == ["owner_package6"] end end hex-2.4.2/test/mix/tasks/hex.package_test.exs000066400000000000000000000145211517471540100211520ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.PackageTest do use HexTest.IntegrationCase @git_diff_command "git diff --no-index --no-color --no-ext-diff __PATH1__ __PATH2__" defp in_diff_fixture(fun) do in_fixture("diff", fn -> Mix.Project.push(ReleaseDeps.MixProject) Mix.Dep.Lock.write(%{ex_doc: {:hex, :ex_doc, "0.0.1"}}) set_home_cwd() Mix.Task.run("deps.get") fun.() end) end test "fetch: success" do in_tmp(fn -> cwd = File.cwd!() Mix.Tasks.Hex.Package.run(["fetch", "ex_doc", "0.0.1"]) msg = "ex_doc v0.0.1 downloaded to #{cwd}/ex_doc-0.0.1.tar" assert_received {:mix_shell, :info, [^msg]} assert File.exists?("#{cwd}/ex_doc-0.0.1.tar") refute File.exists?("#{cwd}/ex_doc-0.0.1/") end) end test "fetch: success using latest version" do in_tmp(fn -> cwd = File.cwd!() Mix.Tasks.Hex.Package.run(["fetch", "postgrex"]) msg = "postgrex v0.2.1 downloaded to #{cwd}/postgrex-0.2.1.tar" assert_received {:mix_shell, :info, [^msg]} assert File.exists?("#{cwd}/postgrex-0.2.1.tar") refute File.exists?("#{cwd}/postgrex-0.2.1/") end) end test "fetch: custom repo" do in_tmp(fn -> cwd = File.cwd!() Mix.Tasks.Hex.Package.run(["fetch", "ex_doc", "0.0.1", "--repo", "hexpm"]) msg = "ex_doc v0.0.1 downloaded to #{cwd}/ex_doc-0.0.1.tar" assert_received {:mix_shell, :info, [^msg]} assert File.exists?("#{cwd}/ex_doc-0.0.1.tar") end) end test "fetch: to folder" do in_tmp(fn -> cwd = File.cwd!() Mix.Tasks.Hex.Package.run(["fetch", "ex_doc", "--output", "#{cwd}/test", "0.0.1"]) msg = "ex_doc v0.0.1 downloaded to #{cwd}/test/ex_doc-0.0.1.tar" assert_received {:mix_shell, :info, [^msg]} assert File.exists?("#{cwd}/test/ex_doc-0.0.1.tar") end) end test "fetch: to folder unpack" do in_tmp(fn -> cwd = File.cwd!() Mix.Tasks.Hex.Package.run([ "fetch", "ex_doc", "--output", "#{cwd}/test", "--unpack", "0.0.1" ]) msg = "ex_doc v0.0.1 extracted to #{cwd}/test" assert_received {:mix_shell, :info, [^msg]} assert File.exists?("#{cwd}/test") && File.dir?("#{cwd}/test") end) end # TODO: add `capture_bin_io/2`. # test "fetch: to stdout" do # in_tmp(fn -> # tarball = # capture_io(fn -> # Mix.Tasks.Hex.Package.run(["fetch", "ex_doc", "--output", "-", "0.0.1"]) # end) # Hex.Tar.unpack!({:binary, tarball}, :memory) # end) # end test "fetch: to stdout with unpack flag" do assert_raise Mix.Error, ~r"Cannot unpack the package while output destination is stdout", fn -> Mix.Tasks.Hex.Package.run([ "fetch", "ex_doc", "--output", "-", "--unpack", "0.0.1" ]) end end test "fetch: package not found" do assert_raise Mix.Error, ~r"Request failed \(404\)", fn -> Mix.Tasks.Hex.Package.run(["fetch", "ex_doc", "2.0.0"]) end end test "diff: success with version number" do in_diff_fixture(fn -> Hex.State.put(:diff_command, @git_diff_command) assert catch_throw(Mix.Tasks.Hex.Package.run(["diff", "ex_doc", "0.1.0"])) == {:exit_code, 1} assert_received {:mix_shell, :run, [out]} assert out =~ ~s(-{<<"version">>,<<"0.0.1">>}.) assert out =~ ~s(+{<<"version">>,<<"0.1.0">>}.) end) after purge([ReleaseDeps.MixProject]) end test "diff: outdated lockfile with single version number" do msg = "Can't continue due to errors on dependencies" in_diff_fixture(fn -> assert_raise Mix.Error, msg, fn -> Mix.Dep.Lock.write(%{ ok: {:ex_doc, "https://github.com/elixir-lang/ex_doc.git", "abcdefghi", []} }) Mix.Tasks.Hex.Package.run(["diff", "ex_doc", "1.0.0"]) end end) after purge([ReleaseDeps.MixProject]) end test "diff: not having target package with single version number" do msg = "Cannot find the app \"tesla\" in \"mix.lock\" file, " <> "please ensure it has been specified in \"mix.exs\" and run \"mix deps.get\"" in_diff_fixture(fn -> assert_raise Mix.Error, msg, fn -> Mix.Tasks.Hex.Package.run(["diff", "tesla", "1.0.0"]) end end) after purge([ReleaseDeps.MixProject]) end test "diff: success with version range" do in_diff_fixture(fn -> Hex.State.put(:diff_command, @git_diff_command) assert catch_throw(Mix.Tasks.Hex.Package.run(["diff", "ex_doc", "0.0.1..0.1.0"])) == {:exit_code, 1} assert_received {:mix_shell, :run, [out]} assert out =~ ~s(-{<<"version">>,<<"0.0.1">>}.) assert out =~ ~s(+{<<"version">>,<<"0.1.0">>}.) end) after purge([ReleaseDeps.MixProject]) end test "diff: success (variant args)" do in_diff_fixture(fn -> Hex.State.put(:diff_command, @git_diff_command) assert catch_throw(Mix.Tasks.Hex.Package.run(["diff", "ex_doc", "0.0.1", "0.1.0"])) == {:exit_code, 1} assert_received {:mix_shell, :run, [out]} assert out =~ ~s(-{<<"version">>,<<"0.0.1">>}.) assert out =~ ~s(+{<<"version">>,<<"0.1.0">>}.) end) after purge([ReleaseDeps.MixProject]) end test "diff: custom diff command" do in_diff_fixture(fn -> Hex.State.put(:diff_command, "ls __PATH1__ __PATH2__") assert catch_throw(Mix.Tasks.Hex.Package.run(["diff", "ex_doc", "0.0.1..0.1.0"])) == {:exit_code, 0} assert_received {:mix_shell, :run, [out]} assert out =~ "hex_metadata.config\nmix.exs" end) after purge([ReleaseDeps.MixProject]) end test "diff: package not found" do Hex.State.put(:shell_process, self()) in_diff_fixture(fn -> assert_raise Mix.Error, ~r"Request failed \(404\)", fn -> Mix.Tasks.Hex.Package.run(["diff", "bad", "1.0.0..1.1.0"]) end assert_raise Mix.Error, ~r"Request failed \(404\)", fn -> Mix.Tasks.Hex.Package.run(["diff", "ex_doc", "0.0.1..2.0.0"]) end assert_raise Mix.Error, ~r"Request failed \(404\)", fn -> Mix.Tasks.Hex.Package.run(["diff", "ex_doc", "2.0.0"]) end end) after purge([ReleaseDeps.MixProject]) end end hex-2.4.2/test/mix/tasks/hex.publish_test.exs000066400000000000000000000530671517471540100212350ustar00rootroot00000000000000defmodule Mix.Tasks.Docs do def run(_) do File.mkdir_p!("doc") File.write!("doc/index.html", "the index") end end defmodule Mix.Tasks.Hex.PublishTest do use HexTest.IntegrationCase defmodule DocsSimple.MixProject do def project do [app: :ex_doc, version: "0.1.0", aliases: [docs: [&docs/1]]] end defp docs(_) do File.mkdir_p!("doc") File.write!("doc/index.html", "the index") end end defmodule DocsError.MixProject do def project do [app: :ex_doc, version: "0.1.1", aliases: [docs: [&docs/1]]] end defp docs(_) do File.mkdir_p!("doc") File.write!("doc/index.html", "the index") end end defmodule DocsFilenameError.MixProject do def project do [app: :invalid_filename, version: "0.1.0", aliases: [docs: [&docs/1]]] end defp docs(_) do File.mkdir_p!("doc") File.write!("doc/index.html", "the index") File.write!("doc/1.5.5", "") end end defmodule DocsDirnameError.MixProject do def project do [app: :invalid_dirname, version: "0.1.0", aliases: [docs: [&docs/1]]] end defp docs(_) do File.mkdir_p!("doc/1.5.5") File.write!("doc/index.html", "the index") File.write!("doc/1.5.5/index.html", "") end end defmodule DocsOutputConfigured.MixProject do def project do [ app: :ex_doc, version: "0.1.0", aliases: [docs: [&docs/1]], docs: [output: "my_docs"] ] end defp docs(_) do File.mkdir_p!("my_docs") File.write!("my_docs/index.html", "the index") end end defmodule DocsOutputConfiguredFunction.MixProject do def project do [ app: :ex_doc, version: "0.1.0", aliases: [docs: [&docs/1]], docs: &docs_config/0 ] end defp docs(_) do File.mkdir_p!("my_docs") File.write!("my_docs/index.html", "the index") end defp docs_config, do: [output: "my_docs"] end defmodule DocsOutputNoOutput.MixProject do def project do [ app: :ex_doc, version: "0.1.0", aliases: [docs: [&docs/1]], docs: [output: "my_docs"] ] end defp docs(_), do: :ok end test "ensure user exists" do Process.put(:hex_test_app_name, :publish_ensure_user_exists) Mix.Project.push(ReleaseSimple.MixProject) set_home_path(tmp_path("does_not_exist")) in_tmp(fn -> File.write!("myfile.txt", "hello") send(self(), {:mix_shell_input, :yes?, false}) assert_raise Mix.Error, "No authenticated user found. Run `mix hex.user auth`", fn -> Mix.Tasks.Hex.Publish.run([]) end end) after purge([ReleaseSimple.MixProject]) end test "create and revert a package" do Process.put(:hex_test_app_name, :publish_and_revert) Mix.Project.push(ReleaseNewSimple.MixProject) in_tmp(fn -> set_home_tmp() File.write!("mix.exs", "mix.exs") File.write!("myfile.txt", "hello") File.write_stat!("mix.exs", %{File.stat!("mix.exs") | mode: 0o100644}) File.write_stat!("myfile.txt", %{File.stat!("myfile.txt") | mode: 0o100644}) setup_auth("user2", "hunter42") send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["package", "--no-progress", "--replace"]) assert_received {:mix_shell, :info, [ "Package published to http://localhost:4043/packages/publish_and_revert/0.0.1 " <> _ ]} assert {:ok, {200, _, _}} = Hex.API.Release.get("hexpm", "publish_and_revert", "0.0.1") assert_received {:mix_shell, :info, [ "Before publishing, please read the Code of Conduct: https://hex.pm/policies/codeofconduct\n" ]} send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["package", "--revert", "0.0.1"]) assert {:ok, {404, _, _}} = Hex.API.Release.get("hexpm", "publish_and_revert", "0.0.1") end) after purge([ReleaseNewSimple.MixProject]) end test "create a package without confirming" do Process.put(:hex_test_app_name, :publish_without_confirmation) Mix.Project.push(ReleaseSimple.MixProject) in_tmp(fn -> set_home_tmp() File.write!("mix.exs", "mix.exs") File.write!("myfile.txt", "hello") setup_auth("user2", "hunter42") send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["package", "--no-progress", "--replace", "--yes"]) assert {:ok, {200, _, _}} = Hex.API.Release.get("hexpm", "publish_without_confirmation", "0.0.1") end) after purge([ReleaseSimple.MixProject]) end test "create and revert docs" do Mix.Project.push(DocsSimple.MixProject) in_tmp(fn -> set_home_tmp() setup_auth("user", "hunter42") send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["docs", "--no-progress", "--replace"]) assert_received {:mix_shell, :info, [ "Docs will soon be available at http://localhost:4043/docs/ex_doc-0.1.0.tar.gz" ]} send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["docs", "--revert", "0.1.0"]) assert_received {:mix_shell, :info, ["Reverted docs for ex_doc 0.1.0"]} end) end test "try create existing package without permissions" do Process.put(:hex_test_app_name, :ex_doc) Mix.Project.push(ReleaseSimple.MixProject) in_tmp(fn -> set_home_tmp() File.write!("mix.exs", "mix.exs") File.write!("myfile.txt", "hello") setup_auth("user2", "hunter42") send(self(), {:mix_shell_input, :prompt, "hunter42"}) assert catch_throw( Mix.Tasks.Hex.Publish.run(["package", "--no-progress", "--replace", "--yes"]) ) == {:exit_code, 1} message = "Package with name ex_doc already exists. " <> "Make sure you are authenticated and have permissions to publish the package." assert_received {:mix_shell, :error, ["Publishing failed"]} assert_received {:mix_shell, :error, [^message]} end) after purge([ReleaseSimple.MixProject]) end test "publish docs with invalid filename" do Mix.Project.push(DocsFilenameError.MixProject) in_tmp(fn -> set_home_tmp() setup_auth("user", "hunter42") error_msg = "Invalid filename: top-level filenames cannot match a semantic version pattern" assert_raise Mix.Error, error_msg, fn -> send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["docs", "--no-progress", "--replace"]) refute_received {:mix_shell, :info, [ "Docs will soon be available at https://hexdocs.pm/invalid_filename/0.1.0" ]} end end) end test "publish docs with invalid dirname" do Mix.Project.push(DocsDirnameError.MixProject) in_tmp(fn -> set_home_tmp() setup_auth("user", "hunter42") error_msg = "Invalid filename: top-level filenames cannot match a semantic version pattern" assert_raise Mix.Error, error_msg, fn -> send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["docs", "--no-progress", "--replace"]) refute_received {:mix_shell, :info, [ "Docs will soon be available at https://hexdocs.pm/invalid_dirname/0.1.0" ]} end end) end test "raises if output folder is missing" do Mix.Project.push(DocsOutputNoOutput.MixProject) in_tmp(fn -> set_home_tmp() setup_auth("user", "hunter42") error_msg = "File not found: my_docs/index.html" assert_raise Mix.Error, error_msg, fn -> send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["docs", "--no-progress", "--replace"]) refute_received {:mix_shell, :info, ["Docs will soon be available at https://hexdocs.pm/ex_doc/0.1.0"]} end end) end test "publishes docs with different output configured" do Mix.Project.push(DocsOutputConfigured.MixProject) in_tmp(fn -> set_home_tmp() setup_auth("user", "hunter42") send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["docs", "--no-progress", "--replace"]) assert_received {:mix_shell, :info, [ "Docs will soon be available at http://localhost:4043/docs/ex_doc-0.1.0.tar.gz" ]} end) end test "publishes docs when docs config is a function" do Mix.Project.push(DocsOutputConfiguredFunction.MixProject) in_tmp(fn -> set_home_tmp() setup_auth("user", "hunter42") send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["docs", "--no-progress", "--replace"]) assert_received {:mix_shell, :info, [ "Docs will soon be available at http://localhost:4043/docs/ex_doc-0.1.0.tar.gz" ]} end) end test "docs when package is not published yet" do Mix.Project.push(DocsError.MixProject) in_tmp(fn -> set_home_tmp() setup_auth("user", "hunter42") send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["docs", "--no-progress", "--replace"]) message = "Publishing docs failed due to the package not being published yet" assert_received {:mix_shell, :error, [^message]} send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["docs", "--revert", "0.1.1"]) assert_received {:mix_shell, :info, ["Docs do not exist"]} end) end test "package create with package name" do Process.put(:hex_test_package_name, :publish_package_name) Mix.Project.push(ReleaseName.MixProject) in_tmp(fn -> set_home_tmp() File.write!("mix.exs", "mix.exs") File.write!("myfile.txt", "hello") setup_auth("user2", "hunter42") send(self(), {:mix_shell_input, :prompt, "hunter42"}) assert_raise Mix.Error, ~r"Invalid arguments", fn -> Mix.Tasks.Hex.Publish.run(["invalid", "--no-progress", "--replace"]) end send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["--no-progress", "--replace"]) assert_received {:mix_shell, :info, ["Building publish_package_name 0.0.1"]} send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["package", "--no-progress", "--replace"]) assert {:ok, {200, _, body}} = Hex.API.Release.get("hexpm", "publish_package_name", "0.0.1") assert body["meta"]["app"] == "release_d" end) after purge([ReleaseName.MixProject]) end test "package create with package name no confirm" do Process.put(:hex_test_package_name, :publish_package_name_no_confirm) Mix.Project.push(ReleaseName.MixProject) in_tmp(fn -> set_home_tmp() File.write!("mix.exs", "mix.exs") File.write!("myfile.txt", "hello") setup_auth("user", "hunter42") send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["--no-progress", "--replace", "--yes"]) assert_received {:mix_shell, :info, ["Building publish_package_name_no_confirm 0.0.1"]} assert {:ok, {200, _, body}} = Hex.API.Release.get("hexpm", "publish_package_name_no_confirm", "0.0.1") assert body["meta"]["app"] == "release_d" end) after purge([ReleaseName.MixProject]) end test "publish package and docs with dry run" do Process.put(:hex_test_package_name, :publish_package_name_docs_dry_run) Mix.Project.push(ReleaseName.MixProject) in_tmp(fn -> set_home_tmp() File.write!("mix.exs", "mix.exs") File.write!("myfile.txt", "hello") setup_auth("user", "hunter42") send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["--dry-run", "--yes", "--replace"]) assert_received {:mix_shell, :info, ["Building publish_package_name_docs_dry_run 0.0.1"]} refute_received {:mix_shell, :info, ["Package published to" <> _]} refute_received {:mix_shell, :info, ["Docs will soon be available at" <> _]} end) after purge([ReleaseName.MixProject]) end test "create with key" do Process.put(:hex_test_app_name, :publish_with_key) Mix.Project.push(ReleaseSimple.MixProject) in_tmp(fn -> set_home_tmp() File.write!("mix.exs", "mix.exs") File.write!("myfile.txt", "hello") setup_auth("user2", "hunter42") send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["package", "--no-progress", "--replace"]) assert {:ok, {200, _, _}} = Hex.API.Release.get("hexpm", "publish_with_key", "0.0.1") end) after purge([ReleaseSimple.MixProject]) end test "create with HEX_API_KEY" do Process.put(:hex_test_app_name, :publish_with_hex_api_key) Mix.Project.push(ReleaseSimple.MixProject) in_tmp(fn -> set_home_tmp() File.write!("mix.exs", "mix.exs") File.write!("myfile.txt", "hello") # Set up a test API key for HEX_API_KEY testing auth = Hexpm.new_user("hex_api_key_user", "hex_api_key_user@mail.com", "hunter42", "key") key = auth[:key] Hex.State.put(:api_key, key) Mix.Tasks.Hex.Publish.run(["package", "--yes", "--no-progress", "--replace"]) assert_received {:mix_shell, :info, ["Building publish_with_hex_api_key 0.0.1"]} assert {:ok, {200, _, _}} = Hex.API.Release.get("hexpm", "publish_with_hex_api_key", "0.0.1") end) after purge([ReleaseSimple.MixProject]) end test "create with an invalid HEX_API_KEY" do Process.put(:hex_test_app_name, :publish_with_invalid_hex_api_key) Mix.Project.push(ReleaseSimple.MixProject) in_tmp(fn -> set_home_tmp() File.write!("mix.exs", "mix.exs") File.write!("myfile.txt", "hello") Hex.State.put(:api_key, "invalid hex api key") assert {:exit_code, 1} = ["package", "--yes", "--no-progress", "--replace"] |> Mix.Tasks.Hex.Publish.run() |> catch_throw() assert_received {:mix_shell, :info, ["invalid API key"]} end) after purge([ReleaseSimple.MixProject]) end test "create with deps" do Process.put(:hex_test_app_name, :publish_with_deps) Mix.Project.push(ReleaseDeps.MixProject) in_tmp(fn -> set_home_tmp() File.write!("mix.exs", "mix.exs") setup_auth("user", "hunter42") Mix.Tasks.Deps.Get.run([]) error_msg = "Stopping package build due to errors.\nMissing metadata fields: links" assert_raise Mix.Error, error_msg, fn -> send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["package", "--no-progress", "--replace"]) end assert {:ok, {404, _, _}} = Hex.API.Release.get("hexpm", "publish_with_deps", "0.0.2") end) after purge([ReleaseDeps.MixProject]) end test "raise for missing metadata" do Process.put(:hex_test_app_name, :publish_with_missing_metadata) Mix.Project.push(ReleaseMeta.MixProject) in_tmp(fn -> set_home_tmp() setup_auth("user", "hunter42") error_msg = "Stopping package build due to errors.\n" <> "Missing files: missing.txt, missing/*" assert_raise Mix.Error, error_msg, fn -> File.write!("myfile.txt", "hello") send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["package", "--no-progress", "--replace"]) assert_received {:mix_shell, :info, ["Building release_c 0.0.3"]} assert_received {:mix_shell, :info, [" Files:"]} assert_received {:mix_shell, :info, [" myfile.txt"]} assert_received {:mix_shell, :info, [" Extra: \n c: d"]} refute_received {:mix_shell, :error, ["Missing metadata fields" <> _]} end end) after purge([ReleaseMeta.MixProject]) end test "create with metadata" do Process.put(:hex_test_app_name, :publish_with_metadata) Mix.Project.push(ReleaseMeta.MixProject) in_tmp(fn -> set_home_tmp() setup_auth("user2", "hunter42") File.mkdir!("missing") File.write!("myfile.txt", "hello") File.write!("missing.txt", "hello") File.write!("missing/file.txt", "hello") send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["package", "--no-progress", "--replace"]) assert_received {:mix_shell, :info, ["Building publish_with_metadata 0.0.3"]} assert_received {:mix_shell, :info, [" Files:"]} assert_received {:mix_shell, :info, [" myfile.txt"]} assert_received {:mix_shell, :info, [" Extra: \n c: \n d: e"]} assert_received {:mix_shell, :info, ["Publishing package using http://" <> _]} refute_received {:mix_shell, :error, ["Missing metadata fields" <> _]} end) after purge([ReleaseMeta.MixProject]) end test "create with organization prompt" do Process.put(:hex_test_app_name, :publish_with_organization_prompt) Mix.Project.push(ReleaseSimple.MixProject) in_tmp(fn -> set_home_tmp() setup_auth("user", "hunter42") File.write!("myfile.txt", "hello") send(self(), {:mix_shell_input, :prompt, "2"}) send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["package", "--no-progress", "--replace"]) assert_received {:mix_shell, :info, ["You are a member of one or multiple organizations. " <> _]} assert_received {:mix_shell, :info, ["Publishing package using http://" <> _]} assert_received {:mix_shell, :info, ["Transferring ownership to testorg..."]} assert {:ok, {200, _headers, body}} = Hex.API.Package.get("hexpm", "publish_with_organization_prompt") assert "testorg" in Enum.map(body["owners"], & &1["username"]) end) after purge([ReleaseSimple.MixProject]) end test "create package with :organization config" do Process.put(:hex_test_app_name, :publish_with_org_config) Mix.Project.push(ReleaseRepo.MixProject) in_tmp(fn -> set_home_tmp() File.write!("mix.exs", "mix.exs") bypass_repo("myorg") setup_auth("user", "hunter42") send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run(["package", "--no-progress", "--replace"]) assert_received {:mix_shell, :info, ["Publishing package to private repository myorg using http://" <> _]} assert_received {:mix_shell, :info, ["Package published to myrepo html_url" <> _]} end) after purge([ReleaseRepo.MixProject]) end test "create package with :organization config with no organization in user config" do Process.put(:hex_test_app_name, :publish_without_org_in_user_config) Mix.Project.push(ReleaseRepo.MixProject) in_tmp(fn -> set_home_tmp() File.write!("mix.exs", "mix.exs") setup_auth("user", "hunter42") send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "hunter42"}) assert {:exit_code, 1} = ["package", "--no-progress", "--replace"] |> Mix.Tasks.Hex.Publish.run() |> catch_throw() refute_received {:mix_shell, :info, ["Package published to myrepo html_url" <> _]} end) after purge([ReleaseRepo.MixProject]) end test "create package with --organization flag overrides :organization config" do Process.put(:hex_test_app_name, :publish_organization_flag_override) Mix.Project.push(ReleaseRepo.MixProject) in_tmp(fn -> set_home_tmp() File.write!("mix.exs", "mix.exs") bypass_repo("myorg2") setup_auth("user", "hunter42") send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "hunter42"}) Mix.Tasks.Hex.Publish.run([ "package", "--no-progress", "--replace", "--organization", "myorg2" ]) assert_received {:mix_shell, :info, ["Publishing package to private repository myorg2 using http://" <> _]} assert_received {:mix_shell, :info, ["Package published to myrepo html_url" <> _]} end) after purge([ReleaseRepo.MixProject]) end test "create with empty file list" do Process.put(:hex_test_app_name, :publish_with_empty_file_list) Mix.Project.push(ReleaseMetaNoFiles.MixProject) in_tmp(fn -> set_home_tmp() setup_auth("user2", "hunter42") error_msg = "Stopping package build due to errors.\n" <> "Creating tarball failed: File list was empty." send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "hunter42"}) assert_raise Mix.Error, error_msg, fn -> Mix.Tasks.Hex.Publish.run(["package", "--no-progress", "--replace"]) end end) after purge([ReleaseMetaNoFiles.MixProject]) end end hex-2.4.2/test/mix/tasks/hex.registry_test.exs000066400000000000000000000170641517471540100214340ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.RegistryTest do use HexTest.Case test "build" do in_tmp(fn -> bypass = setup_bypass() 0 = Mix.shell().cmd("openssl genrsa -out private_key.pem") flush() Mix.Task.run( "hex.registry", ~w(build public --name acme --private-key private_key.pem) ) assert_received {:mix_shell, :info, ["* creating public/public_key"]} assert_received {:mix_shell, :info, ["* creating public/tarballs"]} assert_received {:mix_shell, :info, ["* creating public/names"]} assert_received {:mix_shell, :info, ["* creating public/versions"]} refute_received _ config = %{ :mix_hex_core.default_config() | repo_url: "http://localhost:#{bypass.port}", repo_verify: false, repo_verify_origin: false } assert {:ok, {200, _, %{packages: []}}} = :mix_hex_repo.get_names(config) assert {:ok, {200, _, %{packages: []}}} = :mix_hex_repo.get_versions(config) {:ok, %{tarball: tarball}} = :mix_hex_tarball.create(%{name: "foo", version: "0.10.0"}, []) File.write!("public/tarballs/foo-0.10.0.tar", tarball) Mix.Task.reenable("hex.registry") Mix.Task.run( "hex.registry", ~w(build public --name acme --private-key private_key.pem) ) assert_received {:mix_shell, :info, ["* creating public/packages/foo"]} assert_received {:mix_shell, :info, ["* updating public/names"]} assert_received {:mix_shell, :info, ["* updating public/versions"]} refute_received _ assert {:ok, {200, _, names}} = :mix_hex_repo.get_names(config) assert %{packages: [%{name: "foo", updated_at: %{seconds: updated_at}}]} = names assert updated_at == "public/tarballs/foo-0.10.0.tar" |> File.stat!() |> Map.fetch!(:mtime) |> Mix.Tasks.Hex.Registry.to_unix() assert {:ok, {200, _, versions}} = :mix_hex_repo.get_versions(config) assert versions == %{ packages: [%{name: "foo", retired: [], versions: ["0.10.0"]}], repository: "acme" } {:ok, %{tarball: tarball}} = :mix_hex_tarball.create(%{name: "foo", version: "0.9.0"}, []) File.write!("public/tarballs/foo-0.9.0.tar", tarball) Mix.Task.reenable("hex.registry") Mix.Task.run( "hex.registry", ~w(build public --name acme --private-key private_key.pem) ) assert_received {:mix_shell, :info, ["* updating public/packages/foo"]} assert_received {:mix_shell, :info, ["* updating public/names"]} assert_received {:mix_shell, :info, ["* updating public/versions"]} refute_received _ assert {:ok, {200, _, names}} = :mix_hex_repo.get_names(config) assert %{packages: [%{name: "foo", updated_at: %{seconds: updated_at}}]} = names assert updated_at == "public/tarballs/foo-0.9.0.tar" |> File.stat!() |> Map.fetch!(:mtime) |> Mix.Tasks.Hex.Registry.to_unix() assert {:ok, {200, _, versions}} = :mix_hex_repo.get_versions(config) assert versions == %{ packages: [%{name: "foo", retired: [], versions: ["0.9.0", "0.10.0"]}], repository: "acme" } # Versions with hyphen {:ok, %{tarball: tarball}} = :mix_hex_tarball.create(%{name: "foo", version: "1.0.0-rc"}, []) File.write!("public/tarballs/foo-1.0.0-rc.tar", tarball) Mix.Task.reenable("hex.registry") Mix.Task.run( "hex.registry", ~w(build public --name acme --private-key private_key.pem) ) assert_received {:mix_shell, :info, ["* updating public/packages/foo"]} assert_received {:mix_shell, :info, ["* updating public/names"]} assert_received {:mix_shell, :info, ["* updating public/versions"]} refute_received _ assert {:ok, {200, _, names}} = :mix_hex_repo.get_names(config) assert %{packages: [%{name: "foo", updated_at: _}]} = names assert {:ok, {200, _, versions}} = :mix_hex_repo.get_versions(config) assert versions == %{ packages: [%{name: "foo", retired: [], versions: ["0.9.0", "0.10.0", "1.0.0-rc"]}], repository: "acme" } # Re-generating private key 0 = Mix.shell().cmd("openssl genrsa -out private_key.pem") flush() Mix.Task.reenable("hex.registry") Mix.Task.run( "hex.registry", ~w(build public --name acme --private-key private_key.pem) ) assert_received {:mix_shell, :info, ["* public key at public/public_key does not" <> _]} assert_received {:mix_shell, :info, ["* updating public/public_key"]} assert_received {:mix_shell, :info, ["* updating public/packages/foo"]} assert_received {:mix_shell, :info, ["* updating public/names"]} assert_received {:mix_shell, :info, ["* updating public/versions"]} refute_received _ # Package with deps metadata = %{ name: "bar", version: "0.1.0", requirements: %{ "foo" => %{ "app" => "foo", "optional" => false, "repository" => "acme", "requirement" => "~> 0.1.0" }, "baz" => %{ "app" => "baz", "optional" => false, "repository" => "external", "requirement" => "~> 0.1.0" } } } {:ok, %{tarball: tarball}} = :mix_hex_tarball.create(metadata, []) File.write!("public/tarballs/bar-0.1.0.tar", tarball) Mix.Task.reenable("hex.registry") Mix.Task.run( "hex.registry", ~w(build public --name acme --private-key private_key.pem) ) assert_received {:mix_shell, :info, ["* creating public/packages/bar"]} assert_received {:mix_shell, :info, ["* updating public/packages/foo"]} assert_received {:mix_shell, :info, ["* updating public/names"]} assert_received {:mix_shell, :info, ["* updating public/versions"]} refute_received _ assert {:ok, {200, _, names}} = :mix_hex_repo.get_names(config) assert %{packages: [%{name: "bar", updated_at: _}, %{name: "foo", updated_at: _}]} = names assert {:ok, {200, _, %{releases: [package]}}} = :mix_hex_repo.get_package(config, "bar") assert package.dependencies == [ %{ app: "baz", optional: false, package: "baz", requirement: "~> 0.1.0", repository: "external" }, %{ app: "foo", optional: false, package: "foo", requirement: "~> 0.1.0" } ] # Removing all package releases File.rm!("public/tarballs/foo-0.9.0.tar") File.rm!("public/tarballs/foo-0.10.0.tar") File.rm!("public/tarballs/foo-1.0.0-rc.tar") Mix.Task.reenable("hex.registry") Mix.Task.run( "hex.registry", ~w(build public --name acme --private-key private_key.pem) ) assert_received {:mix_shell, :info, ["* updating public/packages/bar"]} assert_received {:mix_shell, :info, ["* removing public/packages/foo"]} assert_received {:mix_shell, :info, ["* updating public/names"]} assert_received {:mix_shell, :info, ["* updating public/versions"]} refute_received _ end) end defp setup_bypass() do bypass = Bypass.open() Bypass.expect(bypass, fn conn -> opts = Plug.Static.init(at: "/", from: File.cwd!() <> "/public") Plug.Static.call(conn, opts) end) bypass end end hex-2.4.2/test/mix/tasks/hex.repo_test.exs000066400000000000000000000253541517471540100205320ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.RepoTest do use HexTest.Case @public_key """ -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApqREcFDt5vV21JVe2QNB Edvzk6w36aNFhVGWN5toNJRjRJ6m4hIuG4KaXtDWVLjnvct6MYMfqhC79HAGwyF+ IqR6Q6a5bbFSsImgBJwz1oadoVKD6ZNetAuCIK84cjMrEFRkELtEIPNHblCzUkkM 3rS9+DPlnfG8hBvGi6tvQIuZmXGCxF/73hU0/MyGhbmEjIKRtG6b0sJYKelRLTPW XgK7s5pESgiwf2YC/2MGDXjAJfpfCd0RpLdvd4eRiXtVlE9qO9bND94E7PgQ/xqZ J1i2xWFndWa6nfFnRxZmCStCOZWYYPlaxr+FZceFbpMwzTNs4g3d4tLNUcbKAIH4 0wIDAQAB -----END PUBLIC KEY----- """ @public_key_fingerprint "SHA256:O1LOYhHFW4kcrblKAxROaDEzLD8bn1seWbe5tq8TRsk" defp bypass_public_key() do bypass = Bypass.open() repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].url, "http://localhost:#{bypass.port}") Hex.State.put(:repos, repos) Bypass.expect(bypass, fn %Plug.Conn{request_path: path} = conn -> case path do "/public_key" -> Plug.Conn.resp(conn, 200, @public_key) "/not_found/public_key" -> Plug.Conn.resp(conn, 404, "not found") end end) bypass end defp bypass_endpoint_url(bypass) do "http://localhost:#{bypass.port}" end test "add" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) File.write!("public_key.pem", @public_key) Mix.Tasks.Hex.Repo.run(["add", "reponame", "url", "--public-key", "public_key.pem"]) assert [ "$repos": %{ "reponame" => %{ auth_key: nil, public_key: "-----BEGIN PUBLIC KEY" <> _, url: "url" } } ] = Hex.Config.read() File.write!("foo.pem", "INVALID PUBLIC KEY") assert_raise Mix.Error, fn -> Mix.Tasks.Hex.Repo.run(["add", "reponame", "url", "--public-key", "foo.pem"]) end bypass = bypass_public_key() bypass_endpoint = bypass_endpoint_url(bypass) Mix.Tasks.Hex.Repo.run([ "add", "another-reponame", bypass_endpoint, "--fetch-public-key", @public_key_fingerprint, "--auth-key", "AAAA" ]) assert [ "$repos": %{ "another-reponame" => %{ auth_key: "AAAA", public_key: "-----BEGIN PUBLIC KEY" <> _, url: ^bypass_endpoint } } ] = Hex.Config.read() assert_raise Mix.Error, ~r/Public key fingerprint mismatch/, fn -> Mix.Tasks.Hex.Repo.run([ "add", "reponame-wrong-fingerprint", bypass_endpoint, "--fetch-public-key", "WRONG-FINGERPRINT" ]) end refute Keyword.get(Hex.Config.read(), :"$repos")["reponame-wrong-fingerprint"] assert catch_throw( Mix.Tasks.Hex.Repo.run([ "add", "not-found-reponame", "#{bypass_endpoint}/not_found", "--fetch-public-key", @public_key_fingerprint ]) ) == {:exit_code, 1} assert_received {:mix_shell, :error, ["Downloading public key failed with code \"404\""]} Bypass.down(bypass) assert catch_throw( Mix.Tasks.Hex.Repo.run([ "add", "reponame-connection-error", bypass_endpoint, "--fetch-public-key", @public_key_fingerprint ]) ) == {:exit_code, 1} assert_received {:mix_shell, :error, ["Downloading public key failed"]} end) end test "remove" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) Mix.Tasks.Hex.Repo.run(["add", "reponame", "url"]) Mix.Tasks.Hex.Repo.run(["remove", "reponame"]) assert ["$repos": %{}] = Hex.Config.read() end) end test "set url" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) Mix.Tasks.Hex.Repo.run(["add", "reponame", "url"]) Mix.Tasks.Hex.Repo.run(["set", "reponame", "--url", "other_url"]) assert ["$repos": %{"reponame" => %{url: "other_url"}}] = Hex.Config.read() end) end test "show url" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) Mix.Tasks.Hex.Repo.run(["add", "reponame", "url"]) Mix.Tasks.Hex.Repo.run(["show", "reponame", "--url"]) assert_received {:mix_shell, :info, ["url"]} end) end test "show prints repo config" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) Mix.Tasks.Hex.Repo.run(["add", "reponame", "url"]) Mix.Tasks.Hex.Repo.run(["show", "reponame"]) assert_received {:mix_shell, :info, [headers]} assert_received {:mix_shell, :info, [config]} assert headers =~ ~r{URL.*Public key.*Auth key} assert config =~ "url" end) end test "show raises an error when called with non-existant repo name" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) assert_raise Mix.Error, "Config does not contain repo non-existant-reponame", fn -> Mix.Tasks.Hex.Repo.run(["show", "non-existant-reponame"]) end end) end describe "OAuth exchange configuration" do test "add with --oauth-exchange enables OAuth token exchange" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) Mix.Tasks.Hex.Repo.run([ "add", "reponame", "http://example.com", "--oauth-exchange" ]) config = Hex.Config.read() repo = config[:"$repos"]["reponame"] assert repo.oauth_exchange == true end) end test "add without --oauth-exchange disables OAuth token exchange by default for non-hexpm repos" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) Mix.Tasks.Hex.Repo.run([ "add", "reponame", "http://example.com" ]) config = Hex.Config.read() repo = config[:"$repos"]["reponame"] assert repo.oauth_exchange == false end) end test "add with --oauth-exchange-url sets custom OAuth exchange URL" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) Mix.Tasks.Hex.Repo.run([ "add", "reponame", "http://example.com", "--oauth-exchange-url", "http://custom-oauth.com" ]) config = Hex.Config.read() repo = config[:"$repos"]["reponame"] assert repo.oauth_exchange_url == "http://custom-oauth.com" end) end test "add with both --oauth-exchange and --oauth-exchange-url" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) Mix.Tasks.Hex.Repo.run([ "add", "reponame", "http://example.com", "--oauth-exchange", "--oauth-exchange-url", "http://custom-oauth.com" ]) config = Hex.Config.read() repo = config[:"$repos"]["reponame"] assert repo.oauth_exchange == true assert repo.oauth_exchange_url == "http://custom-oauth.com" end) end test "set with --oauth-exchange enables OAuth token exchange" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) Mix.Tasks.Hex.Repo.run(["add", "reponame", "http://example.com"]) Mix.Tasks.Hex.Repo.run(["set", "reponame", "--oauth-exchange"]) config = Hex.Config.read() repo = config[:"$repos"]["reponame"] assert repo.oauth_exchange == true end) end test "set with --oauth-exchange-url updates custom OAuth exchange URL" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) Mix.Tasks.Hex.Repo.run(["add", "reponame", "http://example.com"]) Mix.Tasks.Hex.Repo.run([ "set", "reponame", "--oauth-exchange-url", "http://new-oauth.com" ]) config = Hex.Config.read() repo = config[:"$repos"]["reponame"] assert repo.oauth_exchange_url == "http://new-oauth.com" end) end test "show with --oauth-exchange displays oauth_exchange setting" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) Mix.Tasks.Hex.Repo.run([ "add", "reponame", "http://example.com" ]) Mix.Tasks.Hex.Repo.run(["show", "reponame", "--oauth-exchange"]) assert_received {:mix_shell, :info, ["false"]} end) end test "show with --oauth-exchange-url displays custom OAuth URL" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) Mix.Tasks.Hex.Repo.run([ "add", "reponame", "http://example.com", "--oauth-exchange-url", "http://custom-oauth.com" ]) Mix.Tasks.Hex.Repo.run(["show", "reponame", "--oauth-exchange-url"]) assert_received {:mix_shell, :info, ["http://custom-oauth.com"]} end) end test "add with auth key defaults to no oauth exchange for non-hexpm repos" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) Mix.Tasks.Hex.Repo.run([ "add", "reponame", "http://example.com", "--auth-key", "my-api-key" ]) config = Hex.Config.read() repo = config[:"$repos"]["reponame"] assert repo.auth_key == "my-api-key" assert repo.oauth_exchange == false end) end test "list displays repos with different OAuth exchange settings" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) Mix.Tasks.Hex.Repo.run([ "add", "repo1", "http://example1.com", "--oauth-exchange" ]) Mix.Tasks.Hex.Repo.run(["add", "repo2", "http://example2.com"]) Mix.Tasks.Hex.Repo.run(["list"]) assert_received {:mix_shell, :info, [header]} assert header =~ "Name" messages = Stream.unfold(nil, fn _ -> receive do {:mix_shell, :info, [msg]} -> {msg, nil} after 0 -> nil end end) |> Enum.to_list() all_output = Enum.join([header | messages], "\n") assert all_output =~ "repo1" assert all_output =~ "repo2" end) end test "add repo named 'hexpm' defaults to oauth_exchange enabled" do in_tmp(fn -> Hex.State.put(:config_home, File.cwd!()) Mix.Tasks.Hex.Repo.run([ "add", "hexpm", "http://example.com" ]) # Check the actual repo state (after merging with defaults) # not the raw config (which may have oauth_exchange removed if it matches default) repo = Hex.State.fetch!(:repos)["hexpm"] assert repo.oauth_exchange == true end) end end end hex-2.4.2/test/mix/tasks/hex.retire_test.exs000066400000000000000000000026161517471540100210530ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.RetireTest do use HexTest.IntegrationCase test "retire and unretire package" do auth = Hexpm.new_user("retire_user", "retire_user@mail.com", "passpass", "key") Hexpm.new_package("hexpm", "retire_package", "0.0.1", [], %{}, auth) set_home_tmp() Hex.State.put(:api_key, auth[:key]) Mix.Tasks.Hex.Retire.run(["retire_package", "0.0.1", "renamed", "--message", "message"]) assert_received {:mix_shell, :info, ["retire_package 0.0.1 has been retired\n"]} assert {:ok, {200, _, %{"retirement" => %{"message" => "message", "reason" => "renamed"}}}} = Hex.API.Release.get("hexpm", "retire_package", "0.0.1") Mix.Tasks.Hex.Retire.run(["retire_package", "0.0.1", "--unretire"]) assert_received {:mix_shell, :info, ["retire_package 0.0.1 has been unretired"]} assert {:ok, {200, _, %{"retirement" => nil}}} = Hex.API.Release.get("hexpm", "retire_package", "0.0.1") end test "retire --message flag is required" do auth = Hexpm.new_user("retire_user_message", "retire_user_message@mail.com", "passpass", "key") Hexpm.new_package("hexpm", "retire_package_message", "0.0.1", [], %{}, auth) set_home_tmp() Hex.State.put(:api_key, auth[:key]) assert_raise Mix.Error, "Missing required flag --message", fn -> Mix.Tasks.Hex.Retire.run(["retire_package_message", "0.0.1", "renamed"]) end end end hex-2.4.2/test/mix/tasks/hex.search_test.exs000066400000000000000000000075331517471540100210310ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.SearchTest do use HexTest.IntegrationCase defmodule SearchDeps.MixProject do def project do [ app: :search_app, version: "0.0.1", deps: [ {:foo, "0.1.0"} ] ] end end describe "hexdocs" do test "no args" do Mix.Project.push(SearchDeps.MixProject) in_tmp(fn -> write_search_deps() Mix.Tasks.Hex.Search.run([]) assert_received {:hex_system_cmd, _, ["https://hexdocs.pm/?packages=" <> packages]} vsn = System.version() assert packages =~ URI.encode_www_form( "bar:0.1.0,eex:#{vsn},elixir:#{vsn},ex_unit:#{vsn},foo:0.1.0" ) assert String.ends_with?(packages, "&q=") end) end test "--no-stdlib" do Mix.Project.push(SearchDeps.MixProject) in_tmp(fn -> write_search_deps() Mix.Tasks.Hex.Search.run(["--no-stdlib"]) assert_received {:hex_system_cmd, _, ["https://hexdocs.pm/?packages=" <> packages]} assert packages =~ URI.encode_www_form("bar:0.1.0,foo:0.1.0") assert String.ends_with?(packages, "&q=") end) end test "--print-url" do Mix.Project.push(SearchDeps.MixProject) in_tmp(fn -> write_search_deps() Mix.Tasks.Hex.Search.run(["--print-url"]) assert_received {:mix_shell, :info, ["https://hexdocs.pm/?packages=" <> packages]} vsn = System.version() assert packages =~ URI.encode_www_form( "bar:0.1.0,eex:#{vsn},elixir:#{vsn},ex_unit:#{vsn},foo:0.1.0" ) assert String.ends_with?(packages, "&q=") end) end defp write_search_deps do set_home_tmp() Mix.Dep.Lock.write(%{foo: {:hex, :foo, "0.1.0"}, bar: {:hex, :bar, "0.1.0"}}) Mix.Task.run("deps.get") flush() end end describe "package" do test "backwards compatibility" do Mix.Tasks.Hex.Search.run(["bloopdoopbloop"]) assert_received {:mix_shell, :error, ["mix hex.search PACKAGE is deprecated, use --package PACKAGE instead"]} assert_received {:mix_shell, :info, ["No packages found"]} end test "no results" do Mix.Tasks.Hex.Search.run(["--package", "bloopdoopbloop"]) assert_received {:mix_shell, :info, ["No packages found"]} end test "public packages" do Mix.Tasks.Hex.Search.run(["--package", "doc"]) assert_received {:mix_shell, :info, ["ex_doc" <> ex_doc]} assert_received {:mix_shell, :info, ["only_doc" <> only_doc]} assert ex_doc =~ ~r"\w*0\.1\.0.*http://localhost:4043/packages/ex_doc" assert only_doc =~ ~r"\w*0\.1\.0.*http://localhost:4043/packages/only_doc" end test "all private packages" do in_tmp(fn -> set_home_tmp() auth = Hexpm.new_user("searchuser1", "searchuser1@mail.com", "password", "searchuser1") Hexpm.new_repo("searchrepo1", auth) Hex.State.put(:api_key, auth[:key]) Mix.Tasks.Hex.Search.run(["--package", "doc"]) assert_received {:mix_shell, :info, ["ex_doc" <> ex_doc]} assert_received {:mix_shell, :info, ["only_doc" <> only_doc]} assert ex_doc =~ ~r"\w*0\.1\.0.*http://localhost:4043/packages/ex_doc" assert only_doc =~ ~r"\w*0\.1\.0.*http://localhost:4043/packages/only_doc" end) end test "org packages" do in_tmp(fn -> set_home_tmp() auth = Hexpm.new_user("searchuser2", "searchuser2@mail.com", "password", "searchuser2") Hexpm.new_repo("searchrepo2", auth) Hex.State.put(:api_key, auth[:key]) Mix.Tasks.Hex.Search.run(["--package", "doc", "--organization", "searchrepo2"]) refute_received {:mix_shell, :info, ["ex_doc" <> _]} refute_received {:mix_shell, :info, ["only_doc" <> _]} end) end end end hex-2.4.2/test/mix/tasks/hex.sponsor_test.exs000066400000000000000000000042401517471540100212570ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.SponsorTest do use HexTest.IntegrationCase @package :test_sponsored_package @package_name Atom.to_string(@package) defmodule SponsoredDeps.MixProject do def project do [app: :test_app, version: "0.0.1", deps: [{:test_sponsored_package, ">= 0.1.0"}]] end end setup_all do auth = Hexpm.new_user("sponsor_user", "sponsor@mail.com", "passpass", "key") {:ok, [auth: auth]} end test "sponsor with sponsor link", context do url = "https://sponsor.foo.bar" with_test_package("0.1.0", %{links: %{sponsor: url}}, context, fn -> Mix.Task.run("hex.sponsor") end) assert_received {:mix_shell, :info, [header_output]} assert_received {:mix_shell, :info, [package_line]} assert header_output =~ "Dependency" assert header_output =~ "Sponsorship" assert package_line =~ @package_name assert package_line =~ url end test "without sponsor link", context do with_test_package("1.2.0", %{links: %{github: "https://github.com/foo/bar"}}, context, fn -> Mix.Task.run("hex.sponsor") end) assert_received {:mix_shell, :info, [header_output]} refute_received {:mix_shell, :info, [_package_line]} assert header_output =~ "No dependencies with sponsorship link found" end test "outside a mix project", %{auth: auth} do in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) Hex.State.put(:api_key, auth[:key]) flush() error_msg = "The sponsor task only works inside a Mix project. " <> "Please ensure you are in a directory with a mix.exs file." assert_raise Mix.Error, error_msg, fn -> Mix.Tasks.Hex.Sponsor.run([]) end end) end defp with_test_package(version, metadata, %{auth: auth}, fun) do Mix.Project.push(SponsoredDeps.MixProject) Hexpm.new_package( "hexpm", @package_name, version, [], metadata, auth ) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) Hex.State.put(:api_key, auth[:key]) Mix.Dep.Lock.write(%{@package => {:hex, @package, version}}) Mix.Task.run("deps.get") flush() fun.() end) end end hex-2.4.2/test/mix/tasks/hex.user_test.exs000066400000000000000000000401731517471540100205370ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.UserTest do use HexTest.IntegrationCase @tag timeout: 5000 test "auth performs OAuth device flow" do in_tmp(fn -> set_home_cwd() # Clear any existing auth Hex.OAuth.clear_tokens() # Test that device authorization works but don't try to complete the flow assert {:ok, {200, _headers, response}} = Hex.API.OAuth.device_authorization("api") assert %{ "device_code" => device_code, "user_code" => user_code, "verification_uri" => verification_uri } = response assert is_binary(device_code) assert is_binary(user_code) assert is_binary(verification_uri) # Test that polling returns authorization_pending (user hasn't authorized yet) assert {:ok, {400, _headers, %{"error" => "authorization_pending"}}} = Hex.API.OAuth.poll_device_token(device_code) # Verify no tokens were stored since flow didn't complete refute Hex.OAuth.has_tokens?() end) end test "auth uses verification_uri_complete when available" do in_tmp(fn -> set_home_cwd() Hex.OAuth.clear_tokens() # Test device authorization assert {:ok, {200, _headers, %{"device_code" => device_code}}} = Hex.API.OAuth.device_authorization("api") assert is_binary(device_code) end) end test "auth with custom name parameter" do in_tmp(fn -> set_home_cwd() Hex.OAuth.clear_tokens() # Test device authorization with a custom name custom_name = "MyTestDevice" assert {:ok, {200, _headers, response}} = Hex.API.OAuth.device_authorization("api repositories", custom_name) assert %{ "device_code" => device_code, "user_code" => user_code } = response assert is_binary(device_code) assert is_binary(user_code) # Verify the flow works with the name parameter assert {:ok, {400, _headers, %{"error" => "authorization_pending"}}} = Hex.API.OAuth.poll_device_token(device_code) end) end test "auth with nil name parameter" do in_tmp(fn -> set_home_cwd() Hex.OAuth.clear_tokens() # Test device authorization with nil name (should work) assert {:ok, {200, _headers, response}} = Hex.API.OAuth.device_authorization("api repositories", nil) assert %{"device_code" => device_code} = response assert is_binary(device_code) end) end test "auth handles device flow errors gracefully" do in_tmp(fn -> set_home_cwd() Hex.OAuth.clear_tokens() # Test polling with an invalid device code assert {:ok, {400, _headers, %{"error" => error}}} = Hex.API.OAuth.poll_device_token("invalid_device_code") assert error in ["authorization_pending", "invalid_grant", "expired_token"] end) end test "auth handles slow_down response" do in_tmp(fn -> set_home_cwd() Hex.OAuth.clear_tokens() # Test that repeated polling gets proper response assert {:ok, {200, _headers, %{"device_code" => device_code}}} = Hex.API.OAuth.device_authorization("api") # Immediate polling should get authorization_pending assert {:ok, {400, _headers, %{"error" => "authorization_pending"}}} = Hex.API.OAuth.poll_device_token(device_code) end) end test "auth handles user denial" do in_tmp(fn -> set_home_cwd() Hex.OAuth.clear_tokens() # Test device authorization returns proper structure assert {:ok, {200, _headers, response}} = Hex.API.OAuth.device_authorization("api") assert %{ "device_code" => _, "user_code" => _, "verification_uri" => _, "expires_in" => _, "interval" => _ } = response end) end test "token refresh functionality" do in_tmp(fn -> set_home_cwd() # Create a user with OAuth tokens auth = Hexpm.new_oauth_user("refreshuser", "refreshuser@mail.com", "password") # Extract refresh token from auth refresh_token = auth[:refresh_token] # Test token refresh assert {:ok, {200, _headers, response}} = Hex.API.OAuth.refresh_token(refresh_token) assert %{ "access_token" => new_access_token, "refresh_token" => new_refresh_token, "token_type" => "bearer" } = response assert is_binary(new_access_token) assert is_binary(new_refresh_token) end) end test "token revocation functionality" do in_tmp(fn -> set_home_cwd() # Create a user with OAuth tokens auth = Hexpm.new_oauth_user("revokeuser", "revokeuser@mail.com", "password") # Extract access token from auth access_token = auth[:access_token] # Test token revocation assert {:ok, {200, _headers, nil}} = Hex.API.OAuth.revoke_token(access_token) # Token should no longer be valid for API calls config = Hex.API.Client.config(key: access_token, oauth: true) assert {:ok, {401, _headers, _}} = :mix_hex_api.get(config, ["users", "me"]) end) end test "inline authentication when no auth present" do in_tmp(fn -> set_home_cwd() # Clear all auth Hex.OAuth.clear_tokens() # User says no to authenticate inline (to avoid hanging on real OAuth flow) send(self(), {:mix_shell_input, :yes?, false}) # Calling auth_info should ask for inline auth assert_raise Mix.Error, "No authenticated user found. Run `mix hex.user auth`", fn -> Mix.Tasks.Hex.auth_info(:write) end assert_received {:mix_shell, :yes?, ["No authenticated user found. Do you want to authenticate now?"]} end) end test "inline authentication declined by user" do in_tmp(fn -> set_home_cwd() # Clear all auth Hex.OAuth.clear_tokens() # User says no to authenticate inline send(self(), {:mix_shell_input, :yes?, false}) # Should raise when user declines assert_raise Mix.Error, "No authenticated user found. Run `mix hex.user auth`", fn -> Mix.Tasks.Hex.auth_info(:write) end assert_received {:mix_shell, :yes?, ["No authenticated user found. Do you want to authenticate now?"]} end) end test "inline authentication accepted by user" do in_tmp(fn -> set_home_cwd() bypass = Bypass.open() original_url = Hex.State.fetch!(:api_url) Hex.State.put(:api_url, "http://localhost:#{bypass.port}/api") # Clear all auth Hex.OAuth.clear_tokens() # User says yes to authenticate inline send(self(), {:mix_shell_input, :yes?, true}) # Mock the OAuth flow for inline auth Bypass.expect(bypass, "POST", "/api/oauth/device_authorization", fn conn -> conn |> Plug.Conn.put_resp_header("content-type", "application/vnd.hex+erlang") |> Plug.Conn.resp( 200, Hex.Utils.safe_serialize_erlang(%{ "device_code" => "inline_device", "user_code" => "INLINE", "verification_uri" => "https://hex.pm/oauth/device", "expires_in" => 600, "interval" => 0 }) ) end) # Mock polling - succeed immediately Bypass.expect(bypass, "POST", "/api/oauth/token", fn conn -> {:ok, body, conn} = Plug.Conn.read_body(conn) params = Hex.Utils.safe_deserialize_erlang(body) resp_body = case params["grant_type"] do "urn:ietf:params:oauth:grant-type:device_code" -> %{ "access_token" => "inline_token", "token_type" => "bearer", "expires_in" => 3600, "refresh_token" => "inline_refresh", "scope" => "api repositories" } end conn |> Plug.Conn.put_resp_header("content-type", "application/vnd.hex+erlang") |> Plug.Conn.resp(200, Hex.Utils.safe_serialize_erlang(resp_body)) end) # Calling auth_info should trigger inline auth auth = Mix.Tasks.Hex.auth_info(:write) # Should get auth after inline flow with OAuth flag assert [key: _token, oauth: true] = auth assert_received {:mix_shell, :yes?, ["No authenticated user found. Do you want to authenticate now?"]} Hex.State.put(:api_url, original_url) end) end test "auth_info fallback behavior" do in_tmp(fn -> set_home_cwd() # Test fallback from OAuth to API keys Hex.OAuth.clear_tokens() # No auth should trigger inline auth (but we disable it) assert [] = Mix.Tasks.Hex.auth_info(:write, auth_inline: false) # Test with API key set Hex.State.put(:api_key, "test_api_key") assert [key: "test_api_key"] = Mix.Tasks.Hex.auth_info(:write, auth_inline: false) # Test with OAuth tokens future_time = System.system_time(:second) + 3600 tokens = %{ "access_token" => "oauth_token", "refresh_token" => "oauth_refresh", "expires_at" => future_time } Hex.OAuth.store_token(tokens) assert [key: "oauth_token", oauth: true] = Mix.Tasks.Hex.auth_info(:write, auth_inline: false) # Clear OAuth tokens - should fall back to API key Hex.OAuth.clear_tokens() assert [key: "test_api_key"] = Mix.Tasks.Hex.auth_info(:write, auth_inline: false) end) end test "auth_info with expired tokens triggers refresh" do in_tmp(fn -> set_home_cwd() bypass = Bypass.open() original_url = Hex.State.fetch!(:api_url) Hex.State.put(:api_url, "http://localhost:#{bypass.port}/api") # Store expired OAuth tokens past_time = System.system_time(:second) - 3600 tokens = %{ "access_token" => "expired_token", "refresh_token" => "refresh_token", "expires_at" => past_time } Hex.OAuth.store_token(tokens) # Mock refresh token endpoint Bypass.expect(bypass, "POST", "/api/oauth/token", fn conn -> {:ok, body, conn} = Plug.Conn.read_body(conn) # Check which refresh token is being used cond do String.contains?(body, "refresh_token") -> conn |> Plug.Conn.put_resp_header("content-type", "application/vnd.hex+erlang") |> Plug.Conn.resp( 200, Hex.Utils.safe_serialize_erlang(%{ "access_token" => "new_token", "token_type" => "bearer", "expires_in" => 3600, "refresh_token" => "new_refresh_token", "scope" => "api:write" }) ) true -> conn |> Plug.Conn.resp(400, "Bad request") end end) # Call auth_info - should trigger refresh auth = Mix.Tasks.Hex.auth_info(:write, auth_inline: false) # Should get new token after refresh assert [key: "new_token", oauth: true] = auth # Verify new tokens were stored config = Hex.Config.read() assert config[:"$oauth_token"]["access_token"] == "new_token" assert config[:"$oauth_token"]["refresh_token"] == "new_refresh_token" Hex.State.put(:api_url, original_url) end) end test "deauth user and organizations" do in_tmp(fn -> set_home_cwd() # Create OAuth tokens auth = Hexpm.new_oauth_user("userdeauth1", "userdeauth1@mail.com", "password") Hexpm.new_repo("myorguserdeauth1", auth) # Store OAuth tokens tokens = %{ "access_token" => auth[:access_token], "refresh_token" => auth[:refresh_token], "expires_at" => System.system_time(:second) + 3600 } Hex.OAuth.store_token(tokens) # Verify OAuth tokens exist assert Hex.Config.read()[:"$oauth_token"] # Create organization auth send(self(), {:mix_shell_input, :prompt, "password"}) Mix.Tasks.Hex.Organization.run(["auth", "myorguserdeauth1"]) Mix.Tasks.Hex.User.run(["deauth"]) # Verify OAuth tokens are cleared refute Hex.Config.read()[:"$oauth_token"] refute Hex.Config.read()[:"$repos"]["hexpm:myorguserdeauth1"] end) end test "auth handles token refresh failure" do in_tmp(fn -> set_home_cwd() # Try to refresh with invalid refresh token assert {:ok, {400, _headers, %{"error" => _}}} = Hex.API.OAuth.refresh_token("invalid_refresh_token") end) end test "OAuth token storage and retrieval" do in_tmp(fn -> set_home_cwd() # Clear any existing tokens Hex.OAuth.clear_tokens() refute Hex.OAuth.has_tokens?() # Store tokens tokens = %{ "access_token" => "write_access", "refresh_token" => "write_refresh", "expires_at" => System.system_time(:second) + 3600 } Hex.OAuth.store_token(tokens) assert Hex.OAuth.has_tokens?() # Retrieve tokens - same token for both read and write # Same token is returned for both read and write operations assert {:ok, "write_access"} = Hex.OAuth.get_token() assert {:ok, "write_access"} = Hex.OAuth.get_token() # Clear tokens Hex.OAuth.clear_tokens() refute Hex.OAuth.has_tokens?() end) end test "whoami with OAuth" do in_tmp(fn -> set_home_cwd() # Create user with OAuth tokens auth = Hexpm.new_oauth_user("whoamioauth", "whoamioauth@mail.com", "password") # Store OAuth token tokens = %{ "access_token" => auth[:access_token], "refresh_token" => auth[:refresh_token], "expires_at" => System.system_time(:second) + 3600 } Hex.OAuth.store_token(tokens) Mix.Tasks.Hex.User.run(["whoami"]) assert_received {:mix_shell, :info, [username]} assert String.starts_with?(username, "whoamioauth") end) end test "whoami with API key" do in_tmp(fn -> set_home_cwd() auth = Hexpm.new_user("whoamiapi", "whoamiapi@mail.com", "password", "key") Hex.State.put(:api_key, auth[:key]) Mix.Tasks.Hex.User.run(["whoami"]) assert_received {:mix_shell, :info, [username]} assert String.starts_with?(username, "whoamiapi") end) end test "device flow with custom scopes" do in_tmp(fn -> set_home_cwd() # Test device authorization with custom scopes assert {:ok, {200, _headers, response}} = Hex.API.OAuth.device_authorization("api:write repositories") assert %{ "device_code" => device_code, "user_code" => _, "verification_uri" => _ } = response assert is_binary(device_code) # Polling should return pending since user hasn't authorized assert {:ok, {400, _headers, %{"error" => "authorization_pending"}}} = Hex.API.OAuth.poll_device_token(device_code) end) end test "auth_info includes OTP from HEX_OTP environment variable" do in_tmp(fn -> set_home_cwd() # Setup OAuth tokens future_time = System.system_time(:second) + 3600 tokens = %{ "access_token" => "oauth_token", "refresh_token" => "refresh_token", "expires_at" => future_time } Hex.OAuth.store_token(tokens) # Set HEX_OTP in state Hex.State.put(:api_otp, "123456") # Get auth info - should include OTP auth = Mix.Tasks.Hex.auth_info(:write, auth_inline: false) assert [key: "oauth_token", oauth: true, otp: "123456"] = auth end) end test "auth_info does not prompt for OTP when HEX_OTP is not set" do in_tmp(fn -> set_home_cwd() # Setup OAuth tokens future_time = System.system_time(:second) + 3600 tokens = %{ "access_token" => "oauth_token", "refresh_token" => "refresh_token", "expires_at" => future_time } Hex.OAuth.store_token(tokens) # Don't set HEX_OTP - should not prompt upfront Hex.State.put(:api_otp, nil) # Get auth info - should not include OTP (server will prompt if needed) auth = Mix.Tasks.Hex.auth_info(:write, auth_inline: false) assert [key: "oauth_token", oauth: true] = auth end) end end hex-2.4.2/test/mix/tasks/hex_test.exs000066400000000000000000000004401517471540100175530ustar00rootroot00000000000000defmodule Mix.Tasks.HexTest do use HexTest.Case test "run without args shows help" do Mix.Tasks.Hex.run([]) assert_received {:mix_shell, :info, ["Hex is a package manager for the Erlang ecosystem."]} assert_received {:mix_shell, :info, ["mix hex.config" <> _]} end end hex-2.4.2/test/setup_hexpm.exs000066400000000000000000000070571517471540100163620ustar00rootroot00000000000000alias HexTest.Case alias HexTest.Hexpm Hexpm.init() Hexpm.start() # Create OAuth client for testing config = Hex.API.Client.config() body = %{ "client_id" => "78ea6566-89fd-481e-a1d6-7d9d78eacca8", "client_type" => "public", "name" => "Hex CLI" } :mix_hex_api.post(config, ["oauth_client"], body) pkg_meta = %{ "licenses" => ["GPL-2.0", "MIT", "Apache-2.0"], "links" => %{"docs" => "http://docs", "repo" => "http://repo"}, "description" => "Some description" } auth = Hexpm.new_user("user", "user@mail.com", "hunter42", "my_key") Hexpm.new_user("user2", "user2@mail.com", "hunter42", "my_key") package_name_meta = Map.put(pkg_meta, "app", "app_name") Hexpm.new_package("hexpm", "ex_doc", "0.0.1", [], pkg_meta, auth) Hexpm.new_package("hexpm", "ex_doc", "0.1.0", [], pkg_meta, auth) Hexpm.new_package("hexpm", "ex_doc", "0.1.0-rc1", [], pkg_meta, auth) Hexpm.new_package("hexpm", "postgrex", "0.2.1", [ex_doc: "~> 0.1.0"], pkg_meta, auth) Hexpm.new_package("hexpm", "postgrex", "0.2.0", [ex_doc: "0.0.1"], pkg_meta, auth) Hexpm.new_package( "hexpm", "ecto", "0.2.0", [postgrex: "~> 0.2.0", ex_doc: "~> 0.0.1"], pkg_meta, auth ) Hexpm.new_package("hexpm", "phoenix", "0.0.1", [postgrex: "~> 0.2"], pkg_meta, auth) Hexpm.new_package( "hexpm", "only_doc", "0.1.0", [{:ex_doc, ">= 0.0.0", optional: true}], pkg_meta, auth ) Hexpm.new_package("hexpm", "package_name", "0.1.0", [], package_name_meta, auth) Hexpm.new_package("hexpm", "foo", "0.1.0", [], pkg_meta, auth) Hexpm.new_package("hexpm", "foo", "0.1.1", [], pkg_meta, auth) Hexpm.new_package("hexpm", "bar", "0.1.0", [foo: "~> 0.1.0"], pkg_meta, auth) Hexpm.new_package("hexpm", "baz", "0.1.0", [foo: "0.1.0"], pkg_meta, auth) Hexpm.new_package("hexpm", "beta", "1.0.0", [], pkg_meta, auth) Hexpm.new_package("hexpm", "beta", "1.1.0-beta", [], pkg_meta, auth) Hexpm.new_package("hexpm", "tired", "0.1.0", [], pkg_meta, auth) Hexpm.new_package("hexpm", "tired", "0.2.0", [], pkg_meta, auth) Hexpm.new_package( "hexpm", "ecto", "0.2.1", [ {:sample, "0.0.1", path: Case.fixture_path("sample")}, postgrex: "~> 0.2.1", ex_doc: "0.1.0" ], pkg_meta, auth ) Hexpm.new_package( "hexpm", "depend_name", "0.2.0", [{:app_name, ">= 0.0.0", hex: :package_name}], pkg_meta, auth ) {:ok, _} = Hex.API.Release.retire("hexpm", "tired", "0.1.0", %{reason: "invalid"}, auth) Hexpm.new_repo("testorg", auth) Hexpm.new_package("testorg", "foo", "0.1.0", [], pkg_meta, auth) Hexpm.new_package("testorg", "bar", "0.1.0", [foo: "~> 0.1.0"], pkg_meta, auth) Hexpm.new_repo("remote_converger_org", auth) Hexpm.new_package("remote_converger_org", "private_prompt_pkg", "0.1.0", [], pkg_meta, auth) Hexpm.new_package("hexpm", "ecto_sql", "3.3.2", [ecto: "~> 3.3.1"], pkg_meta, auth, [ {"mix.exs", File.read!(Case.fixture_path("ecto_sql_3_3_2/mix.exs"))} ]) Hexpm.new_package("hexpm", "ecto_sql", "3.3.3", [ecto: "~> 3.3.2"], pkg_meta, auth, [ {"mix.exs", File.read!(Case.fixture_path("ecto_sql_3_3_3/mix.exs"))} ]) Hexpm.new_package("hexpm", "ecto_enum", "1.4.0", [ecto: ">= 3.0.0"], pkg_meta, auth, [ {"mix.exs", File.read!(Case.fixture_path("ecto_enum_1_4_0/mix.exs"))} ]) Hexpm.new_package("hexpm", "ecto", "3.3.1", [], pkg_meta, auth, [ {"mix.exs", File.read!(Case.fixture_path("ecto_3_3_1/mix.exs"))} ]) Hexpm.new_package("hexpm", "ecto", "3.3.2", [], pkg_meta, auth, [ {"mix.exs", File.read!(Case.fixture_path("ecto_3_3_2/mix.exs"))} ]) sponsored_meta = put_in(pkg_meta, ["links", "Sponsor"], "https://my.sponsor.link") Hexpm.new_package("hexpm", "sponsored", "0.1.0", [], sponsored_meta, auth) hex-2.4.2/test/support/000077500000000000000000000000001517471540100150035ustar00rootroot00000000000000hex-2.4.2/test/support/case.ex000066400000000000000000000321631517471540100162610ustar00rootroot00000000000000defmodule HexTest.Case do use ExUnit.CaseTemplate using do quote do import unquote(__MODULE__) alias HexTest.Case alias HexTest.Hexpm end end def flush do flush([]) end defp flush(acc) do receive do any -> flush([any | acc]) after 0 -> Enum.reverse(acc) end end def tmp_path() do Path.expand("../../tmp", __DIR__) end def tmp_path(extension) do Path.join(tmp_path(), extension) end def fixture_path() do Path.expand("../fixtures", __DIR__) end def fixture_path(extension) do Path.join(fixture_path(), extension) end defp escape_path(path) do case :os.type() do {:win32, _} -> String.replace(path, ~r'[~#%&*{}\\:<>?/+|"]', "_") _ -> path end end defmacro test_tmp() do module = escape_path("#{__CALLER__.module}") function = escape_path("#{elem(__CALLER__.function || {nil}, 0)}") Path.join([HexTest.Case.tmp_path(), module, function]) end defmacro test_name() do module = escape_path("#{__CALLER__.module}") function = escape_path("#{elem(__CALLER__.function || {nil}, 0)}") Path.join([module, function]) end defmacro in_tmp(fun) do module = escape_path("#{__CALLER__.module}") function = escape_path("#{elem(__CALLER__.function || {nil}, 0)}") path = Path.join([module, function]) quote do in_tmp(unquote(path), unquote(fun)) end end def in_tmp(tmp, function) do path = tmp_path(tmp) File.rm_rf!(path) File.mkdir_p!(path) File.cd!(path, function) end defmacro in_fixture(which, block) do module = inspect(__CALLER__.module) function = Atom.to_string(elem(__CALLER__.function || {nil}, 0)) tmp = Path.join(module, function) quote do unquote(__MODULE__).in_fixture(unquote(which), unquote(tmp), unquote(block)) end end def in_fixture(which, tmp, function) do src = fixture_path(which) dest = tmp_path(String.replace(tmp, ":", "_")) flag = String.to_charlist(tmp_path()) File.rm_rf!(dest) File.mkdir_p!(dest) File.cp_r!(src, dest) get_path = :code.get_path() previous = :code.all_loaded() try do File.cd!(dest, function) after :code.set_path(get_path) for {mod, file} <- :code.all_loaded() -- previous, file == [] or (is_list(file) and :lists.prefix(flag, file)) do purge([mod]) end end end def purge(modules) do Enum.each(modules, fn m -> :code.delete(m) :code.purge(m) end) end @ets_table :hex_index def create_test_registry(path, registry) do registry = Enum.sort(registry) versions = Enum.reduce(registry, %{}, fn {repo, name, vsn, _deps}, map -> key = {Atom.to_string(repo), Atom.to_string(name)} Map.update(map, key, [vsn], &(&1 ++ [vsn])) end) |> Enum.to_list() deps = Enum.map(registry, fn {outer_repo, name, vsn, deps} -> deps = Enum.map(deps, fn config -> destructure [name, req, optional, app, repo], Tuple.to_list(config) optional = optional || false app = app || name repo = repo || outer_repo {Atom.to_string(repo), Atom.to_string(name), Atom.to_string(app), req, optional} end) {{Atom.to_string(outer_repo), Atom.to_string(name), vsn}, deps} end) create_registry(path, versions, deps) end defp create_registry(path, versions, deps) do tid = :ets.new(@ets_table, []) versions = Enum.map(versions, fn {{repo, pkg}, versions} -> {{:versions, repo, pkg}, versions} end) deps = Enum.map(deps, fn {{repo, pkg, vsn}, deps} -> {{:deps, repo, pkg, vsn}, deps} end) :ets.insert(tid, versions ++ deps ++ [{:version, 3}]) File.mkdir_p!(Path.dirname(path)) :ok = :ets.tab2file(tid, String.to_charlist(path)) :ets.delete(tid) end defp test_registry() do [ {:hexpm, :bar, "0.0.1", []}, {:hexpm, :bar, "0.1.0", [foo: "~> 0.1.0"]}, {:hexpm, :bar, "0.2.0", [foo: "~> 0.2.0"]}, {:hexpm, :beta, "1.0.0", []}, {:hexpm, :beta, "1.1.0-beta", []}, {:hexpm, :decimal, "0.0.1", []}, {:hexpm, :decimal, "0.1.0", []}, {:hexpm, :decimal, "0.2.0", []}, {:hexpm, :decimal, "0.2.1", []}, {:hexpm, :depend_name, "0.2.0", [{:package_name, ">= 0.0.0", false, :app_name}]}, {:hexpm, :ecto, "0.2.0", [postgrex: "~> 0.2.0", ex_doc: "~> 0.0.1"]}, {:hexpm, :ecto, "0.2.1", [postgrex: "~> 0.2.1", ex_doc: "0.1.0"]}, {:hexpm, :ecto, "1.1.0", [poison: "~> 1.0"]}, {:hexpm, :eric, "0.0.1", []}, {:hexpm, :eric, "0.0.2", []}, {:hexpm, :eric, "0.1.0", [jose: "~> 0.1.0"]}, {:hexpm, :eric, "0.1.2", [jose: "~> 0.1.0"]}, {:hexpm, :eric, "0.2.0", [jose: "~> 0.3.0"]}, {:hexpm, :ex_doc, "0.0.1", []}, {:hexpm, :ex_doc, "0.0.2", []}, {:hexpm, :ex_doc, "0.1.0", []}, {:hexpm, :ex_plex, "0.0.1", []}, {:hexpm, :ex_plex, "0.0.2", [decimal: "0.1.1"]}, {:hexpm, :ex_plex, "0.1.0", [decimal: "~> 0.1.0"]}, {:hexpm, :ex_plex, "0.1.2", [decimal: "~> 0.1.0"]}, {:hexpm, :ex_plex, "0.2.0", [decimal: "~> 0.2.0"]}, {:hexpm, :foo, "0.0.1", []}, {:hexpm, :foo, "0.1.0", []}, {:hexpm, :foo, "0.2.0", []}, {:hexpm, :foo, "0.2.1", []}, {:hexpm, :has_optional, "0.1.0", [{:ex_doc, "~> 0.0.1", true}]}, {:hexpm, :invalid_dirname, "0.1.0", []}, {:hexpm, :invalid_filename, "0.1.0", []}, {:hexpm, :jose, "0.2.0", []}, {:hexpm, :jose, "0.2.1", []}, {:hexpm, :only_doc, "0.1.0", [{:ex_doc, ">= 0.0.0", true}]}, {:hexpm, :package_name, "0.1.0", []}, {:hexpm, :phoenix, "0.0.1", [postgrex: "~> 0.2"]}, {:hexpm, :phoenix, "1.1.2", [poison: "~> 1.5 or ~> 2.0"]}, {:hexpm, :phoenix, "1.1.3", [poison: "~> 1.5 or ~> 2.0"]}, {:hexpm, :poison, "1.5.2", []}, {:hexpm, :poison, "2.0.0", []}, {:hexpm, :phoenix_ecto, "2.0.0", [ecto: "~> 1.1", poison: "~> 1.3"]}, {:hexpm, :phoenix_ecto, "2.0.1", [ecto: "~> 1.1", poison: "~> 1.3"]}, {:hexpm, :phoenix_live_reload, "1.0.0", [phoenix: "~> 0.16 or ~> 1.0"]}, {:hexpm, :phoenix_live_reload, "1.0.3", [phoenix: "~> 0.16 or ~> 1.0"]}, {:hexpm, :postgrex, "0.2.0", [ex_doc: "0.0.1"]}, {:hexpm, :postgrex, "0.2.1", [ex_doc: "~> 0.1.0"]}, {:hexpm, :umb, "0.2.1", [ex_doc: "~> 0.1.0"]}, {:repo2, :hexpm_deps, "0.1.0", [{:poison, ">= 0.0.0", false, :poison, :hexpm}]}, {:repo2, :poison, "2.0.0", []}, {:repo2, :repo2_deps, "0.1.0", [poison: ">= 0.0.0"]} ] end def setup_auth(username, password) do write_permissions = [%{"domain" => "api"}] read_permissions = [%{"domain" => "api", "resource" => "read"}] {:ok, {201, _, write_body}} = Hex.API.Key.new("setup_auth_write", write_permissions, user: username, pass: password) {:ok, {201, _, _read_body}} = Hex.API.Key.new("setup_auth_read", read_permissions, user: username, pass: password) write_key = write_body["secret"] Hex.State.put(:api_key, write_key) [key: write_key] end def get_auth(username, password) do permissions = [%{"domain" => "api"}] {:ok, {201, _, body}} = Hex.API.Key.new("setup_auth", permissions, user: username, pass: password) [key: body["secret"]] end def init_reset_state() do public_key = File.read!(Path.join(__DIR__, "../fixtures/test_pub.pem")) Hex.State.put(:config_home, Path.expand("../../tmp/hex_config_home", __DIR__)) Hex.State.put(:cache_home, Path.expand("../../tmp/hex_cache_home", __DIR__)) Hex.State.put(:data_home, Path.expand("../../tmp/hex_data_home", __DIR__)) Hex.State.put(:api_url, "http://localhost:4043/api") Hex.State.put(:api_key, nil) Hex.State.put(:oauth_token, nil) Hex.State.update!(:repos, &put_in(&1["hexpm"].url, "http://localhost:4043/repo")) Hex.State.update!(:repos, &put_in(&1["hexpm"].public_key, public_key)) Hex.State.update!(:repos, &put_in(&1["hexpm"].auth_key, nil)) Hex.State.update!(:repos, &put_in(&1["hexpm"].oauth_exchange, true)) Hex.State.update!( :repos, &update_in(&1, ["hexpm"], fn repo -> Map.delete(repo, :oauth_token) end) ) Hex.State.put(:repos_key, nil) Hex.State.put(:pbkdf2_iters, 10) Hex.State.put(:clean_pass, false) Application.put_env(:hex, :reset_state, Hex.State.get_all()) end def reset_state do Hex.State.put_all(Application.get_env(:hex, :reset_state)) Hex.OAuth.clear_tokens() Hex.Repo.clear_exchange_cache() end def set_home_cwd() do set_home_path(File.cwd!()) end def set_home_tmp() do set_home_path(tmp_path()) end def set_home_path(path) do Hex.State.put(:config_home, path) Hex.State.put(:cache_home, path) Hex.State.put(:data_home, path) end def wait_on_exit({:ok, pid}) do on_exit(fn -> ref = Process.monitor(pid) receive do {:DOWN, ^ref, :process, ^pid, _info} -> :ok end end) end setup_all context do unless context[:async] do ets_path = tmp_path("cache.ets") File.rm(ets_path) create_test_registry(ets_path, test_registry()) reset_state() end :ok end setup context do unless context[:async] do wait_on_exit(Hex.Registry.Server.start_link()) wait_on_exit(Hex.UpdateChecker.start_link()) reset_state() Hex.Parallel.clear(:hex_fetcher) Mix.shell(Hex.Shell.Process) Mix.Task.clear() Hex.Shell.Process.flush() Mix.State.clear_cache() Mix.ProjectStack.clear_stack() end :ok end def bypass_mirror() do bypass = Bypass.open() repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].url, "http://localhost:#{bypass.port}") Hex.State.put(:repos, repos) Bypass.expect(bypass, fn conn -> case conn do %Plug.Conn{request_path: "/docs/docs_package-1.1.2.tar.gz"} -> tar_file = tmp_path("docs_package-1.1.2.tar.gz") index_file = String.to_charlist("index.html") epub_file = String.to_charlist("docs_package.epub") :mix_hex_erl_tar.create(tar_file, [{index_file, ""}, {epub_file, ""}], [:compressed]) package = File.read!(tar_file) Plug.Conn.resp(conn, 200, package) %Plug.Conn{request_path: "/docs/docs_package"} -> Plug.Conn.resp(conn, 404, "") %Plug.Conn{request_path: "/docs/pre_only_package-0.0.1-rc1.tar.gz"} -> tar_file = tmp_path("pre_only_package-0.0.1-rc1.tar.gz") index_file = String.to_charlist("index.html") :mix_hex_erl_tar.create(tar_file, [{index_file, ""}], [:compressed]) package = File.read!(tar_file) Plug.Conn.resp(conn, 200, package) end end) bypass end def bypass_repo(repo) do bypass = Bypass.open() map = %{ url: "http://localhost:#{bypass.port}/repo", public_key: nil, auth_key: nil, organization: "hexpm" } repos = Hex.State.fetch!(:repos) repos = Map.put(repos, repo, map) Hex.State.put(:repos, repos) Hex.State.put(:api_url, "http://localhost:#{bypass.port}/api") package_path = "/api/repos/#{repo}/packages/ecto" Bypass.expect(bypass, fn conn -> case conn do %Plug.Conn{method: "GET", request_path: ^package_path} -> body = %{"meta" => %{"description" => "ecto description"}} conn |> Plug.Conn.put_resp_header("content-type", "application/vnd.hex+erlang") |> Plug.Conn.resp(200, Hex.Utils.safe_serialize_erlang(body)) %Plug.Conn{ method: "POST", path_info: ["api", "repos", ^repo, "packages", _name, "releases"] } -> body = %{"html_url" => "myrepo html_url"} conn |> Plug.Conn.put_resp_header("content-type", "application/vnd.hex+erlang") |> Plug.Conn.resp(201, Hex.Utils.safe_serialize_erlang(body)) %Plug.Conn{method: "GET", request_path: "/api/users/me"} -> body = %{"organizations" => [%{"name" => repo}]} conn |> Plug.Conn.put_resp_header("content-type", "application/vnd.hex+erlang") |> Plug.Conn.resp(200, Hex.Utils.safe_serialize_erlang(body)) %Plug.Conn{method: "POST", request_path: "/api/keys"} -> body = %{"secret" => "myrepo secret"} conn |> Plug.Conn.put_resp_header("content-type", "application/vnd.hex+erlang") |> Plug.Conn.resp(201, Hex.Utils.safe_serialize_erlang(body)) end end) bypass end def bypass_package() do bypass = Bypass.open() repos = Hex.State.fetch!(:repos) repos = put_in(repos["hexpm"].url, "http://localhost:#{bypass.port}") Hex.State.put(:repos, repos) Bypass.expect(bypass, fn conn -> case conn do %Plug.Conn{request_path: "/tarballs/package-1.0.0.tar"} -> tar_file = tmp_path("package-1.0.0.tar") index_file = String.to_charlist("index.html") :mix_hex_erl_tar.create(tar_file, [{index_file, ""}], [:compressed]) package = File.read!(tar_file) Plug.Conn.resp(conn, 200, package) %Plug.Conn{request_path: "/tarballs/package-1.0.1.tar"} -> Plug.Conn.resp(conn, 404, "") end end) bypass end end hex-2.4.2/test/support/hexpm.ex000066400000000000000000000256741517471540100165000ustar00rootroot00000000000000defmodule HexTest.Hexpm do import ExUnit.Assertions defmodule WrappedCollectable do defstruct [:collectable, :fun] defimpl Collectable do def into(%{collectable: collectable, fun: fun}) do {term, collectable_fun} = Collectable.into(collectable) {term, wrap(collectable_fun, fun)} end defp wrap(collectable_fun, fun) do fn term, {:cont, x} -> collectable_fun.(term, {:cont, fun.(x)}) term, command -> collectable_fun.(term, command) end end end end defp stream_hexpm do %WrappedCollectable{ collectable: IO.stream(:stdio, :line), fun: &IO.ANSI.format([:blue, &1]) } end @mix_exs_template """ defmodule ~s.NoConflict.MixProject do use Mix.Project def project do [app: :~s, version: "~s", deps: deps()] end defp deps do ~s end end """ def init() do check_hexpm() mix = hexpm_mix() |> List.to_string() cmd(mix, ["deps.get"]) cmd(mix, ["ecto.drop", "-r", "Hexpm.RepoBase", "--quiet"]) cmd(mix, ["ecto.create", "-r", "Hexpm.RepoBase", "--quiet"]) cmd(mix, ["ecto.load", "-r", "Hexpm.RepoBase", "--quiet"]) cmd(mix, ["ecto.migrate", "-r", "Hexpm.RepoBase", "--quiet"]) end def start() do path = String.to_charlist(path()) hexpm_mix_home = String.to_charlist(hexpm_mix_home()) hexpm_mix_archives = String.to_charlist(hexpm_mix_archives()) key = Path.join(__DIR__, "../fixtures/test_priv.pem") |> File.read!() |> String.to_charlist() env = [ {~c"MIX_ENV", ~c"hex"}, {~c"MIX_HOME", hexpm_mix_home}, {~c"MIX_ARCHIVES", hexpm_mix_archives}, {~c"PATH", path}, {~c"HEX_SIGNING_KEY", key}, {~c"HEXPM_SETUP", ~c"1"} ] spawn(fn -> port = Port.open({:spawn_executable, hexpm_mix()}, [ :exit_status, :use_stdio, :stderr_to_stdout, :binary, :hide, env: env, cd: hexpm_dir(), args: ["phx.server"] ]) fun = fn fun -> receive do {^port, {:data, data}} -> IO.ANSI.format([:blue, data]) |> IO.write() fun.(fun) {^port, {:exit_status, status}} -> IO.puts("Hexpm quit with status #{status}") System.halt(status) end end fun.(fun) end) wait_on_start() end defp check_hexpm do dir = hexpm_dir() unless File.exists?(dir) do IO.puts( "Unable to find #{dir}, make sure to clone the hexpm repository " <> "into it to run integration tests or set HEXPM_PATH to its location" ) System.halt(1) end end defp hexpm_dir do System.get_env("HEXPM_PATH") || "../hexpm" end defp hexpm_mix do if path = hexpm_elixir() do path = String.to_charlist(path) :os.find_executable(~c"mix", path) else :os.find_executable(~c"mix") end end defp hexpm_elixir do if path = System.get_env("HEXPM_ELIXIR_PATH") do path |> Path.expand() |> Path.join("bin") end end defp hexpm_otp do if path = System.get_env("HEXPM_OTP_PATH") do path |> Path.expand() |> Path.join("bin") end end defp hexpm_mix_home do (System.get_env("HEXPM_MIX_HOME") || Mix.Utils.mix_home()) |> Path.expand() end defp hexpm_mix_archives do (System.get_env("HEXPM_MIX_ARCHIVES") || archives_path()) |> Path.expand() end cond do function_exported?(Mix, :path_for, 1) -> defp archives_path(), do: Mix.path_for(:archives) function_exported?(Mix.Local, :path_for, 1) -> defp archives_path(), do: Mix.Local.path_for(:archive) true -> defp archives_path(), do: Mix.Local.archives_path() end defp cmd(command, args) do env = [ {"MIX_ENV", "hex"}, {"PATH", path()}, {"MIX_HOME", hexpm_mix_home()}, {"MIX_ARCHIVES", hexpm_mix_archives()} ] opts = [stderr_to_stdout: true, into: stream_hexpm(), env: env, cd: hexpm_dir()] 0 = System.cmd(command, args, opts) |> elem(1) end defp path do [hexpm_elixir(), hexpm_otp(), System.get_env("PATH")] |> Enum.join(":") end defp wait_on_start do case :httpc.request(:get, {~c"http://localhost:4043", []}, [], []) do {:ok, _} -> :ok {:error, _} -> :timer.sleep(10) wait_on_start() end end def new_repo(repository, auth) do config = Hex.API.Client.config(auth) body = %{"name" => to_string(repository)} :mix_hex_api.post(config, ["repo"], body) end def new_user(username, email, password, key) do permissions = [%{"domain" => "api"}, %{"domain" => "repositories"}] {:ok, {201, _, _}} = Hex.API.User.new(username, email, password) {:ok, {201, _, %{"secret" => secret}}} = Hex.API.Key.new(key, permissions, user: username, pass: password) [key: secret, "$write_key": secret, "$read_key": secret] end def new_key(auth) do permissions = [%{"domain" => "api"}] {:ok, {201, _, %{"secret" => secret}}} = Hex.API.Key.new("key", permissions, auth) [key: secret] end def new_key(username, password, key) do permissions = [%{"domain" => "api"}] {:ok, {201, _, %{"secret" => secret}}} = Hex.API.Key.new(key, permissions, user: username, pass: password) [key: secret, "$write_key": secret, "$read_key": secret] end def new_organization_key(organization, key, auth) do permissions = [%{"domain" => "api"}] {:ok, {201, _, %{"secret" => secret}}} = Hex.API.Key.Organization.new(organization, key, permissions, auth) [key: secret] end def new_package(organization, name, version, deps, meta, auth, files \\ nil) do reqs = Enum.filter(deps, fn {_app, _req, opts} -> !(opts[:path] || opts[:git] || opts[:github]) _ -> true end) reqs = Map.new(reqs, fn {app, req} -> {app, %{app: app, requirement: req, optional: false}} {app, req, opts} -> opts = Map.new(opts) default_opts = %{app: app, requirement: req, optional: false} {opts[:hex] || app, Map.merge(default_opts, opts)} end) meta = meta |> Map.merge(%{name: name, version: version, requirements: reqs}) |> Map.put_new(:description, "empty") |> Map.put_new(:licenses, ["MIT"]) |> Map.put_new(:app, name) |> Map.put_new(:build_tools, ["mix"]) |> Map.put_new(:files, ["mix.exs"]) deps = inspect(deps, pretty: true) module = String.capitalize(name) mix_exs = :io_lib.format(@mix_exs_template, [module, name, version, deps]) files = files || [{"mix.exs", List.to_string(mix_exs)}] %{tarball: tarball} = Hex.Tar.create!(meta, files, :memory) {:ok, {result, _, %{"version" => ^version}}} = Hex.API.Release.publish(organization, tarball, auth) assert result in [200, 201] end @doc """ Creates OAuth tokens for testing using the real OAuth server endpoints. Returns the same format that would be stored after OAuth authentication. """ def new_oauth_user(username, email, password) do # Add timestamp and random bytes to make usernames and emails unique across test runs timestamp = System.system_time(:millisecond) random_suffix = Base.encode16(:crypto.strong_rand_bytes(4), case: :lower) unique_username = "#{username}_#{timestamp}_#{random_suffix}" unique_email = "#{timestamp}_#{random_suffix}_#{email}" # Create user account {:ok, {201, _, _}} = Hex.API.User.new(unique_username, unique_email, password) # Create real OAuth tokens through the test server endpoints config = Hex.API.Client.config() # Create OAuth token case :mix_hex_api.post(config, ["oauth_token"], %{ "username" => unique_username, "scope" => "api repositories" }) do {:ok, {200, _, token_response}} -> # Success case - continue with token creation create_oauth_token(token_response, unique_username) error -> # If we can't create OAuth token, fall back to API key approach IO.warn( "Failed to create OAuth token: #{inspect(error, limit: :infinity)}, falling back to API key approach" ) fallback_to_api_key(unique_username, unique_email, password) end end defp create_oauth_token(token_response, unique_username) do # Calculate expires_at from expires_in expires_at = System.system_time(:second) + token_response["expires_in"] token_data = %{ "access_token" => token_response["access_token"], "refresh_token" => token_response["refresh_token"], "expires_at" => expires_at } # Store OAuth token Hex.OAuth.store_token(token_data) # Return auth format for API calls [ access_token: token_response["access_token"], refresh_token: token_response["refresh_token"], key: token_response["access_token"], "$oauth_token": token_data, username: unique_username ] end defp fallback_to_api_key(unique_username, unique_email, password) do # Create API key instead of OAuth tokens key_name = "test_key_#{Base.encode16(:crypto.strong_rand_bytes(4), case: :lower)}" auth = new_user(unique_username, unique_email, password, key_name) # Create minimal OAuth-like structure for backward compatibility access_token = auth[:key] refresh_token = "refresh_" <> Base.encode16(:crypto.strong_rand_bytes(16)) expires_at = System.system_time(:second) + 3600 token_data = %{ "access_token" => access_token, "refresh_token" => refresh_token, "expires_at" => expires_at } # Store OAuth token Hex.OAuth.store_token(token_data) # Return auth format for API calls [ access_token: access_token, refresh_token: refresh_token, key: access_token, "$oauth_token": token_data, username: unique_username ] end @doc """ Creates OAuth token for a user that already exists. """ def new_oauth_tokens() do access_token = "oauth_" <> Base.encode16(:crypto.strong_rand_bytes(16)) refresh_token = "refresh_" <> Base.encode16(:crypto.strong_rand_bytes(16)) expires_at = System.system_time(:second) + 3600 token_data = %{ "access_token" => access_token, "refresh_token" => refresh_token, "expires_at" => expires_at } Hex.OAuth.store_token(token_data) [key: access_token, "$oauth_token": token_data] end @doc """ Creates expired OAuth token for testing refresh logic. """ def new_expired_oauth_tokens() do access_token = "oauth_" <> Base.encode16(:crypto.strong_rand_bytes(16)) refresh_token = "refresh_" <> Base.encode16(:crypto.strong_rand_bytes(16)) # Set expiration in the past expires_at = System.system_time(:second) - 100 token_data = %{ "access_token" => access_token, "refresh_token" => refresh_token, "expires_at" => expires_at } Hex.OAuth.store_token(token_data) [key: access_token, "$oauth_token": token_data] end end hex-2.4.2/test/support/integration_case.ex000066400000000000000000000004141517471540100206560ustar00rootroot00000000000000defmodule HexTest.IntegrationCase do use ExUnit.CaseTemplate using do quote do use HexTest.Case @moduletag :integration setup_all do Code.require_file("../setup_hexpm.exs", unquote(__DIR__)) :ok end end end end hex-2.4.2/test/support/release_samples.ex000066400000000000000000000202231517471540100205040ustar00rootroot00000000000000defmodule Sample.MixProject do def project do [ app: Process.get(:hex_test_app_name) || raise("missing app name"), description: "sample", version: "0.0.1", deps: [], package: [ licenses: ["MIT"], files: ["myfile.txt", "mix.exs"], links: %{"a" => "http://a"} ] ] end end defmodule ReleaseSimple.MixProject do def project do [ app: Process.get(:hex_test_app_name) || raise("missing app name"), description: "baz", version: "0.0.1", package: [ licenses: ["MIT"], files: ["myfile.txt"], links: %{"a" => "http://a"} ] ] end end defmodule ReleaseNewSimple.MixProject do def project do [ app: Process.get(:hex_test_app_name) || raise("missing app name"), description: "baz", version: "0.0.1", package: [ licenses: ["MIT"], files: ["myfile.txt"], links: %{"a" => "http://a"} ] ] end end defmodule ReleaseDeps.MixProject do def project do [ app: Process.get(:hex_test_app_name, :release_deps), description: "bar", version: "0.0.2", deps: [ {:ex_doc, "0.0.1"} ], package: [ licenses: ["MIT"] ] ] end end defmodule ReleaseCustomRepoDeps.MixProject do def project do [ app: Process.get(:hex_test_app_name) || raise("missing app name"), description: "bar", version: "0.0.2", deps: [ {:ex_doc, "0.0.1"}, {:ecto, "0.0.1", repo: :my_repo} ], package: [ licenses: ["MIT"] ] ] end end defmodule ReleaseMeta.MixProject do def project do [ app: Process.get(:hex_test_app_name) || raise("missing app name"), version: "0.0.3", description: "foo", package: [ files: ["myfile.txt", "missing.txt", "missing/*"], licenses: ["Apache-2.0"], links: %{"a" => "http://a"}, extra: %{"c" => %{"d" => "e"}} ] ] end end defmodule ReleaseMetaNoFiles.MixProject do def project do [ app: Process.get(:hex_test_app_name) || raise("missing app name"), version: "0.0.3", description: "foo", package: [ files: [], licenses: ["Apache-2.0"], links: %{"a" => "http://a"}, extra: %{"c" => "d"} ] ] end end defmodule ReleaseName.MixProject do def project do [ app: :release_d, description: "Whatever", version: "0.0.1", package: [ name: Process.get(:hex_test_package_name) || raise("missing package name"), licenses: ["MIT"], files: ["myfile.txt"], links: %{"a" => "http://a"} ] ] end end defmodule ReleaseNoDescription.MixProject do def project do [ app: Process.get(:hex_test_app_name) || raise("missing app name"), version: "0.0.1" ] end end defmodule ReleaseTooLongDescription.MixProject do def project do [ app: Process.get(:hex_test_app_name) || raise("missing app name"), description: String.duplicate("w", 301), version: "0.0.1" ] end end defmodule ReleasePreDeps.MixProject do def project do [ app: Process.get(:hex_test_app_name) || raise("missing app name"), description: "bar", version: "0.0.1", deps: [ {:ex_doc, "~> 0.0.1-pre"} ], package: [ files: ["myfile.txt"], licenses: ["MIT"], links: %{"a" => "http://a"} ] ] end end defmodule ReleaseFiles.MixProject do def project do [ app: Process.get(:hex_test_app_name) || raise("missing app name"), version: "0.0.1", description: "foo", package: [ files: ["myfile.txt", "executable.sh", "dir", "empty_dir", "link_dir"], licenses: ["MIT"], links: %{"a" => "http://a"} ] ] end end defmodule ReleaseExcludePatterns.MixProject do def project do [ app: Process.get(:hex_test_app_name) || raise("missing app name"), version: "0.0.1", description: "foo", package: [ files: ["myfile.txt"], exclude_patterns: ["exclude.*"], licenses: ["MIT"], links: %{"a" => "http://a"} ] ] end end defmodule ReleaseRepo.MixProject do def project do [ app: Process.get(:hex_test_app_name) || raise("missing app name"), description: "baz", version: "0.0.1", package: [ organization: "myorg", licenses: ["MIT"], links: %{"a" => "http://a"} ] ] end end defmodule ReleaseRepoInvalidLicenses.MixProject do def project do [ app: :release_repo_invalid_licenses, description: "Invalid license", version: "0.0.1", package: [ organization: "myorg", licenses: ["CustomLicense"], files: ["myfile.txt"], links: %{"a" => "http://a"} ] ] end end defmodule ReleaseMisspelledOrganization.MixProject do def project do [ app: Process.get(:hex_test_app_name) || raise("missing app name"), description: "baz", version: "0.0.1", package: [ organisation: "myorg", licenses: ["MIT"], links: %{"a" => "http://a"} ] ] end end defmodule ReleaseOrganizationWrongLocation.MixProject do def project do [ app: Process.get(:hex_test_app_name) || raise("missing app name"), description: "baz", version: "0.0.1", organization: "myorg", package: [ licenses: ["MIT"], files: ["myfile.txt"], links: %{"a" => "http://a"} ] ] end end defmodule ReleaseIncludeReservedFile.MixProject do def project do [ app: Process.get(:hex_test_app_name) || raise("missing app name"), description: "baz", version: "0.0.1", package: [ licenses: ["MIT"], links: %{"a" => "http://a"}, files: ["hex_meta*"] ] ] end end defmodule ReleaseGitDeps.MixProject do def project do [ app: Process.get(:hex_test_app_name) || raise("missing app name"), description: "Package with git dependencies", version: "0.0.2", deps: [ {:ex_doc, "0.0.1"}, {:ecto, github: "elixir-ecto/ecto", tag: "v0.2.5"}, {:gettext, git: "https://github.com/elixir-lang/gettext.git", branch: "master"} ], package: [ licenses: ["MIT"], links: %{"example" => "http://example.com"} ] ] end end defmodule ReleaseCustomApiUrl.MixProject do def project do [ app: Process.get(:hex_test_app_name) || raise("missing app name"), description: "Package with custom api_url", version: "0.0.1", deps: [], package: [ licenses: ["MIT"], links: %{"a" => "http://a"} ], hex: [ api_url: "https://custom" ] ] end end defmodule ReleaseMissingLicenses.MixProject do def project do [ app: :release_missing_licenses, description: "Package with missing licenses", version: "0.0.1", package: [ licenses: [], files: ["myfile.txt"], links: %{"a" => "http://a"} ] ] end end defmodule ReleaseInvalidLicenses.MixProject do def project do [ app: :release_invalid_licenses, description: "Package with invalid licenses", version: "0.0.1", package: [ licenses: ["CustomLicense"], files: ["myfile.txt"], links: %{"a" => "http://a"} ] ] end end defmodule ReleaseAppFalseDep.MixProject do def project do [ app: :release_app_false_dep, description: "Package with invalid licenses", version: "0.0.1", deps: [ {:ex_doc, "0.0.1", app: false} ], package: [ licenses: [], files: ["myfile.txt"], links: %{"a" => "http://a"} ] ] end end defmodule ReleaseInUmbrellaDeps.MixProject do def project do [ app: Process.get(:hex_test_app_name) || raise("missing app name"), description: "includes umbrella", version: "0.2.1", deps: [ {:ecto, "3.3.2", in_umbrella: true}, {:postgrex, "0.2.1"} ], package: [ licenses: ["MIT"], files: ["myfile.txt"], links: %{"a" => "http://a"} ] ] end end hex-2.4.2/test/support/solver_helper.ex000066400000000000000000000053111517471540100202120ustar00rootroot00000000000000defmodule HexTest.SolverHelper do alias Hex.Registry.Server, as: Registry def solve(deps, locked \\ []) do dependencies = dependencies(deps) locked = locked(locked) overridden = overridden(deps) (dependencies ++ locked) |> Enum.map(fn %{repo: repo, name: name} -> {repo, name} end) |> Registry.prefetch() case Hex.Solver.run(Registry, dependencies, locked, overridden) do {:ok, result} -> Map.new(result, fn {package, {version, nil}} -> {String.to_atom(package), to_string(version)} {package, {version, repo}} -> {{repo, String.to_atom(package)}, to_string(version)} end) {:error, message} -> message end end defp dependencies(dependencies) do Enum.map(dependencies, fn {package, requirement} -> %{ repo: nil, name: to_string(package), constraint: Hex.Solver.parse_constraint!(requirement || ">= 0.0.0"), optional: false, label: package, dependencies: [] } {package, requirement, opts} -> repo = Keyword.get(opts, :repo) optional = Keyword.get(opts, :optional, false) app = Keyword.get(opts, :app, false) %{ repo: repo, name: to_string(package), constraint: Hex.Solver.parse_constraint!(requirement || ">= 0.0.0"), optional: optional, label: app, dependencies: [] } end) end defp locked(dependencies) do Enum.map(dependencies, fn {package, requirement} -> %{ repo: nil, name: to_string(package), version: Hex.Solver.parse_constraint!(requirement || ">= 0.0.0"), label: package } {package, requirement, opts} -> repo = Keyword.get(opts, :repo) app = Keyword.get(opts, :app, false) %{ repo: repo, name: to_string(package), version: Hex.Solver.parse_constraint!(requirement || ">= 0.0.0"), label: app } end) end defp overridden(dependencies) do Enum.flat_map(dependencies, fn {_package, _requirement} -> [] {package, _requirement, opts} -> if opts[:override] do [package] else [] end end) end def setup_registry(path, registry) do registry = expand_versions(registry) HexTest.Case.create_test_registry(path, registry) Registry.close() Registry.open(registry_path: path) end def expand_versions(registry) do Enum.flat_map(registry, fn {repo, package, versions, deps} -> Enum.map(versions, fn version -> {repo, package, version, deps} end) end) end end hex-2.4.2/test/test_helper.exs000066400000000000000000000005071517471540100163300ustar00rootroot00000000000000ExUnit.configure(exclude: [:skip | ExUnit.configuration()[:exclude]]) ExUnit.start() Application.ensure_all_started(:bypass) # Set up Mox for HTTP mocking in OAuth tests Mox.defmock(Hex.HTTP.Mock, for: :mix_hex_http) File.rm_rf!(HexTest.Case.tmp_path()) File.mkdir_p!(HexTest.Case.tmp_path()) HexTest.Case.init_reset_state()