pax_global_header00006660000000000000000000000064152240254450014515gustar00rootroot0000000000000052 comment=a43131c26aeca06064959297d737991163f5ac5d hex-2.5.1/000077500000000000000000000000001522402544500123065ustar00rootroot00000000000000hex-2.5.1/.credo.exs000066400000000000000000000076411522402544500142110ustar00rootroot00000000000000%{ 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.5.1/.formatter.exs000066400000000000000000000001411522402544500151040ustar00rootroot00000000000000[ inputs: [ "*.exs", "config/*.exs", "lib/**/*.ex", "test/**/*.{ex,exs}" ] ] hex-2.5.1/.github/000077500000000000000000000000001522402544500136465ustar00rootroot00000000000000hex-2.5.1/.github/dependabot.yml000066400000000000000000000002321522402544500164730ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" cooldown: default-days: 7 hex-2.5.1/.github/workflows/000077500000000000000000000000001522402544500157035ustar00rootroot00000000000000hex-2.5.1/.github/workflows/codeql.yml000066400000000000000000000026221522402544500176770ustar00rootroot00000000000000name: "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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Initialize CodeQL uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 with: category: "/language:${{matrix.language}}" zizmor: name: Zizmor runs-on: ubuntu-latest permissions: security-events: write steps: - name: Checkout repository uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Run zizmor uses: zizmorcore/zizmor-action@192e21d79ab29983730a13d1382995c2307fbcaa # v0.5.7 hex-2.5.1/.github/workflows/main.yml000066400000000000000000000073721522402544500173630ustar00rootroot00000000000000name: CI on: [push, pull_request] permissions: contents: read jobs: format: name: Format runs-on: ubuntu-22.04 steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Install OTP and Elixir uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1.24.1 with: otp-version: 28.5 elixir-version: 1.19.5 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: "29.0" elixir: 1.20.2 - erlang: 28.5 elixir: 1.19.5 - erlang: 27.3 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.5 HEXPM_ELIXIR: v1.19.5 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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Install OTP and Elixir uses: erlef/setup-beam@54075bcc5e249e4758d363f27d099f55d843f124 # v1.24.1 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.5.1/.gitignore000066400000000000000000000001461522402544500142770ustar00rootroot00000000000000/_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.5.1/CHANGELOG.md000066400000000000000000001233371522402544500141300ustar00rootroot00000000000000# CHANGELOG ## v2.5.1 (2026-07-09) ### Enhancements * Add `ignore_advisories` and `ignore_retirements` configs to acknowledge security advisories and package retirements. Configure them in the `mix.exs` `:hex` block (`ignore_advisories: ["CVE-2026-32686"]`, `ignore_retirements: [:decimal, phoenix: "1.0.0"]`) or with the `HEX_IGNORE_ADVISORIES` and `HEX_IGNORE_RETIREMENTS` environment variables. An advisory can be ignored by any of its aliased identifiers (for example the GHSA alias of a CVE). `mix hex.audit` lists ignored findings in separate sections without failing and warns about entries that no longer match anything, and `mix deps.get`/`mix deps.update` stop warning about ignored findings * List each package override with its requirement and allow/deny status in `mix hex.policy show` instead of only an override count. `mix hex.policy` without a subcommand now prints usage instead of defaulting to `show` ### Bug fixes * Remove regexes from compiled code. Compiled regex patterns are not portable across OTP versions, so the precompiled archive could crash with `:re.import/1 is undefined` when built and run on different OTP versions ## v2.5.0 (2026-06-28) ### Enhancements * Add organization-defined dependency policies that filter the package versions available during dependency resolution. An organization publishes a named policy through its repository, and a project opts into one with the `policy` config (`HEX_POLICY`, `[org: "ORG", name: "NAME"]` in the `mix.exs` `:hex` block, or `mix hex.config`). A policy constrains one or more repositories — typically the organization's own repo and `hexpm` — and for each can block releases that: * carry a security advisory at or above a minimum severity * are retired for one of a given set of reasons * are newer than a release-age cooldown window Per-package allow/deny overrides take precedence over the restriction (an allow also exempts the release, and the most specific match wins), and versions already in `mix.lock` are never filtered. Use `mix hex.policy show` to summarize the active policy and `mix hex.policy why PACKAGE` to see why specific versions are blocked. * Add a configurable release-age cooldown to dependency resolution that withholds freshly published versions until they reach a minimum age, mitigating supply-chain attacks where a compromised release is pulled into projects before it can be detected and retired. Configure it with the `cooldown` config (`HEX_COOLDOWN`), accepting durations like `7d`, `2w`, or `1mo`, and exempt specific repositories with `cooldown_exclude_repos`. Versions already in `mix.lock`, and locked versions that are retired or carry an advisory, bypass the cooldown so existing projects and security fixes are never held back. * Warn about packages with known security advisories during `mix deps.get` and `mix deps.update` * Add `mix hex.search QUERY` to search documentation from the terminal, and move package name search to `mix hex.package search` * Add `--page` and `--format md` options to `mix hex.docs` * Support JSON output in `mix hex.outdated` * Annotate cooldown-held versions in `mix hex.outdated` * Validate package files in `mix hex.build` * Accept `LicenseRef-` license identifiers in `mix hex.build` * Use subdomain URLs (`PACKAGE.hexdocs.pm`) for package docs * Escape terminal control sequences in server-provided `x-hex-message` headers ### Bug fixes * Fix a crash when a server responds with an `x-hex-message` header * Deduplicate aliased security advisories * Warn when the OAuth session cannot be refreshed instead of silently sending unauthenticated requests ### Deprecations * Deprecate `mix hex.organization auth ORGANIZATION` without `--key`; authenticate as a user with `mix hex.user auth` instead, or pass a pre-generated organization key with `--key` for CI * Deprecate authenticating to organization repositories with a stored key; a future release will require `mix hex.user auth` or a short-lived organization token * Deprecate authenticating to organization repositories with `HEX_REPOS_KEY`; authenticate per organization with `mix hex.organization auth ORGANIZATION --key KEY` (`HEX_REPOS_KEY` continues to authenticate the base hexpm repository and trusted mirrors) ## 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.5.1/README.md000066400000000000000000000040371522402544500135710ustar00rootroot00000000000000# 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.5.1/RELEASE.md000066400000000000000000000053451522402544500137170ustar00rootroot00000000000000# 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. Publish the documentation to https://hex.hexdocs.pm: `./scripts/release_docs.sh VERSION` 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.5.1/config/000077500000000000000000000000001522402544500135535ustar00rootroot00000000000000hex-2.5.1/config/config.exs000066400000000000000000000002271522402544500155420ustar00rootroot00000000000000import Config if config_env() == :test do config :logger, level: :warning config :logger, :console, format: "$date $time [$level] $message\n" end hex-2.5.1/lib/000077500000000000000000000000001522402544500130545ustar00rootroot00000000000000hex-2.5.1/lib/hex.ex000066400000000000000000000010521522402544500141740ustar00rootroot00000000000000defmodule 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.5.1/lib/hex/000077500000000000000000000000001522402544500136405ustar00rootroot00000000000000hex-2.5.1/lib/hex/api/000077500000000000000000000000001522402544500144115ustar00rootroot00000000000000hex-2.5.1/lib/hex/api/auth.ex000066400000000000000000000005701522402544500157120ustar00rootroot00000000000000defmodule 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) } Hex.Auth.with_api(:read, config, &:mix_hex_api_auth.test(&1, params), auth_inline: false, optional: true ) end end hex-2.5.1/lib/hex/api/client.ex000066400000000000000000000035041522402544500162270ustar00rootroot00000000000000defmodule 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(), cli_auth_callbacks: Hex.Auth.callbacks() } 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]}") token = "Basic #{base64}" Map.put(config, :api_key, token) true -> config end end defp maybe_put_otp(config, opts) do if otp = opts[:otp] || Hex.State.get(:api_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.5.1/lib/hex/api/key.ex000066400000000000000000000041331522402544500155400ustar00rootroot00000000000000defmodule Hex.API.Key do @moduledoc false alias Hex.API.Client def new(name, permissions, auth \\ []) do config = Client.config(auth) # 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) Hex.Auth.with_api(:write, config, &:mix_hex_api_key.add(&1, to_string(name), permissions)) end def get(auth \\ []) do config = Client.config(auth) Hex.Auth.with_api(:read, config, &:mix_hex_api_key.list(&1)) end def delete(name, auth \\ []) do config = Client.config(auth) Hex.Auth.with_api(:write, config, &:mix_hex_api_key.delete(&1, to_string(name))) end def delete_all(auth \\ []) do config = Client.config(auth) Hex.Auth.with_api(:write, config, &:mix_hex_api_key.delete_all(&1)) end defmodule Organization do @moduledoc false alias Hex.API.Client def new(organization, name, permissions, auth \\ []) do config = Client.config(Keyword.put(auth, :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) Hex.Auth.with_api(:write, config, &:mix_hex_api_key.add(&1, to_string(name), permissions)) end def get(organization, auth \\ []) do config = Client.config(Keyword.put(auth, :api_organization, to_string(organization))) Hex.Auth.with_api(:read, config, &:mix_hex_api_key.list(&1)) end def delete(organization, name, auth \\ []) do config = Client.config(Keyword.put(auth, :api_organization, to_string(organization))) Hex.Auth.with_api(:write, config, &:mix_hex_api_key.delete(&1, to_string(name))) end def delete_all(organization, auth \\ []) do config = Client.config(Keyword.put(auth, :api_organization, to_string(organization))) Hex.Auth.with_api(:write, config, &:mix_hex_api_key.delete_all(&1)) end end end hex-2.5.1/lib/hex/api/oauth.ex000066400000000000000000000033071522402544500160720ustar00rootroot00000000000000defmodule Hex.API.OAuth do @moduledoc false alias Hex.API.Client @client_id "78ea6566-89fd-481e-a1d6-7d9d78eacca8" @doc false def client_id, do: @client_id @doc """ Runs the complete OAuth device authorization flow. See `:mix_hex_api_oauth.device_auth_flow/5` for more details. ## Examples iex> prompt_fn = fn uri, code -> IO.puts("Visit \#{uri} and enter: \#{code}") end iex> Hex.API.OAuth.device_auth_flow("api", prompt_fn) {:ok, %{access_token: "...", refresh_token: "...", expires_at: 1234567890}} iex> Hex.API.OAuth.device_auth_flow("api", prompt_fn, open_browser: true) {:ok, %{access_token: "...", refresh_token: "...", expires_at: 1234567890}} """ def device_auth_flow(scopes, prompt_user, opts \\ []) do config = Client.config() case :mix_hex_api_oauth.device_auth_flow(config, @client_id, scopes, prompt_user, opts) do {:ok, tokens} -> {:ok, drop_undefined_refresh_token(tokens)} other -> other end end # :mix_hex_api_oauth always includes a :refresh_token key, using the atom # :undefined when the server didn't return one. Drop it so stored token maps # only ever contain a binary refresh token (or no key at all). defp drop_undefined_refresh_token(%{refresh_token: refresh} = tokens) when refresh in [:undefined, nil] do Map.delete(tokens, :refresh_token) end defp drop_undefined_refresh_token(tokens), do: tokens @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.5.1/lib/hex/api/package.ex000066400000000000000000000033031522402544500163410ustar00rootroot00000000000000defmodule Hex.API.Package do @moduledoc false alias Hex.API.Client def get(repo, name, auth \\ []) when name != "" do config = Client.build_config(repo, auth) Hex.Auth.with_api( :read, config, &:mix_hex_api_package.get(&1, to_string(name)), auth_inline: false, optional: true ) end def search(repo, search, auth \\ []) do config = Client.build_config(repo, auth) search_params = [{:sort, "downloads"}] Hex.Auth.with_api( :read, config, &:mix_hex_api_package.search(&1, to_string(search), search_params), auth_inline: false, optional: true ) end defmodule Owner do @moduledoc false alias Hex.API.Client def add(repo, package, owner, level, transfer, auth \\ []) when package != "" do config = Client.build_config(repo, auth) Hex.Auth.with_api( :write, config, &:mix_hex_api_package_owner.add( &1, to_string(package), to_string(owner), to_string(level), transfer ) ) end def delete(repo, package, owner, auth \\ []) when package != "" do config = Client.build_config(repo, auth) Hex.Auth.with_api( :write, config, &:mix_hex_api_package_owner.delete( &1, to_string(package), to_string(owner) ) ) end def get(repo, package, auth \\ []) when package != "" do config = Client.build_config(repo, auth) Hex.Auth.with_api( :read, config, &:mix_hex_api_package_owner.list(&1, to_string(package)), auth_inline: false, optional: true ) end end end hex-2.5.1/lib/hex/api/release.ex000066400000000000000000000032361522402544500163730ustar00rootroot00000000000000defmodule Hex.API.Release do @moduledoc false alias Hex.API.Client def get(repo, name, version, auth \\ []) do config = Client.build_config(repo, auth) Hex.Auth.with_api( :read, config, &:mix_hex_api_release.get(&1, to_string(name), to_string(version)), auth_inline: false, optional: true ) end def publish(repo, tar, auth \\ [], progress \\ fn _ -> nil end, replace \\ false) def publish(repo, tar, auth, progress, replace?) do config = Client.build_config(repo, auth) # Pass progress callback through adapter config adapter_config = %{progress_callback: progress} Hex.Auth.with_api(:write, config, fn config -> config = Map.put(config, :http_adapter, {Hex.HTTP, adapter_config}) :mix_hex_api_release.publish(config, tar, replace: replace?) end) end def delete(repo, name, version, auth \\ []) do config = Client.build_config(repo, auth) Hex.Auth.with_api( :write, config, &:mix_hex_api_release.delete(&1, to_string(name), to_string(version)) ) end def retire(repo, name, version, body, auth \\ []) do config = Client.build_config(repo, auth) # Convert body to binary map for hex_core params = Map.new(body, fn {k, v} -> {to_string(k), to_string(v)} end) Hex.Auth.with_api( :write, config, &:mix_hex_api_release.retire(&1, to_string(name), to_string(version), params) ) end def unretire(repo, name, version, auth \\ []) do config = Client.build_config(repo, auth) Hex.Auth.with_api( :write, config, &:mix_hex_api_release.unretire(&1, to_string(name), to_string(version)) ) end end hex-2.5.1/lib/hex/api/release_docs.ex000066400000000000000000000026701522402544500174040ustar00rootroot00000000000000defmodule 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" ]) Hex.Auth.with_api(:read, config, &:mix_hex_api.get(&1, path), auth_inline: false, optional: true ) end def publish(repo, name, version, tar, auth \\ [], progress \\ fn _ -> nil end) do config = Client.build_config(repo, auth) # Pass progress callback through adapter config adapter_config = %{progress_callback: progress} path = :mix_hex_api.build_repository_path(config, [ "packages", to_string(name), "releases", to_string(version), "docs" ]) Hex.Auth.with_api(:write, config, fn config -> config = Map.put(config, :http_adapter, {Hex.HTTP, adapter_config}) body = {"application/octet-stream", tar} :mix_hex_api.post(config, path, body) end) end def delete(repo, name, version, auth \\ []) do config = Client.build_config(repo, auth) path = :mix_hex_api.build_repository_path(config, [ "packages", to_string(name), "releases", to_string(version), "docs" ]) Hex.Auth.with_api(:write, config, &:mix_hex_api.delete(&1, path)) end end hex-2.5.1/lib/hex/api/short_url.ex000066400000000000000000000005011522402544500167640ustar00rootroot00000000000000defmodule 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.5.1/lib/hex/api/user.ex000066400000000000000000000012151522402544500157240ustar00rootroot00000000000000defmodule Hex.API.User do @moduledoc false alias Hex.API.Client def me(auth \\ []) do config = Client.config(auth) Hex.Auth.with_api(:read, config, &:mix_hex_api_user.me(&1)) end def get(username, auth \\ []) do config = Client.config(auth) Hex.Auth.with_api(:read, config, &:mix_hex_api_user.get(&1, to_string(username)), auth_inline: false, optional: true ) 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.5.1/lib/hex/application.ex000066400000000000000000000023541522402544500165050ustar00rootroot00000000000000defmodule 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.State, Hex.Server, {Hex.Parallel, [:hex_fetcher]} ] end else defp children do [ Hex.Netrc.Cache, Hex.State, Hex.Server, {Hex.Parallel, [:hex_fetcher]}, Hex.Registry.Server, Hex.UpdateChecker ] end end end hex-2.5.1/lib/hex/auth.ex000066400000000000000000000103631522402544500151420ustar00rootroot00000000000000defmodule Hex.Auth do @moduledoc false @doc """ Execute a function with API authentication. Options: * :auth_inline - When true (default), initiates device auth for write ops when no credentials found. When false, returns error. """ def with_api(permission, config, fun, opts \\ []) do :mix_hex_cli_auth.with_api(permission, config, fun, opts) end @doc """ Execute a function with repository authentication. """ def with_repo(config, fun, opts \\ []) do case :mix_hex_cli_auth.with_repo(config, fun, opts) do {:error, {:auth_error, :oauth_exchange_failed}} -> raise "Failed to exchange API key for OAuth token" other -> other end end @doc """ Execute a function with preemptive authentication using the provided auth data. """ def with_preemptive_auth(auth, config, fun, opts \\ []) do config = put_in(config.cli_auth_callbacks.get_auth_config, fn _ -> auth end) :mix_hex_cli_auth.with_repo(config, fun, opts) end @doc false @spec callbacks() :: :mix_hex_cli_auth.callbacks() def callbacks do %{ get_auth_config: &get_auth_config/1, get_oauth_tokens: &get_oauth_tokens/0, persist_oauth_tokens: &persist_oauth_tokens/4, clear_oauth_tokens: &clear_oauth_tokens/0, prompt_otp: &prompt_otp/1, get_client_id: &Hex.API.OAuth.client_id/0, should_authenticate: &should_authenticate/1 } end defp get_auth_config(repo) do case {Hex.Repo.fetch_repo(repo), Hex.State.get(:api_key)} do {{:ok, config}, nil} -> config {{:ok, config}, api_key} -> Map.put_new(config, :api_key, api_key) {:error, nil} -> :undefined {:error, api_key} -> %{api_key: api_key} end end defp get_oauth_tokens do case Hex.State.get(:oauth_token) do nil -> :error %{access_token: access_token, expires_at: expires_at} = token_data -> tokens = %{access_token: access_token, expires_at: expires_at} tokens = if token_data[:refresh_token], do: Map.put(tokens, :refresh_token, token_data[:refresh_token]), else: tokens {:ok, tokens} end end defp persist_oauth_tokens(repo, access_token, refresh_token, expires_at) defp persist_oauth_tokens(:global, access_token, refresh_token, expires_at) do token_data = %{ access_token: access_token, expires_at: expires_at } token_data = if refresh_token, do: Map.put(token_data, :refresh_token, refresh_token), else: token_data Hex.OAuth.store_token(token_data) :ok end defp persist_oauth_tokens(repo, access_token, refresh_token, expires_at) do token_data = %{ access_token: access_token, expires_at: expires_at } token_data = if refresh_token, do: Map.put(token_data, :refresh_token, refresh_token), else: token_data repo_config = Hex.Repo.get_repo(repo) |> Map.put(:oauth_token, token_data) Hex.State.fetch!(:repos) |> Map.put(repo, repo_config) |> Hex.Config.update_repos() :ok end # Invoked by hex_cli_auth when the stored global OAuth token is expired and # could not be refreshed. Drop it from in-memory state only — keeping the # on-disk token, since a refresh can fail transiently — so the rest of this # run stops retrying the doomed refresh and proceeds unauthenticated. defp clear_oauth_tokens do Hex.State.put(:oauth_token, nil) Hex.Shell.warn( "Your authentication session has expired and could not be refreshed. " <> "Continuing without credentials; requests for private resources will fail or " <> "prompt for authentication. Run `mix hex.user auth` to re-authenticate" ) :ok end defp prompt_otp(message) do case Hex.Shell.prompt(message) do nil -> :cancelled otp -> otp = String.trim(otp) Hex.State.put(:api_otp, otp) {:ok, otp} end end defp should_authenticate(reason) defp should_authenticate(:no_credentials) do Hex.Shell.yes?("No authenticated user found. Do you want to authenticate now?") end defp should_authenticate(:token_refresh_failed) do Hex.Shell.yes?("Token refresh failed. Do you want to renew your authentication?") end end hex-2.5.1/lib/hex/config.ex000066400000000000000000000072231522402544500154470ustar00rootroot00000000000000defmodule Hex.Config do @moduledoc false def read() do case File.read(config_path()) do {:ok, binary} -> case decode_term(binary) do {:ok, term} -> migrate(term) {:error, _} -> config = decode_elixir(binary) write(config) migrate(config) end {:error, _} -> [] end end # OAuth token maps were historically persisted with string keys. We now use # atom keys consistently (matching the :mix_hex_cli_auth border), so migrate # any string-keyed token maps from older configs on read. defp migrate(config) do Enum.map(config, fn {:"$oauth_token", token} -> {:"$oauth_token", migrate_oauth_token(token)} {:"$repos", repos} when is_map(repos) -> repos = Map.new(repos, fn {name, repo} -> {name, migrate_repo_oauth_token(repo)} end) {:"$repos", repos} pair -> pair end) end defp migrate_repo_oauth_token(repo) when is_map(repo) do case repo do %{oauth_token: token} -> %{repo | oauth_token: migrate_oauth_token(token)} _ -> repo end end defp migrate_repo_oauth_token(repo), do: repo defp migrate_oauth_token(token) when is_map(token) do Map.new(token, fn {"access_token", value} -> {:access_token, value} {"refresh_token", value} -> {:refresh_token, value} {"expires_at", value} -> {:expires_at, value} pair -> pair end) end defp migrate_oauth_token(token), do: token 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.5.1/lib/hex/cooldown.ex000066400000000000000000000156731522402544500160360ustar00rootroot00000000000000defmodule Hex.Cooldown do @moduledoc false @type duration :: String.t() @type cutoff :: {:cutoff, integer(), non_neg_integer()} | :disabled @doc """ Parses a duration string into a normalized form for `Hex.State`. Returns `{:ok, duration}` for valid input or `:error` for invalid input. Accepted forms: `"0"`, `"d"`, `"w"`, `"mo"`. """ @spec parse_config(String.t() | nil) :: {:ok, duration()} | :error def parse_config(nil), do: {:ok, "0d"} def parse_config(""), do: {:ok, "0d"} def parse_config(string) when is_binary(string) do case duration_to_seconds(string) do {:ok, _} -> {:ok, string} :error -> :error end end def parse_config(_), do: :error @doc """ Parses the `cooldown_exclude_repos` config value into a list of repo name strings. Accepts either a list of strings (project / global config) or a comma-separated string (env var). Empty entries are dropped, whitespace is trimmed. Repo names should match `Hex.Repo` keys, e.g. `"hexpm"`, `"hexpm:myorg"`, or a custom repo name. """ @spec parse_exclude_repos([String.t()] | String.t() | nil) :: {:ok, [String.t()]} | :error def parse_exclude_repos(nil), do: {:ok, []} def parse_exclude_repos([]), do: {:ok, []} def parse_exclude_repos(list) when is_list(list) do if Enum.all?(list, &is_binary/1) do {:ok, list |> Enum.map(&String.trim/1) |> Enum.reject(&(&1 == ""))} else :error end end def parse_exclude_repos(string) when is_binary(string) do {:ok, string |> String.split(",") |> Enum.map(&String.trim/1) |> Enum.reject(&(&1 == ""))} end def parse_exclude_repos(_), do: :error @doc """ Returns true if cooldown should be skipped for the given repo. """ @spec repo_excluded?(String.t() | nil) :: boolean() def repo_excluded?(repo) do name = repo || "hexpm" name in Hex.State.fetch!(:cooldown_exclude_repos) end @doc """ Converts a duration string into a number of seconds. """ @spec duration_to_seconds(String.t()) :: {:ok, non_neg_integer()} | :error def duration_to_seconds("0"), do: {:ok, 0} # The leading-digit guard rejects the signs Integer.parse/1 accepts ("+5d", "-5d") def duration_to_seconds(<> = string) when digit in ?0..?9 do case Integer.parse(string) do {n, unit} when unit in ["d", "w", "mo"] -> {:ok, n * unit_seconds(unit)} _ -> :error end end def duration_to_seconds(string) when is_binary(string), do: :error defp unit_seconds("d"), do: 86_400 defp unit_seconds("w"), do: 86_400 * 7 defp unit_seconds("mo"), do: 86_400 * 30 @doc """ Picks the strictest (longest) duration from a list of `{tag, duration}` candidates. `nil`, `""`, and unparseable durations are treated as `"0d"`. Returns the chosen `{tag, duration}` so callers can attribute the decision to its source (e.g. `:local` vs `{repo, name}`). """ @spec strictest([{tag, String.t() | nil}]) :: {tag, String.t()} when tag: term() def strictest(candidates) do candidates |> Enum.map(fn {tag, dur} -> case is_binary(dur) && duration_to_seconds(dur) do {:ok, 0} -> {tag, "0d", 0} {:ok, seconds} -> {tag, dur, seconds} _ -> {tag, "0d", 0} end end) |> Enum.max_by(&elem(&1, 2)) |> then(fn {tag, dur, _} -> {tag, dur} end) end @doc """ Builds a resolution cutoff from the local cooldown configuration. Returns `:disabled` when the effective duration is zero. """ @spec build_cutoff() :: cutoff() def build_cutoff(), do: build_cutoff(Hex.State.fetch!(:cooldown)) @doc """ Builds a resolution cutoff from a duration string. `build_cutoff/0` is equivalent to `build_cutoff(Hex.State.fetch!(:cooldown))`. """ @spec build_cutoff(String.t() | nil) :: cutoff() def build_cutoff(duration) do case duration_to_seconds(duration || "0d") do {:ok, 0} -> :disabled {:ok, seconds} -> now = System.system_time(:second) {:cutoff, now - seconds, seconds} :error -> :disabled end end @doc """ Returns true if the release is eligible under the cutoff. A release with no `published_at` (legacy registry data) is treated as eligible; cooldown only applies to releases whose publish time is known. """ @spec eligible?(integer() | nil, cutoff()) :: boolean() def eligible?(_published_at, :disabled), do: true def eligible?(nil, _cutoff), do: true def eligible?(published_at, {:cutoff, cutoff_seconds, _}) when is_integer(published_at) do published_at <= cutoff_seconds end @doc """ Returns the date a release would become eligible under the cutoff. """ @spec eligible_on(integer(), cutoff()) :: Date.t() def eligible_on(published_at, {:cutoff, _cutoff_seconds, window_seconds}) when is_integer(published_at) do published_at |> Kernel.+(window_seconds) |> DateTime.from_unix!() |> DateTime.to_date() end @versions_listed_per_package 5 @doc """ Formats the post-solver summary of versions skipped by cooldown. Takes a list of `{repo, package, version, published_at}` tuples accumulated during the solve. Returns `nil` when nothing eligible to report — empty input, disabled cutoff, or every entry missing a `published_at`. Entries are deduplicated and grouped by package; within a package versions are listed newest-first, capped at #{@versions_listed_per_package} with an overflow line. Repo is used for keying only; it is not rendered. """ @spec format_summary([{String.t(), String.t(), String.t(), integer() | nil}], cutoff()) :: String.t() | nil def format_summary(_entries, :disabled), do: nil def format_summary([], _cutoff), do: nil def format_summary(entries, cutoff) do entries = entries |> Enum.reject(fn {_repo, _pkg, _vsn, published_at} -> is_nil(published_at) end) |> Enum.uniq() case entries do [] -> nil entries -> lines = entries |> Enum.group_by(fn {repo, pkg, _vsn, _} -> {repo, pkg} end) |> Enum.sort() |> Enum.flat_map(fn {_key, entries} -> package_lines(entries, cutoff) end) iodata = ["\nVersions filtered by cooldown:\n", Enum.intersperse(lines, "\n"), "\n"] IO.chardata_to_string(Hex.Shell.format(iodata)) end end defp package_lines(entries, cutoff) do {listed, rest} = entries |> Enum.sort_by(fn {_repo, _pkg, vsn, _} -> Version.parse!(vsn) end, {:desc, Version}) |> Enum.split(@versions_listed_per_package) lines = Enum.map(listed, fn {_repo, pkg, vsn, published_at} -> eligible_date = eligible_on(published_at, cutoff) [ " ", :bright, pkg, :reset, " ", :yellow, vsn, :reset, " — eligible ", :cyan, to_string(eligible_date), :reset ] end) case rest do [] -> lines rest -> lines ++ [" ...and #{length(rest)} more"] end end end hex-2.5.1/lib/hex/dev.ex000066400000000000000000000045451522402544500147640ustar00rootroot00000000000000if Mix.env() == :dev do defmodule Hex.Dev do @moduledoc false @ets_name __MODULE__ @registry_filename "cache.ets" @repo "hexpm" @ets_version 5 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, {:advisories, @repo, package, version}) copy(original_ets, new_ets, {:published_at, @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.5.1/lib/hex/http.ex000066400000000000000000000336011522402544500151600ustar00rootroot00000000000000defmodule 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} = IO.iodata_to_binary(header) |> parse_hex_message message = Hex.Utils.escape_terminal(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.5.1/lib/hex/http/000077500000000000000000000000001522402544500146175ustar00rootroot00000000000000hex-2.5.1/lib/hex/http/ca-bundle.crt000066400000000000000000005620211522402544500171710ustar00rootroot00000000000000## ## Bundle of CA Root Certificates ## ## Certificate data from Mozilla as of: Thu Jul 9 23:07:11 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. ## Configure this file as the SSLCACertificateFile. ## ## Conversion done with mk-ca-bundle.pl version 1.33. ## SHA256: 77130ef91213772844561fbd3aa31d413b25c2ac7f576fea3bc3bbff7ef93489 ## 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----- 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----- 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----- 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----- 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----- 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----- 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----- 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----- 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 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----- 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.5.1/lib/hex/http/certs.ex000066400000000000000000000007421522402544500163000ustar00rootroot00000000000000defmodule 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.5.1/lib/hex/http/ssl.ex000066400000000000000000000052551522402544500157650ustar00rootroot00000000000000defmodule 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.5.1/lib/hex/http/verify_hostname.ex000066400000000000000000000133721522402544500203650ustar00rootroot00000000000000defmodule 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.5.1/lib/hex/ignores.ex000066400000000000000000000071211522402544500156450ustar00rootroot00000000000000defmodule Hex.Ignores do @moduledoc false # Parsing and matching for the ignore_advisories and ignore_retirements # configs. Parsed advisory entries are advisory ID strings. Parsed # retirement entries are {name, version} tuples where a nil version # matches every version of the package. def parse_advisories(nil), do: {:ok, []} def parse_advisories(""), do: {:ok, []} def parse_advisories(value) when is_binary(value) do {:ok, split_env_list(value)} end def parse_advisories(value) when is_list(value) do if Enum.all?(value, &(is_binary(&1) and &1 != "")) do {:ok, value} else :error end end def parse_advisories(_), do: :error def parse_retirements(nil), do: {:ok, []} def parse_retirements(""), do: {:ok, []} def parse_retirements(value) when is_binary(value) do value |> split_env_list() |> Enum.map(&parse_retirement_string/1) |> collect_entries() end def parse_retirements(value) when is_list(value) do value |> Enum.map(&parse_retirement_term/1) |> collect_entries() end def parse_retirements(_), do: :error def advisory_ignored?(advisory, ids) do Enum.any?(ids, &advisory_matches?(advisory, &1)) end def advisory_matches?(%{id: advisory_id} = advisory, id) do id = String.downcase(id) Enum.any?([advisory_id | Map.get(advisory, :aliases, [])], fn candidate -> String.downcase(alias_id(candidate)) == id end) end def retirement_ignored?(package, version, entries) do Enum.any?(entries, &retirement_matches?(package, version, &1)) end def retirement_matches?(package, _version, {name, nil}), do: package == name def retirement_matches?(package, version, {name, pinned}), do: package == name and version == pinned def split_advisories(advisories, []), do: {[], advisories} def split_advisories(advisories, ids) do ignored_ids = advisories |> :mix_hex_advisory.group_for_display() |> Enum.filter(&advisory_ignored?(&1, ids)) |> Enum.flat_map(&group_identifiers/1) |> MapSet.new() Enum.split_with(advisories, fn %{id: id} -> MapSet.member?(ignored_ids, String.downcase(id)) end) end def reject_ignored_advisories(advisories, ids) do {_ignored, active} = split_advisories(advisories, ids) active end defp split_env_list(value) do value |> String.split(",") |> Enum.map(&String.trim/1) |> Enum.reject(&(&1 == "")) end defp parse_retirement_string(entry) do case String.split(entry, "@", parts: 2) do [name] -> {:ok, {name, nil}} [name, version] -> entry(name, version) end end defp parse_retirement_term(name) when is_atom(name) and name not in [nil, true, false] do {:ok, {Atom.to_string(name), nil}} end defp parse_retirement_term({name, version}) when is_atom(name) and name not in [nil, true, false] do entry(Atom.to_string(name), version) end defp parse_retirement_term(_), do: :error defp entry(name, version) when name != "" and is_binary(version) do case Version.parse(version) do {:ok, _} -> {:ok, {name, version}} :error -> :error end end defp entry(_name, _version), do: :error defp collect_entries(results) do if Enum.all?(results, &match?({:ok, _}, &1)) do {:ok, Enum.map(results, fn {:ok, entry} -> entry end)} else :error end end defp group_identifiers(%{id: id} = group) do aliases = Map.get(group, :aliases, []) [String.downcase(id) | Enum.map(aliases, &String.downcase(alias_id(&1)))] end defp alias_id(%{id: id}), do: id defp alias_id(id) when is_binary(id), do: id end hex-2.5.1/lib/hex/mix.ex000066400000000000000000000170311522402544500147750ustar00rootroot00000000000000defmodule 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.5.1/lib/hex/netrc.ex000066400000000000000000000005151522402544500153120ustar00rootroot00000000000000defmodule 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.5.1/lib/hex/netrc/000077500000000000000000000000001522402544500147535ustar00rootroot00000000000000hex-2.5.1/lib/hex/netrc/cache.ex000066400000000000000000000012541522402544500163560ustar00rootroot00000000000000defmodule 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.5.1/lib/hex/netrc/parser.ex000066400000000000000000000030121522402544500166010ustar00rootroot00000000000000defmodule 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.5.1/lib/hex/oauth.ex000066400000000000000000000020141522402544500153130ustar00rootroot00000000000000defmodule Hex.OAuth do @moduledoc false @doc """ Gets the current access token from state. Returns `{:ok, token}` if a valid token exists, or `{:error, reason}` otherwise. """ def get_token do case Hex.State.get(:oauth_token) do nil -> {:error, :no_token} %{access_token: token} -> {:ok, token} _ -> {:error, :invalid_token} 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. """ def clear_tokens do Hex.Config.remove([:"$oauth_token"]) Hex.State.put(:oauth_token, nil) end @doc """ Checks if we have any OAuth tokens stored. """ def has_tokens? do Hex.State.get(:oauth_token) != nil end end hex-2.5.1/lib/hex/package.ex000066400000000000000000000043501522402544500155730ustar00rootroot00000000000000defmodule 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. Use SPDX license identifiers or `LicenseRef-` for custom licenses included in 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.5.1/lib/hex/parallel.ex000066400000000000000000000065711522402544500160030ustar00rootroot00000000000000defmodule 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.5.1/lib/hex/policy.ex000066400000000000000000000100161522402544500154730ustar00rootroot00000000000000defmodule Hex.Policy do @moduledoc false alias Hex.Registry.Server, as: Registry @type ref :: {repo :: String.t(), name :: String.t()} @doc """ Validates a `policy` configuration value and normalizes it to the canonical `"REPO/NAME"` string. Accepts: * a keyword list in mix.exs: `[org: "myorg", name: "strict-prod"]` for a hexpm organization (resolves to the `hexpm:myorg` repo), or `[repo: "REPO", name: "NAME"]` for any configured repo * a `"REPO/NAME"` string (env-var / `mix hex.config` form), e.g. `"hexpm:myorg/strict-prod"` * `nil` or `""` (no policy) Returns `{:ok, string}`, `{:ok, nil}`, or `:error`. The bare `"hexpm"` repo is rejected because the global hexpm has no organization-scoped policies; policies live under `hexpm:` (or any non-`hexpm` repo for self-hosted setups). This would be relaxed if hexpm ever serves a global policy (e.g. a `recommended` set) directly under the `hexpm` repo. """ @spec parse_config(term()) :: {:ok, String.t() | nil} | :error def parse_config(nil), do: {:ok, nil} def parse_config(""), do: {:ok, nil} def parse_config([]), do: {:ok, nil} def parse_config(string) when is_binary(string) do string = String.trim(string) case parse_ref(string) do {:ok, _ref} -> {:ok, string} :error -> :error end end def parse_config([{key, _} | _] = kw) when is_atom(key) do case {Keyword.get(kw, :repo), Keyword.get(kw, :org), Keyword.get(kw, :name)} do {nil, org, name} when is_binary(org) and org != "" and is_binary(name) -> parse_config("hexpm:" <> org <> "/" <> name) {repo, nil, name} when is_binary(repo) and is_binary(name) -> parse_config(repo <> "/" <> name) _ -> :error end end def parse_config(_), do: :error defp parse_ref(string) do case String.split(string, "/") do [repo, name] when byte_size(repo) > 0 and byte_size(name) > 0 and repo != "hexpm" -> {:ok, {repo, name}} _ -> :error end end @doc """ Reads the configured policy ref from `Hex.State`, fetches it through the registry, and returns the decoded policy (or `nil` when none is configured). A policy is an enforcement feature, so anything short of materializing the configured policy fails closed: a malformed configuration value or a fetch that yields nothing raises instead of resolving unenforced. Fetch failures with no usable cache raise through the registry's standard fetch error path. """ @spec load() :: {:ok, map() | nil} def load() do case Hex.State.fetch!(:policy) do nil -> {:ok, nil} {:invalid, value} -> Mix.raise( "Invalid policy configuration: #{inspect(value)}. Expected \"REPO/NAME\" " <> "(e.g. \"hexpm:myorg/strict-prod\") or [org: \"ORG\", name: \"NAME\"] / " <> "[repo: \"REPO\", name: \"NAME\"] in mix.exs, where REPO is not the " <> "bare \"hexpm\" repo" ) string when is_binary(string) -> {:ok, {repo, name} = ref} = parse_ref(string) Registry.open() Registry.prefetch_policies([ref]) case Registry.policy(repo, name) do {:ok, decoded} -> {:ok, decoded} :error -> Mix.raise("Failed to load policy #{repo}/#{name}") end end end @doc """ Returns the active policy, lazy-loading and caching it in `Hex.State` on first call. When the remote converger has already populated `:active_policy` (the normal `mix deps.get` path) this is a cheap state read. When called standalone (e.g. from `mix hex.policy show`) and a policy is configured it triggers the registry fetch and stores the result for subsequent calls. """ @spec active() :: {:ok, map() | nil} def active() do loaded = Hex.State.fetch!(:active_policy) cond do loaded != nil -> {:ok, loaded} Hex.State.fetch!(:policy) == nil -> {:ok, nil} true -> {:ok, policy} = load() Hex.State.put(:active_policy, policy) {:ok, policy} end end end hex-2.5.1/lib/hex/policy/000077500000000000000000000000001522402544500151375ustar00rootroot00000000000000hex-2.5.1/lib/hex/policy/diagnostics.ex000066400000000000000000000133341522402544500200100ustar00rootroot00000000000000defmodule Hex.Policy.Diagnostics do @moduledoc false alias Hex.Cooldown @type filtered_entry :: %{ repo: String.t(), package: String.t(), version: String.t(), reasons: [term()] } @versions_listed_per_package 5 @doc """ Builds the resolution summary block. Returns `nil` when no policy is loaded; with an active policy the block always includes the header line, plus the cooldown line and a capped per-version listing of hidden versions when relevant. `policy` is the decoded policy map (or `nil`); `filtered` is the list of `%{repo, package, version, reasons}` entries recorded by Hex.Registry.Policy. """ @spec resolution_summary(map() | nil, [filtered_entry()], String.t() | nil) :: String.t() | nil def resolution_summary(nil, _filtered, _local_cooldown), do: nil def resolution_summary(policy, filtered, local_cooldown) do header = "Active policy: #{policy.repository}/#{policy.name}" cooldown_line = policy_cooldown_line(policy, local_cooldown) hidden_lines = if filtered == [] do [] else ["Policy hid #{count(length(filtered), "candidate version")}:" | version_lines(filtered)] end ([header, cooldown_line] ++ hidden_lines) |> Enum.reject(&is_nil/1) |> Enum.join("\n") end defp version_lines(filtered) do filtered |> Enum.group_by(fn entry -> {entry.repo, entry.package} end) |> Enum.sort() |> Enum.flat_map(fn {{_repo, package}, entries} -> package_lines(package, entries) end) end # Newest-first because the interesting hidden versions are the ones newer # than the resolved one; the cap keeps packages with long advisory or # retirement histories from flooding the report. `mix hex.policy why` is # the uncapped view. defp package_lines(package, entries) do {listed, rest} = entries |> Enum.sort_by(&Version.parse!(&1.version), {:desc, Version}) |> Enum.split(@versions_listed_per_package) lines = Enum.map(listed, fn entry -> attribution = entry.reasons |> Enum.map(&format_reason/1) |> Enum.join(", ") " #{package} #{entry.version} — #{attribution}" end) case rest do [] -> lines rest -> lines ++ [" ...and #{length(rest)} more — run `mix hex.policy why #{package}`"] end end @doc """ Computes the effective cooldown across the local configuration and the active policy, returning `{source, duration}` or `nil` when zero. The policy contributes the strictest cooldown across its repository tabs. """ @spec effective_cooldown(map() | nil, String.t() | nil) :: {:local | {String.t(), String.t()}, String.t()} | nil def effective_cooldown(policy, local_cooldown) do case Cooldown.strictest([{:local, local_cooldown} | policy_cooldown(policy)]) do {_source, "0d"} -> nil result -> result end end # Returns a single `{{repo, name}, duration}` cooldown candidate for the # policy, where the duration is the strictest across its repository tabs, or # an empty list when the policy sets no cooldown. defp policy_cooldown(nil), do: [] defp policy_cooldown(policy) do durations = for rp <- Map.get(policy, :repositories, []), restriction = Map.get(rp, :restriction), restriction != nil, duration = Map.get(restriction, :cooldown), is_binary(duration), do: duration case durations do [] -> [] _ -> {_tag, strictest} = Cooldown.strictest([{nil, "0d"} | Enum.map(durations, &{nil, &1})]) [{{policy.repository, policy.name}, strictest}] end end # A locally configured cooldown gets its own "Versions filtered by cooldown" # report after resolution, so the policy summary only calls out a cooldown # the policy itself imposes. defp policy_cooldown_line(policy, local_cooldown) do case effective_cooldown(policy, local_cooldown) do {{repo, name}, duration} -> "Effective cooldown: #{duration} (#{repo}/#{name})" _ -> nil end end @doc """ Renders a Note: block to append to a solver failure when the active policy hid candidate versions. The per-package listing is capped the same way as the resolution summary. Returns `nil` if there's nothing relevant to say. """ @spec failure_note([filtered_entry()]) :: String.t() | nil def failure_note([]), do: nil def failure_note(filtered) do by_package = filtered |> Enum.group_by(fn entry -> {entry.repo, entry.package} end) |> Enum.sort() blocks = Enum.map(by_package, fn {{_repo, package}, entries} -> "Note: active policy hides #{count(length(entries), "version")} of \"#{package}\":\n" <> Enum.join(package_lines(package, entries), "\n") end) Enum.join(blocks, "\n\n") end @doc """ Formats a blocker reason. """ @spec format_reason(term()) :: String.t() def format_reason({:advisory, sev}), do: "advisory ≥ #{Hex.Utils.advisory_severity(sev)}" def format_reason({:retirement, r}), do: "retirement: #{Hex.Utils.package_retirement_reason(r)}" def format_reason({:cooldown, duration, eligible_on}), do: "cooldown #{duration}; eligible #{eligible_on}" def format_reason(:override_deny), do: "override deny" def format_reason(other), do: inspect(other) @doc """ Maps a blocker reason to an ANSI color used to highlight it in output. """ @spec reason_color(term()) :: atom() def reason_color({:advisory, _}), do: :red def reason_color({:retirement, _}), do: :yellow def reason_color({:cooldown, _, _}), do: :cyan def reason_color(:override_deny), do: :red def reason_color(_other), do: :reset defp count(1, noun), do: "1 #{noun}" defp count(n, noun), do: "#{n} #{noun}s" end hex-2.5.1/lib/hex/policy/filter.ex000066400000000000000000000152231522402544500167650ustar00rootroot00000000000000defmodule Hex.Policy.Filter do @moduledoc false alias Hex.Cooldown alias Hex.Registry.Server @severity_order [ :SEVERITY_NONE, :SEVERITY_LOW, :SEVERITY_MEDIUM, :SEVERITY_HIGH, :SEVERITY_CRITICAL ] @type policy :: map() @type candidate :: map() @type reason :: {:advisory, atom()} | {:retirement, atom()} | {:cooldown, String.t(), Date.t() | nil} | :override_deny @doc """ Classifies a single candidate release against a single policy. Finds the `RepositoryPolicy` matching the candidate's repository, evaluates its overrides (an `ALLOW` override permits the release and exempts it from the restriction; a `DENY` override blocks it; the most specific matching override wins), and otherwise applies the restriction limits. Returns `:allowed` or `{:blocked, [reason]}`. """ @spec classify(policy(), candidate(), keyword()) :: :allowed | {:blocked, [reason()]} def classify(policy, candidate, opts \\ []) do case repository_policy(policy, candidate) do nil -> :allowed repo_policy -> case override(repo_policy, candidate) do :allow -> :allowed :deny -> {:blocked, [:override_deny]} :none -> restriction(repo_policy, candidate, opts) end end end @doc """ Builds a candidate map for `classify/3` from the registry. Carries the metadata the restriction reads: advisories, retirement status, and publish time. """ @spec candidate_from_registry(String.t() | nil, String.t(), term()) :: candidate() def candidate_from_registry(repo, package, version) do version_str = to_string(version) %{ repo: repo || "hexpm", package: package, version: version_str, advisories: Server.advisories(repo, package, version_str) || [], retired: Server.retired(repo, package, version_str), published_at: Server.published_at(repo, package, version_str) } end # Finds the RepositoryPolicy whose repository matches the candidate's repo. # Candidate repos are named "hexpm" or "hexpm:"; the policy's tabs use # "hexpm" or the bare org name, so the "hexpm:" prefix is stripped to match. defp repository_policy(policy, candidate) do repo = candidate_repo(candidate) key = strip_prefix(repo) Enum.find(Map.get(policy, :repositories, []), fn rp -> rp.repository == repo or rp.repository == key end) end defp candidate_repo(%{repo: repo}) when is_binary(repo), do: repo defp candidate_repo(_), do: "hexpm" defp strip_prefix("hexpm:" <> org), do: org defp strip_prefix(repo), do: repo # Returns the effect of the most specific override matching the candidate: # :allow, :deny, or :none. A requirement-bearing override is more specific # than a bare-package one for the same package. defp override(repo_policy, candidate) do repo_policy |> Map.get(:overrides, []) |> Enum.filter(&known_action?/1) |> Enum.filter(&override_matches?(&1, candidate)) |> Enum.sort_by(&override_specificity/1, :desc) |> case do [override | _] -> override_effect(override.action) [] -> :none end end # Ignore overrides with an unknown future action (decoded as an integer) # rather than crash, mirroring how unknown advisory/retirement enums decode. defp known_action?(%{action: action}), do: action in [:OVERRIDE_ACTION_ALLOW, :OVERRIDE_ACTION_DENY] defp override_matches?(%{ref: %{package: package} = ref}, %{package: package} = candidate) do case Map.get(ref, :requirement) do nil -> true "" -> true requirement -> version_satisfies?(candidate.version, requirement) end end defp override_matches?(_override, _candidate), do: false defp override_specificity(%{ref: ref}) do case Map.get(ref, :requirement) do nil -> 0 "" -> 0 _ -> 1 end end defp override_effect(:OVERRIDE_ACTION_ALLOW), do: :allow defp override_effect(:OVERRIDE_ACTION_DENY), do: :deny defp restriction(repo_policy, candidate, opts) do case Map.get(repo_policy, :restriction) do nil -> :allowed restriction -> reasons = [] |> add_advisory(restriction, candidate) |> add_retirement(restriction, candidate) |> add_cooldown(restriction, candidate, opts) if reasons == [], do: :allowed, else: {:blocked, reasons} end end defp add_advisory(reasons, %{advisory_min_severity: threshold}, candidate) when not is_nil(threshold) do threshold_rank = severity_rank(threshold) advisories = Map.get(candidate, :advisories, []) if Enum.any?(advisories, fn a -> severity_rank(Map.get(a, :severity)) >= threshold_rank end) do [{:advisory, threshold} | reasons] else reasons end end defp add_advisory(reasons, _restriction, _candidate), do: reasons defp add_retirement(reasons, %{retirement_reasons: ret_reasons}, candidate) when is_list(ret_reasons) and ret_reasons != [] do case Map.get(candidate, :retired) do %{reason: retired_atom} -> if retired_atom in ret_reasons, do: [{:retirement, retired_atom} | reasons], else: reasons _ -> reasons end end defp add_retirement(reasons, _restriction, _candidate), do: reasons defp add_cooldown(reasons, %{cooldown: duration}, candidate, opts) when is_binary(duration) do now = Keyword.get(opts, :now, System.system_time(:second)) published_at = Map.get(candidate, :published_at) case Cooldown.duration_to_seconds(duration) do {:ok, seconds} when seconds > 0 and is_integer(published_at) -> cutoff = {:cutoff, now - seconds, seconds} if Cooldown.eligible?(published_at, cutoff) do reasons else [{:cooldown, duration, Cooldown.eligible_on(published_at, cutoff)} | reasons] end _ -> reasons end end defp add_cooldown(reasons, _restriction, _candidate, _opts), do: reasons defp version_satisfies?(version, requirement) do with {:ok, version} <- Version.parse(to_string(version)), {:ok, requirement} <- Version.parse_requirement(to_string(requirement)) do Version.match?(version, requirement) else _ -> false end end # A severity the client doesn't recognize (a future enum value decoded as an # integer, or a missing field) ranks above every known severity so it can # never slip under a threshold. Contrast with unknown override actions, # which are dropped: ignoring an override falls back to the restriction, # while ignoring an advisory would lift it. defp severity_rank(severity) do Enum.find_index(@severity_order, &(&1 == severity)) || length(@severity_order) end end hex-2.5.1/lib/hex/registry/000077500000000000000000000000001522402544500155105ustar00rootroot00000000000000hex-2.5.1/lib/hex/registry/cooldown.ex000066400000000000000000000034711522402544500176770ustar00rootroot00000000000000defmodule Hex.Registry.Cooldown do @moduledoc false @behaviour Hex.Solver.Registry alias Hex.Registry.Server @impl true defdelegate prefetch(packages), to: Server @impl true defdelegate dependencies(repo, package, version), to: Server @impl true def versions(repo, package) do case Server.versions(repo, package) do {:ok, versions} -> cutoff = Hex.State.fetch!(:cooldown_cutoff) bypass = Hex.State.fetch!(:cooldown_bypass_packages) cond do cutoff == :disabled -> {:ok, versions} Hex.Cooldown.repo_excluded?(repo) -> {:ok, versions} MapSet.member?(bypass, package) -> {:ok, versions} true -> locked = Map.get(Hex.State.fetch!(:cooldown_locked_versions), {repo || "hexpm", package}, []) {:ok, filter(versions, repo, package, cutoff, locked)} end :error -> :error end end defp filter(versions, repo, package, cutoff, locked) do {eligible, filtered_out} = Enum.split_with(versions, fn version -> version_str = to_string(version) if version_str in locked do true else published_at = Server.published_at(repo, package, version_str) Hex.Cooldown.eligible?(published_at, cutoff) end end) record_filtered(repo, package, filtered_out) eligible end defp record_filtered(_repo, _package, []), do: :ok defp record_filtered(repo, package, versions) do repo_key = repo || "hexpm" entries = Enum.map(versions, fn version -> version_str = to_string(version) {repo_key, package, version_str, Server.published_at(repo, package, version_str)} end) Hex.State.update!(:cooldown_filtered_versions, &(entries ++ &1)) end end hex-2.5.1/lib/hex/registry/policy.ex000066400000000000000000000035541522402544500173540ustar00rootroot00000000000000defmodule Hex.Registry.Policy do @moduledoc false @behaviour Hex.Solver.Registry alias Hex.Registry.Cooldown alias Hex.Policy.Filter @impl true defdelegate prefetch(packages), to: Cooldown @impl true defdelegate dependencies(repo, package, version), to: Cooldown @impl true def versions(repo, package) do case Cooldown.versions(repo, package) do {:ok, versions} -> case Hex.State.fetch!(:active_policy) do nil -> {:ok, versions} policy -> {:ok, filter(versions, repo, package, policy)} end :error -> :error end end defp filter(versions, repo, package, policy) do locked = locked_versions(repo, package) Enum.filter(versions, fn version -> if to_string(version) in locked do true else candidate = Filter.candidate_from_registry(repo, package, version) case Filter.classify(policy, candidate) do :allowed -> true {:blocked, reasons} -> record_block(repo, package, version, reasons) false end end end) end # Versions already in the lockfile are trusted at install and exempt from # policy filtering, so re-resolution keeps a locked-but-now-blocked entry. defp locked_versions(repo, package) do Hex.State.fetch!(:policy_locked_versions) |> Map.get({repo || "hexpm", package}, []) end # The solver may ask for a package's versions once per requirement, so the # same blocked version comes through here repeatedly; record it once. defp record_block(repo, package, version, reasons) do entry = %{ repo: repo || "hexpm", package: package, version: to_string(version), reasons: reasons } Hex.State.update!(:policy_filtered_versions, fn entries -> if entry in entries, do: entries, else: [entry | entries] end) end end hex-2.5.1/lib/hex/registry/server.ex000066400000000000000000000537521522402544500173700ustar00rootroot00000000000000defmodule Hex.Registry.Server do @moduledoc false use GenServer @behaviour Hex.Solver.Registry @name __MODULE__ @filename "cache.ets" @timeout 60_000 @ets_version 5 @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 case GenServer.call(@name, {:prefetch, packages}, @timeout) do :ok -> :ok {:error, message} -> Mix.raise(message) end end def prefetch_policies(refs) do case GenServer.call(@name, {:prefetch_policies, refs}, @timeout) do :ok -> :ok {:error, message} -> Mix.raise(message) end end def policy(repo, name) do GenServer.call(@name, {:policy, repo, name}, @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 advisories(repo, package, version) do GenServer.call(@name, {:advisories, repo, package, version}, @timeout) end def published_at(repo, package, version) do GenServer.call(@name, {:published_at, 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, pending_policies: MapSet.new(), fetched_policies: MapSet.new(), waiting_policies: %{} } 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({:prefetch_policies, refs}, _from, state) do refs = refs |> Enum.map(fn {repo, name} -> {repo || "hexpm", name} end) |> Enum.uniq() |> Enum.reject(&(&1 in state.fetched_policies)) |> Enum.reject(&(&1 in state.pending_policies)) purge_repo_from_cache(refs, state) if Hex.State.fetch!(:offline) do prefetch_policies_offline(refs, state) else prefetch_policies_online(refs, state) end end def handle_call({:policy, repo, name}, from, state) do maybe_wait_policy({repo, name}, from, state, fn -> case lookup(state.ets, {:policy, repo || "hexpm", name}) do nil -> :error decoded -> {:ok, decoded} end 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({:advisories, repo, package, version}, from, state) do maybe_wait({repo, package}, from, state, fn -> lookup(state.ets, {:advisories, repo || "hexpm", package, version}) end) end def handle_call({:published_at, repo, package, version}, from, state) do maybe_wait({repo, package}, from, state, fn -> lookup(state.ets, {:published_at, 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 def handle_info({:get_policy, repo, name, result}, state) do repo = repo || "hexpm" ref = {repo, name} pending = MapSet.delete(state.pending_policies, ref) fetched = MapSet.put(state.fetched_policies, ref) {replys, waiting} = Map.pop(state.waiting_policies, ref, []) write_policy_result(result, repo, name, state) Enum.each(replys, fn {from, fun} -> GenServer.reply(from, fun.()) end) state = %{ state | pending_policies: pending, waiting_policies: waiting, fetched_policies: 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 # {{:advisories, ^repo, _package, _version}, _} -> true # {{:published_at, ^repo, _package, _version}, _} -> true # {{:registry_etag, ^repo, _package}, _} -> true # {{:timestamp, ^repo, _package}, _} -> true # {{:timestamp, ^repo, _package, _version}, _} -> true # {{:policy, ^repo, _name}, _} -> true # {{:policy_etag, ^repo, _name}, _} -> 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]}, {{{:advisories, :"$1", :"$2", :"$3"}, :_}, [{:"=:=", {:const, repo}, :"$1"}], [true]}, {{{:published_at, :"$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]}, {{{:policy, :"$1", :"$2"}, :_}, [{:"=:=", {:const, repo}, :"$1"}], [true]}, {{{:policy_etag, :"$1", :"$2"}, :_}, [{:"=:=", {: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 prefetch_policies_online(refs, state) do Enum.each(refs, fn {repo, name} -> etag = policy_etag(repo, name, state) Hex.Parallel.run(:hex_fetcher, {:policy, repo, name}, [await: false], fn -> {:get_policy, repo, name, Hex.Repo.get_policy(repo, name, etag)} end) end) pending = MapSet.union(MapSet.new(refs), state.pending_policies) state = %{state | pending_policies: pending} {:reply, :ok, state} end defp prefetch_policies_offline(refs, state) do case Enum.find(refs, fn {repo, name} -> !lookup(state.ets, {:policy, repo, name}) end) do {repo, name} -> message = "Hex is running in offline mode and policy " <> "#{repo}/#{name} is not cached locally" {:reply, {:error, message}, state} nil -> fetched = MapSet.union(MapSet.new(refs), state.fetched_policies) {:reply, :ok, %{state | fetched_policies: fetched}} end end defp write_result({:ok, {code, headers, %{releases: releases} = result}}, repo, package, %{ ets: tid }) when code in 200..299 do delete_package(repo, package, tid) now = :calendar.universal_time() pkg_advisories = result[:advisories] || [] 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]}) # The registry encodes published_at as a {seconds, nanos} Timestamp # map. Cooldown only needs second granularity, so store the integer # to keep the consumers simple. :ets.insert( tid, {{:published_at, repo, package, version}, timestamp_seconds(release[:published_at])} ) release_advisories = (release[:advisory_indexes] || []) |> Enum.map(&Enum.at(pkg_advisories, &1)) |> Enum.reject(&is_nil/1) :ets.insert(tid, {{:advisories, repo, package, version}, release_advisories}) 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 write_policy_result({:ok, {code, headers, decoded}}, repo, name, %{ets: tid}) when code in 200..299 and is_map(decoded) do :ets.insert(tid, {{:policy, repo, name}, decoded}) if etag = headers[~c"etag"] do :ets.insert(tid, {{:policy_etag, repo, name}, List.to_string(etag)}) end end defp write_policy_result({:ok, {304, _, _}}, _repo, _name, _state) do :ok end defp write_policy_result(other, repo, name, %{ets: tid}) do cached? = !!:ets.lookup(tid, {:policy, repo, name}) print_policy_error(other, repo, name, cached?) unless cached? do raise "Stopping due to errors" end end defp print_policy_error(result, repo, name, cached?) do cached_message = if cached?, do: " (using cache instead)" Hex.Shell.error("Failed to fetch policy #{repo}/#{name} from registry#{cached_message}") missing? = missing_status?(result) unauthorized? = unauthorized_status?(result) if missing? or unauthorized? do Hex.Shell.error( "This could be because the policy does not exist, it was spelled " <> "incorrectly or you don't have permissions to it" ) if unauthorized? 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?) or Mix.debug?() do Hex.Utils.print_error_result(result) 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}" ) missing? = missing_status?(result) unauthorized? = unauthorized_status?(result) if missing? or unauthorized? 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? 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?) 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_status?({:ok, {status, _, _}}), do: status in [404] defp missing_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 -> Mix.raise("Package #{repo}/#{package} not prefetched, please report this issue") end end defp maybe_wait_policy({repo, name}, from, state, fun) do repo = repo || "hexpm" cond do {repo, name} in state.fetched_policies -> {:reply, fun.(), state} {repo, name} in state.pending_policies -> tuple = {from, fun} waiting = Map.update(state.waiting_policies, {repo, name}, [tuple], &[tuple | &1]) state = %{state | waiting_policies: waiting} {:noreply, state} true -> Mix.raise("Policy #{repo}/#{name} not prefetched, please report this issue") end end defp wait_pending(state, fun) do if MapSet.size(state.pending) == 0 and MapSet.size(state.pending_policies) == 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 policy_etag(repo, name, %{ets: tid}) do case :ets.lookup(tid, {:policy_etag, repo, name}) 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, {:advisories, repo, package, version}) :ets.delete(tid, {:published_at, 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 defp timestamp_seconds(nil), do: nil defp timestamp_seconds(%{seconds: seconds}), do: seconds end hex-2.5.1/lib/hex/remote_converger.ex000066400000000000000000000727231522402544500175560ustar00rootroot00000000000000defmodule 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_retired_warning) do Hex.Shell.error("Found retired packages, see above for details") Hex.State.put(:print_retired_warning, false) end if Hex.State.get(:print_advisory_warning) do Hex.Shell.error("Found packages with security advisories, see above for details") Hex.State.put(:print_advisory_warning, false) end 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() {:ok, policy} = Hex.Policy.load() Hex.State.put(:active_policy, policy) # 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() 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) setup_cooldown(old_lock, locked) 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( Hex.Registry.Policy, 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) print_cooldown_summary() print_policy_summary() solver_success(resolved, requests, lock, old_lock) {:error, message} -> Hex.Shell.info(message) if note = Hex.Policy.Diagnostics.failure_note(Hex.State.fetch!(:policy_filtered_versions)) do Hex.Shell.info("\n" <> note) end print_cooldown_summary() # The failure note above already lists the hidden versions, so the # summary here contributes only the active-policy header. print_policy_summary(list_filtered: false) Mix.raise("Hex dependency resolution failed") end end defp setup_cooldown(old_lock, locked) do # The global cutoff covers only the local cooldown configuration. Policy # cooldowns are per-repository restrictions enforced in Hex.Policy.Filter, # so they are not folded into this cutoff; AND composition across the two # filters yields the strictest minimum age per repository. local = Hex.State.fetch!(:cooldown) cutoff = Hex.Cooldown.build_cutoff(local) Hex.State.put(:cooldown_cutoff, cutoff) bypass = build_cooldown_bypass(old_lock, locked, cutoff) Hex.State.put(:cooldown_bypass_packages, bypass) locked_versions = build_cooldown_locked_versions(old_lock) Hex.State.put(:cooldown_locked_versions, locked_versions) Hex.State.put(:policy_locked_versions, locked_versions) Hex.State.put(:cooldown_filtered_versions, []) Hex.State.put(:policy_filtered_versions, []) end @doc false def build_cooldown_locked_versions(old_lock) do # Maps {repo, package} to the list of versions currently in the lockfile # for that package. The cooldown filter treats these as exempt: a version # the user is already running is trusted, even if cooldown would otherwise # filter it. This lets re-resolution fall back to the locked version when # no newer eligible candidate exists, instead of failing. for %{repo: repo, name: name, version: version} <- Hex.Mix.from_lock(old_lock), into: %{} do {{repo || "hexpm", name}, [to_string(version)]} end end @doc false def build_cooldown_bypass(_old_lock, _locked, :disabled), do: MapSet.new() def build_cooldown_bypass(old_lock, locked, _cutoff) do # The bypass set has three sources: # # 1. Packages in `locked` (the post-`prepare_locked/3` set): the lockfile # entry survived requirement checking and dep unlocking, so this # package is being installed from the lock — no re-resolution, no # cooldown. Matches the design's "cooldown does not apply when # proceeding from the lockfile" promise. # # 2. Packages in `old_lock` whose locked version is known-unsafe (retired # or carrying a security advisory): the user re-resolving is trying # to escape that version; cooldown must not block the escape. Walks # `old_lock` rather than `locked` because `mix deps.update foo` to # escape an unsafe foo removes foo from `locked`. # # 3. The currently-locked children of any package in (2): escaping an # unsafe parent typically requires re-resolving its dependency # subtree, and the new parent version usually wants newer versions of # those children. Mirroring `mix deps.update`'s behavior of unlocking # the children alongside the explicit target, we lift cooldown on the # same set. lock_satisfied = for %{name: name} <- locked, into: MapSet.new(), do: name MapSet.union(lock_satisfied, unsafe_lock_bypass(old_lock)) end @doc """ Set of package names whose cooldown should be lifted because the lock currently points at an unsafe version of that package, plus the names of that package's lock-children. """ def unsafe_lock_bypass(lock) do Enum.reduce(lock, MapSet.new(), fn {_app, lock_entry}, acc -> case Hex.Utils.lock(lock_entry) do %{repo: repo, name: name, version: version, deps: deps} -> if locked_version_unsafe?(repo || "hexpm", name, to_string(version)) do children = for {_app, _req, opts} <- deps || [], do: opts[:hex] acc |> MapSet.put(name) |> MapSet.union(MapSet.new(children)) else acc end nil -> acc end end) end defp locked_version_unsafe?(repo, name, version) do Registry.retired(repo, name, version) != nil or Registry.advisories(repo, name, version) not in [nil, []] end @doc false def print_cooldown_summary() do cutoff = Hex.State.fetch!(:cooldown_cutoff) entries = Hex.State.fetch!(:cooldown_filtered_versions) if summary = Hex.Cooldown.format_summary(entries, cutoff) do Hex.Shell.info(summary) 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 = resolved |> group_dependency_changes(previously_locked_versions) |> annotate_dependency_changes() Enum.each(dep_changes, fn {mod, deps} -> unless deps == [], do: print_category(mod) print_dependency_group(deps, mod) end) all_deps = Enum.flat_map(dep_changes, fn {_mod, deps} -> deps end) has_retired = Enum.any?(all_deps, fn {_dep, retired, _adv} -> retired != nil end) has_advisory = Enum.any?(all_deps, fn {_dep, _retired, adv} -> adv != [] end) if has_retired do Hex.State.put(:print_retired_warning, true) end if has_advisory do Hex.State.put(:print_advisory_warning, true) end end end defp annotate_dependency_changes(dep_changes) do ignore_advisories = Hex.State.fetch!(:ignore_advisories) ignore_retirements = Hex.State.fetch!(:ignore_retirements) Enum.map(dep_changes, fn {mod, deps} -> annotated = Enum.map(deps, fn {name, repo, _prev, version, _warning} = dep -> retired = case Registry.retired(repo, name, version) do %{} = retired -> if Hex.Ignores.retirement_ignored?(name, version, ignore_retirements) do nil else retired end nil -> nil end advisories = (Registry.advisories(repo, name, version) || []) |> Hex.Ignores.reject_ignored_advisories(ignore_advisories) {dep, retired, advisories} end) {mod, annotated} 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}, retired, advisories} -> print_status(retired, advisories, mod, name, previous_version, version, warning) end) end defp print_status(retired, advisories, mod, name, previous_version, version, warning) do version_str = version_string(mod, name, previous_version, version) retired_tag = if retired, do: [:yellow, " RETIRED!", :reset], else: [] vulnerable_tag = if advisories != [], do: [:red, " VULNERABLE!", :reset], else: [] warning_tag = if warning, do: [:red, warning, :reset], else: [] line = [version_color(mod), version_str, :reset, retired_tag, vulnerable_tag, warning_tag] Hex.Shell.info(Hex.Shell.format(line)) if retired do Hex.Shell.warn(" #{Hex.Utils.package_retirement_message(retired)}") end advisories |> :mix_hex_advisory.group_for_display() |> Enum.with_index() |> Enum.each(fn {advisory, index} -> if retired || index > 0, do: Hex.Shell.info("") Hex.Shell.info( Hex.Shell.format([" " | Hex.Utils.format_advisory_ansi(advisory, " ")]) ) end) end defp version_color(:new), do: :green defp version_color(:gt), do: :green defp version_color(:lt), do: :yellow defp version_color(:eq), do: [] defp version_string(mod, name, _previous_version, version) when mod in [:eq, :new] do " #{name} #{version}" end defp version_string(_mod, name, previous_version, version) do " #{name} #{previous_version} => #{version}" 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(_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 # hex_cli_auth ensures only one process prompts even if multiple processes # detect the expired token concurrently config = Hex.API.Client.config([]) :read |> Hex.Auth.with_api( config, fn %{api_key: api_key} when is_binary(api_key) -> :ok %{} -> {:error, :no_auth} end, optional: true, auth_inline: true ) |> case do :ok -> # Token is valid, was successfully refreshed, or user authenticated :ok {:error, {:auth_error, :auth_declined}} -> # User declined authentication Hex.Shell.warn( "Private packages will not be available. " <> "Run `mix hex.user auth` to authenticate." ) {:error, {:auth_error, _reason}} -> Hex.Shell.warn( "Authentication failed. Private packages will not be available. " <> "Run `mix hex.user auth` to authenticate." ) {:error, _reason} -> # Other errors (shouldn't happen with prompt_auth: true, but handle gracefully) :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 @doc false def print_policy_summary(opts \\ []) do case Hex.State.fetch!(:active_policy) do nil -> :ok policy -> filtered = if Keyword.get(opts, :list_filtered, true) do Hex.State.fetch!(:policy_filtered_versions) else [] end local_cooldown = Hex.State.fetch!(:cooldown) if summary = Hex.Policy.Diagnostics.resolution_summary(policy, filtered, local_cooldown) do Hex.Shell.info(summary) end end end end hex-2.5.1/lib/hex/repo.ex000066400000000000000000000262661522402544500151570ustar00rootroot00000000000000defmodule Hex.Repo do @moduledoc false @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 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 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, :undefined), Map.get(source, :oauth_exchange_url, :undefined) ) repo |> put_organization_url(source, name) |> 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 # build_url appends "/repos/" via :repo_organization. Normalize a missing # or baked-in "/repos/" URL to that form; keep a custom URL as-is. defp put_organization_url(repo, source, name) do url = Map.get(repo, :url) if url in [nil, source.url <> "/repos/#{name}"] do repo |> Map.put(:url, source.url) |> Map.put(:repo_organization, name) else repo end 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) default = default_organization(%{}, source, organization) {name, clean_repo(repo, default)} _ -> {name, Map.delete(repo, :trusted)} end 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(:undefined, 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) Hex.Auth.with_repo(config, &:mix_hex_repo.get_package(&1, package), optional: true) end def get_policy(repo, name, etag) do repo_config = get_repo(repo) config = build_hex_core_config(repo_config, repo, etag) Hex.Auth.with_repo(config, &:mix_hex_repo.get_policy(&1, name), optional: true) end def get_docs(repo, package, version) do repo_config = get_repo(repo) config = build_hex_core_config(repo_config, repo) Hex.Auth.with_repo(config, &:mix_hex_repo.get_docs(&1, package, version), optional: true) end def get_tarball(repo, package, version) do repo_config = get_repo(repo) config = build_hex_core_config(repo_config, repo) Hex.Auth.with_repo(config, &:mix_hex_repo.get_tarball(&1, package, version), optional: true) end def get_public_key(repo_config) when is_map(repo_config) do config = build_hex_core_config(repo_config, "") Hex.Auth.with_preemptive_auth(repo_config, config, &:mix_hex_repo.get_public_key/1, auth_inline: false, optional: true ) end def get_installs() do repo = get_repo("hexpm") config = build_hex_core_config(repo, "") Hex.Auth.with_repo(config, &:mix_hex_repo.get_hex_installs/1) 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) {repo_name, organization} = case split_repo_name(repo_name) do [source, organization] -> {source, organization} [name] -> {name, nil} end config = %{ Hex.API.Client.config() | repo_name: repo_name, repo_organization: Map.get(repo_config, :repo_organization, :undefined), 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, trusted: Map.get(repo_config, :trusted, false), oauth_exchange: Map.get(repo_config, :oauth_exchange, false) } if repo_config.auth_key do maybe_warn_deprecated_repo_key(repo_name, organization, repo_config) end config = case Map.fetch(repo_config, :oauth_exchange_url) do {:ok, oauth_exchange_url} -> Map.put(config, :oauth_exchange_url, oauth_exchange_url) :error -> Map.put(config, :oauth_exchange_url, config.api_url) end if etag do %{config | http_etag: etag} else config end end # Only organization repositories are deprecated here. HEX_REPOS_KEY still # authenticates the base hexpm repo and trusted mirrors (organization is nil # for those), so they must not be warned about. defp maybe_warn_deprecated_repo_key("hexpm", organization, repo_config) when is_binary(organization) do case deprecated_repo_key_source(repo_config) do :env -> if Hex.Server.should_warn?({:deprecated_repos_key, organization}) do Hex.Shell.warn(""" Authenticating to the #{organization} repository with HEX_REPOS_KEY is deprecated \ and will be removed. For development authenticate as a user with `mix hex.user auth`. For CI \ authenticate per organization with `mix hex.organization auth #{organization} --key KEY`. """) end :config -> if Hex.Server.should_warn?({:deprecated_repo_key, organization}) do Hex.Shell.warn(""" Authenticating to the #{organization} repository with a stored key is deprecated \ and will be removed. For development authenticate as a user with `mix hex.user auth`. For CI generate an \ organization key with `mix hex.organization key #{organization} generate` and pass it \ with `mix hex.organization auth #{organization} --key KEY`. """) end end end defp maybe_warn_deprecated_repo_key(_repo_name, _organization, _repo_config), do: :ok # HEX_REPOS_KEY is exposed as the hexpm source `auth_key` and inherited by # `hexpm:*` repos, so an `auth_key` equal to `repos_key` came from the # environment, while any other value was stored in the local config. defp deprecated_repo_key_source(repo_config) do repos_key = Hex.State.fetch!(:repos_key) if repos_key != nil and repo_config.auth_key == repos_key do :env else :config end end end hex-2.5.1/lib/hex/scm.ex000066400000000000000000000307071522402544500147670ustar00rootroot00000000000000defmodule 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://mix.hexdocs.pm/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.5.1/lib/hex/server.ex000066400000000000000000000032471522402544500155120ustar00rootroot00000000000000defmodule 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 should_warn?(key, name \\ @name) do GenServer.call(name, {:should_warn?, key}) 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 def handle_call({:should_warn?, key}, _from, state) do if MapSet.member?(state.warned, key) do {:reply, false, state} else {:reply, true, %{state | warned: MapSet.put(state.warned, key)}} end end defp state() do %{ warned_lock_version: false, warned_registry_version: false, warned: MapSet.new() } end end hex-2.5.1/lib/hex/shell.ex000066400000000000000000000060641522402544500153130ustar00rootroot00000000000000defmodule 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.5.1/lib/hex/shell/000077500000000000000000000000001522402544500147475ustar00rootroot00000000000000hex-2.5.1/lib/hex/shell/process.ex000066400000000000000000000031641522402544500167670ustar00rootroot00000000000000defmodule 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.5.1/lib/hex/solver.ex000066400000000000000000000060601522402544500155120ustar00rootroot00000000000000# 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.5.1/lib/hex/solver/000077500000000000000000000000001522402544500151525ustar00rootroot00000000000000hex-2.5.1/lib/hex/solver/assignment.ex000066400000000000000000000020551522402544500176620ustar00rootroot00000000000000# 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.5.1/lib/hex/solver/constraint.ex000066400000000000000000000006001522402544500176700ustar00rootroot00000000000000# 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.5.1/lib/hex/solver/constraints/000077500000000000000000000000001522402544500175215ustar00rootroot00000000000000hex-2.5.1/lib/hex/solver/constraints/empty.ex000066400000000000000000000021231522402544500212130ustar00rootroot00000000000000# 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.5.1/lib/hex/solver/constraints/impl.ex000066400000000000000000000022711522402544500210220ustar00rootroot00000000000000# 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.5.1/lib/hex/solver/constraints/range.ex000066400000000000000000000272421522402544500211620ustar00rootroot00000000000000# 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.5.1/lib/hex/solver/constraints/union.ex000066400000000000000000000117441522402544500212160ustar00rootroot00000000000000# 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.5.1/lib/hex/solver/constraints/util.ex000066400000000000000000000042751522402544500210440ustar00rootroot00000000000000# 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.5.1/lib/hex/solver/constraints/version.ex000066400000000000000000000060421522402544500215460ustar00rootroot00000000000000# 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.5.1/lib/hex/solver/failure.ex000066400000000000000000000173261522402544500171500ustar00rootroot00000000000000# 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.5.1/lib/hex/solver/incompatibility.ex000066400000000000000000000307701522402544500207170ustar00rootroot00000000000000# 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.5.1/lib/hex/solver/package_lister.ex000066400000000000000000000217731522402544500204770ustar00rootroot00000000000000# 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.5.1/lib/hex/solver/package_range.ex000066400000000000000000000021721522402544500202610ustar00rootroot00000000000000# 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.5.1/lib/hex/solver/partial_solution.ex000066400000000000000000000122351522402544500211030ustar00rootroot00000000000000# 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.5.1/lib/hex/solver/registry.ex000066400000000000000000000016141522402544500173620ustar00rootroot00000000000000# 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.5.1/lib/hex/solver/requirement.ex000066400000000000000000000203651522402544500200560ustar00rootroot00000000000000# 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.5.1/lib/hex/solver/solver.ex000066400000000000000000000340541522402544500170300ustar00rootroot00000000000000# 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.5.1/lib/hex/solver/term.ex000066400000000000000000000110231522402544500164540ustar00rootroot00000000000000# 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.5.1/lib/hex/solver/util.ex000066400000000000000000000003301522402544500164610ustar00rootroot00000000000000# 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.5.1/lib/hex/sponsor.ex000066400000000000000000000014641522402544500157060ustar00rootroot00000000000000defmodule 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.5.1/lib/hex/state.ex000066400000000000000000000252551522402544500153270ustar00rootroot00000000000000defmodule 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} }, cooldown: %{ env: ["HEX_COOLDOWN"], config: [:cooldown], default: "0d", skip_env_if_empty: true, fun: {Hex.Cooldown, :parse_config} }, cooldown_exclude_repos: %{ env: ["HEX_COOLDOWN_EXCLUDE_REPOS"], config: [:cooldown_exclude_repos], default: [], skip_env_if_empty: true, fun: {Hex.Cooldown, :parse_exclude_repos} }, policy: %{ env: ["HEX_POLICY"], config: [:policy], default: nil, fun: {Hex.Policy, :parse_config}, on_invalid: :keep }, ignore_advisories: %{ env: ["HEX_IGNORE_ADVISORIES"], config: [:ignore_advisories], default: [], skip_env_if_empty: true, fun: {Hex.Ignores, :parse_advisories} }, ignore_retirements: %{ env: ["HEX_IGNORE_RETIREMENTS"], config: [:ignore_retirements], default: [], skip_env_if_empty: true, fun: {Hex.Ignores, :parse_retirements} } } 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}, active_policy: {:computed, nil}, policy_filtered_versions: {:computed, []}, policy_locked_versions: {:computed, %{}} }) 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, spec[:skip_env_if_empty]) || maybe_load_project(project_config, spec) || maybe_load_global(global_config, spec) {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 -> if spec[:on_invalid] == :keep do # Keys that gate enforcement keep the malformed value tagged as # invalid so reads fail closed, instead of degrading to the # default behind a warning. {source, {:invalid, value}} else print_invalid_config_error(value, source) {:ok, value} = apply(module, func, [spec[:default]]) {:default, value} end 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 maybe_load_project(_config, %{config_scope: :global}), do: nil defp maybe_load_project(config, spec), do: load_project_config(config, spec[:config]) defp maybe_load_global(_config, %{config_scope: :project}), do: nil defp maybe_load_global(config, spec), do: load_global_config(config, spec[:config]) defp load_env(keys, env, skip_if_empty) do Enum.find_value(keys || [], fn key -> case Map.fetch(env, key) do {:ok, ""} when skip_if_empty == true -> nil {: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.5.1/lib/hex/stdlib.ex000066400000000000000000000005261522402544500154620ustar00rootroot00000000000000defmodule Hex.Stdlib do @moduledoc false # 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.5.1/lib/hex/tar.ex000066400000000000000000000027421522402544500147710ustar00rootroot00000000000000defmodule 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) config = :mix_hex_core.default_config() |> Map.put(:tarball_files_root, File.cwd!() |> String.to_charlist()) case :mix_hex_tarball.create(metadata, files, config) 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.5.1/lib/hex/update_checker.ex000066400000000000000000000067231522402544500171540ustar00rootroot00000000000000defmodule 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.5.1/lib/hex/utils.ex000066400000000000000000000306751522402544500153510ustar00rootroot00000000000000defmodule 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 # Unicode bidirectional formatting characters that can reorder displayed text. @bidi_controls [0x061C, 0x202A, 0x202B, 0x202C, 0x202D, 0x202E, 0x2066, 0x2067, 0x2068, 0x2069] # Escapes characters that could manipulate the terminal when printing an # untrusted string. Printable characters, newlines and tabs are kept as-is; # ANSI escapes, bidirectional overrides and other control characters are # rendered as visible escape sequences. def escape_terminal(binary) when is_binary(binary) do binary |> String.to_charlist() |> Enum.map_join(&escape_terminal_char/1) end defp escape_terminal_char(?\n), do: "\n" defp escape_terminal_char(?\t), do: "\t" defp escape_terminal_char(?\e), do: "\\e" defp escape_terminal_char(?\r), do: "\\r" defp escape_terminal_char(?\b), do: "\\b" defp escape_terminal_char(?\a), do: "\\a" defp escape_terminal_char(cp) when cp in @bidi_controls, do: unicode_escape(cp) defp escape_terminal_char(cp) when cp < 0x20, do: hex_escape(cp) defp escape_terminal_char(0x7F), do: hex_escape(0x7F) defp escape_terminal_char(cp) when cp in 0x80..0x9F, do: unicode_escape(cp) defp escape_terminal_char(cp), do: <> defp unicode_escape(cp), do: "\\u{" <> Integer.to_string(cp, 16) <> "}" defp hex_escape(cp) do "\\x" <> (cp |> Integer.to_string(16) |> String.pad_leading(2, "0")) 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://#{package_subdomain(package)}.hexdocs.pm" def hexdocs_url(organization, package), do: "https://#{organization}.hexorgs.pm/#{package}" def hexdocs_url(organization, package, version) when organization in ["hexpm", nil], do: "https://#{package_subdomain(package)}.hexdocs.pm/#{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://#{package_subdomain(package)}.hexdocs.pm/#{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://#{package_subdomain(package)}.hexdocs.pm/#{version}/#{module}.html" def hexdocs_module_url(organization, package, version, module), do: "https://#{organization}.hexorgs.pm/#{package}/#{version}/#{module}.html" # Hex package names allow underscores. RFC 1123 hostname labels and # RFC 6125 wildcard SAN matching don't, so Fastly returns 421 for # underscore subdomains under *.hexdocs.pm. Map `_` -> `-` here; the # Fastly Compute subdomain handler reverses it for the bucket lookup. defp package_subdomain(package), do: String.replace(package, "_", "-") 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 def advisory_severity(:SEVERITY_NONE), do: "NONE" def advisory_severity(:SEVERITY_LOW), do: "LOW" def advisory_severity(:SEVERITY_MEDIUM), do: "MEDIUM" def advisory_severity(:SEVERITY_HIGH), do: "HIGH" def advisory_severity(:SEVERITY_CRITICAL), do: "CRITICAL" def advisory_severity(other), do: other def advisory_severity_color(:SEVERITY_LOW), do: :yellow def advisory_severity_color(:SEVERITY_MEDIUM), do: :yellow def advisory_severity_color(:SEVERITY_HIGH), do: :red def advisory_severity_color(:SEVERITY_CRITICAL), do: [:bright, :red] def advisory_severity_color(_), do: [] def format_advisory_ansi(%{id: id, summary: summary} = advisory, line_prefix \\ "") do severity = case advisory do %{severity: s} -> [" ", advisory_severity_color(s), "(#{advisory_severity(s)})", :reset] _ -> [] end aliases = case advisory do %{aliases: [_ | _] = aliases} -> ids = Enum.map_join(aliases, ", ", &alias_id/1) ["\n", line_prefix, "aka: ", ids] _ -> [] end url = case advisory do %{html_url: url} -> ["\n", line_prefix, :underline, url, :reset] _ -> [] end [id, severity, aliases, "\n", line_prefix, summary, url] end defp alias_id(%{id: id}), do: id defp alias_id(id) when is_binary(id), do: id # 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.5.1/lib/mix/000077500000000000000000000000001522402544500136515ustar00rootroot00000000000000hex-2.5.1/lib/mix/task_description.ex000066400000000000000000000002701522402544500175530ustar00rootroot00000000000000defmodule 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.5.1/lib/mix/tasks/000077500000000000000000000000001522402544500147765ustar00rootroot00000000000000hex-2.5.1/lib/mix/tasks/hex.audit.ex000066400000000000000000000174351522402544500172370ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Audit do use Mix.Task alias Hex.Registry.Server, as: Registry @shortdoc "Shows retired Hex deps and security advisories for the current project" @moduledoc """ Shows all Hex dependencies that have been marked as retired or have security advisories. 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. Security advisories indicate known vulnerabilities in a package version. The task will display advisory details and exit with a non-zero code if any packages with advisories are found. ## Ignoring advisories and retirements Advisories that do not affect your project and retirements you cannot act on yet can be acknowledged in the `:hex` section of your `mix.exs` project configuration: def project() do [ # ... hex: [ ignore_advisories: ["CVE-2026-32686"], ignore_retirements: [:decimal, phoenix: "1.0.0"] ] ] end `ignore_advisories` lists advisory IDs; an advisory is ignored when the given ID matches its primary ID or any of its aliases, so a GHSA advisory can also be ignored by its CVE ID. `ignore_retirements` lists packages by name, optionally pinned to a single version. Ignored findings are listed in separate "Ignored" sections and do not cause a non-zero exit code. Entries that do not match anything in the lock file produce a warning so they can be cleaned up. The same configuration silences the advisory and retirement warnings printed by `mix deps.get` and `mix deps.update`. Both values can also be set with the `HEX_IGNORE_ADVISORIES` and `HEX_IGNORE_RETIREMENTS` environment variables as comma-separated lists, where retirement entries are `NAME` or `NAME@VERSION`. > 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() ignore_advisories = Hex.State.fetch!(:ignore_advisories) ignore_retirements = Hex.State.fetch!(:ignore_retirements) all_retired = retired_packages(lock) raw_advisories = advisory_packages(lock) {ignored_retired, retired} = Enum.split_with(all_retired, fn {package, version, _message} -> Hex.Ignores.retirement_ignored?(package, version, ignore_retirements) end) {ignored_advisories, advisories} = split_advisory_findings(raw_advisories, ignore_advisories) if retired == [] and advisories == [] and ignored_retired == [] and ignored_advisories == [] do Hex.Shell.info("No retired or security advisory packages found") else print_sections([ {:retired, "Retired:", retired}, {:advisories, "Advisories:", advisories}, {:retired, "Ignored retired:", ignored_retired}, {:advisories, "Ignored advisories:", ignored_advisories} ]) end warn_unused_ignores(all_retired, raw_advisories, ignore_advisories, ignore_retirements) if retired != [] or advisories != [] do Hex.Shell.info("") if retired != [], do: Hex.Shell.error("Found retired packages") if advisories != [], do: Hex.Shell.error("Found packages with security advisories") Mix.Tasks.Hex.set_exit_code(1) end end @impl true def tasks() do [ {"", "Shows retired Hex deps and security advisories 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 case Registry.retired(repo, package, version) do %{} = retired -> [{package, version, Hex.Utils.package_retirement_message(retired)}] nil -> [] end end defp retirement_status(nil), do: [] defp advisory_packages(lock) do Enum.flat_map(lock, fn {_app, lock} -> advisory_status(Hex.Utils.lock(lock)) end) end defp advisory_status(%{repo: repo, name: package, version: version}) do case Registry.advisories(repo, package, version) || [] do [] -> [] advisories -> [{package, version, advisories}] end end defp advisory_status(nil), do: [] defp split_advisory_findings(raw_advisories, ignores) do splits = Enum.map(raw_advisories, fn {package, version, advisories} -> {ignored, active} = Hex.Ignores.split_advisories(advisories, ignores) {display_findings(package, version, ignored), display_findings(package, version, active)} end) {Enum.flat_map(splits, &elem(&1, 0)), Enum.flat_map(splits, &elem(&1, 1))} end defp display_findings(package, version, advisories) do advisories |> :mix_hex_advisory.group_for_display() |> Enum.map(fn advisory -> {package, version, advisory} end) end defp print_sections(sections) do sections |> Enum.reject(fn {_type, _header, entries} -> entries == [] end) |> Enum.with_index() |> Enum.each(fn {{type, header, entries}, index} -> if index > 0, do: Hex.Shell.info("") print_section(type, header, entries) end) end defp print_section(:retired, header, entries) do Hex.Shell.info(Hex.Shell.format([:bright, header, :reset])) Enum.each(entries, fn {package, version, message} -> Hex.Shell.info(Hex.Shell.format([" #{package} #{version} - ", :yellow, message, :reset])) end) end defp print_section(:advisories, header, entries) do Hex.Shell.info(Hex.Shell.format([:bright, header, :reset])) entries |> Enum.with_index() |> Enum.each(fn {{package, version, advisory}, index} -> if index > 0, do: Hex.Shell.info("") Hex.Shell.info( Hex.Shell.format([ " #{package} #{version} - " | Hex.Utils.format_advisory_ansi(advisory, " ") ]) ) end) end defp warn_unused_ignores(all_retired, raw_advisories, ignore_advisories, ignore_retirements) do all_advisories = Enum.flat_map(raw_advisories, fn {_package, _version, advisories} -> advisories end) advisory_warnings = ignore_advisories |> Enum.reject(fn id -> Enum.any?(all_advisories, &Hex.Ignores.advisory_matches?(&1, id)) end) |> Enum.map(fn id -> "ignore_advisories entry \"#{id}\"#{ignore_source(:ignore_advisories)} does not " <> "match any advisory for the locked dependencies and can be removed" end) retirement_warnings = ignore_retirements |> Enum.reject(fn entry -> Enum.any?(all_retired, fn {package, version, _message} -> Hex.Ignores.retirement_matches?(package, version, entry) end) end) |> Enum.map(fn {name, version} -> "ignore_retirements entry #{format_retirement_entry(name, version)}" <> "#{ignore_source(:ignore_retirements)} does not match any retired locked " <> "dependency and can be removed" end) case advisory_warnings ++ retirement_warnings do [] -> :ok warnings -> Hex.Shell.info("") Enum.each(warnings, &Hex.Shell.warn/1) end end defp ignore_source(key) do case Hex.State.fetch_source!(key) do {:env, var} -> " (set in environment variable #{var})" {:project_config, _key} -> " (set in mix.exs)" {:global_config, _key} -> " (set in global config)" _other -> "" end end defp format_retirement_entry(name, nil), do: name defp format_retirement_entry(name, version), do: "#{name} #{version}" end hex-2.5.1/lib/mix/tasks/hex.build.ex000066400000000000000000000311141522402544500172160ustar00rootroot00000000000000defmodule 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] || [] files = files |> expand_paths(File.cwd!()) |> Enum.reject(fn path -> Path.basename(path) == ".DS_Store" or 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.5.1/lib/mix/tasks/hex.config.ex000066400000000000000000000251131522402544500173660ustar00rootroot00000000000000defmodule 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`) * `cooldown` - Minimum age a package release must have to be considered during dependency resolution, e.g. `"7d"`, `"2w"`, `"1mo"`. Releases published more recently are filtered out of the candidate set when resolving dependencies. Does not apply to installs from an existing lockfile. Can be overridden by setting the environment variable `HEX_COOLDOWN` (Default: `"0d"` — no cooldown). See https://hex.pm/docs/dependency-policies for the full guide. * `cooldown_exclude_repos` - List of repository names for which `cooldown` does not apply, e.g. `["hexpm:myorg"]`. Useful when an organization publishes hotfixes to its own repository and wants to consume them without cooldown delay. Can be overridden by setting the environment variable `HEX_COOLDOWN_EXCLUDE_REPOS` to a comma-separated list (Default: `[]`). See https://hex.pm/docs/dependency-policies for the full guide. * `ignore_advisories` - List of security advisory IDs to acknowledge, e.g. `["CVE-2026-32686"]`. Matching advisories are not reported by `mix hex.audit`, `mix deps.get`, and `mix deps.update`. An ID matches an advisory's primary ID or any of its aliases. Can be overridden by setting `HEX_IGNORE_ADVISORIES` as a comma-separated list. (Default: `[]`) * `ignore_retirements` - List of packages whose retirement to acknowledge, as atoms with an optional pinned version, e.g. `[:decimal, phoenix: "1.0.0"]`. Matching retirements are not reported by `mix hex.audit`, `mix deps.get`, and `mix deps.update`. Can be overridden by setting `HEX_IGNORE_RETIREMENTS` as a comma-separated list of `NAME` or `NAME@VERSION` entries. (Default: `[]`) * `policy` - The policy this Hex client honors at resolution time, as a `REPO/NAME` pair, e.g. `hexpm:myorg/strict-prod`. The `mix.exs` `:hex` block also accepts `policy: [org: "ORG", name: "NAME"]` for a hexpm organization, or `policy: [repo: "REPO", name: "NAME"]` for any other configured repository. Can be overridden by setting the environment variable `HEX_POLICY`, where an empty value disables the configured policy. See https://hex.pm/docs/dependency-policies for the full guide. 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.5.1/lib/mix/tasks/hex.docs.ex000066400000000000000000000273071522402544500170600ustar00rootroot00000000000000defmodule 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 * `--page "Some.Module"`, `--page guide` - Open a specified 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`, `--format md` - When opening documentation offline, use this flag to open the epub/md formatted version """ @behaviour Hex.Mix.TaskDescription @elixir_apps ~w(eex elixir ex_unit iex logger mix) # TODO: Remove :module switch @switches [ module: :string, page: :string, organization: :string, latest: :boolean, format: :string ] @impl true def run(args) do Hex.start() {opts, args} = OptionParser.parse!(args, strict: @switches) opts = opts |> normalize_module_opt() |> Keyword.put(: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 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 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") page = Keyword.get(opts, :page) 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" -> file_location(default_path, fallback_path, "#{page || "index"}.html", organization) "md" when page == nil -> file_location(default_path, fallback_path, "llms.txt", organization) "md" -> file_location(default_path, fallback_path, "#{page}.md", organization) end end defp file_location(default_path, fallback_path, page, organization) do default_path = Path.join([default_path, page]) fallback_path = Path.join([fallback_path, page]) 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 page = opts[:page] do Hex.Utils.hexdocs_module_url(opts[:organization], name, page) else Hex.Utils.hexdocs_url(opts[:organization], name) end end defp get_docs_url([name, version], opts) do if page = opts[:page] do Hex.Utils.hexdocs_module_url(opts[:organization], name, version, page) else Hex.Utils.hexdocs_url(opts[:organization], name, version) end end # TODO: Remove :module support defp normalize_module_opt(opts) do case Keyword.pop(opts, :module) do {nil, opts} -> opts {module, opts} -> Hex.Shell.error("--module is deprecated, use --page instead") Keyword.put_new(opts, :page, module) 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.5.1/lib/mix/tasks/hex.ex000066400000000000000000000210021522402544500161130ustar00rootroot00000000000000defmodule 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 @doc false def auth_device(_opts \\ []) do # Clean up any existing authentication revoke_existing_oauth_tokens() revoke_and_cleanup_old_api_keys() prompt_user = fn verification_uri, user_code -> Hex.Shell.info("To authenticate, visit: #{verification_uri}") 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("") Hex.Shell.info("Waiting for authentication...") :ok end case Hex.API.OAuth.device_auth_flow("api repositories", prompt_user, open_browser: true) do {:ok, tokens} -> store_token(tokens) {:error, :timeout} -> Hex.Shell.error("Device code expired. Please try again.") :error {:error, {:access_denied, _status, _body}} -> Hex.Shell.error("Authentication was denied.") :error {:error, {:device_auth_failed, status, body}} -> Hex.Shell.error("Device authorization failed (#{status}): #{inspect(body)}") :error {:error, {:poll_failed, status, body}} -> Hex.Shell.error("Authentication failed (#{status}): #{inspect(body)}") :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(tokens) do Hex.OAuth.store_token(tokens) Hex.Shell.info("You are authenticated!") {:ok, tokens} end @doc false def generate_organization_key(organization_name, key_name, permissions) do case Hex.API.Key.Organization.new(organization_name, key_name, permissions) do {:ok, {201, _, body}} -> {:ok, body["secret"]} {:error, {:auth_error, _}} -> Mix.shell().error("Generation of key failed: authentication required") :error 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 @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.5.1/lib/mix/tasks/hex.info.ex000066400000000000000000000176661522402544500170720ustar00rootroot00000000000000defmodule 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 case Hex.API.Package.get(organization, package) 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 case Hex.API.Release.get(organization, package, version) 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.5.1/lib/mix/tasks/hex.organization.ex000066400000000000000000000226061522402544500206310ustar00rootroot00000000000000defmodule 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 CI and build systems where access to an organization is needed without authorizing a user. For CI, generate an organization key with `mix hex.organization key ORGANIZATION generate` and pass it with `mix hex.organization auth ORGANIZATION --key KEY`. > #### Deprecation {: .warning} > > Authorizing an organization without `--key` is deprecated and will be removed. > It creates a user key tied to your account; use `mix hex.user auth` for > development instead, or pass a pre-generated organization key with `--key` for CI. 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. Omitting `--key` is deprecated; authenticate as a user with `mix hex.user auth` instead * `--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 Hex.Shell.warn(""" Authorizing an organization without --key is deprecated and will be removed. For development authenticate as a user instead, which gives you access to \ all your organizations: mix hex.user auth For CI generate an organization key and pass it with --key: mix hex.organization key #{organization} generate mix hex.organization auth #{organization} --key KEY """) key_name = Mix.Tasks.Hex.repository_key_name(organization, opts[:key_name]) permissions = [%{"domain" => "repository", "resource" => organization}] case Hex.API.Key.new(key_name, permissions) 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 Hex.Shell.info("Revoking all keys...") case Hex.API.Key.Organization.delete_all(organization) 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 Hex.Shell.info("Revoking key #{key}...") case Hex.API.Key.Organization.delete(organization, key) 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 case Hex.API.Key.Organization.get(organization) 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.5.1/lib/mix/tasks/hex.outdated.ex000066400000000000000000000366401522402544500177410ustar00rootroot00000000000000defmodule 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. If a `:cooldown` is configured (see `mix hex.config`) and the latest version of a dependency falls within the cooldown window, the row is annotated with `(cooldown)` and the version is listed under "Versions in cooldown". Cooldown-held updates do not contribute to the non-zero exit code — `mix deps.update` would not pick them up until they age out of the window. 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) * `--json` - output the result as JSON instead of a human readable table. Requires OTP 27 or later. """ @behaviour Hex.Mix.TaskDescription @switches [ all: :boolean, pre: :boolean, within_requirements: :boolean, sort: :string, only: :string, json: :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) Registry.open() lock = Mix.Dep.Lock.read() lock |> Hex.Mix.packages_from_lock() |> Hex.Registry.Server.prefetch() lock |> process_lockfile(args, opts) |> display_outdated(args, opts) |> set_exit_code(args, opts) 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 process_lockfile(lock, args, opts) do deps = Hex.Mix.top_level_deps() deps |> requested_dep_names(lock, args, opts) |> Enum.sort() |> get_versions(deps, lock, opts[:pre]) |> filter_by_only(args, opts) end defp requested_dep_names(_deps, lock, [app], _opts) do app = String.to_atom(app) if is_nil(Hex.Utils.lock(lock[app])) do Mix.raise("Dependency #{app} not locked as a Hex package") end [app] end defp requested_dep_names(deps, lock, [], opts) do if opts[:all], do: Map.keys(lock), else: Map.keys(deps) end defp requested_dep_names(_deps, _lock, _args, _opts) do Mix.raise(""" Invalid arguments, expected: mix hex.outdated [APP] """) end defp display_outdated(versions, args, opts) do if opts[:json] do versions |> Enum.map(&cast_version_map/1) |> encode_json!() |> Hex.Shell.info() else display_table(versions, args, opts) end versions end defp display_table( [{_package, _dep_only, current, latest, requirements, outdated?, cooldown}], [_app], _opts ) do 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]) maybe_print_single_cooldown(latest, cooldown) end defp display_table(versions, _args, opts) do 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) maybe_print_cooldown_legend(versions) 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]) end end defp maybe_print_single_cooldown(_latest, nil), do: :ok defp maybe_print_single_cooldown(latest, %{eligible_on: eligible_on}) do days = Date.diff(eligible_on, Date.utc_today()) Hex.Shell.info("\nVersion #{latest} is in cooldown — eligible #{eligible_on} (#{days} days)") end defp maybe_print_cooldown_legend(versions) do entries = Enum.flat_map(versions, fn {package, _, _, latest, _, _, %{eligible_on: eligible_on}} -> [{package, latest, eligible_on}] _ -> [] end) case entries do [] -> :ok entries -> today = Date.utc_today() Hex.Shell.info("") Hex.Shell.info("Versions in cooldown:") Enum.each(entries, fn {package, version, eligible_on} -> days = Date.diff(eligible_on, today) Hex.Shell.info(" #{package} #{version} — eligible #{eligible_on} (#{days} days)") end) end end defp set_exit_code(versions, args, opts) do outdated_versions = Enum.filter(versions, fn {_p, _o, _l, _la, _r, outdated?, cooldown} -> outdated? and is_nil(cooldown) end) if outdated_versions != [] and exit_with_error?(outdated_versions, args, opts) do Mix.Tasks.Hex.set_exit_code(1) end end defp exit_with_error?(_outdated, [_app], _opts), do: true defp exit_with_error?(outdated, [], opts) do !opts[:within_requirements] || any_possible_to_update?(outdated) 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 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, [_app], _opts), do: versions 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, _reqs, _outdated?, _c} -> 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 cutoff = Hex.Cooldown.build_cutoff() bypass = if cutoff == :disabled, do: MapSet.new(), else: Hex.RemoteConverger.unsafe_lock_bypass(lock) 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) outdated? = Version.compare(lock_version, latest_version) == :lt requirements = deps_requirements ++ lock_requirements dep_only = get_dep_only(deps, name) cooldown = cond do not outdated? -> nil not req_matches?(requirements, latest_version) -> nil MapSet.member?(bypass, package) -> nil true -> cooldown_info(repo, package, latest_version, cutoff) end [ {Atom.to_string(name), dep_only, lock_version, latest_version, requirements, outdated?, cooldown} ] _ -> [] end end) end defp cooldown_info(_repo, _package, _version, :disabled), do: nil defp cooldown_info(repo, package, version, cutoff) do if Hex.Cooldown.repo_excluded?(repo) do nil else published_at = Registry.published_at(repo, package, version) if Hex.Cooldown.eligible?(published_at, cutoff) do nil else %{ published_at: published_at, eligible_on: Hex.Cooldown.eligible_on(published_at, cutoff) } 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, outdated?, cooldown}) do latest_color = if outdated?, do: :red, else: :green req_matches? = req_matches?(requirements, latest) base_status = case {outdated?, req_matches?} do {true, true} -> [:yellow, "Update possible"] {true, false} -> [:red, "Update not possible"] {false, _} -> [:green, "Up-to-date"] end status = if cooldown, do: base_status ++ [" (cooldown)"], else: base_status [ [:bright, package], dep_only, lock, [latest_color, latest], status ] end defp build_diff_link({package, _dep_only, lock, latest, requirements, outdated?, _cooldown}) do req_matches? = req_matches?(requirements, latest) 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 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, _outdated?, _cooldown} -> req_matches?(requirements, latest) end) end defp req_matches?(requirements, latest) do Enum.all?(requirements, fn [_source, req_version] -> version_match?(latest, req_version) end) 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 defp cast_version_map({package, dep_only, lock, latest, requirements, outdated?, cooldown}) do %{ package: package, only: dep_only, lock_version: lock, latest_version: latest, requirements: Enum.map(requirements, fn [source, req_version] -> %{ source: source, requirement: req_version, up_to_date: version_match?(latest, req_version) } end), outdated: outdated?, cooldown: cast_cooldown_map(cooldown) } end defp cast_cooldown_map(nil), do: nil defp cast_cooldown_map(%{published_at: published_at, eligible_on: eligible_on}) do %{ published_at: published_at |> DateTime.from_unix!() |> DateTime.to_iso8601(), eligible_on: Date.to_iso8601(eligible_on) } end defp encode_json!(term) do if Code.ensure_loaded?(:json) do term |> :json.encode() |> IO.iodata_to_binary() else Mix.raise(":json module is not available, upgrade OTP to use this feature") end end end hex-2.5.1/lib/mix/tasks/hex.owner.ex000066400000000000000000000125761522402544500172640ustar00rootroot00000000000000defmodule 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 Hex.Shell.info("Adding owner #{owner} with ownership level #{level} to #{package}") case Hex.API.Package.Owner.add(organization, package, owner, level, false) 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 Hex.Shell.info("Transferring ownership to #{owner} for #{package}") case Hex.API.Package.Owner.add(organization, package, owner, "full", true) 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 Hex.Shell.info("Removing owner #{owner} from #{package}") case Hex.API.Package.Owner.delete(organization, package, owner) 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 case Hex.API.Package.Owner.get(organization, package) 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 case Hex.API.User.me() 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.5.1/lib/mix/tasks/hex.package.ex000066400000000000000000000325511522402544500175200ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Package do use Mix.Task alias Hex.Registry.Server, as: Registry @shortdoc "Fetches, diffs, or searches 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. ## Search packages $ mix hex.package search PACKAGE Search for package names. If you are authenticated, it will additionally search all organizations you are member of. 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 ["search", package] -> package_search(package, opts) ["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 search PACKAGE 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 [ {"search PACKAGE", "Search package names"}, {"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 package_search(package, opts) do Hex.start() opts |> search_repo() |> Hex.API.Package.search(package) |> lookup_packages() end defp search_repo(opts) do cond do organization = opts[:organization] -> organization repo = opts[:repo] -> repo true -> nil end 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_release(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_release(package["releases"]), package["html_url"] || package["url"] ] end) Mix.Tasks.Hex.print_table( ["Package", "Description", "Version", "URL"], values ) end defp latest_stable_release(releases) do %{"version" => version} = Enum.find( releases, %{"version" => nil}, &(Version.parse!(&1["version"]).pre == []) ) 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 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.5.1/lib/mix/tasks/hex.policy.ex000066400000000000000000000161071522402544500174230ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Policy do use Mix.Task alias Hex.Policy.{Diagnostics, Filter} @shortdoc "Inspects the active Hex dependency policy" @moduledoc """ Shows the active Hex policy and explains why specific versions are blocked. $ mix hex.policy show $ mix hex.policy why PACKAGE ## Commands * `show` — Summarize the active policy: visibility and the per-repository restrictions (cooldown, advisory, retirement) and the package overrides, plus the effective cooldown. * `why PACKAGE` — Walk every version of the named package in the registry, classify each against the active policy, and print a per-version table of status and the reasons it is blocked. ## Opting in A project opts into a single policy. It is configured like any other Hex setting, with the usual precedence (`HEX_POLICY` env var, then `mix.exs`, then `mix hex.config`): * `mix.exs` `:hex` block, with `org:` for a hexpm organization or `repo:` for any configured repo: defp project() do [hex: [policy: [org: "myorg", name: "strict-prod"]]] end * `HEX_POLICY` env var (a `REPO/NAME` pair). An empty value disables the configured policy for the invocation: $ HEX_POLICY=hexpm:myorg/strict-prod mix deps.get * `mix hex.config`: $ mix hex.config policy hexpm:myorg/strict-prod See https://hex.pm/docs/dependency-policies for the full guide. """ @behaviour Hex.Mix.TaskDescription @impl true def run(args) do Hex.start() case args do ["show"] -> show() ["why", package] -> why(package) _ -> Mix.raise(""" Invalid arguments, expected one of: mix hex.policy show mix hex.policy why PACKAGE """) end end @impl true def tasks() do [ {"show", "Shows the active policy"}, {"why PACKAGE", "Shows which versions of PACKAGE are blocked by the active policy"} ] end defp show() do case Hex.Policy.active() do {:ok, nil} -> Hex.Shell.info("No active policy configured.") {:ok, policy} -> render_show(policy) end end defp render_show(policy) do Hex.Shell.info([ "Active policy: ", [:cyan, "#{policy.repository}/#{policy.name}"], " ", visibility_tag(policy.visibility), "\n" ]) render_repositories(policy) Hex.Shell.info("") local = Hex.State.fetch!(:cooldown) case Diagnostics.effective_cooldown(policy, local) do nil -> :ok {source, duration} -> Hex.Shell.info([ "Effective cooldown: ", [:cyan, duration], " (#{cooldown_source(source)})" ]) end end defp render_repositories(p) do case Map.get(p, :repositories, []) do [] -> Hex.Shell.info(" (no repositories configured)") repositories -> Enum.each(repositories, &render_repository/1) end end defp render_repository(rp) do restriction = Map.get(rp, :restriction) overrides = Map.get(rp, :overrides, []) Hex.Shell.info([" ", [:bright, "#{rp.repository}:"]]) Hex.Shell.info([" Cooldown: ", cooldown_label(restriction)]) Hex.Shell.info([" Advisory rule: ", advisory_label(restriction)]) Hex.Shell.info([" Retirement rule: ", retirement_label(restriction)]) render_overrides(overrides) end defp render_overrides([]) do Hex.Shell.info([" Overrides: ", [:faint, "(none)"]]) end defp render_overrides(overrides) do Hex.Shell.info([" Overrides:"]) Enum.each(overrides, fn override -> Hex.Shell.info([" ", override_label(override)]) end) end defp override_label(%{action: action, ref: ref}) do package = Map.get(ref, :package) package_text = case Map.get(ref, :requirement) do requirement when is_binary(requirement) and requirement != "" -> "#{package} #{requirement}" _ -> "#{package}" end [package_text, " ", override_action_label(action)] end defp override_action_label(:OVERRIDE_ACTION_ALLOW), do: [:green, "ALLOW"] defp override_action_label(:OVERRIDE_ACTION_DENY), do: [:red, "DENY"] defp override_action_label(other), do: [to_string(other)] defp why(arg) do {repo, package} = parse_package_arg(arg) case Hex.Policy.active() do {:ok, nil} -> Hex.Shell.info("No active policy configured.") {:ok, policy} -> Hex.Registry.Server.open() Hex.Registry.Server.prefetch([{repo, package}]) case Hex.Registry.Server.versions(repo, package) do {:ok, versions} -> render_why(repo, package, versions, policy) :error -> Mix.raise("No package with name #{package} in registry") end end end defp parse_package_arg(arg) do case String.split(arg, "/", parts: 2) do [package] when byte_size(package) > 0 -> {"hexpm", package} [repo, package] when byte_size(repo) > 0 and byte_size(package) > 0 -> {repo, package} _ -> Mix.raise("Invalid package argument: #{inspect(arg)}; expected PACKAGE or REPO/PACKAGE") end end defp render_why(repo, package, versions, policy) do Hex.Shell.info("Versions of #{inspect(package)} (#{length(versions)}):") Hex.Shell.info("") rows = Enum.map(versions, fn v -> version = to_string(v) candidate = Filter.candidate_from_registry(repo, package, version) case Filter.classify(policy, candidate) do :allowed -> [version, [:green, "ALLOWED"], ""] {:blocked, reasons} -> reason_cell = reasons |> Enum.uniq() |> Enum.map(&[Diagnostics.reason_color(&1), Diagnostics.format_reason(&1)]) |> Enum.intersperse(", ") [version, [:red, "BLOCKED"], reason_cell] end end) Mix.Tasks.Hex.print_table(["Version", "Status", "Blocked by"], rows) end defp cooldown_source(:local), do: "local" defp cooldown_source({repo, name}), do: "#{repo}/#{name}" defp visibility_tag(:VISIBILITY_PUBLIC), do: [:green, "[public]"] defp visibility_tag(:VISIBILITY_PRIVATE), do: [:yellow, "[private]"] defp visibility_tag(other), do: ["[", to_string(other), "]"] defp cooldown_label(%{cooldown: duration}) when is_binary(duration), do: [:cyan, duration] defp cooldown_label(_restriction), do: [:faint, "(none)"] defp advisory_label(%{advisory_min_severity: :SEVERITY_NONE}), do: [:red, "block any advisory"] defp advisory_label(%{advisory_min_severity: severity}) when not is_nil(severity), do: [:red, "block ≥ #{Hex.Utils.advisory_severity(severity)}"] defp advisory_label(_restriction), do: [:faint, "(disabled)"] defp retirement_label(%{retirement_reasons: reasons}) when is_list(reasons) and reasons != [] do text = reasons |> Enum.map(fn r -> r |> Hex.Utils.package_retirement_reason() |> String.upcase() end) |> Enum.join(", ") [:yellow, text] end defp retirement_label(_restriction), do: [:faint, "(disabled)"] end hex-2.5.1/lib/mix/tasks/hex.publish.ex000066400000000000000000000371351522402544500175760ustar00rootroot00000000000000defmodule 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://my_package.hexdocs.pm/1.0.0`, `https://my_package.hexdocs.pm` 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 -> revert_package(build, organization, revert_version) ["docs"] when revert -> revert_docs(build, organization, revert_version) [] when revert -> revert_package(build, organization, revert_version) ["package"] -> case proceed_with_owner(build, organization, opts) do {:ok, owner} -> Hex.Shell.info("Publishing package...") case create_release(build, organization, opts) do :ok -> transfer_owner(build, owner, opts) _ -> Mix.Tasks.Hex.set_exit_code(1) end :error -> :ok end ["docs"] -> docs_task() create_docs(build, organization, 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() Hex.Shell.info("Publishing package...") case create_release(build, organization, opts) do :ok -> Hex.Shell.info("Publishing docs...") create_docs(build, organization, opts) transfer_owner(build, owner, opts) _ -> Mix.Tasks.Hex.set_exit_code(1) end :error -> :ok end end defp create_docs(build, organization, 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, 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 organizations = user_organizations() 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 do case Hex.API.User.me() do {:ok, {200, _header, body}} -> Enum.map(body["organizations"], & &1["name"]) {:error, {:auth_error, :auth_declined}} -> Mix.raise("No authenticated user found. Run `mix hex.user auth`") other -> Hex.Utils.print_error_result(other) [] end end defp public_organization?(organization), do: organization in [nil, "hexpm"] defp transfer_owner(_build, nil, _opts) do :ok end defp transfer_owner(build, owner, 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) 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) do name = build.meta.name case Hex.API.Release.delete(organization, name, version) 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) do name = build.meta.name case Hex.API.ReleaseDocs.delete(organization, name, version) 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, progress?) do progress = progress_fun(progress?, byte_size(tarball)) case Hex.API.ReleaseDocs.publish(organization, name, version, tarball, [], 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, 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, opts) end end defp send_release(tarball, checksum, organization, 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, [], 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) 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.5.1/lib/mix/tasks/hex.registry.ex000066400000000000000000000164061522402544500177760ustar00rootroot00000000000000defmodule 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.5.1/lib/mix/tasks/hex.repo.ex000066400000000000000000000203121522402544500170620ustar00rootroot00000000000000defmodule 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 Hex.Stdlib.ensure_application!(:ssh) public_key |> :mix_hex_repo.fingerprint() |> 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}} -> Hex.Stdlib.ensure_application!(:ssh) if :mix_hex_repo.fingerprint_equal(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.5.1/lib/mix/tasks/hex.retire.ex000066400000000000000000000061751522402544500174220ustar00rootroot00000000000000defmodule 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 body = %{reason: reason, message: message_option(opts[:message])} case Hex.API.Release.retire(organization, package, version, body) 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 case Hex.API.Release.unretire(organization, package, version) 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.5.1/lib/mix/tasks/hex.search.ex000066400000000000000000000174241522402544500173740ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.Search do use Mix.Task @shortdoc "Open and perform documentation search" @search_url "https://search.hexdocs.pm/" @stdlib_apps [:elixir, :iex, :logger, :ex_unit, :eex, :mix] @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 $ mix hex.search --packages foo,bar You may also pass command line flags, to execute searches via the command line, according to the modes below. For searching package names, see `mix hex.package search`. ## Docs search Pass a single positional argument to search docs and print results directly to the terminal. $ mix hex.search "ecto changeset" $ mix hex.search "ecto changeset" --packages ecto,ecto_sql $ mix hex.search "ecto changeset" --limit 25 ### Options * `--packages PACKAGE1,PACKAGE2,...` - Restrict docs search to an explicit package list * `--limit NUMBER` - Limit terminal docs search results (default: 10) * `--print-url` - Print the docs URL instead of opening it in the browser """ @behaviour Hex.Mix.TaskDescription @switches [ limit: :integer, packages: :string, stdlib: :boolean, print_url: :boolean ] @impl true def run(args) do {opts, args} = OptionParser.parse!(args, strict: @switches) case args do [] -> hexdocs_search(opts) [query] -> docs_query_search(query, opts) _ -> invalid_args!() end end @impl true def tasks() do [ {"", "Opens up hexdocs.pm with your dependencies"}, {"--packages PACKAGE1,PACKAGE2,...", "Opens up hexdocs.pm with explicit packages"}, {"QUERY", "Searches docs and prints results"}, {"QUERY --packages PACKAGE1,PACKAGE2,...", "Searches docs within explicit packages"}, {"QUERY --limit NUMBER", "Searches docs with a custom result limit"}, {"QUERY --no-stdlib", "Searches docs without stdlib packages"} ] end defp hexdocs_search(opts) do if opts[:limit] do invalid_args!() end # Hexdocs accepts package1,package2 and # it will automatically fetch the latest packages = case opts[:packages] do nil -> project_packages(opts) packages -> parse_packages!(packages) end |> 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 docs_query_search(query, opts) do if opts[:print_url] do invalid_args!() end validate_limit!(opts[:limit]) # Typesense expects exact project-vsn pairs packages = case opts[:packages] do nil -> opts |> project_packages() |> Enum.map(&String.replace(&1, ":", "-")) packages -> packages |> parse_packages!() |> Enum.map(&add_version_to_package!/1) end url = @search_url <> "?" <> URI.encode_query( %{q: query, query_by: "doc,title", filter_by: "package:=[#{Enum.join(packages, ",")}]"} |> maybe_put_limit(opts[:limit]) ) case search_http_module().request(:get, url, %{}, nil, %{}) do {:ok, {200, _headers, body}} -> body |> decode_json!() |> print_results() {:ok, {status, _headers, body}} -> Mix.raise("Docs search failed with HTTP status #{status}: #{inspect(body)}") {:error, reason} -> Mix.raise("Docs search request failed: #{inspect(reason)}") end end defp latest_stable(releases) do %{"version" => version} = Enum.find( releases, %{"version" => nil}, &(Version.parse!(&1["version"]).pre == []) ) version end defp project_packages(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 Enum.map(@stdlib_apps, &"#{&1}:#{System.version()}") else [] end Enum.sort(deps ++ stdlib) end defp parse_packages!(packages) do packages = packages |> String.split(",", trim: true) |> Enum.map(&String.trim/1) |> Enum.reject(&(&1 == "")) if packages == [] do Mix.raise("Expected --packages to include at least one package name") end packages end defp add_version_to_package!(package) when package in ["elixir", "iex", "logger", "ex_unit", "eex", "mix"] do "#{package}-#{System.version()}" end defp add_version_to_package!(package) do Hex.start() case Hex.API.Package.get("hexpm", package) do {:ok, {200, _headers, %{"releases" => releases}}} -> case latest_stable(releases) do nil -> Mix.raise("Package #{package} has no stable release for docs search") version -> "#{package}-#{version}" end {:ok, {status, _headers, body}} -> Mix.raise( "Failed to resolve package #{package} for docs search, got HTTP status #{status}: #{inspect(body)}" ) {:error, reason} -> Mix.raise("Failed to resolve package #{package} for docs search: #{inspect(reason)}") end end defp decode_json!(body) do if Code.ensure_loaded?(:json) do try do :json.decode(body) rescue error in ErlangError -> Mix.raise("Docs search returned invalid JSON: #{inspect(error.original)}") end else Mix.raise(":json module is not available, upgrade OTP to use this feature") end end defp print_results(%{"found" => 0}) do Hex.Shell.info("No results found") end defp print_results(%{"found" => _found, "hits" => hits}) do total = Integer.to_string(length(hits)) hits |> Enum.with_index(1) |> Enum.each(fn {hit, index} -> %{ "document" => %{ "doc" => doc, "package" => package, "ref" => ref, "title" => title } } = hit Hex.Shell.info([ :bright, ["# ", title, " (", Integer.to_string(index), "/", total, ")"], :reset, ["\n", document_url(package, ref), "\n\n"], [strip_html_comments(doc), "\n\n"] ]) end) end defp document_url(package, ref) do case :binary.split(package, "-") do [name, version] -> "https://#{name}.hexdocs.pm/#{version}/#{ref}" _ -> Mix.raise("Unexpected package search result format: #{inspect(package)}") end end defp strip_html_comments(doc) do case :binary.split(doc, "") do [_comment] -> before [_comment, after_comment] -> before <> strip_html_comments(after_comment) end end end defp maybe_put_limit(params, nil), do: params defp maybe_put_limit(params, limit), do: Map.put(params, :per_page, limit) defp validate_limit!(nil), do: :ok defp validate_limit!(limit) when is_integer(limit) and limit > 0 and limit <= 250, do: :ok defp validate_limit!(limit) do Mix.raise("Expected --limit to be between 1 and 250, got: #{inspect(limit)}") end defp search_http_module do Application.get_env(:hex, :search_http_module, Hex.HTTP) end defp invalid_args! do Mix.raise(""" Invalid arguments, expected: mix hex.search mix hex.search --packages PACKAGE1,PACKAGE2,... mix hex.search QUERY mix hex.search QUERY --packages PACKAGE1,PACKAGE2,... mix hex.search QUERY --limit NUMBER """) end end hex-2.5.1/lib/mix/tasks/hex.sponsor.ex000066400000000000000000000031771522402544500176320ustar00rootroot00000000000000defmodule 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.5.1/lib/mix/tasks/hex.user.ex000066400000000000000000000037541522402544500171060ustar00rootroot00000000000000defmodule 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 case Hex.API.User.me() 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.5.1/mix.exs000066400000000000000000000074371522402544500136370ustar00rootroot00000000000000defmodule Hex.MixProject do use Mix.Project @version "2.5.1" 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.5.1/mix.lock000066400000000000000000000066761522402544500137740ustar00rootroot00000000000000%{ "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.5.1/scripts/000077500000000000000000000000001522402544500137755ustar00rootroot00000000000000hex-2.5.1/scripts/docs_config.exs000077500000000000000000000017251522402544500170030ustar00rootroot00000000000000#!/usr/bin/env elixir # # Usage: git tag | docs_config.exs PROJECT [MIN_VERSION] # # Reads version tags on stdin and prints the docs_config.js that HexDocs serves # to populate the version dropdown. For unmanaged packages such as hex, HexDocs # uses the docs_config.js bundled in the docs tarball rather than generating one, # so this file must be included at the root of the tarball. [project | rest] = System.argv() min_version = List.first(rest) || "0.0.0" nodes = IO.read(:stdio, :eof) |> String.split(~r/\s+/, trim: true) |> Enum.filter(&String.starts_with?(&1, "v")) |> Enum.map(&String.trim_leading(&1, "v")) |> Enum.filter(&match?({:ok, _}, Version.parse(&1))) |> Enum.filter(&(Version.compare(&1, min_version) in [:gt, :eq])) |> Enum.sort({:desc, Version}) |> Enum.map_join(", ", fn version -> ~s({"version":"v#{version}", "url":"https://#{String.replace(project, "_", "-")}.hexdocs.pm/#{version}"}) end) IO.puts("var versionNodes = [#{nodes}];") hex-2.5.1/scripts/hex_logo.png000066400000000000000000000313601522402544500163120ustar00rootroot00000000000000PNG  IHDR@@!}:PLTEW̙`Ϟƛ˜>a|ׯڶ}aʹT^_ɮrԨԖ߿iѢ#׋ւmnvvIISSuLqy8QϘ_w^JsJl]f䋏@|)r 5KfWKKvSɼYs_n{ek)WҮLvǃ͐ڭӞ+ڑ9C6EhpP*|8FPMD;m,mFHׁ#R-qDbC3e>/2 (y:+H RmF+A,-Ar2!Db 5K2=9C [2!-4%H TGq M#hS nHhX7qb"qDb xfBSb@ŎSA2n-1Db ȸJb2my^b@Ǿ*Ej*>A3$H $tB$'nZ)QDbw,cJ5Hh6LhJ\Y"1Pk&=A$π0qī "nrl\"1PshΉg p[5Sze?93^ "1g@_DuqzLhSZ!!HoQsQDHL I$Q*ʼP-{Y{ڟ= _WTQ6?J8G-PTxfDڧW%)2 BCf-P^ּ (1 =; S.e@L&\J ΀84CA 7%Sn  q!G> 0sp^I0 H%Fߨ&pNǰ.Snڃ=NCqNǰ>)7kRbLFC¡:ѡȀru 1pN0JLxۿ”c01#P]V)#b1QM:@1:Tc%ƄPa4zaZȀ%F$g %Fc)WĔ;!Pbd@L:Pba4r챎aPb\*%wu J )W0J Cu:Pbd@ W!G(1&\SxPbd@1#nc)Wġ:ȀrI0PbLյ×Pl8)WpZȀr[P ʀ8Tc8)W0J )W0J WЄ%%t ǁ#P] {=(1&\uCï1p13ϳu J CugA?d\oȀdҢ﨨;nIu}%FĔcHqx3(1.9`(1̡:CJ WPǐQ:\Ȁti=ZPbd@[u )1ʉwˍ:Sn,=Cu:2Snbu )12 X>y&(1)yj-P:@tGSjAǐhu )1)P#)ׄP!%&NM:ȀrGǐ\CJ d@bQwTȀDcH H:CJ d@ʜrI0Sn,?{(1I1]CJ \rCJ L7S !%& d@b8{cH HYp#Ȁ8TcHqNwTL: cfcHA)Sw&RbLOCJ Rbd@Mo#`ʕDr@1z@t )12 CJ cHq1Ęp!%pAǐ#ht )12 O2!%Fchu< zJ Dǐ#1Ęp/#1Մ!Pbd@PCJ 1Ȁ ;%FcHRbL)!.%FRb:SnA_9%&)oFǐ#ht ;umcc}eaasss;s67V7Ve@cE%fuce֠W5.< _PbN7Vz%3mmpӠc%%fm}7u nt ;Ĭ? Ȥ1zwKEǰ JV<[Y)%rW{]|sCdR` +l݄[;<ܾ^r/; /<<:%ܻ0cX.G<U~GHŬ[3޼?j+?x,~ cX$cϾ9t Kda,|~Gb(1McXG{= :ޏ.ot dpǏ t c&1,_ g^v8 G"nAX&o:+\%3K 2wId L*tMeԺ@:] 6Ht h+;HQ %6Vg1%-UT}Hǰ6B[~Zy1L@܋DԷKrc֙H̒-Ёa%&> R95i) 2:aG{ L?06_d@t ڷXTa n,Nt^ k:i$f .PUcX7,aww@xR'!OcNdNG9.##.(1t vAk:2d8 1,탱w%fH:2B|s(-ÒyG1,ɱ3it ۊЎa&czȞK0i[hoEPǐ$[11D0LPWbt u t un]ɀ!ꆹ-K0:r- Oֻ@\1tk(7JPu0CC@! ::n95:+CC t 3Z3ꆅ11D0LPu0\,DPWbt u Q7 1̦t% ::4ꆭX0GsnXCC {Z :C;ÜIG/F\ǐ@fpk=Egt 11,ϒ2kCC cca!a::⡺ dOPu97IV@t u Q7|APWbt u Q7 11 Dp8 3衺2(7LPu0CC S.##cܟ11D0LPu0CCcyiCPu_1֍E1ٶS7,!a::Y {.un\)!11Dp dՈ'u):U@!m}@weu q\ FCC t u Q7 11D0LPu0CC t o cc0CC @ôV͗+111D0LPWbt u Q7 11QnaϦ:11$Bt =E0CC t {uoVEǰO:o91Dp> d݈n؂[: )n7d:tP5JL/ :91DݰY󖥎! 4U_#~ּ@~cܶ\D-[L yJ-[V3\!e|B2J ct+HEa dϏ@\oAՖܣ2Dܰq1\;$W̯r ܔ'it3{0BBxkK %ޔ5KY93錜ǿn\W5 H%v-`B۸hJ&v 6uHN8c1 {[ڳ=ٳzVSWd-Y@l. P .”?fx7ԢijG-hGa枃A%+@M\$0ZBP]EEM1r`2w )nIe2}.}!*@|g.S\Tǿ 3wndf>A2Iބ)7-{z8 īڈ.Ib/ '+/'[4wcb3`3/@_Țcbw d˱n1.Q3/Ǽ`oȦ`o13/=΅e3/m.zhf^;6\'m3/%nC3/Ǧ`7o3/ǤqKN7L vjE-]ACN!HI r:@y943r݆0_A~˲:{h;L@#/ɂQ;mȺPQ.ۑ[0QF8*Jqq[ چ6r\gzʚܢF'kppS {jZ$a$;mȺQ1@f_o@|Zn庅Y +F^mjs#m*X[*` RKV^N 騍]ïJ ꨕ0ֳF%q+Aӛ}Im(^1,Q#W7D\RC6nGR!t 3VnHJ c|q%F.%uCuCf DP{@ Gt@ Q7T7DNuDݰ2a(wX: #uCuCB0C `nn\{6nï!t ,nnl #W7J C@ 1xd:+1dmHR7,ˆ6: Ǹ 6] ]LCup# $=quCϩ|!t .GaQ7T7a@ c-uá2nchz:Q7T7A9!nnlB0[ 1,)yWb o8bE]I@nXXes;G iWbj´aCnnquCcH{nX&LSǰQ7T7a@ ad,^ 'qn~nnlB0[ ꆵaLPǰqQ7T7a@ êanX#XanX# u &uCG1H ĨS kŦa=k/uzqVu "Wb cؗ@ c-uÚ1ncY 鎑{Nǐ@rr%FPǐ@vP7#6cX ˮĔefĜ~qn,R\2\ _ \ e9ju# $m۸!J!nnêJa He;jEY^DP:Q7T7Hm:Ȩ:%Wb cX@1#/q]dn#!t nnlb> nncH l=quCCQ7T7a_0ruCCQ7'c={1@ 9:}nBpEHR7T7tK 7ru0naMn6=TcXSB0[ 1!t - uâLNضannOe DCu1!t nٷa`$uCuCGnXW6,\ǰvQ7T7a@ )Z侺aMYo8 9Q7T7ħ\t%u&ncX/"%LB0[ ]@ c-uÆ1:OEHl:nnt $t u $uCuC`DPݰ |B}DݰzÚDݰyNǰ&Q7T71D2Ja#f u DP.+1:K :@Ҹ!Ȕ\uCCQ7)] eaNǰ8m< B. [ fL/2n(^Sf:6q&Pnh6}ѮX 鞍+Ԑ9!k5䰇%uÆgp $mxYV AE(@])̘R.N :,zo]٠JL2+4$uCs¬ D0?!uCe@2JLy6GQ7 e߃|9s~?m@]1}1H8ʍǨ7y Q7Cxo~P7 o=ǬY!c/rȊ+1հD G*~}R}pJw?3"q%&.wwY:.{\yn1;fn)3KR;zDK D0$u%rÛt@ C3ZxVZX:}630BQ7Kgrҵ7~A7>:1*f~b1w%Gn ՗a#\|`(7.p%@ <dlWb D0/n\)*uhpS\ (nX138JnMIG Oq%*:+1 D0 p]T ꆡx$\i@F GS@\qJ )n8p6_bWb"I/{Uzqxx.kR*5PHFc jCB<"U њ{$lmy!*?}LbP%_IaZ2 &1@ pMbrn8B N&1 qg/b~TpMb"|@ p'Ipd|III }@^r0Q7t5Q7t5 I %/ `@^W_ʀ0T_:N* 돫̫2LVn1~pJϾILVg:з uCv:K $nb lˀ49N&1i:OK&1I#uCݯn(bC_I }[5uC/9Џ]UIID$rU/Nw p1 ʀ`sUOYjɀ$1T7c1T7$R I̥=zR7Rߐ4ת'uC/9`s_ p1 m$K&1:N&1:N&1:zɁn1T7$&cY_ CuC\e{) IL֟V=zKUzn%T=zaN^b8::V0 ʀ`x$FP $&'ϫ I̠C'\LbNS7ILb2q'uC'\Lb:9P $&aO S bcجzʀ:IP`cnK&1iVz_A$&qOIaupMb`bIa0h>֙IIL^T% 1j?Jv4Ugc&1:&1d@Lb`"3&bcl΃Ia('\Cu11T7Lbt x$&mWsʍ $ W0IaɀdgM06/9 $FP0IaҮʀmNI᰼`cn8'\CuIaNթ114 %bcnœCuqILnv %CuC'\.rSǰp11 uq$F0P '\uCCu!d@Lbt PU2ʀ`cn%u2ߥIIa`c1s¥eCu>8R$G3uC/9 ;F0rIa[u<BE'1|3_֑Қż@qG|1zɁ'1:e8K8pc &1:e:GK\ꦎa!>#ˀ~1T7K51T7 1eKw ``7p11 s$濌`BՒ11T7]CuqZ1ic_ມ.>1,n8%,xɁ'1Nc%#uC/9$W8J*C#Q @u XIļg/9apI}?8pe"'1pGbFVu 0ےyk:%"X1T7t%I̺yɁ Ĵu GmGCuC/9jEǰp`Yǰ NĘ@ $AOG^7aR&1G.t Vu Нv%5CuC-eƏ$FPK?i)'\bLbt @#BɀaCsg]P P;:^r Iɀ.iOb5/9.t ˰1~Ob6^u ґOn:jEPK[1,ô21T7t%Ԏ7'\"1T7Vu н&BkF0NZ1,|hnsܼ2 Dcn%1T77dZ1,iKDcX'\Lbp %.cn1~'1:N1,ѡ. ("tg@K:2 Obt KDcn%1T7!Ԋ cXkNDzɁP;:NObt @I=d/8apjH0isŏ6)'2 d4pMz8 2 XI.nzS^r Ȋ)ێ`ٟaU~ڏ5I@M4]8$?!K춗hޮa!d@`Se8)Ob_Ɣ!#Ĭ6KI@-'RĴYˀ$江gN@V4@Jq¥q˞)ŇC/9$n.pIl#a/ļɱ. (ǵc'\<(p$ƿYK^ ObXAp=i\uoxɁ&W[1'1Y~>{ R;8?&1oo7q%$pK4?8XIk^C'\9p-Jw fYKi@3_OKtp(B}ՏHZ2 ڻa =7]*^DkLNPûiw.Զ7fҡ}*`cӕπ`"ּٜT-7S0!^*OclF9>!E2jSp&T=FHeGQ̩s>c j41`QS%sZ{SG9K8IENDB`hex-2.5.1/scripts/release_docs.sh000077500000000000000000000072351522402544500167730ustar00rootroot00000000000000#!/usr/bin/env bash # Build and publish Hex's own documentation to https://hex.hexdocs.pm. # # Hex is distributed as an archive rather than a registry package, so its docs # can't be published with `mix hex.publish docs`. Instead they are built with # ExDoc and the tarball is uploaded to the docs store, exactly as # elixir-lang/elixir's release workflow publishes the standard-library docs. # # Usage: # # $ ./scripts/release_docs.sh HEX_VERSION # # Pass --dry-run to build the docs tarball without uploading: # # $ ./scripts/release_docs.sh --dry-run HEX_VERSION # # Requires docker, aws, and the fastly CLI, with their credentials configured, # as for release_hex.sh. set -e -u -o pipefail # Toolchain used to build the docs. Keep roughly in step with release_hex.sh. elixir_image="hexpm/elixir:1.18.4-erlang-27.3.4.1-ubuntu-jammy-20250530" ex_doc_version="0.40.3" # Oldest Hex version listed in the docs version dropdown. docs_min_version="0.17.0" # Bucket fronted by the repo Fastly service; docs live under its docs/ prefix. s3_bucket="${HEX_AWS_S3_BUCKET:-s3.hex.pm}" # Fastly service for repo.hex.pm, which serves the docs/ tarballs. fastly_repo_service="hiohGRsDbo44nWc8jzdHaH" function main { dry_run=false local args=() for arg in "$@"; do if [ "$arg" = "--dry-run" ]; then dry_run=true else args+=("$arg") fi done if [ ${#args[@]} -ne 1 ]; then echo "usage: release_docs.sh [--dry-run] HEX_VERSION" >&2 exit 1 fi local version="${args[0]#v}" local ref="v${version}" local tarball="hex-${version}.tar.gz" local surrogate_key="docs/hex-${version}" build_docs "${ref}" "${version}" tar -czf "${tarball}" -C doc . if [ "${dry_run}" = true ]; then echo "Dry run complete: ${tarball}" exit 0 fi aws s3 cp "${tarball}" "s3://${s3_bucket}/docs/${tarball}" \ --cache-control "public,max-age=3600" \ --metadata "{\"surrogate-key\":\"${surrogate_key}\",\"surrogate-control\":\"public,max-age=604800\"}" purge "${surrogate_key}" echo "Published https://hex.hexdocs.pm/${version}" } # Build hex's docs for $ref into ./doc using ExDoc inside a pinned container. # The release is cloned fresh at its tag (with full history so all tags are # available for the version dropdown) so the docs match the published code and # the build never touches the working tree. # # A docs_config.js listing every release is written into the docs: HexDocs reads # it from the tarball for unmanaged packages like hex instead of generating one. function build_docs { local ref="$1" version="$2" rm -rf doc docker run --rm -v "$(pwd)":/out "${elixir_image}" bash -euxc " apt-get update && apt-get install -y git mix local.hex --force mix local.rebar --force mix escript.install --force hex ex_doc ${ex_doc_version} git clone --quiet --branch ${ref} https://github.com/hexpm/hex.git /hex cd /hex mix deps.get mix compile \"\$HOME/.mix/escripts/ex_doc\" Hex ${version} _build/dev/lib/hex/ebin \ -m Mix.Tasks.Hex \ -u https://github.com/hexpm/hex \ --source-ref ${ref} \ --logo /out/scripts/hex_logo.png git tag | elixir /out/scripts/docs_config.exs hex ${docs_min_version} > doc/docs_config.js rm -rf /out/doc cp -R doc /out/doc " } # Purge the docs tarball from the repo Fastly service so repo.hex.pm/docs serves # the new build; HexDocs purges its own serving cache after ingesting it. Run # twice with a pause to beat any in-flight requests, like release_hex.sh. function purge { local key="$1" fastly service purge --service-id "${fastly_repo_service}" --key "${key}" sleep 5 fastly service purge --service-id "${fastly_repo_service}" --key "${key}" } main "$@" hex-2.5.1/scripts/release_hex.sh000077500000000000000000000121141522402544500166170ustar00rootroot00000000000000#!/usr/bin/env bash # Usage: # # $ ELIXIR_PEM=/path/to/elixir.pem \ # FASTLY_API_TOKEN=... \ # 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 # Elixir v1.20 build ${hex_version} 27.3.4.14 1.20.2 1.20.0 noble-20260610 build ${hex_version} 28.5.0.3 1.20.2 1.20.0 noble-20260610 build ${hex_version} 29.0.3 1.20.2 1.20.0 noble-20260610 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 fastly service purge --service-id GbeDoh1ZO7MEM3zut4K2fR --key installs sleep 5 fastly service purge --service-id GbeDoh1ZO7MEM3zut4K2fR --key 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 archive_ez=hex-${hex_version}.ez 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 ${archive_ez}" mv "${archive_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}" } main $* hex-2.5.1/scripts/release_rebar.sh000077500000000000000000000103121522402544500171240ustar00rootroot00000000000000#!/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.5.1/scripts/vendor_hex_core.sh000077500000000000000000000044231522402544500175100ustar00rootroot00000000000000#!/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_advisory.erl \ hex_cli_auth.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_policy.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_policy \ hex_pb_signed \ hex_pb_versions \ hex_registry \ hex_tarball \ hex_safe_binary_to_term \ hex_http \ hex_repo \ hex_api \ hex_advisory \ hex_cli_auth \ 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.5.1/scripts/vendor_hex_solver.sh000077500000000000000000000015761522402544500201000ustar00rootroot00000000000000#!/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.5.1/src/000077500000000000000000000000001522402544500130755ustar00rootroot00000000000000hex-2.5.1/src/mix_hex_advisory.erl000066400000000000000000000070711522402544500171670ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), do not edit manually %% @doc %% Display-time deduplication of security advisories. %% %% Multiple advisory sources (EEF, GHSA, NVD, ...) can publish the same %% vulnerability under different identifiers and cross-reference each other %% via the `aliases' field. `group_for_display/1' groups such advisories %% and picks a deterministic primary so callers can render one entry per %% vulnerability. -module(mix_hex_advisory). -export([group_for_display/1]). -type advisory() :: map(). -type group() :: map(). -spec group_for_display([advisory()]) -> [group()]. group_for_display(Advisories) -> %% Preserve input order of first member per group. {GroupOrder, GroupsByKey} = lists:foldl( fun(Adv, {Order, Map}) -> Key = group_key(Adv), case maps:is_key(Key, Map) of true -> Existing = maps:get(Key, Map), {Order, Map#{Key := Existing ++ [Adv]}}; false -> {Order ++ [Key], Map#{Key => [Adv]}} end end, {[], #{}}, Advisories ), [merge_group(maps:get(Key, GroupsByKey)) || Key <- GroupOrder]. %%==================================================================== %% Grouping %%==================================================================== %% Key is the first CVE-prefixed identifier across {id, aliases}, else id. group_key(#{id := Id} = Adv) -> Ids = [Id | maps:get(aliases, Adv, [])], case lists:dropwhile(fun(I) -> not is_cve(I) end, Ids) of [Cve | _] -> Cve; [] -> Id end. is_cve(<<"CVE-", _/binary>>) -> true; is_cve(_) -> false. %%==================================================================== %% Merging %%==================================================================== merge_group(Advisories) -> Primary = pick_primary(Advisories), Rest = [A || A <- Advisories, maps:get(id, A) =/= maps:get(id, Primary)], Primary#{aliases => display_aliases(Primary, [Primary | Rest])}. pick_primary(Advisories) -> [Primary | _] = lists:sort( fun(A, B) -> source_key(A) =< source_key(B) end, Advisories ), Primary. source_key(#{id := Id}) -> {source_priority(Id), Id}. source_priority(<<"EEF-", _/binary>>) -> 0; source_priority(<<"GHSA-", _/binary>>) -> 1; source_priority(<<"NVD-", _/binary>>) -> 2; source_priority(_) -> 3. %%==================================================================== %% Aliases %%==================================================================== display_aliases(Primary, Advisories) -> PrimaryId = maps:get(id, Primary), AdvisoryIds = sets:from_list([maps:get(id, A) || A <- Advisories]), AllIds = lists:flatmap(fun identifiers/1, Advisories), Unique = uniq(AllIds), [ #{ id => Id, url => alias_url(Id, AdvisoryIds) } || Id <- Unique, Id =/= PrimaryId ]. identifiers(Advisory) -> [maps:get(id, Advisory) | maps:get(aliases, Advisory, [])]. alias_url(Id, AdvisoryIds) -> case sets:is_element(Id, AdvisoryIds) of true -> <<"https://osv.dev/vulnerability/", Id/binary>>; false -> undefined end. uniq(List) -> {_, Out} = lists:foldl( fun(X, {Seen, Acc}) -> case sets:is_element(X, Seen) of true -> {Seen, Acc}; false -> {sets:add_element(X, Seen), Acc ++ [X]} end end, {sets:new(), []}, List ), Out. hex-2.5.1/src/mix_hex_api.erl000066400000000000000000000135171522402544500161020ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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() :: #{binary() => value()} | [#{binary() => value()}]. -type value() :: binary() | boolean() | nil | number() | [value()] | #{binary() => value()}. %% @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.5.1/src/mix_hex_api_auth.erl000066400000000000000000000012511522402544500171130ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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.5.1/src/mix_hex_api_key.erl000066400000000000000000000127611522402544500167520ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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.5.1/src/mix_hex_api_oauth.erl000066400000000000000000000333301522402544500172750ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), do not edit manually %% @doc %% Hex HTTP API - OAuth. -module(mix_hex_api_oauth). -export([ device_authorization/3, device_authorization/4, device_auth_flow/4, device_auth_flow/5, poll_device_token/3, refresh_token/3, revoke_token/3, client_credentials_token/4, client_credentials_token/5 ]). -export_type([oauth_tokens/0, device_auth_error/0]). -type oauth_tokens() :: #{ access_token := binary(), refresh_token => binary() | undefined, expires_at := integer() }. -type device_auth_error() :: timeout | {access_denied, Status :: non_neg_integer(), Body :: term()} | {device_auth_failed, Status :: non_neg_integer(), Body :: term()} | {poll_failed, Status :: non_neg_integer(), Body :: term()} | term(). %% @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 (defaults to the machine's hostname) %% %% 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">>, Name = case proplists:get_value(name, Opts) of undefined -> get_hostname(); N -> N end, Params = #{ <<"client_id">> => ClientId, <<"scope">> => Scope, <<"name">> => Name }, mix_hex_api:post(Config, Path, Params). %% @doc %% Runs the complete OAuth device authorization flow. %% %% @see device_auth_flow/5 %% @end -spec device_auth_flow( mix_hex_core:config(), ClientId :: binary(), Scope :: binary(), PromptUser :: fun((VerificationUri :: binary(), UserCode :: binary()) -> ok) ) -> {ok, oauth_tokens()} | {error, device_auth_error()}. device_auth_flow(Config, ClientId, Scope, PromptUser) -> device_auth_flow(Config, ClientId, Scope, PromptUser, []). %% @doc %% Runs the complete OAuth device authorization flow with options. %% %% This function handles the entire device authorization flow: %% 1. Requests a device code from the server %% 2. Calls `PromptUser' callback with the verification URI and user code %% 3. Optionally opens the browser for the user (when `open_browser' is true) %% 4. Polls the token endpoint until authorization completes or times out %% %% The `PromptUser' callback is responsible for displaying the verification URI %% and user code to the user (e.g., printing to console). %% %% Options: %% * `name' - A name to identify the token (defaults to the machine's hostname) %% * `open_browser' - When `true', automatically opens the browser %% to the verification URI. When `false' (default), only the callback is invoked. %% %% Returns: %% - `{ok, Tokens}' - Authorization successful, returns access token and optional refresh token %% - `{error, timeout}' - Device code expired before user completed authorization %% - `{error, {access_denied, Status, Body}}' - User denied the authorization request %% - `{error, {device_auth_failed, Status, Body}}' - Initial device authorization request failed %% - `{error, {poll_failed, Status, Body}}' - Unexpected error during polling %% %% Examples: %% %% ``` %% 1> Config = mix_hex_core:default_config(). %% 2> PromptUser = fun(Uri, Code) -> %% io:format("Visit ~s and enter code: ~s~n", [Uri, Code]) %% end. %% 3> mix_hex_api_oauth:device_auth_flow(Config, <<"cli">>, <<"api:write">>, PromptUser). %% {ok, #{ %% access_token => <<"...">>, %% refresh_token => <<"...">>, %% expires_at => 1234567890 %% }} %% ''' %% @end -spec device_auth_flow( mix_hex_core:config(), ClientId :: binary(), Scope :: binary(), PromptUser :: fun((VerificationUri :: binary(), UserCode :: binary()) -> ok), proplists:proplist() ) -> {ok, oauth_tokens()} | {error, device_auth_error()}. device_auth_flow(Config, ClientId, Scope, PromptUser, Opts) -> case device_authorization(Config, ClientId, Scope, Opts) of {ok, {200, _, DeviceResponse}} when is_map(DeviceResponse) -> #{ <<"device_code">> := DeviceCode, <<"user_code">> := UserCode, <<"verification_uri_complete">> := VerificationUri, <<"expires_in">> := ExpiresIn, <<"interval">> := IntervalSeconds } = DeviceResponse, ok = PromptUser(VerificationUri, UserCode), OpenBrowser = proplists:get_value(open_browser, Opts, false), case OpenBrowser of true -> open_browser(VerificationUri); false -> ok end, ExpiresAt = erlang:system_time(second) + ExpiresIn, poll_for_token_loop(Config, ClientId, DeviceCode, IntervalSeconds, ExpiresAt); {ok, {Status, _, Body}} -> {error, {device_auth_failed, Status, Body}}; {error, Reason} -> {error, Reason} end. %% @private poll_for_token_loop(Config, ClientId, DeviceCode, IntervalSeconds, ExpiresAt) -> Now = erlang:system_time(second), case Now >= ExpiresAt of true -> {error, timeout}; false -> timer:sleep(IntervalSeconds * 1000), case poll_device_token(Config, ClientId, DeviceCode) of {ok, {200, _, TokenResponse}} when is_map(TokenResponse) -> #{ <<"access_token">> := AccessToken, <<"expires_in">> := ExpiresIn } = TokenResponse, RefreshToken = maps:get(<<"refresh_token">>, TokenResponse, undefined), TokenExpiresAt = erlang:system_time(second) + ExpiresIn, {ok, #{ access_token => AccessToken, refresh_token => RefreshToken, expires_at => TokenExpiresAt }}; {ok, {400, _, #{<<"error">> := <<"authorization_pending">>}}} -> poll_for_token_loop(Config, ClientId, DeviceCode, IntervalSeconds, ExpiresAt); {ok, {400, _, #{<<"error">> := <<"slow_down">>}}} -> %% Increase polling interval as requested by server poll_for_token_loop( Config, ClientId, DeviceCode, IntervalSeconds + 5, ExpiresAt ); {ok, {400, _, #{<<"error">> := <<"expired_token">>}}} -> {error, timeout}; {ok, {Status, _, #{<<"error">> := <<"access_denied">>} = Body}} -> {error, {access_denied, Status, Body}}; {ok, {Status, _, Body}} -> {error, {poll_failed, Status, Body}}; {error, Reason} -> {error, Reason} end end. %% @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). %%==================================================================== %% Internal functions %%==================================================================== %% @private %% Open a URL in the default browser. %% Uses platform-specific commands: open (macOS), xdg-open (Linux), start (Windows). -spec open_browser(binary()) -> ok | {error, browser_not_found}. open_browser(Url) when is_binary(Url) -> ok = ensure_valid_http_url(Url), UrlStr = binary_to_list(Url), {Cmd, Args} = case os:type() of {unix, darwin} -> {"open", [UrlStr]}; {unix, _} -> {"xdg-open", [UrlStr]}; {win32, _} -> {"cmd", ["/c", "start", "", UrlStr]} end, case os:find_executable(Cmd) of false -> {error, browser_not_found}; Executable -> open_port({spawn_executable, Executable}, [{args, Args}]), ok end. %% @private %% Validates that a URL uses http:// or https:// scheme. -spec ensure_valid_http_url(binary()) -> ok. ensure_valid_http_url(Url) when is_binary(Url) -> case uri_string:parse(Url) of #{scheme := <<"https">>} -> ok; #{scheme := <<"http">>} -> ok; _ -> throw({invalid_url, Url}) end. %% @private %% Get the hostname of the current machine. -spec get_hostname() -> binary(). get_hostname() -> {ok, Hostname} = inet:gethostname(), list_to_binary(Hostname). hex-2.5.1/src/mix_hex_api_organization.erl000066400000000000000000000036601522402544500206640ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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.5.1/src/mix_hex_api_organization_member.erl000066400000000000000000000071211522402544500222070ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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.5.1/src/mix_hex_api_package.erl000066400000000000000000000032371522402544500175530ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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.5.1/src/mix_hex_api_package_owner.erl000066400000000000000000000070571522402544500207710ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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.5.1/src/mix_hex_api_release.erl000066400000000000000000000161171522402544500176010ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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.5.1/src/mix_hex_api_short_url.erl000066400000000000000000000011551522402544500201760ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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.5.1/src/mix_hex_api_user.erl000066400000000000000000000056671522402544500171470ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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.5.1/src/mix_hex_cli_auth.erl000066400000000000000000000751161522402544500171240ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), do not edit manually %% @doc %% Authentication handling with callback functions for build-tool-specific operations. %% %% This module provides generic authentication handling that allows both rebar3 %% and Elixir Hex (and future build tools) to share the common auth logic while %% customizing prompting, persistence, and configuration retrieval. %% %% == Callbacks == %% %% Callbacks are provided via the `cli_auth_callbacks' key in the config map. %% All callbacks below are required unless marked optional: %% %% ``` %% #{ %% %% Auth configuration for a specific repo %% get_auth_config => fun((RepoName :: binary()) -> %% #{api_key => binary(), %% auth_key => binary(), %% oauth_exchange => boolean(), %% oauth_exchange_url => binary()} | undefined), %% %% %% Global OAuth tokens - storage and retrieval %% get_oauth_tokens => fun(() -> {ok, #{access_token := binary(), %% refresh_token => binary(), %% expires_at := integer()}} | error), %% persist_oauth_tokens => fun((Scope :: global | binary(), %% AccessToken :: binary(), %% RefreshToken :: binary() | undefined, %% ExpiresAt :: integer()) -> ok), %% %% %% Invalidate the stored global OAuth token after it expired and could %% %% not be refreshed (optional). Lets the build tool drop the unusable %% %% token so concurrent and subsequent callers stop retrying the doomed %% %% refresh, and warn the user. Invoked at most once per resolution, while %% %% holding the token-refresh lock. %% clear_oauth_tokens => fun(() -> ok), %% %% %% User interaction %% prompt_otp => fun((Message :: binary()) -> {ok, OtpCode :: binary()} | cancelled), %% should_authenticate => fun((Reason :: no_credentials | token_refresh_failed) -> boolean()), %% %% %% OAuth client configuration %% get_client_id => fun(() -> binary()) %% } %% ''' %% %% == Auth Resolution Order == %% %% For API calls: %%
    %%
  1. Per-repo `api_key' from config (with optional OAuth exchange for hex.pm)
  2. %%
  3. Parent repo `api_key' (for "hexpm:org" organizations)
  4. %%
  5. Global OAuth token (refreshed if expired)
  6. %%
  7. Device auth flow (for write operations only)
  8. %%
%% %% For repo calls: %%
    %%
  1. Per-repo `auth_key' with optional OAuth exchange (default true for hex.pm)
  2. %%
  3. Parent repo `auth_key'
  4. %%
  5. Global OAuth token
  6. %%
%% %% == OAuth Exchange == %% %% For hex.pm URLs, `api_key' and `auth_key' are exchanged for short-lived OAuth %% tokens via the client credentials grant. This behavior can be controlled per-repo %% via the `oauth_exchange' option in the repo config (defaults to `true' for hex.pm). %% %% == Auth Context == %% %% Internally, authentication resolution tracks context via `auth_context()': %%
    %%
  • `source' - Where the credentials came from (`env', `config', or `oauth')
  • %%
  • `has_refresh_token' - Whether token refresh is possible on 401
  • %%
%% %% == Token Format == %% %% OAuth access tokens are automatically prefixed with `<<"Bearer ">>' when used %% as `api_key' or `repo_key' in the config. -module(mix_hex_cli_auth). -export([ with_api/3, with_api/4, with_repo/2, with_repo/3, resolve_api_auth/2, resolve_repo_auth/1 ]). -export_type([ callbacks/0, permission/0, auth_error/0, auth_context/0, repo_auth_config/0, auth_prompt_reason/0, opts/0 ]). %% 5 minute buffer before expiry -define(EXPIRY_BUFFER_SECONDS, 300). %% Maximum OTP retry attempts -define(MAX_OTP_RETRIES, 3). -type permission() :: read | write. -type callbacks() :: #{ get_auth_config := fun((RepoName :: binary()) -> repo_auth_config() | undefined), get_oauth_tokens := fun(() -> {ok, oauth_tokens()} | error), persist_oauth_tokens := fun( ( Scope :: global | binary(), AccessToken :: binary(), RefreshToken :: binary() | undefined, ExpiresAt :: integer() ) -> ok ), clear_oauth_tokens => fun(() -> ok), prompt_otp := fun((Message :: binary()) -> {ok, OtpCode :: binary()} | cancelled), should_authenticate := fun((Reason :: auth_prompt_reason()) -> boolean()), get_client_id := fun(() -> binary()) }. -type auth_prompt_reason() :: no_credentials | token_refresh_failed. -type repo_auth_config() :: #{ api_key => binary(), repo_key => binary(), auth_key => binary(), oauth_token => oauth_tokens() }. -type oauth_tokens() :: #{ access_token := binary(), refresh_token => binary(), expires_at := integer() }. -type auth_error() :: {auth_error, no_credentials} | {auth_error, auth_declined} | {auth_error, otp_cancelled} | {auth_error, otp_max_retries} | {auth_error, token_refresh_failed} | {auth_error, device_auth_timeout} | {auth_error, device_auth_denied} | {auth_error, oauth_exchange_failed} | {auth_error, term()}. -type auth_context() :: #{ source => env | config | oauth, has_refresh_token => boolean() }. -type opts() :: [ {optional, boolean()} | {auth_inline, boolean()} | {oauth_open_browser, boolean()} ]. %%==================================================================== %% API functions %%==================================================================== %% @doc %% Execute a function with API authentication. %% %% Equivalent to `with_api(Permission, Config, Fun, [])'. %% %% @see with_api/4 -spec with_api(permission(), mix_hex_core:config(), fun((mix_hex_core:config()) -> Result)) -> Result | {error, auth_error()} when Result :: term(). with_api(Permission, BaseConfig, Fun) -> with_api(Permission, BaseConfig, Fun, []). %% @doc %% Execute a function with API authentication. %% %% Resolves credentials in this order: %%
    %%
  1. Per-repo `api_key' from config (with optional OAuth exchange for hex.pm)
  2. %%
  3. Parent repo `api_key' (for "hexpm:org" organizations)
  4. %%
  5. Global OAuth token (refreshed if expired)
  6. %%
  7. Device auth flow (when `should_authenticate' callback returns true)
  8. %%
%% %% On 401 responses, handles OTP prompts and token refresh automatically. %% %% The repository name is taken from the config (`repo_name' or `repo_organization'). %% %% Callbacks are taken from the `cli_auth_callbacks' key in the config map. %% %% Options: %%
    %%
  • `optional' - When `true', if no credentials are found, executes the function %% without authentication first. If the server returns 401, triggers auth %% (respecting `auth_inline'). When `false' (default), missing credentials %% immediately triggers the `should_authenticate' callback.
  • %%
  • `auth_inline' - When `true' (default), prompts the user via `should_authenticate' %% callback when authentication is needed. When `false', returns %% `{error, {auth_error, no_credentials}}' instead of prompting.
  • %%
  • `oauth_open_browser' - When `true' (default), automatically opens the browser %% during device auth flow. When `false', only prints the URL for the user.
  • %%
%% %% Example: %% ``` %% mix_hex_cli_auth:with_api(write, Config, fun(C) -> %% mix_hex_api_release:publish(C, Tarball) %% end, [{optional, false}, {auth_inline, true}]). %% ''' -spec with_api( permission(), mix_hex_core:config(), fun((mix_hex_core:config()) -> Result), opts() ) -> Result | {error, auth_error()} when Result :: term(). with_api(Permission, BaseConfig, Fun, Opts) -> Optional = proplists:get_value(optional, Opts, false), AuthInline = proplists:get_value(auth_inline, Opts, true), case resolve_api_auth(Permission, BaseConfig) of {ok, ApiKey, AuthContext} -> Config = BaseConfig#{api_key => ApiKey}, execute_with_retry(Config, Fun, AuthContext, 0, undefined, Opts); {error, no_auth} when Optional =:= true -> %% Auth is optional, try without credentials first execute_optional_with_retry(BaseConfig, Fun, Opts); {error, no_auth} when AuthInline =:= true -> %% No auth found, ask user if they want to authenticate maybe_authenticate_and_retry(BaseConfig, Fun, no_credentials, Opts); {error, no_auth} -> %% auth_inline is false, just return error {error, {auth_error, no_credentials}}; {error, {auth_error, token_refresh_failed}} when Optional =:= true -> %% Token refresh failed but auth is optional, fall back to no credentials execute_optional_with_retry(BaseConfig, Fun, Opts); {error, _} = Error -> Error end. %% @doc %% Execute a function with repository authentication. %% %% Equivalent to `with_repo(Config, Fun, [])'. %% %% @see with_repo/3 -spec with_repo(mix_hex_core:config(), fun((mix_hex_core:config()) -> Result)) -> Result | {error, auth_error()} when Result :: term(). with_repo(BaseConfig, Fun) -> with_repo(BaseConfig, Fun, []). %% @doc %% Execute a function with repository authentication. %% %% Resolves credentials in this order: %%
    %%
  1. `repo_key' in config - passthrough
  2. %%
  3. `repo_key' from `get_auth_config' callback - passthrough
  4. %%
  5. `auth_key' from `get_auth_config' when `trusted' is true and `oauth_exchange' is true - exchange for OAuth token
  6. %%
  7. `auth_key' from `get_auth_config' when `trusted' is true - use directly
  8. %%
  9. Global OAuth token from `get_oauth_tokens' callback
  10. %%
  11. No auth when `optional' is true (with retry on 401)
  12. %%
  13. Prompt via `should_authenticate' when `auth_inline' is true
  14. %%
%% %% The repository name is taken from the config (`repo_name' or `repo_organization'). %% %% Callbacks are taken from the `cli_auth_callbacks' key in the config map. %% %% Options: %%
    %%
  • `optional' - When `true' (default), proceeds without auth if none found; retries with auth on 401.
  • %%
  • `auth_inline' - When `true', prompts user via `should_authenticate' callback. Default is `false'.
  • %%
  • `oauth_open_browser' - When `true' (default), automatically opens the browser %% during device auth flow. When `false', only prints the URL for the user.
  • %%
%% %% Example: %% ``` %% mix_hex_cli_auth:with_repo(Config, fun(C) -> %% mix_hex_repo:get_tarball(C, <<"ecto">>, <<"3.0.0">>) %% end). %% ''' -spec with_repo(mix_hex_core:config(), fun((mix_hex_core:config()) -> Result), opts()) -> Result | {error, auth_error()} when Result :: term(). with_repo(BaseConfig, Fun, Opts) -> Optional = proplists:get_value(optional, Opts, true), AuthInline = proplists:get_value(auth_inline, Opts, false), case resolve_repo_auth(BaseConfig) of {ok, RepoKey, _AuthContext} when is_binary(RepoKey) -> Config = BaseConfig#{repo_key => RepoKey}, Fun(Config); no_auth when Optional =:= true -> %% Auth is optional, try without credentials first execute_optional_with_retry(BaseConfig, Fun, Opts); no_auth when AuthInline =:= true -> %% No auth found, ask user if they want to authenticate maybe_authenticate_and_retry(BaseConfig, Fun, no_credentials, Opts); no_auth -> %% auth_inline is false, return error {error, {auth_error, no_credentials}}; {error, {auth_error, token_refresh_failed}} when Optional =:= true -> %% Token refresh failed but auth is optional, fall back to no credentials execute_optional_with_retry(BaseConfig, Fun, Opts); {error, _} = Error -> Error end. %% @private %% Extract repository name from config. -spec repo_name(mix_hex_core:config()) -> binary(). repo_name(#{repo_name := Name, repo_organization := Org}) when is_binary(Name) and is_binary(Org) -> <>; repo_name(#{repo_name := Name}) when is_binary(Name) -> Name; repo_name(_) -> <<"hexpm">>. %% @private %% Ask user if they want to authenticate, and if yes, initiate device auth. %% %% Serialized with a global lock so concurrent callers don't each trigger their %% own device auth flow. The first caller to acquire the lock runs device auth %% and persists the resulting token; subsequent callers re-check for an existing %% (now-valid) token inside the lock and reuse it instead of re-authenticating. maybe_authenticate_and_retry(BaseConfig, Fun, Reason, Opts) -> global:trans( {{?MODULE, device_auth}, self()}, fun() -> do_maybe_authenticate_and_retry(BaseConfig, Fun, Reason, Opts) end, [node()], infinity ). %% @private %% Another caller may have authenticated while we waited for the lock. Re-resolve %% and, if we get a token that differs from the one we arrived with (none when %% credentials were missing; the rejected one on token_refresh_failed), reuse it %% instead of prompting again. Otherwise proceed to prompt + device auth. do_maybe_authenticate_and_retry(BaseConfig, Fun, Reason, Opts) -> CurrentApiKey = maps:get(api_key, BaseConfig, undefined), case resolve_api_auth(write, BaseConfig) of {ok, ApiKey, AuthContext} when ApiKey =/= CurrentApiKey -> Config = BaseConfig#{api_key => ApiKey}, execute_with_retry(Config, Fun, AuthContext, 0, undefined, Opts); _ -> prompt_and_device_auth(BaseConfig, Fun, Reason, Opts) end. %% @private prompt_and_device_auth(BaseConfig, Fun, Reason, Opts) -> case call_callback(BaseConfig, should_authenticate, [Reason]) of true -> case device_auth(BaseConfig, <<"api repositories">>, Opts) of {ok, #{access_token := Token}} -> BearerToken = <<"Bearer ", Token/binary>>, Config = BaseConfig#{api_key => BearerToken}, AuthContext = #{source => oauth, has_refresh_token => true}, execute_with_retry(Config, Fun, AuthContext, 0, undefined, Opts); {error, _} = Error -> Error end; false -> {error, {auth_error, auth_declined}} end. %% @private %% Execute function without auth, but retry with auth if we get a 401. execute_optional_with_retry(BaseConfig, Fun, Opts) -> AuthInline = proplists:get_value(auth_inline, Opts, true), case Fun(BaseConfig) of {ok, {401, _Headers, _Body}} when AuthInline =:= true -> %% Got 401, need auth - ask user if they want to authenticate maybe_authenticate_and_retry(BaseConfig, Fun, no_credentials, Opts); {ok, {401, _Headers, _Body}} -> %% Got 401 but auth_inline is false, return error {error, {auth_error, no_credentials}}; Other -> Other end. %%==================================================================== %% Internal functions - Device Auth %%==================================================================== %% @private %% Initiate OAuth device authorization flow. %% Prompts user, optionally opens the browser for user authentication, %% polls for token completion, and persists tokens via callback on success. -spec device_auth(mix_hex_core:config(), binary(), opts()) -> {ok, oauth_tokens()} | {error, auth_error()}. device_auth(Config, Scope, Opts) -> ClientId = call_callback(Config, get_client_id, []), OpenBrowser = proplists:get_value(oauth_open_browser, Opts, true), PromptUser = fun(VerificationUri, UserCode) -> io:format("Open ~ts in your browser and enter code: ~ts~n", [VerificationUri, UserCode]) end, FlowOpts = [{open_browser, OpenBrowser}], case mix_hex_api_oauth:device_auth_flow(Config, ClientId, Scope, PromptUser, FlowOpts) of {ok, #{access_token := AccessToken, refresh_token := RefreshToken, expires_at := ExpiresAt}} -> ok = call_callback(Config, persist_oauth_tokens, [ global, AccessToken, RefreshToken, ExpiresAt ]), {ok, #{ access_token => AccessToken, refresh_token => RefreshToken, expires_at => ExpiresAt }}; {error, timeout} -> {error, {auth_error, device_auth_timeout}}; {error, {access_denied, _Status, _Body}} -> {error, {auth_error, device_auth_denied}}; {error, {device_auth_failed, _Status, _Body} = Reason} -> {error, {auth_error, Reason}}; {error, {poll_failed, _Status, _Body} = Reason} -> {error, {auth_error, Reason}}; {error, Reason} -> {error, {auth_error, Reason}} end. %% @private %% Check if a token is expired (within 5 minute buffer). -spec is_token_expired(integer()) -> boolean(). is_token_expired(ExpiresAt) -> Now = erlang:system_time(second), ExpiresAt - Now < ?EXPIRY_BUFFER_SECONDS. %%==================================================================== %% Internal functions - Auth Resolution %%==================================================================== %% @private -spec resolve_api_auth(permission(), mix_hex_core:config()) -> {ok, binary(), auth_context()} | {error, no_auth} | {error, auth_error()}. resolve_api_auth(_Permission, #{api_key := ApiKey}) when is_binary(ApiKey) -> %% api_key already in config, pass through directly {ok, ApiKey, #{source => config, has_refresh_token => false}}; resolve_api_auth(_Permission, Config) -> RepoName = repo_name(Config), %% 1. Check per-repo api_key case call_callback(Config, get_auth_config, [RepoName]) of #{api_key := ApiKey} when is_binary(ApiKey) -> {ok, ApiKey, #{source => config, has_refresh_token => false}}; _ -> %% 2. Check parent repo (for "hexpm:org" organizations) case get_parent_repo_key(Config, RepoName, api_key) of {ok, ApiKey} -> {ok, ApiKey, #{source => config, has_refresh_token => false}}; error -> %% 3. Try global OAuth token resolve_oauth_token_with_context(Config) end end. %% @private %% Resolve repo auth credentials in this order: %% 0. repo_key in config => passthrough %% 1. repo_key from get_auth_config => passthrough %% 2. trusted + auth_key + oauth_exchange => exchange for OAuth token %% 3. trusted + auth_key => use directly %% 4. trusted + global OAuth tokens => use those %% 5. Fallthrough to no_auth (handled by with_repo/3 for optional/auth_inline) -spec resolve_repo_auth(mix_hex_core:config()) -> {ok, binary(), auth_context()} | no_auth | {error, auth_error()}. resolve_repo_auth(#{repo_key := RepoKey}) when is_binary(RepoKey) -> %% repo_key already in config, pass through directly {ok, RepoKey, #{source => config, has_refresh_token => false}}; resolve_repo_auth(Config) -> RepoName = repo_name(Config), global:trans( {{?MODULE, repo, RepoName}, self()}, fun() -> do_resolve_repo_auth(RepoName, RepoName, Config) end, [node()], infinity ). do_resolve_repo_auth(RepoName, LookupRepo, Config) -> Trusted = maps:get(trusted, Config, false), OAuthExchange = maps:get(oauth_exchange, Config, false), case call_callback(Config, get_auth_config, [LookupRepo]) of #{repo_key := RepoKey} when is_binary(RepoKey) -> %% 1. repo_key from get_auth_config => passthrough {ok, RepoKey, #{source => config, has_refresh_token => false}}; #{oauth_token := OAuthToken, auth_key := AuthKey} when is_binary(AuthKey) and OAuthExchange, Trusted -> %% 2. trusted + oauth_token + auth_key + oauth_exchange => use/refresh existing token resolve_repo_oauth_token(RepoName, Config, AuthKey, OAuthToken); #{auth_key := AuthKey} when is_binary(AuthKey) and OAuthExchange, Trusted -> %% 3. trusted + auth_key + oauth_exchange => exchange for new OAuth token exchange_for_oauth_token(RepoName, Config, AuthKey, <<"repositories">>); #{auth_key := AuthKey} when is_binary(AuthKey), Trusted -> %% 4. trusted + auth_key => use directly {ok, AuthKey, #{source => config, has_refresh_token => false}}; _ when Trusted -> %% 5. Check parent repo (for "hexpm:org" organizations) case binary:split(LookupRepo, <<":">>) of [ParentName, _OrgName] -> do_resolve_repo_auth(RepoName, ParentName, Config); _ -> %% 6. trusted + global OAuth tokens => use those resolve_global_oauth_for_repo(Config) end; _ -> %% 7. Not trusted, no auth no_auth end. %% @private resolve_global_oauth_for_repo(Config) -> case resolve_oauth_token_with_context(Config) of {ok, Token, AuthContext} -> {ok, Token, AuthContext}; {error, no_auth} -> no_auth; {error, _} = Error -> Error end. %% @private %% Resolve repo OAuth token: use if valid, re-exchange if expiring. resolve_repo_oauth_token(RepoName, Config, AuthKey, #{ access_token := AccessToken, expires_at := ExpiresAt }) -> case is_token_expired(ExpiresAt) of false -> %% Token is still valid, use it BearerToken = <<"Bearer ", AccessToken/binary>>, {ok, BearerToken, #{source => oauth, has_refresh_token => false}}; true -> %% Token expired, do a new exchange exchange_for_oauth_token(RepoName, Config, AuthKey, <<"repositories">>) end. %% @private %% Exchange api_key/auth_key for OAuth token via client credentials grant. %% Persists the token with the repo name for per-repo token storage. exchange_for_oauth_token(RepoName, Config, AuthKey, Scope) -> ClientId = call_callback(Config, get_client_id, []), ExchangeConfig = case maps:get(oauth_exchange_url, Config, undefined) of undefined -> Config; OAuthUrl -> Config#{api_url => OAuthUrl} end, case mix_hex_api_oauth:client_credentials_token(ExchangeConfig, ClientId, AuthKey, Scope) of {ok, {200, _, #{<<"access_token">> := AccessToken, <<"expires_in">> := ExpiresIn}}} -> ExpiresAt = erlang:system_time(second) + ExpiresIn, ok = call_callback(Config, persist_oauth_tokens, [ RepoName, AccessToken, undefined, ExpiresAt ]), BearerToken = <<"Bearer ", AccessToken/binary>>, {ok, BearerToken, #{source => oauth, has_refresh_token => false}}; {ok, {_Status, _, _Body}} -> {error, {auth_error, oauth_exchange_failed}}; {error, _} -> {error, {auth_error, oauth_exchange_failed}} end. %% @private get_parent_repo_key(Config, RepoName, KeyType) -> case binary:split(RepoName, <<":">>) of [ParentName, _OrgName] -> case call_callback(Config, get_auth_config, [ParentName]) of #{KeyType := Key} when is_binary(Key) -> {ok, Key}; _ -> error end; _ -> error end. %% @private %% Resolve OAuth token with global lock to prevent concurrent refresh attempts. resolve_oauth_token_with_context(Config) -> global:trans( {{?MODULE, token_refresh}, self()}, fun() -> do_resolve_oauth_token_with_context(Config) end, [node()], infinity ). %% @private do_resolve_oauth_token_with_context(Config) -> case call_callback(Config, get_oauth_tokens, []) of {ok, #{access_token := AccessToken, expires_at := ExpiresAt} = Tokens} -> HasRefreshToken = maps:is_key(refresh_token, Tokens) andalso is_binary(maps:get(refresh_token, Tokens)), case is_token_expired(ExpiresAt) of true -> refresh_or_clear(Config, Tokens); false -> BearerToken = <<"Bearer ", AccessToken/binary>>, {ok, BearerToken, #{source => oauth, has_refresh_token => HasRefreshToken}} end; error -> {error, no_auth} end. %% @private %% Refresh an expired global token; if the refresh fails, invalidate the stored %% token via the optional clear_oauth_tokens callback. This runs inside the %% token_refresh lock, so the unusable token is dropped exactly once and the %% callers serialized behind the lock re-read it as absent instead of each %% retrying the doomed refresh against the server. refresh_or_clear(Config, Tokens) -> case maybe_refresh_token_with_context(Config, Tokens) of {ok, _Bearer, _Ctx} = Ok -> Ok; {error, _} = Error -> maybe_call_callback(Config, clear_oauth_tokens, []), Error end. %% @private maybe_refresh_token_with_context(Config, #{refresh_token := RefreshToken}) when is_binary(RefreshToken) -> ClientId = call_callback(Config, get_client_id, []), case mix_hex_api_oauth:refresh_token(Config, ClientId, RefreshToken) of {ok, {200, _, TokenResponse}} when is_map(TokenResponse) -> #{ <<"access_token">> := NewAccessToken, <<"expires_in">> := ExpiresIn } = TokenResponse, NewRefreshToken = maps:get(<<"refresh_token">>, TokenResponse, RefreshToken), ExpiresAt = erlang:system_time(second) + ExpiresIn, ok = call_callback(Config, persist_oauth_tokens, [ global, NewAccessToken, NewRefreshToken, ExpiresAt ]), BearerToken = <<"Bearer ", NewAccessToken/binary>>, HasRefreshToken = is_binary(NewRefreshToken), {ok, BearerToken, #{source => oauth, has_refresh_token => HasRefreshToken}}; {ok, {_Status, _, _Body}} -> {error, {auth_error, token_refresh_failed}}; {error, _Reason} -> {error, {auth_error, token_refresh_failed}} end; maybe_refresh_token_with_context(_Config, _Tokens) -> {error, {auth_error, token_refresh_failed}}. %%==================================================================== %% Internal functions - Retry Logic %%==================================================================== %% @private execute_with_retry(Config, Fun, AuthContext, OtpRetries, LastOtpError, Opts) -> case Fun(Config) of {error, otp_required} -> handle_otp_retry( Config, Fun, AuthContext, OtpRetries, <<"Enter OTP code:">>, Opts ); {error, invalid_totp} -> handle_otp_retry( Config, Fun, AuthContext, OtpRetries, <<"Invalid OTP code. Please try again:">>, Opts ); {ok, {401, Headers, _Body}} = Response -> case detect_auth_error(Headers) of otp_required -> handle_otp_retry( Config, Fun, AuthContext, OtpRetries, <<"Enter OTP code:">>, Opts ); invalid_totp -> Msg = case LastOtpError of invalid_totp -> <<"Invalid OTP code. Please try again:">>; _ -> <<"Enter OTP code:">> end, handle_otp_retry(Config, Fun, AuthContext, OtpRetries, Msg, Opts); token_expired -> handle_token_refresh_retry(Config, Fun, AuthContext, Opts); none -> Response end; Other -> Other end. %% @private handle_otp_retry(_Config, _Fun, _AuthContext, OtpRetries, _Message, _Opts) when OtpRetries >= ?MAX_OTP_RETRIES -> {error, {auth_error, otp_max_retries}}; handle_otp_retry(Config, Fun, AuthContext, OtpRetries, Message, Opts) -> case call_callback(Config, prompt_otp, [Message]) of {ok, OtpCode} -> NewConfig = Config#{api_otp => OtpCode}, execute_with_retry( NewConfig, Fun, AuthContext, OtpRetries + 1, invalid_totp, Opts ); cancelled -> {error, {auth_error, otp_cancelled}} end. %% @private handle_token_refresh_retry(Config, Fun, AuthContext, Opts) -> %% Only attempt refresh if we have a refresh token case maps:get(has_refresh_token, AuthContext, false) of true -> case resolve_oauth_token_with_context(Config) of {ok, NewBearerToken, NewAuthContext} -> NewConfig = Config#{api_key => NewBearerToken}, execute_with_retry( NewConfig, Fun, NewAuthContext, 0, undefined, Opts ); {error, _} -> maybe_reauthenticate(Config, Fun, Opts) end; false -> maybe_reauthenticate(Config, Fun, Opts) end. %% @private %% After token refresh failure, prompt the user to re-authenticate via device auth %% (only when auth_inline is true). Mirrors Hex.OAuth.reauthenticate/1. maybe_reauthenticate(Config, Fun, Opts) -> AuthInline = proplists:get_value(auth_inline, Opts, true), case AuthInline of true -> maybe_authenticate_and_retry(Config, Fun, token_refresh_failed, Opts); false -> {error, {auth_error, token_refresh_failed}} end. %% @private -spec detect_auth_error(mix_hex_http:headers()) -> otp_required | invalid_totp | token_expired | none. detect_auth_error(Headers) -> case maps:get(<<"www-authenticate">>, Headers, undefined) of undefined -> none; Value -> parse_www_authenticate(Value) end. %% @private parse_www_authenticate(Value) when is_binary(Value) -> case Value of <<"Bearer realm=\"hex\", error=\"totp_required\"", _/binary>> -> otp_required; <<"Bearer realm=\"hex\", error=\"invalid_totp\"", _/binary>> -> invalid_totp; <<"Bearer realm=\"hex\", error=\"token_expired\"", _/binary>> -> token_expired; _ -> none end. %%==================================================================== %% Internal functions - Utilities %%==================================================================== %% @private call_callback(Config, Name, Args) -> #{cli_auth_callbacks := Callbacks} = Config, Fun = maps:get(Name, Callbacks), erlang:apply(Fun, Args). %% @private %% Like call_callback/3 but for optional callbacks: returns ok without doing %% anything when the callback is not provided. maybe_call_callback(Config, Name, Args) -> #{cli_auth_callbacks := Callbacks} = Config, case maps:find(Name, Callbacks) of {ok, Fun} -> erlang:apply(Fun, Args); error -> ok end. hex-2.5.1/src/mix_hex_core.erl000066400000000000000000000162721522402544500162620ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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. %% %% * `tarball_files_root' - Root directory for source files when creating tarballs. %% Filesystem source paths must resolve inside this root after following symlinks. %% Relative source paths are resolved from this root and absolute source paths must be %% inside it (default: `"."'). %% %% * `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. %% %% * `metadata_fields' - Either `all' or a list of metadata.config keys (binaries) to read. %% When set to a list, the metadata decoder streams past unrequested fields without %% buffering their tokens, which keeps peak memory bounded for packages with very %% large fields like `<<"files">>'. Defaults to `all'. -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_files_root => file:filename(), 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, metadata_fields => all | [binary()], trusted => boolean(), oauth_exchange => boolean(), oauth_exchange_url => binary() | undefined, cli_auth_callbacks => mix_hex_cli_auth:callbacks() | undefined }. -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_files_root => ".", 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, metadata_fields => all, trusted => true, oauth_exchange => true, oauth_exchange_url => undefined, cli_auth_callbacks => undefined }. hex-2.5.1/src/mix_hex_core.hrl000066400000000000000000000001501522402544500162510ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), do not edit manually -define(HEX_CORE_VERSION, "0.18.0"). hex-2.5.1/src/mix_hex_erl_tar.erl000066400000000000000000002411551522402544500167620ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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.5.1/src/mix_hex_erl_tar.hrl000066400000000000000000000405171522402544500167640ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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.5.1/src/mix_hex_http.erl000066400000000000000000000061431522402544500163050ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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.5.1/src/mix_hex_http_httpc.erl000066400000000000000000000121161522402544500175040ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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, [{sync, false}, {stream, self}], Profile ) of {ok, RequestId} -> stream_to_file(RequestId, Filename); {error, Reason} -> {error, Reason} end. %%==================================================================== %% Internal functions %%==================================================================== %% @private %% httpc streams 200/206 responses as messages and returns non-2xx as %% a normal response tuple. stream_start includes the response headers. stream_to_file(RequestId, Filename) -> receive {http, {RequestId, stream_start, Headers}} -> {ok, File} = file:open(Filename, [write, binary]), case stream_body(RequestId, File) of ok -> ok = file:close(File), {ok, {200, load_headers(Headers)}}; {error, Reason} -> ok = file:close(File), {error, Reason} end; {http, {RequestId, {{_, StatusCode, _}, RespHeaders, _RespBody}}} -> {ok, {StatusCode, load_headers(RespHeaders)}}; {http, {RequestId, {error, Reason}}} -> {error, Reason} end. %% @private stream_body(RequestId, File) -> receive {http, {RequestId, stream, BinBodyPart}} -> ok = file:write(File, BinBodyPart), stream_body(RequestId, File); {http, {RequestId, stream_end, _Headers}} -> ok; {http, {RequestId, {error, Reason}}} -> {error, Reason} end. %% @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.5.1/src/mix_hex_licenses.erl000066400000000000000000000560161522402544500171370ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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(<<"LicenseRef-", IdString/binary>>) -> valid_license_ref_idstring(IdString); valid(_) -> false. valid_license_ref_idstring(<<>>) -> false; valid_license_ref_idstring(IdString) -> valid_license_ref_idstring(IdString, true). valid_license_ref_idstring(<<>>, Valid) -> Valid; valid_license_ref_idstring(_, false) -> false; valid_license_ref_idstring(<>, true) -> valid_license_ref_idstring(Rest, valid_license_ref_char(Char)). valid_license_ref_char(Char) when Char >= $A, Char =< $Z -> true; valid_license_ref_char(Char) when Char >= $a, Char =< $z -> true; valid_license_ref_char(Char) when Char >= $0, Char =< $9 -> true; valid_license_ref_char($-) -> true; valid_license_ref_char($.) -> true; valid_license_ref_char(_) -> false. hex-2.5.1/src/mix_hex_pb_names.erl000066400000000000000000001152141522402544500171120ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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.5.1/src/mix_hex_pb_package.erl000066400000000000000000003201431522402544500174010ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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([enum_symbol_by_value_AdvisorySeverity/1, enum_value_by_symbol_AdvisorySeverity/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'. -type 'AdvisorySeverity'() :: 'SEVERITY_NONE' | 'SEVERITY_LOW' | 'SEVERITY_MEDIUM' | 'SEVERITY_HIGH' | 'SEVERITY_CRITICAL'. -export_type(['RetirementReason'/0, 'AdvisorySeverity'/0]). %% message types -type 'Package'() :: #{releases => ['Release'()], % = 1, repeated name => unicode:chardata(), % = 2, required repository => unicode:chardata(), % = 3, required advisories => ['SecurityAdvisory'()] % = 4, repeated }. -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 advisory_indexes => [non_neg_integer()], % = 6, repeated, 32 bits published_at => 'Timestamp'() % = 7, 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 'SecurityAdvisory'() :: #{id => unicode:chardata(), % = 1, required summary => unicode:chardata(), % = 2, required html_url => unicode:chardata(), % = 3, required severity => 'SEVERITY_NONE' | 'SEVERITY_LOW' | 'SEVERITY_MEDIUM' | 'SEVERITY_HIGH' | 'SEVERITY_CRITICAL' | integer(), % = 4, optional, enum AdvisorySeverity cvss_score => float() | integer() | infinity | '-infinity' | nan, % = 5, optional api_url => unicode:chardata(), % = 6, required aliases => [unicode:chardata()] % = 7, repeated }. -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 }. -type 'Timestamp'() :: #{seconds => integer(), % = 1, required, 64 bits nanos => integer() % = 2, required, 32 bits }. -export_type(['Package'/0, 'Release'/0, 'RetirementStatus'/0, 'SecurityAdvisory'/0, 'Dependency'/0, 'Timestamp'/0]). -type '$msg_name'() :: 'Package' | 'Release' | 'RetirementStatus' | 'SecurityAdvisory' | 'Dependency' | 'Timestamp'. -type '$msg'() :: 'Package'() | 'Release'() | 'RetirementStatus'() | 'SecurityAdvisory'() | 'Dependency'() | 'Timestamp'(). -export_type(['$msg_name'/0, '$msg'/0]). -spec encode_msg('$msg'(), '$msg_name'()) -> binary(). encode_msg(Msg, MsgName) when is_atom(MsgName) -> encode_msg(Msg, MsgName, []). -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); 'SecurityAdvisory' -> encode_msg_SecurityAdvisory(id(Msg, TrUserData), TrUserData); 'Dependency' -> encode_msg_Dependency(id(Msg, TrUserData), TrUserData); 'Timestamp' -> encode_msg_Timestamp(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, B3 = begin TrF3 = id(F3, TrUserData), e_type_string(TrF3, <>, TrUserData) end, case M of #{advisories := F4} -> TrF4 = id(F4, TrUserData), if TrF4 == [] -> B3; true -> e_field_Package_advisories(TrF4, B3, TrUserData) end; _ -> B3 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, B5 = case M of #{outer_checksum := F5} -> begin TrF5 = id(F5, TrUserData), e_type_bytes(TrF5, <>, TrUserData) end; _ -> B4 end, B6 = case M of #{advisory_indexes := F6} -> TrF6 = id(F6, TrUserData), if TrF6 == [] -> B5; true -> e_field_Release_advisory_indexes(TrF6, B5, TrUserData) end; _ -> B5 end, case M of #{published_at := F7} -> begin TrF7 = id(F7, TrUserData), e_mfield_Release_published_at(TrF7, <>, TrUserData) end; _ -> B6 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_SecurityAdvisory(Msg, TrUserData) -> encode_msg_SecurityAdvisory(Msg, <<>>, TrUserData). encode_msg_SecurityAdvisory(#{id := F1, summary := F2, html_url := F3, api_url := F6} = 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 = begin TrF3 = id(F3, TrUserData), e_type_string(TrF3, <>, TrUserData) end, B4 = case M of #{severity := F4} -> begin TrF4 = id(F4, TrUserData), e_enum_AdvisorySeverity(TrF4, <>, TrUserData) end; _ -> B3 end, B5 = case M of #{cvss_score := F5} -> begin TrF5 = id(F5, TrUserData), e_type_float(TrF5, <>, TrUserData) end; _ -> B4 end, B6 = begin TrF6 = id(F6, TrUserData), e_type_string(TrF6, <>, TrUserData) end, case M of #{aliases := F7} -> TrF7 = id(F7, TrUserData), if TrF7 == [] -> B6; true -> e_field_SecurityAdvisory_aliases(TrF7, B6, TrUserData) end; _ -> B6 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. 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_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_Package_advisories(Msg, Bin, TrUserData) -> SubBin = encode_msg_SecurityAdvisory(Msg, <<>>, TrUserData), Bin2 = e_varint(byte_size(SubBin), Bin), <>. e_field_Package_advisories([Elem | Rest], Bin, TrUserData) -> Bin2 = <>, Bin3 = e_mfield_Package_advisories(id(Elem, TrUserData), Bin2, TrUserData), e_field_Package_advisories(Rest, Bin3, TrUserData); e_field_Package_advisories([], 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_field_Release_advisory_indexes([Elem | Rest], Bin, TrUserData) -> Bin2 = <>, Bin3 = e_varint(id(Elem, TrUserData), Bin2, TrUserData), e_field_Release_advisory_indexes(Rest, Bin3, TrUserData); e_field_Release_advisory_indexes([], Bin, _TrUserData) -> Bin. e_mfield_Release_published_at(Msg, Bin, TrUserData) -> SubBin = encode_msg_Timestamp(Msg, <<>>, TrUserData), Bin2 = e_varint(byte_size(SubBin), Bin), <>. e_field_SecurityAdvisory_aliases([Elem | Rest], Bin, TrUserData) -> Bin2 = <>, Bin3 = e_type_string(id(Elem, TrUserData), Bin2, TrUserData), e_field_SecurityAdvisory_aliases(Rest, Bin3, TrUserData); e_field_SecurityAdvisory_aliases([], Bin, _TrUserData) -> 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). e_enum_AdvisorySeverity('SEVERITY_NONE', Bin, _TrUserData) -> <>; e_enum_AdvisorySeverity('SEVERITY_LOW', Bin, _TrUserData) -> <>; e_enum_AdvisorySeverity('SEVERITY_MEDIUM', Bin, _TrUserData) -> <>; e_enum_AdvisorySeverity('SEVERITY_HIGH', Bin, _TrUserData) -> <>; e_enum_AdvisorySeverity('SEVERITY_CRITICAL', Bin, _TrUserData) -> <>; e_enum_AdvisorySeverity(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('SecurityAdvisory', Bin, TrUserData) -> id(decode_msg_SecurityAdvisory(Bin, TrUserData), TrUserData); decode_msg_2_doit('Dependency', Bin, TrUserData) -> id(decode_msg_Dependency(Bin, TrUserData), TrUserData); decode_msg_2_doit('Timestamp', Bin, TrUserData) -> id(decode_msg_Timestamp(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), id([], TrUserData), TrUserData). dfp_read_field_def_Package(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_releases(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); dfp_read_field_def_Package(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_name(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); dfp_read_field_def_Package(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_repository(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); dfp_read_field_def_Package(<<34, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_advisories(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); dfp_read_field_def_Package(<<>>, 0, 0, _, R1, F@_2, F@_3, R2, TrUserData) -> S1 = #{name => F@_2, repository => F@_3}, S2 = if R1 == '$undef' -> S1; true -> S1#{releases => lists_reverse(R1, TrUserData)} end, if R2 == '$undef' -> S2; true -> S2#{advisories => lists_reverse(R2, TrUserData)} end; dfp_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dg_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData). dg_read_field_def_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 32 - 7 -> dg_read_field_def_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); dg_read_field_def_Package(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, F@_4, TrUserData) -> Key = X bsl N + Acc, case Key of 10 -> d_field_Package_releases(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); 18 -> d_field_Package_name(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); 26 -> d_field_Package_repository(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); 34 -> d_field_Package_advisories(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); 1 -> skip_64_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); 2 -> skip_length_delimited_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); 3 -> skip_group_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); 5 -> skip_32_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData) end end; dg_read_field_def_Package(<<>>, 0, 0, _, R1, F@_2, F@_3, R2, TrUserData) -> S1 = #{name => F@_2, repository => F@_3}, S2 = if R1 == '$undef' -> S1; true -> S1#{releases => lists_reverse(R1, TrUserData)} end, if R2 == '$undef' -> S2; true -> S2#{advisories => lists_reverse(R2, TrUserData)} end. d_field_Package_releases(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_releases(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); d_field_Package_releases(<<0:1, X:7, Rest/binary>>, N, Acc, F, Prev, F@_2, F@_3, F@_4, 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, F@_4, TrUserData). d_field_Package_name(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_name(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); d_field_Package_name(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, 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_Package(RestF, 0, 0, F, F@_1, NewFValue, F@_3, F@_4, TrUserData). d_field_Package_repository(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_repository(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); d_field_Package_repository(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, _, 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_Package(RestF, 0, 0, F, F@_1, F@_2, NewFValue, F@_4, TrUserData). d_field_Package_advisories(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_advisories(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); d_field_Package_advisories(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, Prev, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, {id(decode_msg_SecurityAdvisory(Bs, TrUserData), TrUserData), Rest2} end, dfp_read_field_def_Package(RestF, 0, 0, F, F@_1, F@_2, F@_3, cons(NewFValue, Prev, TrUserData), TrUserData). skip_varint_Package(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> skip_varint_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); skip_varint_Package(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData). skip_length_delimited_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> skip_length_delimited_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); skip_length_delimited_Package(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, 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, F@_4, TrUserData). skip_group_Package(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, F@_4, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_Package(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, F@_4, TrUserData). skip_32_Package(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData). skip_64_Package(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, 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), id([], 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, F@_6, F@_7, TrUserData) -> d_field_Release_version(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_Release(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_inner_checksum(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_Release(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_dependencies(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_Release(<<34, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_retired(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_Release(<<42, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_outer_checksum(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_Release(<<50, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_pfield_Release_advisory_indexes(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_Release(<<48, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_advisory_indexes(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_Release(<<58, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_published_at(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_Release(<<>>, 0, 0, _, F@_1, F@_2, R1, F@_4, F@_5, R2, F@_7, TrUserData) -> S1 = #{version => F@_1, inner_checksum => F@_2, advisory_indexes => lists_reverse(R2, TrUserData)}, 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, S4 = if F@_5 == '$undef' -> S3; true -> S3#{outer_checksum => F@_5} end, if F@_7 == '$undef' -> S4; true -> S4#{published_at => F@_7} end; dfp_read_field_def_Release(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dg_read_field_def_Release(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, 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, F@_6, F@_7, TrUserData); dg_read_field_def_Release(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData); 18 -> d_field_Release_inner_checksum(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 26 -> d_field_Release_dependencies(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 34 -> d_field_Release_retired(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 42 -> d_field_Release_outer_checksum(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 50 -> d_pfield_Release_advisory_indexes(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 48 -> d_field_Release_advisory_indexes(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 58 -> d_field_Release_published_at(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData); 1 -> skip_64_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 2 -> skip_length_delimited_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 3 -> skip_group_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 5 -> skip_32_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) end end; dg_read_field_def_Release(<<>>, 0, 0, _, F@_1, F@_2, R1, F@_4, F@_5, R2, F@_7, TrUserData) -> S1 = #{version => F@_1, inner_checksum => F@_2, advisory_indexes => lists_reverse(R2, TrUserData)}, 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, S4 = if F@_5 == '$undef' -> S3; true -> S3#{outer_checksum => F@_5} end, if F@_7 == '$undef' -> S4; true -> S4#{published_at => F@_7} end. d_field_Release_version(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData); d_field_Release_version(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, 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, F@_6, F@_7, 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, F@_6, F@_7, TrUserData); d_field_Release_inner_checksum(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData). d_field_Release_dependencies(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData); d_field_Release_dependencies(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, Prev, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData). d_field_Release_retired(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData); d_field_Release_retired(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, Prev, F@_5, F@_6, F@_7, 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, F@_6, F@_7, 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, F@_6, F@_7, 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, F@_6, F@_7, TrUserData); d_field_Release_outer_checksum(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, _, F@_6, F@_7, 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, F@_6, F@_7, TrUserData). d_field_Release_advisory_indexes(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_Release_advisory_indexes(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); d_field_Release_advisory_indexes(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, Prev, F@_7, TrUserData) -> {NewFValue, RestF} = {id((X bsl N + Acc) band 4294967295, TrUserData), Rest}, dfp_read_field_def_Release(RestF, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, cons(NewFValue, Prev, TrUserData), F@_7, TrUserData). d_pfield_Release_advisory_indexes(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_pfield_Release_advisory_indexes(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); d_pfield_Release_advisory_indexes(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, E, F@_7, TrUserData) -> Len = X bsl N + Acc, <> = Rest, NewSeq = d_packed_field_Release_advisory_indexes(PackedBytes, 0, 0, F, E, TrUserData), dfp_read_field_def_Release(Rest2, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, NewSeq, F@_7, TrUserData). d_packed_field_Release_advisory_indexes(<<1:1, X:7, Rest/binary>>, N, Acc, F, AccSeq, TrUserData) when N < 57 -> d_packed_field_Release_advisory_indexes(Rest, N + 7, X bsl N + Acc, F, AccSeq, TrUserData); d_packed_field_Release_advisory_indexes(<<0:1, X:7, Rest/binary>>, N, Acc, F, AccSeq, TrUserData) -> {NewFValue, RestF} = {id((X bsl N + Acc) band 4294967295, TrUserData), Rest}, d_packed_field_Release_advisory_indexes(RestF, 0, 0, F, [NewFValue | AccSeq], TrUserData); d_packed_field_Release_advisory_indexes(<<>>, 0, 0, _, AccSeq, _) -> AccSeq. d_field_Release_published_at(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_Release_published_at(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); d_field_Release_published_at(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, Prev, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, {id(decode_msg_Timestamp(Bs, TrUserData), TrUserData), Rest2} end, dfp_read_field_def_Release(RestF, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, if Prev == '$undef' -> NewFValue; true -> merge_msg_Timestamp(Prev, NewFValue, TrUserData) end, TrUserData). skip_varint_Release(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> skip_varint_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); skip_varint_Release(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dfp_read_field_def_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). skip_length_delimited_Release(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData); skip_length_delimited_Release(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData). skip_group_Release(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData). skip_32_Release(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dfp_read_field_def_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). skip_64_Release(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dfp_read_field_def_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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_SecurityAdvisory(Bin, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Bin, 0, 0, 0, id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id([], TrUserData), TrUserData). dfp_read_field_def_SecurityAdvisory(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_id(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_SecurityAdvisory(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_summary(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_SecurityAdvisory(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_html_url(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_SecurityAdvisory(<<32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_severity(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_SecurityAdvisory(<<45, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_cvss_score(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_SecurityAdvisory(<<50, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_api_url(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_SecurityAdvisory(<<58, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_aliases(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_SecurityAdvisory(<<>>, 0, 0, _, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, R1, TrUserData) -> S1 = #{id => F@_1, summary => F@_2, html_url => F@_3, api_url => F@_6, aliases => lists_reverse(R1, TrUserData)}, S2 = if F@_4 == '$undef' -> S1; true -> S1#{severity => F@_4} end, if F@_5 == '$undef' -> S2; true -> S2#{cvss_score => F@_5} end; dfp_read_field_def_SecurityAdvisory(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dg_read_field_def_SecurityAdvisory(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). dg_read_field_def_SecurityAdvisory(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 32 - 7 -> dg_read_field_def_SecurityAdvisory(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dg_read_field_def_SecurityAdvisory(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> Key = X bsl N + Acc, case Key of 10 -> d_field_SecurityAdvisory_id(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 18 -> d_field_SecurityAdvisory_summary(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 26 -> d_field_SecurityAdvisory_html_url(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 32 -> d_field_SecurityAdvisory_severity(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 45 -> d_field_SecurityAdvisory_cvss_score(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 50 -> d_field_SecurityAdvisory_api_url(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 58 -> d_field_SecurityAdvisory_aliases(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_SecurityAdvisory(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 1 -> skip_64_SecurityAdvisory(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 2 -> skip_length_delimited_SecurityAdvisory(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 3 -> skip_group_SecurityAdvisory(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 5 -> skip_32_SecurityAdvisory(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) end end; dg_read_field_def_SecurityAdvisory(<<>>, 0, 0, _, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, R1, TrUserData) -> S1 = #{id => F@_1, summary => F@_2, html_url => F@_3, api_url => F@_6, aliases => lists_reverse(R1, TrUserData)}, S2 = if F@_4 == '$undef' -> S1; true -> S1#{severity => F@_4} end, if F@_5 == '$undef' -> S2; true -> S2#{cvss_score => F@_5} end. d_field_SecurityAdvisory_id(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_SecurityAdvisory_id(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); d_field_SecurityAdvisory_id(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_SecurityAdvisory(RestF, 0, 0, F, NewFValue, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). d_field_SecurityAdvisory_summary(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_SecurityAdvisory_summary(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); d_field_SecurityAdvisory_summary(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_SecurityAdvisory(RestF, 0, 0, F, F@_1, NewFValue, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). d_field_SecurityAdvisory_html_url(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_SecurityAdvisory_html_url(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); d_field_SecurityAdvisory_html_url(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, _, F@_4, F@_5, F@_6, F@_7, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_SecurityAdvisory(RestF, 0, 0, F, F@_1, F@_2, NewFValue, F@_4, F@_5, F@_6, F@_7, TrUserData). d_field_SecurityAdvisory_severity(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_SecurityAdvisory_severity(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); d_field_SecurityAdvisory_severity(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, _, F@_5, F@_6, F@_7, TrUserData) -> {NewFValue, RestF} = {id(d_enum_AdvisorySeverity(begin <> = <<(X bsl N + Acc):32/unsigned-native>>, id(Res, TrUserData) end), TrUserData), Rest}, dfp_read_field_def_SecurityAdvisory(RestF, 0, 0, F, F@_1, F@_2, F@_3, NewFValue, F@_5, F@_6, F@_7, TrUserData). d_field_SecurityAdvisory_cvss_score(<<0:16, 128, 127, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, _, F@_6, F@_7, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, id(infinity, TrUserData), F@_6, F@_7, TrUserData); d_field_SecurityAdvisory_cvss_score(<<0:16, 128, 255, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, _, F@_6, F@_7, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, id('-infinity', TrUserData), F@_6, F@_7, TrUserData); d_field_SecurityAdvisory_cvss_score(<<_:16, 1:1, _:7, _:1, 127:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, _, F@_6, F@_7, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, id(nan, TrUserData), F@_6, F@_7, TrUserData); d_field_SecurityAdvisory_cvss_score(<>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, _, F@_6, F@_7, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, id(Value, TrUserData), F@_6, F@_7, TrUserData). d_field_SecurityAdvisory_api_url(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_SecurityAdvisory_api_url(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); d_field_SecurityAdvisory_api_url(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, _, F@_7, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_SecurityAdvisory(RestF, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, NewFValue, F@_7, TrUserData). d_field_SecurityAdvisory_aliases(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_SecurityAdvisory_aliases(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); d_field_SecurityAdvisory_aliases(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, Prev, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_SecurityAdvisory(RestF, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, cons(NewFValue, Prev, TrUserData), TrUserData). skip_varint_SecurityAdvisory(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> skip_varint_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); skip_varint_SecurityAdvisory(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). skip_length_delimited_SecurityAdvisory(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> skip_length_delimited_SecurityAdvisory(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); skip_length_delimited_SecurityAdvisory(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> Length = X bsl N + Acc, <<_:Length/binary, Rest2/binary>> = Rest, dfp_read_field_def_SecurityAdvisory(Rest2, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). skip_group_SecurityAdvisory(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_SecurityAdvisory(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). skip_32_SecurityAdvisory(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). skip_64_SecurityAdvisory(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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). 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). 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. d_enum_AdvisorySeverity(0) -> 'SEVERITY_NONE'; d_enum_AdvisorySeverity(1) -> 'SEVERITY_LOW'; d_enum_AdvisorySeverity(2) -> 'SEVERITY_MEDIUM'; d_enum_AdvisorySeverity(3) -> 'SEVERITY_HIGH'; d_enum_AdvisorySeverity(4) -> 'SEVERITY_CRITICAL'; d_enum_AdvisorySeverity(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); 'SecurityAdvisory' -> merge_msg_SecurityAdvisory(Prev, New, TrUserData); 'Dependency' -> merge_msg_Dependency(Prev, New, TrUserData); 'Timestamp' -> merge_msg_Timestamp(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}, S2 = 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, case {PMsg, NMsg} of {#{advisories := PFadvisories}, #{advisories := NFadvisories}} -> S2#{advisories => 'erlang_++'(PFadvisories, NFadvisories, TrUserData)}; {_, #{advisories := NFadvisories}} -> S2#{advisories => NFadvisories}; {#{advisories := PFadvisories}, _} -> S2#{advisories => PFadvisories}; {_, _} -> S2 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, S4 = case {PMsg, NMsg} of {_, #{outer_checksum := NFouter_checksum}} -> S3#{outer_checksum => NFouter_checksum}; {#{outer_checksum := PFouter_checksum}, _} -> S3#{outer_checksum => PFouter_checksum}; _ -> S3 end, S5 = case {PMsg, NMsg} of {#{advisory_indexes := PFadvisory_indexes}, #{advisory_indexes := NFadvisory_indexes}} -> S4#{advisory_indexes => 'erlang_++'(PFadvisory_indexes, NFadvisory_indexes, TrUserData)}; {_, #{advisory_indexes := NFadvisory_indexes}} -> S4#{advisory_indexes => NFadvisory_indexes}; {#{advisory_indexes := PFadvisory_indexes}, _} -> S4#{advisory_indexes => PFadvisory_indexes}; {_, _} -> S4 end, case {PMsg, NMsg} of {#{published_at := PFpublished_at}, #{published_at := NFpublished_at}} -> S5#{published_at => merge_msg_Timestamp(PFpublished_at, NFpublished_at, TrUserData)}; {_, #{published_at := NFpublished_at}} -> S5#{published_at => NFpublished_at}; {#{published_at := PFpublished_at}, _} -> S5#{published_at => PFpublished_at}; {_, _} -> S5 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_SecurityAdvisory/3}). merge_msg_SecurityAdvisory(#{} = PMsg, #{id := NFid, summary := NFsummary, html_url := NFhtml_url, api_url := NFapi_url} = NMsg, TrUserData) -> S1 = #{id => NFid, summary => NFsummary, html_url => NFhtml_url, api_url => NFapi_url}, S2 = case {PMsg, NMsg} of {_, #{severity := NFseverity}} -> S1#{severity => NFseverity}; {#{severity := PFseverity}, _} -> S1#{severity => PFseverity}; _ -> S1 end, S3 = case {PMsg, NMsg} of {_, #{cvss_score := NFcvss_score}} -> S2#{cvss_score => NFcvss_score}; {#{cvss_score := PFcvss_score}, _} -> S2#{cvss_score => PFcvss_score}; _ -> S2 end, case {PMsg, NMsg} of {#{aliases := PFaliases}, #{aliases := NFaliases}} -> S3#{aliases => 'erlang_++'(PFaliases, NFaliases, TrUserData)}; {_, #{aliases := NFaliases}} -> S3#{aliases => NFaliases}; {#{aliases := PFaliases}, _} -> S3#{aliases => PFaliases}; {_, _} -> S3 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. -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 'Package' -> v_msg_Package(Msg, [MsgName], TrUserData); 'Release' -> v_msg_Release(Msg, [MsgName], TrUserData); 'RetirementStatus' -> v_msg_RetirementStatus(Msg, [MsgName], TrUserData); 'SecurityAdvisory' -> v_msg_SecurityAdvisory(Msg, [MsgName], TrUserData); 'Dependency' -> v_msg_Dependency(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_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), case M of #{advisories := F4} -> if is_list(F4) -> _ = [v_submsg_SecurityAdvisory(Elem, [advisories | Path], TrUserData) || Elem <- F4], ok; true -> mk_type_error({invalid_list_of, {msg, 'SecurityAdvisory'}}, F4, [advisories | Path]) end; _ -> ok end, lists:foreach(fun (releases) -> ok; (name) -> ok; (repository) -> ok; (advisories) -> 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}). v_submsg_Release(Msg, Path, TrUserData) -> v_msg_Release(Msg, Path, TrUserData). -compile({nowarn_unused_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, case M of #{advisory_indexes := F6} -> if is_list(F6) -> _ = [v_type_uint32(Elem, [advisory_indexes | Path], TrUserData) || Elem <- F6], ok; true -> mk_type_error({invalid_list_of, uint32}, F6, [advisory_indexes | Path]) end; _ -> ok end, case M of #{published_at := F7} -> v_submsg_Timestamp(F7, [published_at | Path], TrUserData); _ -> ok end, lists:foreach(fun (version) -> ok; (inner_checksum) -> ok; (dependencies) -> ok; (retired) -> ok; (outer_checksum) -> ok; (advisory_indexes) -> ok; (published_at) -> 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}). v_submsg_RetirementStatus(Msg, Path, TrUserData) -> v_msg_RetirementStatus(Msg, Path, TrUserData). -compile({nowarn_unused_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_SecurityAdvisory/3}). v_submsg_SecurityAdvisory(Msg, Path, TrUserData) -> v_msg_SecurityAdvisory(Msg, Path, TrUserData). -compile({nowarn_unused_function,v_msg_SecurityAdvisory/3}). v_msg_SecurityAdvisory(#{id := F1, summary := F2, html_url := F3, api_url := F6} = M, Path, TrUserData) -> v_type_string(F1, [id | Path], TrUserData), v_type_string(F2, [summary | Path], TrUserData), v_type_string(F3, [html_url | Path], TrUserData), case M of #{severity := F4} -> v_enum_AdvisorySeverity(F4, [severity | Path], TrUserData); _ -> ok end, case M of #{cvss_score := F5} -> v_type_float(F5, [cvss_score | Path], TrUserData); _ -> ok end, v_type_string(F6, [api_url | Path], TrUserData), case M of #{aliases := F7} -> if is_list(F7) -> _ = [v_type_string(Elem, [aliases | Path], TrUserData) || Elem <- F7], ok; true -> mk_type_error({invalid_list_of, string}, F7, [aliases | Path]) end; _ -> ok end, lists:foreach(fun (id) -> ok; (summary) -> ok; (html_url) -> ok; (severity) -> ok; (cvss_score) -> ok; (api_url) -> ok; (aliases) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), ok; v_msg_SecurityAdvisory(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [id, summary, html_url, api_url] -- maps:keys(M), 'SecurityAdvisory'}, M, Path); v_msg_SecurityAdvisory(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'SecurityAdvisory'}, X, Path). -compile({nowarn_unused_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}). 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_submsg_Timestamp/3}). v_submsg_Timestamp(Msg, Path, TrUserData) -> v_msg_Timestamp(Msg, Path, TrUserData). -compile({nowarn_unused_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_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_enum_AdvisorySeverity/3}). v_enum_AdvisorySeverity('SEVERITY_NONE', _Path, _TrUserData) -> ok; v_enum_AdvisorySeverity('SEVERITY_LOW', _Path, _TrUserData) -> ok; v_enum_AdvisorySeverity('SEVERITY_MEDIUM', _Path, _TrUserData) -> ok; v_enum_AdvisorySeverity('SEVERITY_HIGH', _Path, _TrUserData) -> ok; v_enum_AdvisorySeverity('SEVERITY_CRITICAL', _Path, _TrUserData) -> ok; v_enum_AdvisorySeverity(V, _Path, _TrUserData) when -2147483648 =< V, V =< 2147483647, is_integer(V) -> ok; v_enum_AdvisorySeverity(X, Path, _TrUserData) -> mk_type_error({invalid_enum, 'AdvisorySeverity'}, X, Path). -compile({nowarn_unused_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}). 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_uint32/3}). v_type_uint32(N, _Path, _TrUserData) when is_integer(N), 0 =< N, N =< 4294967295 -> ok; v_type_uint32(N, Path, _TrUserData) when is_integer(N) -> mk_type_error({value_out_of_range, uint32, unsigned, 32}, N, Path); v_type_uint32(X, Path, _TrUserData) -> mk_type_error({bad_integer, uint32, unsigned, 32}, X, Path). -compile({nowarn_unused_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_float/3}). v_type_float(N, _Path, _TrUserData) when is_float(N) -> ok; v_type_float(N, _Path, _TrUserData) when is_integer(N) -> ok; v_type_float(infinity, _Path, _TrUserData) -> ok; v_type_float('-infinity', _Path, _TrUserData) -> ok; v_type_float(nan, _Path, _TrUserData) -> ok; v_type_float(X, Path, _TrUserData) -> mk_type_error(bad_float_value, X, Path). -compile({nowarn_unused_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}). 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}). prettify_path([]) -> top_level; prettify_path(PathR) -> string: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}]}, {{enum, 'AdvisorySeverity'}, [{'SEVERITY_NONE', 0}, {'SEVERITY_LOW', 1}, {'SEVERITY_MEDIUM', 2}, {'SEVERITY_HIGH', 3}, {'SEVERITY_CRITICAL', 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 => []}, #{name => advisories, fnum => 4, rnum => 5, type => {msg, 'SecurityAdvisory'}, occurrence => repeated, 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 => []}, #{name => advisory_indexes, fnum => 6, rnum => 7, type => uint32, occurrence => repeated, opts => []}, #{name => published_at, fnum => 7, rnum => 8, type => {msg, 'Timestamp'}, 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, 'SecurityAdvisory'}, [#{name => id, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => summary, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}, #{name => html_url, fnum => 3, rnum => 4, type => string, occurrence => required, opts => []}, #{name => severity, fnum => 4, rnum => 5, type => {enum, 'AdvisorySeverity'}, occurrence => optional, opts => []}, #{name => cvss_score, fnum => 5, rnum => 6, type => float, occurrence => optional, opts => []}, #{name => api_url, fnum => 6, rnum => 7, type => string, occurrence => required, opts => []}, #{name => aliases, fnum => 7, rnum => 8, type => string, occurrence => repeated, 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 => []}]}, {{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() -> ['Package', 'Release', 'RetirementStatus', 'SecurityAdvisory', 'Dependency', 'Timestamp']. get_group_names() -> []. get_msg_or_group_names() -> ['Package', 'Release', 'RetirementStatus', 'SecurityAdvisory', 'Dependency', 'Timestamp']. get_enum_names() -> ['RetirementReason', 'AdvisorySeverity']. 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 => []}, #{name => advisories, fnum => 4, rnum => 5, type => {msg, 'SecurityAdvisory'}, occurrence => repeated, 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 => []}, #{name => advisory_indexes, fnum => 6, rnum => 7, type => uint32, occurrence => repeated, opts => []}, #{name => published_at, fnum => 7, rnum => 8, type => {msg, 'Timestamp'}, 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('SecurityAdvisory') -> [#{name => id, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => summary, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}, #{name => html_url, fnum => 3, rnum => 4, type => string, occurrence => required, opts => []}, #{name => severity, fnum => 4, rnum => 5, type => {enum, 'AdvisorySeverity'}, occurrence => optional, opts => []}, #{name => cvss_score, fnum => 5, rnum => 6, type => float, occurrence => optional, opts => []}, #{name => api_url, fnum => 6, rnum => 7, type => string, occurrence => required, opts => []}, #{name => aliases, fnum => 7, rnum => 8, type => string, occurrence => repeated, 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('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('RetirementReason') -> [{'RETIRED_OTHER', 0}, {'RETIRED_INVALID', 1}, {'RETIRED_SECURITY', 2}, {'RETIRED_DEPRECATED', 3}, {'RETIRED_RENAMED', 4}]; find_enum_def('AdvisorySeverity') -> [{'SEVERITY_NONE', 0}, {'SEVERITY_LOW', 1}, {'SEVERITY_MEDIUM', 2}, {'SEVERITY_HIGH', 3}, {'SEVERITY_CRITICAL', 4}]; find_enum_def(_) -> error. enum_symbol_by_value('RetirementReason', Value) -> enum_symbol_by_value_RetirementReason(Value); enum_symbol_by_value('AdvisorySeverity', Value) -> enum_symbol_by_value_AdvisorySeverity(Value). enum_value_by_symbol('RetirementReason', Sym) -> enum_value_by_symbol_RetirementReason(Sym); enum_value_by_symbol('AdvisorySeverity', Sym) -> enum_value_by_symbol_AdvisorySeverity(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. enum_symbol_by_value_AdvisorySeverity(0) -> 'SEVERITY_NONE'; enum_symbol_by_value_AdvisorySeverity(1) -> 'SEVERITY_LOW'; enum_symbol_by_value_AdvisorySeverity(2) -> 'SEVERITY_MEDIUM'; enum_symbol_by_value_AdvisorySeverity(3) -> 'SEVERITY_HIGH'; enum_symbol_by_value_AdvisorySeverity(4) -> 'SEVERITY_CRITICAL'. enum_value_by_symbol_AdvisorySeverity('SEVERITY_NONE') -> 0; enum_value_by_symbol_AdvisorySeverity('SEVERITY_LOW') -> 1; enum_value_by_symbol_AdvisorySeverity('SEVERITY_MEDIUM') -> 2; enum_value_by_symbol_AdvisorySeverity('SEVERITY_HIGH') -> 3; enum_value_by_symbol_AdvisorySeverity('SEVERITY_CRITICAL') -> 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(<<"SecurityAdvisory">>) -> 'SecurityAdvisory'; fqbin_to_msg_name(<<"Dependency">>) -> 'Dependency'; fqbin_to_msg_name(<<"Timestamp">>) -> 'Timestamp'; 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('SecurityAdvisory') -> <<"SecurityAdvisory">>; msg_name_to_fqbin('Dependency') -> <<"Dependency">>; msg_name_to_fqbin('Timestamp') -> <<"Timestamp">>; msg_name_to_fqbin(E) -> error({gpb_error, {badmsg, E}}). fqbin_to_enum_name(<<"RetirementReason">>) -> 'RetirementReason'; fqbin_to_enum_name(<<"AdvisorySeverity">>) -> 'AdvisorySeverity'; fqbin_to_enum_name(E) -> error({gpb_error, {badenum, E}}). enum_name_to_fqbin('RetirementReason') -> <<"RetirementReason">>; enum_name_to_fqbin('AdvisorySeverity') -> <<"AdvisorySeverity">>; 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', 'SecurityAdvisory', 'Timestamp']; 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") -> ['AdvisorySeverity', 'RetirementReason']; get_enum_containment(P) -> error({gpb_error, {badproto, P}}). get_proto_by_msg_name_as_fqbin(<<"Timestamp">>) -> "mix_hex_pb_package"; 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(<<"SecurityAdvisory">>) -> "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(<<"AdvisorySeverity">>) -> "mix_hex_pb_package"; 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.5.1/src/mix_hex_pb_policy.erl000066400000000000000000004777601522402544500173270ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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_policy). -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_Visibility/1, enum_value_by_symbol_Visibility/1]). -export([enum_symbol_by_value_OverrideAction/1, enum_value_by_symbol_OverrideAction/1]). -export([enum_symbol_by_value_RetirementReason/1, enum_value_by_symbol_RetirementReason/1]). -export([enum_symbol_by_value_AdvisorySeverity/1, enum_value_by_symbol_AdvisorySeverity/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 'Visibility'() :: 'VISIBILITY_PRIVATE' | 'VISIBILITY_PUBLIC'. -type 'OverrideAction'() :: 'OVERRIDE_ACTION_ALLOW' | 'OVERRIDE_ACTION_DENY'. -type 'RetirementReason'() :: 'RETIRED_OTHER' | 'RETIRED_INVALID' | 'RETIRED_SECURITY' | 'RETIRED_DEPRECATED' | 'RETIRED_RENAMED'. -type 'AdvisorySeverity'() :: 'SEVERITY_NONE' | 'SEVERITY_LOW' | 'SEVERITY_MEDIUM' | 'SEVERITY_HIGH' | 'SEVERITY_CRITICAL'. -export_type(['Visibility'/0, 'OverrideAction'/0, 'RetirementReason'/0, 'AdvisorySeverity'/0]). %% message types -type 'Policy'() :: #{repository => unicode:chardata(), % = 1, required name => unicode:chardata(), % = 2, required description => unicode:chardata(), % = 3, optional visibility => 'VISIBILITY_PRIVATE' | 'VISIBILITY_PUBLIC' | integer(), % = 4, required, enum Visibility repositories => ['RepositoryPolicy'()] % = 5, repeated }. -type 'RepositoryPolicy'() :: #{repository => unicode:chardata(), % = 1, required restriction => 'Restriction'(), % = 2, optional overrides => ['Override'()] % = 3, repeated }. -type 'Restriction'() :: #{advisory_min_severity => 'SEVERITY_NONE' | 'SEVERITY_LOW' | 'SEVERITY_MEDIUM' | 'SEVERITY_HIGH' | 'SEVERITY_CRITICAL' | integer(), % = 1, optional, enum AdvisorySeverity retirement_reasons => ['RETIRED_OTHER' | 'RETIRED_INVALID' | 'RETIRED_SECURITY' | 'RETIRED_DEPRECATED' | 'RETIRED_RENAMED' | integer()], % = 2, repeated, enum RetirementReason cooldown => unicode:chardata() % = 3, optional }. -type 'PackageRef'() :: #{package => unicode:chardata(), % = 1, required requirement => unicode:chardata() % = 2, optional }. -type 'Override'() :: #{action => 'OVERRIDE_ACTION_ALLOW' | 'OVERRIDE_ACTION_DENY' | integer(), % = 1, required, enum OverrideAction ref => 'PackageRef'() % = 2, required }. -type 'Package'() :: #{releases => ['Release'()], % = 1, repeated name => unicode:chardata(), % = 2, required repository => unicode:chardata(), % = 3, required advisories => ['SecurityAdvisory'()] % = 4, repeated }. -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 advisory_indexes => [non_neg_integer()], % = 6, repeated, 32 bits published_at => 'Timestamp'() % = 7, 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 'SecurityAdvisory'() :: #{id => unicode:chardata(), % = 1, required summary => unicode:chardata(), % = 2, required html_url => unicode:chardata(), % = 3, required severity => 'SEVERITY_NONE' | 'SEVERITY_LOW' | 'SEVERITY_MEDIUM' | 'SEVERITY_HIGH' | 'SEVERITY_CRITICAL' | integer(), % = 4, optional, enum AdvisorySeverity cvss_score => float() | integer() | infinity | '-infinity' | nan, % = 5, optional api_url => unicode:chardata(), % = 6, required aliases => [unicode:chardata()] % = 7, repeated }. -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 }. -type 'Timestamp'() :: #{seconds => integer(), % = 1, required, 64 bits nanos => integer() % = 2, required, 32 bits }. -export_type(['Policy'/0, 'RepositoryPolicy'/0, 'Restriction'/0, 'PackageRef'/0, 'Override'/0, 'Package'/0, 'Release'/0, 'RetirementStatus'/0, 'SecurityAdvisory'/0, 'Dependency'/0, 'Timestamp'/0]). -type '$msg_name'() :: 'Policy' | 'RepositoryPolicy' | 'Restriction' | 'PackageRef' | 'Override' | 'Package' | 'Release' | 'RetirementStatus' | 'SecurityAdvisory' | 'Dependency' | 'Timestamp'. -type '$msg'() :: 'Policy'() | 'RepositoryPolicy'() | 'Restriction'() | 'PackageRef'() | 'Override'() | 'Package'() | 'Release'() | 'RetirementStatus'() | 'SecurityAdvisory'() | 'Dependency'() | 'Timestamp'(). -export_type(['$msg_name'/0, '$msg'/0]). -spec encode_msg('$msg'(), '$msg_name'()) -> binary(). encode_msg(Msg, MsgName) when is_atom(MsgName) -> encode_msg(Msg, MsgName, []). -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 'Policy' -> encode_msg_Policy(id(Msg, TrUserData), TrUserData); 'RepositoryPolicy' -> encode_msg_RepositoryPolicy(id(Msg, TrUserData), TrUserData); 'Restriction' -> encode_msg_Restriction(id(Msg, TrUserData), TrUserData); 'PackageRef' -> encode_msg_PackageRef(id(Msg, TrUserData), TrUserData); 'Override' -> encode_msg_Override(id(Msg, TrUserData), TrUserData); 'Package' -> encode_msg_Package(id(Msg, TrUserData), TrUserData); 'Release' -> encode_msg_Release(id(Msg, TrUserData), TrUserData); 'RetirementStatus' -> encode_msg_RetirementStatus(id(Msg, TrUserData), TrUserData); 'SecurityAdvisory' -> encode_msg_SecurityAdvisory(id(Msg, TrUserData), TrUserData); 'Dependency' -> encode_msg_Dependency(id(Msg, TrUserData), TrUserData); 'Timestamp' -> encode_msg_Timestamp(id(Msg, TrUserData), TrUserData) end. encode_msg_Policy(Msg, TrUserData) -> encode_msg_Policy(Msg, <<>>, TrUserData). encode_msg_Policy(#{repository := F1, name := F2, visibility := F4} = 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 #{description := F3} -> begin TrF3 = id(F3, TrUserData), e_type_string(TrF3, <>, TrUserData) end; _ -> B2 end, B4 = begin TrF4 = id(F4, TrUserData), e_enum_Visibility(TrF4, <>, TrUserData) end, case M of #{repositories := F5} -> TrF5 = id(F5, TrUserData), if TrF5 == [] -> B4; true -> e_field_Policy_repositories(TrF5, B4, TrUserData) end; _ -> B4 end. encode_msg_RepositoryPolicy(Msg, TrUserData) -> encode_msg_RepositoryPolicy(Msg, <<>>, TrUserData). encode_msg_RepositoryPolicy(#{repository := F1} = M, Bin, TrUserData) -> B1 = begin TrF1 = id(F1, TrUserData), e_type_string(TrF1, <>, TrUserData) end, B2 = case M of #{restriction := F2} -> begin TrF2 = id(F2, TrUserData), e_mfield_RepositoryPolicy_restriction(TrF2, <>, TrUserData) end; _ -> B1 end, case M of #{overrides := F3} -> TrF3 = id(F3, TrUserData), if TrF3 == [] -> B2; true -> e_field_RepositoryPolicy_overrides(TrF3, B2, TrUserData) end; _ -> B2 end. encode_msg_Restriction(Msg, TrUserData) -> encode_msg_Restriction(Msg, <<>>, TrUserData). encode_msg_Restriction(#{} = M, Bin, TrUserData) -> B1 = case M of #{advisory_min_severity := F1} -> begin TrF1 = id(F1, TrUserData), e_enum_AdvisorySeverity(TrF1, <>, TrUserData) end; _ -> Bin end, B2 = case M of #{retirement_reasons := F2} -> TrF2 = id(F2, TrUserData), if TrF2 == [] -> B1; true -> e_field_Restriction_retirement_reasons(TrF2, B1, TrUserData) end; _ -> B1 end, case M of #{cooldown := F3} -> begin TrF3 = id(F3, TrUserData), e_type_string(TrF3, <>, TrUserData) end; _ -> B2 end. encode_msg_PackageRef(Msg, TrUserData) -> encode_msg_PackageRef(Msg, <<>>, TrUserData). encode_msg_PackageRef(#{package := F1} = M, Bin, TrUserData) -> B1 = begin TrF1 = id(F1, TrUserData), e_type_string(TrF1, <>, TrUserData) end, case M of #{requirement := F2} -> begin TrF2 = id(F2, TrUserData), e_type_string(TrF2, <>, TrUserData) end; _ -> B1 end. encode_msg_Override(Msg, TrUserData) -> encode_msg_Override(Msg, <<>>, TrUserData). encode_msg_Override(#{action := F1, ref := F2}, Bin, TrUserData) -> B1 = begin TrF1 = id(F1, TrUserData), e_enum_OverrideAction(TrF1, <>, TrUserData) end, begin TrF2 = id(F2, TrUserData), e_mfield_Override_ref(TrF2, <>, 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, B3 = begin TrF3 = id(F3, TrUserData), e_type_string(TrF3, <>, TrUserData) end, case M of #{advisories := F4} -> TrF4 = id(F4, TrUserData), if TrF4 == [] -> B3; true -> e_field_Package_advisories(TrF4, B3, TrUserData) end; _ -> B3 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, B5 = case M of #{outer_checksum := F5} -> begin TrF5 = id(F5, TrUserData), e_type_bytes(TrF5, <>, TrUserData) end; _ -> B4 end, B6 = case M of #{advisory_indexes := F6} -> TrF6 = id(F6, TrUserData), if TrF6 == [] -> B5; true -> e_field_Release_advisory_indexes(TrF6, B5, TrUserData) end; _ -> B5 end, case M of #{published_at := F7} -> begin TrF7 = id(F7, TrUserData), e_mfield_Release_published_at(TrF7, <>, TrUserData) end; _ -> B6 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_SecurityAdvisory(Msg, TrUserData) -> encode_msg_SecurityAdvisory(Msg, <<>>, TrUserData). encode_msg_SecurityAdvisory(#{id := F1, summary := F2, html_url := F3, api_url := F6} = 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 = begin TrF3 = id(F3, TrUserData), e_type_string(TrF3, <>, TrUserData) end, B4 = case M of #{severity := F4} -> begin TrF4 = id(F4, TrUserData), e_enum_AdvisorySeverity(TrF4, <>, TrUserData) end; _ -> B3 end, B5 = case M of #{cvss_score := F5} -> begin TrF5 = id(F5, TrUserData), e_type_float(TrF5, <>, TrUserData) end; _ -> B4 end, B6 = begin TrF6 = id(F6, TrUserData), e_type_string(TrF6, <>, TrUserData) end, case M of #{aliases := F7} -> TrF7 = id(F7, TrUserData), if TrF7 == [] -> B6; true -> e_field_SecurityAdvisory_aliases(TrF7, B6, TrUserData) end; _ -> B6 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. 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_Policy_repositories(Msg, Bin, TrUserData) -> SubBin = encode_msg_RepositoryPolicy(Msg, <<>>, TrUserData), Bin2 = e_varint(byte_size(SubBin), Bin), <>. e_field_Policy_repositories([Elem | Rest], Bin, TrUserData) -> Bin2 = <>, Bin3 = e_mfield_Policy_repositories(id(Elem, TrUserData), Bin2, TrUserData), e_field_Policy_repositories(Rest, Bin3, TrUserData); e_field_Policy_repositories([], Bin, _TrUserData) -> Bin. e_mfield_RepositoryPolicy_restriction(Msg, Bin, TrUserData) -> SubBin = encode_msg_Restriction(Msg, <<>>, TrUserData), Bin2 = e_varint(byte_size(SubBin), Bin), <>. e_mfield_RepositoryPolicy_overrides(Msg, Bin, TrUserData) -> SubBin = encode_msg_Override(Msg, <<>>, TrUserData), Bin2 = e_varint(byte_size(SubBin), Bin), <>. e_field_RepositoryPolicy_overrides([Elem | Rest], Bin, TrUserData) -> Bin2 = <>, Bin3 = e_mfield_RepositoryPolicy_overrides(id(Elem, TrUserData), Bin2, TrUserData), e_field_RepositoryPolicy_overrides(Rest, Bin3, TrUserData); e_field_RepositoryPolicy_overrides([], Bin, _TrUserData) -> Bin. e_field_Restriction_retirement_reasons(Elems, Bin, TrUserData) when Elems =/= [] -> SubBin = e_pfield_Restriction_retirement_reasons(Elems, <<>>, TrUserData), Bin2 = <>, Bin3 = e_varint(byte_size(SubBin), Bin2), <>; e_field_Restriction_retirement_reasons([], Bin, _TrUserData) -> Bin. e_pfield_Restriction_retirement_reasons([Value | Rest], Bin, TrUserData) -> Bin2 = e_enum_RetirementReason(id(Value, TrUserData), Bin, TrUserData), e_pfield_Restriction_retirement_reasons(Rest, Bin2, TrUserData); e_pfield_Restriction_retirement_reasons([], Bin, _TrUserData) -> Bin. e_mfield_Override_ref(Msg, Bin, TrUserData) -> SubBin = encode_msg_PackageRef(Msg, <<>>, TrUserData), Bin2 = e_varint(byte_size(SubBin), Bin), <>. 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_Package_advisories(Msg, Bin, TrUserData) -> SubBin = encode_msg_SecurityAdvisory(Msg, <<>>, TrUserData), Bin2 = e_varint(byte_size(SubBin), Bin), <>. e_field_Package_advisories([Elem | Rest], Bin, TrUserData) -> Bin2 = <>, Bin3 = e_mfield_Package_advisories(id(Elem, TrUserData), Bin2, TrUserData), e_field_Package_advisories(Rest, Bin3, TrUserData); e_field_Package_advisories([], 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_field_Release_advisory_indexes([Elem | Rest], Bin, TrUserData) -> Bin2 = <>, Bin3 = e_varint(id(Elem, TrUserData), Bin2, TrUserData), e_field_Release_advisory_indexes(Rest, Bin3, TrUserData); e_field_Release_advisory_indexes([], Bin, _TrUserData) -> Bin. e_mfield_Release_published_at(Msg, Bin, TrUserData) -> SubBin = encode_msg_Timestamp(Msg, <<>>, TrUserData), Bin2 = e_varint(byte_size(SubBin), Bin), <>. e_field_SecurityAdvisory_aliases([Elem | Rest], Bin, TrUserData) -> Bin2 = <>, Bin3 = e_type_string(id(Elem, TrUserData), Bin2, TrUserData), e_field_SecurityAdvisory_aliases(Rest, Bin3, TrUserData); e_field_SecurityAdvisory_aliases([], Bin, _TrUserData) -> Bin. e_enum_Visibility('VISIBILITY_PRIVATE', Bin, _TrUserData) -> <>; e_enum_Visibility('VISIBILITY_PUBLIC', Bin, _TrUserData) -> <>; e_enum_Visibility(V, Bin, _TrUserData) -> e_varint(V, Bin). e_enum_OverrideAction('OVERRIDE_ACTION_ALLOW', Bin, _TrUserData) -> <>; e_enum_OverrideAction('OVERRIDE_ACTION_DENY', Bin, _TrUserData) -> <>; e_enum_OverrideAction(V, Bin, _TrUserData) -> e_varint(V, 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). e_enum_AdvisorySeverity('SEVERITY_NONE', Bin, _TrUserData) -> <>; e_enum_AdvisorySeverity('SEVERITY_LOW', Bin, _TrUserData) -> <>; e_enum_AdvisorySeverity('SEVERITY_MEDIUM', Bin, _TrUserData) -> <>; e_enum_AdvisorySeverity('SEVERITY_HIGH', Bin, _TrUserData) -> <>; e_enum_AdvisorySeverity('SEVERITY_CRITICAL', Bin, _TrUserData) -> <>; e_enum_AdvisorySeverity(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('Policy', Bin, TrUserData) -> id(decode_msg_Policy(Bin, TrUserData), TrUserData); decode_msg_2_doit('RepositoryPolicy', Bin, TrUserData) -> id(decode_msg_RepositoryPolicy(Bin, TrUserData), TrUserData); decode_msg_2_doit('Restriction', Bin, TrUserData) -> id(decode_msg_Restriction(Bin, TrUserData), TrUserData); decode_msg_2_doit('PackageRef', Bin, TrUserData) -> id(decode_msg_PackageRef(Bin, TrUserData), TrUserData); decode_msg_2_doit('Override', Bin, TrUserData) -> id(decode_msg_Override(Bin, TrUserData), TrUserData); 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('SecurityAdvisory', Bin, TrUserData) -> id(decode_msg_SecurityAdvisory(Bin, TrUserData), TrUserData); decode_msg_2_doit('Dependency', Bin, TrUserData) -> id(decode_msg_Dependency(Bin, TrUserData), TrUserData); decode_msg_2_doit('Timestamp', Bin, TrUserData) -> id(decode_msg_Timestamp(Bin, TrUserData), TrUserData). decode_msg_Policy(Bin, TrUserData) -> dfp_read_field_def_Policy(Bin, 0, 0, 0, id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id([], TrUserData), TrUserData). dfp_read_field_def_Policy(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Policy_repository(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); dfp_read_field_def_Policy(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Policy_name(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); dfp_read_field_def_Policy(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Policy_description(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); dfp_read_field_def_Policy(<<32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Policy_visibility(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); dfp_read_field_def_Policy(<<42, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> d_field_Policy_repositories(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); dfp_read_field_def_Policy(<<>>, 0, 0, _, F@_1, F@_2, F@_3, F@_4, R1, TrUserData) -> S1 = #{repository => F@_1, name => F@_2, visibility => F@_4}, S2 = if F@_3 == '$undef' -> S1; true -> S1#{description => F@_3} end, if R1 == '$undef' -> S2; true -> S2#{repositories => lists_reverse(R1, TrUserData)} end; dfp_read_field_def_Policy(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> dg_read_field_def_Policy(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). dg_read_field_def_Policy(<<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_Policy(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); dg_read_field_def_Policy(<<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_Policy_repository(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 18 -> d_field_Policy_name(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 26 -> d_field_Policy_description(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 32 -> d_field_Policy_visibility(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 42 -> d_field_Policy_repositories(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_Policy(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 1 -> skip_64_Policy(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 2 -> skip_length_delimited_Policy(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 3 -> skip_group_Policy(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); 5 -> skip_32_Policy(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) end end; dg_read_field_def_Policy(<<>>, 0, 0, _, F@_1, F@_2, F@_3, F@_4, R1, TrUserData) -> S1 = #{repository => F@_1, name => F@_2, visibility => F@_4}, S2 = if F@_3 == '$undef' -> S1; true -> S1#{description => F@_3} end, if R1 == '$undef' -> S2; true -> S2#{repositories => lists_reverse(R1, TrUserData)} end. d_field_Policy_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_Policy_repository(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); d_field_Policy_repository(<<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_Policy(RestF, 0, 0, F, NewFValue, F@_2, F@_3, F@_4, F@_5, TrUserData). d_field_Policy_name(<<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_Policy_name(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); d_field_Policy_name(<<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_Policy(RestF, 0, 0, F, F@_1, NewFValue, F@_3, F@_4, F@_5, TrUserData). d_field_Policy_description(<<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_Policy_description(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); d_field_Policy_description(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, _, 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_Policy(RestF, 0, 0, F, F@_1, F@_2, NewFValue, F@_4, F@_5, TrUserData). d_field_Policy_visibility(<<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_Policy_visibility(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); d_field_Policy_visibility(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, _, F@_5, TrUserData) -> {NewFValue, RestF} = {id(d_enum_Visibility(begin <> = <<(X bsl N + Acc):32/unsigned-native>>, id(Res, TrUserData) end), TrUserData), Rest}, dfp_read_field_def_Policy(RestF, 0, 0, F, F@_1, F@_2, F@_3, NewFValue, F@_5, TrUserData). d_field_Policy_repositories(<<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_Policy_repositories(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); d_field_Policy_repositories(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, Prev, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, {id(decode_msg_RepositoryPolicy(Bs, TrUserData), TrUserData), Rest2} end, dfp_read_field_def_Policy(RestF, 0, 0, F, F@_1, F@_2, F@_3, F@_4, cons(NewFValue, Prev, TrUserData), TrUserData). skip_varint_Policy(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> skip_varint_Policy(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); skip_varint_Policy(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> dfp_read_field_def_Policy(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). skip_length_delimited_Policy(<<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_Policy(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData); skip_length_delimited_Policy(<<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_Policy(Rest2, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). skip_group_Policy(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_Policy(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). skip_32_Policy(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> dfp_read_field_def_Policy(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). skip_64_Policy(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData) -> dfp_read_field_def_Policy(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, TrUserData). decode_msg_RepositoryPolicy(Bin, TrUserData) -> dfp_read_field_def_RepositoryPolicy(Bin, 0, 0, 0, id('$undef', TrUserData), id('$undef', TrUserData), id([], TrUserData), TrUserData). dfp_read_field_def_RepositoryPolicy(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_field_RepositoryPolicy_repository(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); dfp_read_field_def_RepositoryPolicy(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_field_RepositoryPolicy_restriction(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); dfp_read_field_def_RepositoryPolicy(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_field_RepositoryPolicy_overrides(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); dfp_read_field_def_RepositoryPolicy(<<>>, 0, 0, _, F@_1, F@_2, R1, TrUserData) -> S1 = #{repository => F@_1}, S2 = if F@_2 == '$undef' -> S1; true -> S1#{restriction => F@_2} end, if R1 == '$undef' -> S2; true -> S2#{overrides => lists_reverse(R1, TrUserData)} end; dfp_read_field_def_RepositoryPolicy(Other, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dg_read_field_def_RepositoryPolicy(Other, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). dg_read_field_def_RepositoryPolicy(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 32 - 7 -> dg_read_field_def_RepositoryPolicy(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); dg_read_field_def_RepositoryPolicy(<<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_RepositoryPolicy_repository(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); 18 -> d_field_RepositoryPolicy_restriction(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); 26 -> d_field_RepositoryPolicy_overrides(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_RepositoryPolicy(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); 1 -> skip_64_RepositoryPolicy(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); 2 -> skip_length_delimited_RepositoryPolicy(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); 3 -> skip_group_RepositoryPolicy(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); 5 -> skip_32_RepositoryPolicy(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData) end end; dg_read_field_def_RepositoryPolicy(<<>>, 0, 0, _, F@_1, F@_2, R1, TrUserData) -> S1 = #{repository => F@_1}, S2 = if F@_2 == '$undef' -> S1; true -> S1#{restriction => F@_2} end, if R1 == '$undef' -> S2; true -> S2#{overrides => lists_reverse(R1, TrUserData)} end. d_field_RepositoryPolicy_repository(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_field_RepositoryPolicy_repository(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); d_field_RepositoryPolicy_repository(<<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_RepositoryPolicy(RestF, 0, 0, F, NewFValue, F@_2, F@_3, TrUserData). d_field_RepositoryPolicy_restriction(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_field_RepositoryPolicy_restriction(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); d_field_RepositoryPolicy_restriction(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, Prev, F@_3, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, {id(decode_msg_Restriction(Bs, TrUserData), TrUserData), Rest2} end, dfp_read_field_def_RepositoryPolicy(RestF, 0, 0, F, F@_1, if Prev == '$undef' -> NewFValue; true -> merge_msg_Restriction(Prev, NewFValue, TrUserData) end, F@_3, TrUserData). d_field_RepositoryPolicy_overrides(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_field_RepositoryPolicy_overrides(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); d_field_RepositoryPolicy_overrides(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, Prev, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, {id(decode_msg_Override(Bs, TrUserData), TrUserData), Rest2} end, dfp_read_field_def_RepositoryPolicy(RestF, 0, 0, F, F@_1, F@_2, cons(NewFValue, Prev, TrUserData), TrUserData). skip_varint_RepositoryPolicy(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> skip_varint_RepositoryPolicy(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); skip_varint_RepositoryPolicy(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dfp_read_field_def_RepositoryPolicy(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). skip_length_delimited_RepositoryPolicy(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> skip_length_delimited_RepositoryPolicy(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); skip_length_delimited_RepositoryPolicy(<<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_RepositoryPolicy(Rest2, 0, 0, F, F@_1, F@_2, F@_3, TrUserData). skip_group_RepositoryPolicy(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_RepositoryPolicy(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, TrUserData). skip_32_RepositoryPolicy(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dfp_read_field_def_RepositoryPolicy(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). skip_64_RepositoryPolicy(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dfp_read_field_def_RepositoryPolicy(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). decode_msg_Restriction(Bin, TrUserData) -> dfp_read_field_def_Restriction(Bin, 0, 0, 0, id('$undef', TrUserData), id([], TrUserData), id('$undef', TrUserData), TrUserData). dfp_read_field_def_Restriction(<<8, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_field_Restriction_advisory_min_severity(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); dfp_read_field_def_Restriction(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_pfield_Restriction_retirement_reasons(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); dfp_read_field_def_Restriction(<<16, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_field_Restriction_retirement_reasons(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); dfp_read_field_def_Restriction(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> d_field_Restriction_cooldown(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); dfp_read_field_def_Restriction(<<>>, 0, 0, _, F@_1, R1, F@_3, TrUserData) -> S1 = #{retirement_reasons => lists_reverse(R1, TrUserData)}, S2 = if F@_1 == '$undef' -> S1; true -> S1#{advisory_min_severity => F@_1} end, if F@_3 == '$undef' -> S2; true -> S2#{cooldown => F@_3} end; dfp_read_field_def_Restriction(Other, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dg_read_field_def_Restriction(Other, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). dg_read_field_def_Restriction(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 32 - 7 -> dg_read_field_def_Restriction(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); dg_read_field_def_Restriction(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, TrUserData) -> Key = X bsl N + Acc, case Key of 8 -> d_field_Restriction_advisory_min_severity(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); 18 -> d_pfield_Restriction_retirement_reasons(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); 16 -> d_field_Restriction_retirement_reasons(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); 26 -> d_field_Restriction_cooldown(Rest, 0, 0, 0, F@_1, F@_2, F@_3, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_Restriction(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); 1 -> skip_64_Restriction(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); 2 -> skip_length_delimited_Restriction(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); 3 -> skip_group_Restriction(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData); 5 -> skip_32_Restriction(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, TrUserData) end end; dg_read_field_def_Restriction(<<>>, 0, 0, _, F@_1, R1, F@_3, TrUserData) -> S1 = #{retirement_reasons => lists_reverse(R1, TrUserData)}, S2 = if F@_1 == '$undef' -> S1; true -> S1#{advisory_min_severity => F@_1} end, if F@_3 == '$undef' -> S2; true -> S2#{cooldown => F@_3} end. d_field_Restriction_advisory_min_severity(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_field_Restriction_advisory_min_severity(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); d_field_Restriction_advisory_min_severity(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, F@_3, TrUserData) -> {NewFValue, RestF} = {id(d_enum_AdvisorySeverity(begin <> = <<(X bsl N + Acc):32/unsigned-native>>, id(Res, TrUserData) end), TrUserData), Rest}, dfp_read_field_def_Restriction(RestF, 0, 0, F, NewFValue, F@_2, F@_3, TrUserData). d_field_Restriction_retirement_reasons(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_field_Restriction_retirement_reasons(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); d_field_Restriction_retirement_reasons(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, Prev, F@_3, 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_Restriction(RestF, 0, 0, F, F@_1, cons(NewFValue, Prev, TrUserData), F@_3, TrUserData). d_pfield_Restriction_retirement_reasons(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_pfield_Restriction_retirement_reasons(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); d_pfield_Restriction_retirement_reasons(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, E, F@_3, TrUserData) -> Len = X bsl N + Acc, <> = Rest, NewSeq = d_packed_field_Restriction_retirement_reasons(PackedBytes, 0, 0, F, E, TrUserData), dfp_read_field_def_Restriction(Rest2, 0, 0, F, F@_1, NewSeq, F@_3, TrUserData). d_packed_field_Restriction_retirement_reasons(<<1:1, X:7, Rest/binary>>, N, Acc, F, AccSeq, TrUserData) when N < 57 -> d_packed_field_Restriction_retirement_reasons(Rest, N + 7, X bsl N + Acc, F, AccSeq, TrUserData); d_packed_field_Restriction_retirement_reasons(<<0:1, X:7, Rest/binary>>, N, Acc, F, AccSeq, TrUserData) -> {NewFValue, RestF} = {id(d_enum_RetirementReason(begin <> = <<(X bsl N + Acc):32/unsigned-native>>, id(Res, TrUserData) end), TrUserData), Rest}, d_packed_field_Restriction_retirement_reasons(RestF, 0, 0, F, [NewFValue | AccSeq], TrUserData); d_packed_field_Restriction_retirement_reasons(<<>>, 0, 0, _, AccSeq, _) -> AccSeq. d_field_Restriction_cooldown(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> d_field_Restriction_cooldown(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); d_field_Restriction_cooldown(<<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_Restriction(RestF, 0, 0, F, F@_1, F@_2, NewFValue, TrUserData). skip_varint_Restriction(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> skip_varint_Restriction(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData); skip_varint_Restriction(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dfp_read_field_def_Restriction(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). skip_length_delimited_Restriction(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, TrUserData) when N < 57 -> skip_length_delimited_Restriction(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, TrUserData); skip_length_delimited_Restriction(<<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_Restriction(Rest2, 0, 0, F, F@_1, F@_2, F@_3, TrUserData). skip_group_Restriction(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_Restriction(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, TrUserData). skip_32_Restriction(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dfp_read_field_def_Restriction(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). skip_64_Restriction(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData) -> dfp_read_field_def_Restriction(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, TrUserData). decode_msg_PackageRef(Bin, TrUserData) -> dfp_read_field_def_PackageRef(Bin, 0, 0, 0, id('$undef', TrUserData), id('$undef', TrUserData), TrUserData). dfp_read_field_def_PackageRef(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> d_field_PackageRef_package(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); dfp_read_field_def_PackageRef(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> d_field_PackageRef_requirement(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); dfp_read_field_def_PackageRef(<<>>, 0, 0, _, F@_1, F@_2, _) -> S1 = #{package => F@_1}, if F@_2 == '$undef' -> S1; true -> S1#{requirement => F@_2} end; dfp_read_field_def_PackageRef(Other, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dg_read_field_def_PackageRef(Other, Z1, Z2, F, F@_1, F@_2, TrUserData). dg_read_field_def_PackageRef(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 32 - 7 -> dg_read_field_def_PackageRef(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); dg_read_field_def_PackageRef(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, TrUserData) -> Key = X bsl N + Acc, case Key of 10 -> d_field_PackageRef_package(Rest, 0, 0, 0, F@_1, F@_2, TrUserData); 18 -> d_field_PackageRef_requirement(Rest, 0, 0, 0, F@_1, F@_2, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_PackageRef(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 1 -> skip_64_PackageRef(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 2 -> skip_length_delimited_PackageRef(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 3 -> skip_group_PackageRef(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 5 -> skip_32_PackageRef(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData) end end; dg_read_field_def_PackageRef(<<>>, 0, 0, _, F@_1, F@_2, _) -> S1 = #{package => F@_1}, if F@_2 == '$undef' -> S1; true -> S1#{requirement => F@_2} end. d_field_PackageRef_package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> d_field_PackageRef_package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); d_field_PackageRef_package(<<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_PackageRef(RestF, 0, 0, F, NewFValue, F@_2, TrUserData). d_field_PackageRef_requirement(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> d_field_PackageRef_requirement(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); d_field_PackageRef_requirement(<<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_PackageRef(RestF, 0, 0, F, F@_1, NewFValue, TrUserData). skip_varint_PackageRef(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> skip_varint_PackageRef(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); skip_varint_PackageRef(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_PackageRef(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). skip_length_delimited_PackageRef(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> skip_length_delimited_PackageRef(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); skip_length_delimited_PackageRef(<<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_PackageRef(Rest2, 0, 0, F, F@_1, F@_2, TrUserData). skip_group_PackageRef(Bin, _, Z2, FNum, F@_1, F@_2, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_PackageRef(Rest, 0, Z2, FNum, F@_1, F@_2, TrUserData). skip_32_PackageRef(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_PackageRef(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). skip_64_PackageRef(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_PackageRef(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). decode_msg_Override(Bin, TrUserData) -> dfp_read_field_def_Override(Bin, 0, 0, 0, id('$undef', TrUserData), id('$undef', TrUserData), TrUserData). dfp_read_field_def_Override(<<8, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> d_field_Override_action(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); dfp_read_field_def_Override(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> d_field_Override_ref(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); dfp_read_field_def_Override(<<>>, 0, 0, _, F@_1, F@_2, _) -> S1 = #{action => F@_1}, if F@_2 == '$undef' -> S1; true -> S1#{ref => F@_2} end; dfp_read_field_def_Override(Other, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dg_read_field_def_Override(Other, Z1, Z2, F, F@_1, F@_2, TrUserData). dg_read_field_def_Override(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 32 - 7 -> dg_read_field_def_Override(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); dg_read_field_def_Override(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, TrUserData) -> Key = X bsl N + Acc, case Key of 8 -> d_field_Override_action(Rest, 0, 0, 0, F@_1, F@_2, TrUserData); 18 -> d_field_Override_ref(Rest, 0, 0, 0, F@_1, F@_2, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_Override(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 1 -> skip_64_Override(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 2 -> skip_length_delimited_Override(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 3 -> skip_group_Override(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData); 5 -> skip_32_Override(Rest, 0, 0, Key bsr 3, F@_1, F@_2, TrUserData) end end; dg_read_field_def_Override(<<>>, 0, 0, _, F@_1, F@_2, _) -> S1 = #{action => F@_1}, if F@_2 == '$undef' -> S1; true -> S1#{ref => F@_2} end. d_field_Override_action(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> d_field_Override_action(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); d_field_Override_action(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, TrUserData) -> {NewFValue, RestF} = {id(d_enum_OverrideAction(begin <> = <<(X bsl N + Acc):32/unsigned-native>>, id(Res, TrUserData) end), TrUserData), Rest}, dfp_read_field_def_Override(RestF, 0, 0, F, NewFValue, F@_2, TrUserData). d_field_Override_ref(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> d_field_Override_ref(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); d_field_Override_ref(<<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_PackageRef(Bs, TrUserData), TrUserData), Rest2} end, dfp_read_field_def_Override(RestF, 0, 0, F, F@_1, if Prev == '$undef' -> NewFValue; true -> merge_msg_PackageRef(Prev, NewFValue, TrUserData) end, TrUserData). skip_varint_Override(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> skip_varint_Override(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData); skip_varint_Override(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Override(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). skip_length_delimited_Override(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, TrUserData) when N < 57 -> skip_length_delimited_Override(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, TrUserData); skip_length_delimited_Override(<<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_Override(Rest2, 0, 0, F, F@_1, F@_2, TrUserData). skip_group_Override(Bin, _, Z2, FNum, F@_1, F@_2, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_Override(Rest, 0, Z2, FNum, F@_1, F@_2, TrUserData). skip_32_Override(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Override(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). skip_64_Override(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, TrUserData) -> dfp_read_field_def_Override(Rest, Z1, Z2, F, F@_1, F@_2, TrUserData). decode_msg_Package(Bin, TrUserData) -> dfp_read_field_def_Package(Bin, 0, 0, 0, id([], TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id([], TrUserData), TrUserData). dfp_read_field_def_Package(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_releases(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); dfp_read_field_def_Package(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_name(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); dfp_read_field_def_Package(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_repository(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); dfp_read_field_def_Package(<<34, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_advisories(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); dfp_read_field_def_Package(<<>>, 0, 0, _, R1, F@_2, F@_3, R2, TrUserData) -> S1 = #{name => F@_2, repository => F@_3}, S2 = if R1 == '$undef' -> S1; true -> S1#{releases => lists_reverse(R1, TrUserData)} end, if R2 == '$undef' -> S2; true -> S2#{advisories => lists_reverse(R2, TrUserData)} end; dfp_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dg_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData). dg_read_field_def_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 32 - 7 -> dg_read_field_def_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); dg_read_field_def_Package(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, F@_4, TrUserData) -> Key = X bsl N + Acc, case Key of 10 -> d_field_Package_releases(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); 18 -> d_field_Package_name(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); 26 -> d_field_Package_repository(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); 34 -> d_field_Package_advisories(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); 1 -> skip_64_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); 2 -> skip_length_delimited_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); 3 -> skip_group_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); 5 -> skip_32_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData) end end; dg_read_field_def_Package(<<>>, 0, 0, _, R1, F@_2, F@_3, R2, TrUserData) -> S1 = #{name => F@_2, repository => F@_3}, S2 = if R1 == '$undef' -> S1; true -> S1#{releases => lists_reverse(R1, TrUserData)} end, if R2 == '$undef' -> S2; true -> S2#{advisories => lists_reverse(R2, TrUserData)} end. d_field_Package_releases(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_releases(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); d_field_Package_releases(<<0:1, X:7, Rest/binary>>, N, Acc, F, Prev, F@_2, F@_3, F@_4, 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, F@_4, TrUserData). d_field_Package_name(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_name(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); d_field_Package_name(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, 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_Package(RestF, 0, 0, F, F@_1, NewFValue, F@_3, F@_4, TrUserData). d_field_Package_repository(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_repository(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); d_field_Package_repository(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, _, 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_Package(RestF, 0, 0, F, F@_1, F@_2, NewFValue, F@_4, TrUserData). d_field_Package_advisories(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_advisories(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); d_field_Package_advisories(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, Prev, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, {id(decode_msg_SecurityAdvisory(Bs, TrUserData), TrUserData), Rest2} end, dfp_read_field_def_Package(RestF, 0, 0, F, F@_1, F@_2, F@_3, cons(NewFValue, Prev, TrUserData), TrUserData). skip_varint_Package(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> skip_varint_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); skip_varint_Package(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData). skip_length_delimited_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> skip_length_delimited_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); skip_length_delimited_Package(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, 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, F@_4, TrUserData). skip_group_Package(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, F@_4, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_Package(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, F@_4, TrUserData). skip_32_Package(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData). skip_64_Package(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, 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), id([], 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, F@_6, F@_7, TrUserData) -> d_field_Release_version(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_Release(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_inner_checksum(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_Release(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_dependencies(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_Release(<<34, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_retired(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_Release(<<42, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_outer_checksum(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_Release(<<50, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_pfield_Release_advisory_indexes(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_Release(<<48, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_advisory_indexes(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_Release(<<58, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_Release_published_at(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_Release(<<>>, 0, 0, _, F@_1, F@_2, R1, F@_4, F@_5, R2, F@_7, TrUserData) -> S1 = #{version => F@_1, inner_checksum => F@_2, advisory_indexes => lists_reverse(R2, TrUserData)}, 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, S4 = if F@_5 == '$undef' -> S3; true -> S3#{outer_checksum => F@_5} end, if F@_7 == '$undef' -> S4; true -> S4#{published_at => F@_7} end; dfp_read_field_def_Release(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dg_read_field_def_Release(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, 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, F@_6, F@_7, TrUserData); dg_read_field_def_Release(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData); 18 -> d_field_Release_inner_checksum(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 26 -> d_field_Release_dependencies(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 34 -> d_field_Release_retired(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 42 -> d_field_Release_outer_checksum(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 50 -> d_pfield_Release_advisory_indexes(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 48 -> d_field_Release_advisory_indexes(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 58 -> d_field_Release_published_at(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData); 1 -> skip_64_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 2 -> skip_length_delimited_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 3 -> skip_group_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 5 -> skip_32_Release(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) end end; dg_read_field_def_Release(<<>>, 0, 0, _, F@_1, F@_2, R1, F@_4, F@_5, R2, F@_7, TrUserData) -> S1 = #{version => F@_1, inner_checksum => F@_2, advisory_indexes => lists_reverse(R2, TrUserData)}, 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, S4 = if F@_5 == '$undef' -> S3; true -> S3#{outer_checksum => F@_5} end, if F@_7 == '$undef' -> S4; true -> S4#{published_at => F@_7} end. d_field_Release_version(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData); d_field_Release_version(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, 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, F@_6, F@_7, 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, F@_6, F@_7, TrUserData); d_field_Release_inner_checksum(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData). d_field_Release_dependencies(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData); d_field_Release_dependencies(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, Prev, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData). d_field_Release_retired(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData); d_field_Release_retired(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, Prev, F@_5, F@_6, F@_7, 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, F@_6, F@_7, 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, F@_6, F@_7, 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, F@_6, F@_7, TrUserData); d_field_Release_outer_checksum(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, _, F@_6, F@_7, 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, F@_6, F@_7, TrUserData). d_field_Release_advisory_indexes(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_Release_advisory_indexes(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); d_field_Release_advisory_indexes(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, Prev, F@_7, TrUserData) -> {NewFValue, RestF} = {id((X bsl N + Acc) band 4294967295, TrUserData), Rest}, dfp_read_field_def_Release(RestF, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, cons(NewFValue, Prev, TrUserData), F@_7, TrUserData). d_pfield_Release_advisory_indexes(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_pfield_Release_advisory_indexes(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); d_pfield_Release_advisory_indexes(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, E, F@_7, TrUserData) -> Len = X bsl N + Acc, <> = Rest, NewSeq = d_packed_field_Release_advisory_indexes(PackedBytes, 0, 0, F, E, TrUserData), dfp_read_field_def_Release(Rest2, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, NewSeq, F@_7, TrUserData). d_packed_field_Release_advisory_indexes(<<1:1, X:7, Rest/binary>>, N, Acc, F, AccSeq, TrUserData) when N < 57 -> d_packed_field_Release_advisory_indexes(Rest, N + 7, X bsl N + Acc, F, AccSeq, TrUserData); d_packed_field_Release_advisory_indexes(<<0:1, X:7, Rest/binary>>, N, Acc, F, AccSeq, TrUserData) -> {NewFValue, RestF} = {id((X bsl N + Acc) band 4294967295, TrUserData), Rest}, d_packed_field_Release_advisory_indexes(RestF, 0, 0, F, [NewFValue | AccSeq], TrUserData); d_packed_field_Release_advisory_indexes(<<>>, 0, 0, _, AccSeq, _) -> AccSeq. d_field_Release_published_at(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_Release_published_at(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); d_field_Release_published_at(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, Prev, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, {id(decode_msg_Timestamp(Bs, TrUserData), TrUserData), Rest2} end, dfp_read_field_def_Release(RestF, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, if Prev == '$undef' -> NewFValue; true -> merge_msg_Timestamp(Prev, NewFValue, TrUserData) end, TrUserData). skip_varint_Release(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> skip_varint_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); skip_varint_Release(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dfp_read_field_def_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). skip_length_delimited_Release(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData); skip_length_delimited_Release(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData). skip_group_Release(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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, F@_6, F@_7, TrUserData). skip_32_Release(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dfp_read_field_def_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). skip_64_Release(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dfp_read_field_def_Release(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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_SecurityAdvisory(Bin, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Bin, 0, 0, 0, id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id('$undef', TrUserData), id([], TrUserData), TrUserData). dfp_read_field_def_SecurityAdvisory(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_id(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_SecurityAdvisory(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_summary(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_SecurityAdvisory(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_html_url(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_SecurityAdvisory(<<32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_severity(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_SecurityAdvisory(<<45, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_cvss_score(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_SecurityAdvisory(<<50, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_api_url(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_SecurityAdvisory(<<58, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> d_field_SecurityAdvisory_aliases(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dfp_read_field_def_SecurityAdvisory(<<>>, 0, 0, _, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, R1, TrUserData) -> S1 = #{id => F@_1, summary => F@_2, html_url => F@_3, api_url => F@_6, aliases => lists_reverse(R1, TrUserData)}, S2 = if F@_4 == '$undef' -> S1; true -> S1#{severity => F@_4} end, if F@_5 == '$undef' -> S2; true -> S2#{cvss_score => F@_5} end; dfp_read_field_def_SecurityAdvisory(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dg_read_field_def_SecurityAdvisory(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). dg_read_field_def_SecurityAdvisory(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 32 - 7 -> dg_read_field_def_SecurityAdvisory(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); dg_read_field_def_SecurityAdvisory(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> Key = X bsl N + Acc, case Key of 10 -> d_field_SecurityAdvisory_id(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 18 -> d_field_SecurityAdvisory_summary(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 26 -> d_field_SecurityAdvisory_html_url(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 32 -> d_field_SecurityAdvisory_severity(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 45 -> d_field_SecurityAdvisory_cvss_score(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 50 -> d_field_SecurityAdvisory_api_url(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 58 -> d_field_SecurityAdvisory_aliases(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_SecurityAdvisory(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 1 -> skip_64_SecurityAdvisory(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 2 -> skip_length_delimited_SecurityAdvisory(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 3 -> skip_group_SecurityAdvisory(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); 5 -> skip_32_SecurityAdvisory(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) end end; dg_read_field_def_SecurityAdvisory(<<>>, 0, 0, _, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, R1, TrUserData) -> S1 = #{id => F@_1, summary => F@_2, html_url => F@_3, api_url => F@_6, aliases => lists_reverse(R1, TrUserData)}, S2 = if F@_4 == '$undef' -> S1; true -> S1#{severity => F@_4} end, if F@_5 == '$undef' -> S2; true -> S2#{cvss_score => F@_5} end. d_field_SecurityAdvisory_id(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_SecurityAdvisory_id(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); d_field_SecurityAdvisory_id(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_SecurityAdvisory(RestF, 0, 0, F, NewFValue, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). d_field_SecurityAdvisory_summary(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_SecurityAdvisory_summary(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); d_field_SecurityAdvisory_summary(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, _, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_SecurityAdvisory(RestF, 0, 0, F, F@_1, NewFValue, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). d_field_SecurityAdvisory_html_url(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_SecurityAdvisory_html_url(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); d_field_SecurityAdvisory_html_url(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, _, F@_4, F@_5, F@_6, F@_7, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_SecurityAdvisory(RestF, 0, 0, F, F@_1, F@_2, NewFValue, F@_4, F@_5, F@_6, F@_7, TrUserData). d_field_SecurityAdvisory_severity(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_SecurityAdvisory_severity(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); d_field_SecurityAdvisory_severity(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, _, F@_5, F@_6, F@_7, TrUserData) -> {NewFValue, RestF} = {id(d_enum_AdvisorySeverity(begin <> = <<(X bsl N + Acc):32/unsigned-native>>, id(Res, TrUserData) end), TrUserData), Rest}, dfp_read_field_def_SecurityAdvisory(RestF, 0, 0, F, F@_1, F@_2, F@_3, NewFValue, F@_5, F@_6, F@_7, TrUserData). d_field_SecurityAdvisory_cvss_score(<<0:16, 128, 127, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, _, F@_6, F@_7, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, id(infinity, TrUserData), F@_6, F@_7, TrUserData); d_field_SecurityAdvisory_cvss_score(<<0:16, 128, 255, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, _, F@_6, F@_7, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, id('-infinity', TrUserData), F@_6, F@_7, TrUserData); d_field_SecurityAdvisory_cvss_score(<<_:16, 1:1, _:7, _:1, 127:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, _, F@_6, F@_7, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, id(nan, TrUserData), F@_6, F@_7, TrUserData); d_field_SecurityAdvisory_cvss_score(<>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, _, F@_6, F@_7, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, id(Value, TrUserData), F@_6, F@_7, TrUserData). d_field_SecurityAdvisory_api_url(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_SecurityAdvisory_api_url(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); d_field_SecurityAdvisory_api_url(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, _, F@_7, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_SecurityAdvisory(RestF, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, NewFValue, F@_7, TrUserData). d_field_SecurityAdvisory_aliases(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> d_field_SecurityAdvisory_aliases(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); d_field_SecurityAdvisory_aliases(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, Prev, TrUserData) -> {NewFValue, RestF} = begin Len = X bsl N + Acc, <> = Rest, Bytes2 = binary:copy(Bytes), {id(Bytes2, TrUserData), Rest2} end, dfp_read_field_def_SecurityAdvisory(RestF, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, cons(NewFValue, Prev, TrUserData), TrUserData). skip_varint_SecurityAdvisory(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> skip_varint_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); skip_varint_SecurityAdvisory(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). skip_length_delimited_SecurityAdvisory(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) when N < 57 -> skip_length_delimited_SecurityAdvisory(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData); skip_length_delimited_SecurityAdvisory(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> Length = X bsl N + Acc, <<_:Length/binary, Rest2/binary>> = Rest, dfp_read_field_def_SecurityAdvisory(Rest2, 0, 0, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). skip_group_SecurityAdvisory(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_SecurityAdvisory(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). skip_32_SecurityAdvisory(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData). skip_64_SecurityAdvisory(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, TrUserData) -> dfp_read_field_def_SecurityAdvisory(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, F@_5, F@_6, F@_7, 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). 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). d_enum_Visibility(0) -> 'VISIBILITY_PRIVATE'; d_enum_Visibility(1) -> 'VISIBILITY_PUBLIC'; d_enum_Visibility(V) -> V. d_enum_OverrideAction(0) -> 'OVERRIDE_ACTION_ALLOW'; d_enum_OverrideAction(1) -> 'OVERRIDE_ACTION_DENY'; d_enum_OverrideAction(V) -> V. 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. d_enum_AdvisorySeverity(0) -> 'SEVERITY_NONE'; d_enum_AdvisorySeverity(1) -> 'SEVERITY_LOW'; d_enum_AdvisorySeverity(2) -> 'SEVERITY_MEDIUM'; d_enum_AdvisorySeverity(3) -> 'SEVERITY_HIGH'; d_enum_AdvisorySeverity(4) -> 'SEVERITY_CRITICAL'; d_enum_AdvisorySeverity(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 'Policy' -> merge_msg_Policy(Prev, New, TrUserData); 'RepositoryPolicy' -> merge_msg_RepositoryPolicy(Prev, New, TrUserData); 'Restriction' -> merge_msg_Restriction(Prev, New, TrUserData); 'PackageRef' -> merge_msg_PackageRef(Prev, New, TrUserData); 'Override' -> merge_msg_Override(Prev, New, TrUserData); 'Package' -> merge_msg_Package(Prev, New, TrUserData); 'Release' -> merge_msg_Release(Prev, New, TrUserData); 'RetirementStatus' -> merge_msg_RetirementStatus(Prev, New, TrUserData); 'SecurityAdvisory' -> merge_msg_SecurityAdvisory(Prev, New, TrUserData); 'Dependency' -> merge_msg_Dependency(Prev, New, TrUserData); 'Timestamp' -> merge_msg_Timestamp(Prev, New, TrUserData) end. -compile({nowarn_unused_function,merge_msg_Policy/3}). merge_msg_Policy(#{} = PMsg, #{repository := NFrepository, name := NFname, visibility := NFvisibility} = NMsg, TrUserData) -> S1 = #{repository => NFrepository, name => NFname, visibility => NFvisibility}, S2 = case {PMsg, NMsg} of {_, #{description := NFdescription}} -> S1#{description => NFdescription}; {#{description := PFdescription}, _} -> S1#{description => PFdescription}; _ -> S1 end, case {PMsg, NMsg} of {#{repositories := PFrepositories}, #{repositories := NFrepositories}} -> S2#{repositories => 'erlang_++'(PFrepositories, NFrepositories, TrUserData)}; {_, #{repositories := NFrepositories}} -> S2#{repositories => NFrepositories}; {#{repositories := PFrepositories}, _} -> S2#{repositories => PFrepositories}; {_, _} -> S2 end. -compile({nowarn_unused_function,merge_msg_RepositoryPolicy/3}). merge_msg_RepositoryPolicy(#{} = PMsg, #{repository := NFrepository} = NMsg, TrUserData) -> S1 = #{repository => NFrepository}, S2 = case {PMsg, NMsg} of {#{restriction := PFrestriction}, #{restriction := NFrestriction}} -> S1#{restriction => merge_msg_Restriction(PFrestriction, NFrestriction, TrUserData)}; {_, #{restriction := NFrestriction}} -> S1#{restriction => NFrestriction}; {#{restriction := PFrestriction}, _} -> S1#{restriction => PFrestriction}; {_, _} -> S1 end, case {PMsg, NMsg} of {#{overrides := PFoverrides}, #{overrides := NFoverrides}} -> S2#{overrides => 'erlang_++'(PFoverrides, NFoverrides, TrUserData)}; {_, #{overrides := NFoverrides}} -> S2#{overrides => NFoverrides}; {#{overrides := PFoverrides}, _} -> S2#{overrides => PFoverrides}; {_, _} -> S2 end. -compile({nowarn_unused_function,merge_msg_Restriction/3}). merge_msg_Restriction(PMsg, NMsg, TrUserData) -> S1 = #{}, S2 = case {PMsg, NMsg} of {_, #{advisory_min_severity := NFadvisory_min_severity}} -> S1#{advisory_min_severity => NFadvisory_min_severity}; {#{advisory_min_severity := PFadvisory_min_severity}, _} -> S1#{advisory_min_severity => PFadvisory_min_severity}; _ -> S1 end, S3 = case {PMsg, NMsg} of {#{retirement_reasons := PFretirement_reasons}, #{retirement_reasons := NFretirement_reasons}} -> S2#{retirement_reasons => 'erlang_++'(PFretirement_reasons, NFretirement_reasons, TrUserData)}; {_, #{retirement_reasons := NFretirement_reasons}} -> S2#{retirement_reasons => NFretirement_reasons}; {#{retirement_reasons := PFretirement_reasons}, _} -> S2#{retirement_reasons => PFretirement_reasons}; {_, _} -> S2 end, case {PMsg, NMsg} of {_, #{cooldown := NFcooldown}} -> S3#{cooldown => NFcooldown}; {#{cooldown := PFcooldown}, _} -> S3#{cooldown => PFcooldown}; _ -> S3 end. -compile({nowarn_unused_function,merge_msg_PackageRef/3}). merge_msg_PackageRef(#{} = PMsg, #{package := NFpackage} = NMsg, _) -> S1 = #{package => NFpackage}, case {PMsg, NMsg} of {_, #{requirement := NFrequirement}} -> S1#{requirement => NFrequirement}; {#{requirement := PFrequirement}, _} -> S1#{requirement => PFrequirement}; _ -> S1 end. -compile({nowarn_unused_function,merge_msg_Override/3}). merge_msg_Override(#{ref := PFref}, #{action := NFaction, ref := NFref}, TrUserData) -> #{action => NFaction, ref => merge_msg_PackageRef(PFref, NFref, TrUserData)}. -compile({nowarn_unused_function,merge_msg_Package/3}). merge_msg_Package(#{} = PMsg, #{name := NFname, repository := NFrepository} = NMsg, TrUserData) -> S1 = #{name => NFname, repository => NFrepository}, S2 = 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, case {PMsg, NMsg} of {#{advisories := PFadvisories}, #{advisories := NFadvisories}} -> S2#{advisories => 'erlang_++'(PFadvisories, NFadvisories, TrUserData)}; {_, #{advisories := NFadvisories}} -> S2#{advisories => NFadvisories}; {#{advisories := PFadvisories}, _} -> S2#{advisories => PFadvisories}; {_, _} -> S2 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, S4 = case {PMsg, NMsg} of {_, #{outer_checksum := NFouter_checksum}} -> S3#{outer_checksum => NFouter_checksum}; {#{outer_checksum := PFouter_checksum}, _} -> S3#{outer_checksum => PFouter_checksum}; _ -> S3 end, S5 = case {PMsg, NMsg} of {#{advisory_indexes := PFadvisory_indexes}, #{advisory_indexes := NFadvisory_indexes}} -> S4#{advisory_indexes => 'erlang_++'(PFadvisory_indexes, NFadvisory_indexes, TrUserData)}; {_, #{advisory_indexes := NFadvisory_indexes}} -> S4#{advisory_indexes => NFadvisory_indexes}; {#{advisory_indexes := PFadvisory_indexes}, _} -> S4#{advisory_indexes => PFadvisory_indexes}; {_, _} -> S4 end, case {PMsg, NMsg} of {#{published_at := PFpublished_at}, #{published_at := NFpublished_at}} -> S5#{published_at => merge_msg_Timestamp(PFpublished_at, NFpublished_at, TrUserData)}; {_, #{published_at := NFpublished_at}} -> S5#{published_at => NFpublished_at}; {#{published_at := PFpublished_at}, _} -> S5#{published_at => PFpublished_at}; {_, _} -> S5 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_SecurityAdvisory/3}). merge_msg_SecurityAdvisory(#{} = PMsg, #{id := NFid, summary := NFsummary, html_url := NFhtml_url, api_url := NFapi_url} = NMsg, TrUserData) -> S1 = #{id => NFid, summary => NFsummary, html_url => NFhtml_url, api_url => NFapi_url}, S2 = case {PMsg, NMsg} of {_, #{severity := NFseverity}} -> S1#{severity => NFseverity}; {#{severity := PFseverity}, _} -> S1#{severity => PFseverity}; _ -> S1 end, S3 = case {PMsg, NMsg} of {_, #{cvss_score := NFcvss_score}} -> S2#{cvss_score => NFcvss_score}; {#{cvss_score := PFcvss_score}, _} -> S2#{cvss_score => PFcvss_score}; _ -> S2 end, case {PMsg, NMsg} of {#{aliases := PFaliases}, #{aliases := NFaliases}} -> S3#{aliases => 'erlang_++'(PFaliases, NFaliases, TrUserData)}; {_, #{aliases := NFaliases}} -> S3#{aliases => NFaliases}; {#{aliases := PFaliases}, _} -> S3#{aliases => PFaliases}; {_, _} -> S3 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. -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 'Policy' -> v_msg_Policy(Msg, [MsgName], TrUserData); 'RepositoryPolicy' -> v_msg_RepositoryPolicy(Msg, [MsgName], TrUserData); 'Restriction' -> v_msg_Restriction(Msg, [MsgName], TrUserData); 'PackageRef' -> v_msg_PackageRef(Msg, [MsgName], TrUserData); 'Override' -> v_msg_Override(Msg, [MsgName], TrUserData); 'Package' -> v_msg_Package(Msg, [MsgName], TrUserData); 'Release' -> v_msg_Release(Msg, [MsgName], TrUserData); 'RetirementStatus' -> v_msg_RetirementStatus(Msg, [MsgName], TrUserData); 'SecurityAdvisory' -> v_msg_SecurityAdvisory(Msg, [MsgName], TrUserData); 'Dependency' -> v_msg_Dependency(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_Policy/3}). v_msg_Policy(#{repository := F1, name := F2, visibility := F4} = M, Path, TrUserData) -> v_type_string(F1, [repository | Path], TrUserData), v_type_string(F2, [name | Path], TrUserData), case M of #{description := F3} -> v_type_string(F3, [description | Path], TrUserData); _ -> ok end, v_enum_Visibility(F4, [visibility | Path], TrUserData), case M of #{repositories := F5} -> if is_list(F5) -> _ = [v_submsg_RepositoryPolicy(Elem, [repositories | Path], TrUserData) || Elem <- F5], ok; true -> mk_type_error({invalid_list_of, {msg, 'RepositoryPolicy'}}, F5, [repositories | Path]) end; _ -> ok end, lists:foreach(fun (repository) -> ok; (name) -> ok; (description) -> ok; (visibility) -> ok; (repositories) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), ok; v_msg_Policy(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [repository, name, visibility] -- maps:keys(M), 'Policy'}, M, Path); v_msg_Policy(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'Policy'}, X, Path). -compile({nowarn_unused_function,v_submsg_RepositoryPolicy/3}). v_submsg_RepositoryPolicy(Msg, Path, TrUserData) -> v_msg_RepositoryPolicy(Msg, Path, TrUserData). -compile({nowarn_unused_function,v_msg_RepositoryPolicy/3}). v_msg_RepositoryPolicy(#{repository := F1} = M, Path, TrUserData) -> v_type_string(F1, [repository | Path], TrUserData), case M of #{restriction := F2} -> v_submsg_Restriction(F2, [restriction | Path], TrUserData); _ -> ok end, case M of #{overrides := F3} -> if is_list(F3) -> _ = [v_submsg_Override(Elem, [overrides | Path], TrUserData) || Elem <- F3], ok; true -> mk_type_error({invalid_list_of, {msg, 'Override'}}, F3, [overrides | Path]) end; _ -> ok end, lists:foreach(fun (repository) -> ok; (restriction) -> ok; (overrides) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), ok; v_msg_RepositoryPolicy(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [repository] -- maps:keys(M), 'RepositoryPolicy'}, M, Path); v_msg_RepositoryPolicy(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'RepositoryPolicy'}, X, Path). -compile({nowarn_unused_function,v_submsg_Restriction/3}). v_submsg_Restriction(Msg, Path, TrUserData) -> v_msg_Restriction(Msg, Path, TrUserData). -compile({nowarn_unused_function,v_msg_Restriction/3}). v_msg_Restriction(#{} = M, Path, TrUserData) -> case M of #{advisory_min_severity := F1} -> v_enum_AdvisorySeverity(F1, [advisory_min_severity | Path], TrUserData); _ -> ok end, case M of #{retirement_reasons := F2} -> if is_list(F2) -> _ = [v_enum_RetirementReason(Elem, [retirement_reasons | Path], TrUserData) || Elem <- F2], ok; true -> mk_type_error({invalid_list_of, {enum, 'RetirementReason'}}, F2, [retirement_reasons | Path]) end; _ -> ok end, case M of #{cooldown := F3} -> v_type_string(F3, [cooldown | Path], TrUserData); _ -> ok end, lists:foreach(fun (advisory_min_severity) -> ok; (retirement_reasons) -> ok; (cooldown) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), ok; v_msg_Restriction(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [] -- maps:keys(M), 'Restriction'}, M, Path); v_msg_Restriction(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'Restriction'}, X, Path). -compile({nowarn_unused_function,v_submsg_PackageRef/3}). v_submsg_PackageRef(Msg, Path, TrUserData) -> v_msg_PackageRef(Msg, Path, TrUserData). -compile({nowarn_unused_function,v_msg_PackageRef/3}). v_msg_PackageRef(#{package := F1} = M, Path, TrUserData) -> v_type_string(F1, [package | Path], TrUserData), case M of #{requirement := F2} -> v_type_string(F2, [requirement | Path], TrUserData); _ -> ok end, lists:foreach(fun (package) -> ok; (requirement) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), ok; v_msg_PackageRef(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [package] -- maps:keys(M), 'PackageRef'}, M, Path); v_msg_PackageRef(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'PackageRef'}, X, Path). -compile({nowarn_unused_function,v_submsg_Override/3}). v_submsg_Override(Msg, Path, TrUserData) -> v_msg_Override(Msg, Path, TrUserData). -compile({nowarn_unused_function,v_msg_Override/3}). v_msg_Override(#{action := F1, ref := F2} = M, Path, TrUserData) -> v_enum_OverrideAction(F1, [action | Path], TrUserData), v_submsg_PackageRef(F2, [ref | Path], TrUserData), lists:foreach(fun (action) -> ok; (ref) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), ok; v_msg_Override(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [action, ref] -- maps:keys(M), 'Override'}, M, Path); v_msg_Override(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'Override'}, X, Path). -compile({nowarn_unused_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), case M of #{advisories := F4} -> if is_list(F4) -> _ = [v_submsg_SecurityAdvisory(Elem, [advisories | Path], TrUserData) || Elem <- F4], ok; true -> mk_type_error({invalid_list_of, {msg, 'SecurityAdvisory'}}, F4, [advisories | Path]) end; _ -> ok end, lists:foreach(fun (releases) -> ok; (name) -> ok; (repository) -> ok; (advisories) -> 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}). v_submsg_Release(Msg, Path, TrUserData) -> v_msg_Release(Msg, Path, TrUserData). -compile({nowarn_unused_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, case M of #{advisory_indexes := F6} -> if is_list(F6) -> _ = [v_type_uint32(Elem, [advisory_indexes | Path], TrUserData) || Elem <- F6], ok; true -> mk_type_error({invalid_list_of, uint32}, F6, [advisory_indexes | Path]) end; _ -> ok end, case M of #{published_at := F7} -> v_submsg_Timestamp(F7, [published_at | Path], TrUserData); _ -> ok end, lists:foreach(fun (version) -> ok; (inner_checksum) -> ok; (dependencies) -> ok; (retired) -> ok; (outer_checksum) -> ok; (advisory_indexes) -> ok; (published_at) -> 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}). v_submsg_RetirementStatus(Msg, Path, TrUserData) -> v_msg_RetirementStatus(Msg, Path, TrUserData). -compile({nowarn_unused_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_SecurityAdvisory/3}). v_submsg_SecurityAdvisory(Msg, Path, TrUserData) -> v_msg_SecurityAdvisory(Msg, Path, TrUserData). -compile({nowarn_unused_function,v_msg_SecurityAdvisory/3}). v_msg_SecurityAdvisory(#{id := F1, summary := F2, html_url := F3, api_url := F6} = M, Path, TrUserData) -> v_type_string(F1, [id | Path], TrUserData), v_type_string(F2, [summary | Path], TrUserData), v_type_string(F3, [html_url | Path], TrUserData), case M of #{severity := F4} -> v_enum_AdvisorySeverity(F4, [severity | Path], TrUserData); _ -> ok end, case M of #{cvss_score := F5} -> v_type_float(F5, [cvss_score | Path], TrUserData); _ -> ok end, v_type_string(F6, [api_url | Path], TrUserData), case M of #{aliases := F7} -> if is_list(F7) -> _ = [v_type_string(Elem, [aliases | Path], TrUserData) || Elem <- F7], ok; true -> mk_type_error({invalid_list_of, string}, F7, [aliases | Path]) end; _ -> ok end, lists:foreach(fun (id) -> ok; (summary) -> ok; (html_url) -> ok; (severity) -> ok; (cvss_score) -> ok; (api_url) -> ok; (aliases) -> ok; (OtherKey) -> mk_type_error({extraneous_key, OtherKey}, M, Path) end, maps:keys(M)), ok; v_msg_SecurityAdvisory(M, Path, _TrUserData) when is_map(M) -> mk_type_error({missing_fields, [id, summary, html_url, api_url] -- maps:keys(M), 'SecurityAdvisory'}, M, Path); v_msg_SecurityAdvisory(X, Path, _TrUserData) -> mk_type_error({expected_msg, 'SecurityAdvisory'}, X, Path). -compile({nowarn_unused_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}). 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_submsg_Timestamp/3}). v_submsg_Timestamp(Msg, Path, TrUserData) -> v_msg_Timestamp(Msg, Path, TrUserData). -compile({nowarn_unused_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_enum_Visibility/3}). v_enum_Visibility('VISIBILITY_PRIVATE', _Path, _TrUserData) -> ok; v_enum_Visibility('VISIBILITY_PUBLIC', _Path, _TrUserData) -> ok; v_enum_Visibility(V, _Path, _TrUserData) when -2147483648 =< V, V =< 2147483647, is_integer(V) -> ok; v_enum_Visibility(X, Path, _TrUserData) -> mk_type_error({invalid_enum, 'Visibility'}, X, Path). -compile({nowarn_unused_function,v_enum_OverrideAction/3}). v_enum_OverrideAction('OVERRIDE_ACTION_ALLOW', _Path, _TrUserData) -> ok; v_enum_OverrideAction('OVERRIDE_ACTION_DENY', _Path, _TrUserData) -> ok; v_enum_OverrideAction(V, _Path, _TrUserData) when -2147483648 =< V, V =< 2147483647, is_integer(V) -> ok; v_enum_OverrideAction(X, Path, _TrUserData) -> mk_type_error({invalid_enum, 'OverrideAction'}, X, Path). -compile({nowarn_unused_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_enum_AdvisorySeverity/3}). v_enum_AdvisorySeverity('SEVERITY_NONE', _Path, _TrUserData) -> ok; v_enum_AdvisorySeverity('SEVERITY_LOW', _Path, _TrUserData) -> ok; v_enum_AdvisorySeverity('SEVERITY_MEDIUM', _Path, _TrUserData) -> ok; v_enum_AdvisorySeverity('SEVERITY_HIGH', _Path, _TrUserData) -> ok; v_enum_AdvisorySeverity('SEVERITY_CRITICAL', _Path, _TrUserData) -> ok; v_enum_AdvisorySeverity(V, _Path, _TrUserData) when -2147483648 =< V, V =< 2147483647, is_integer(V) -> ok; v_enum_AdvisorySeverity(X, Path, _TrUserData) -> mk_type_error({invalid_enum, 'AdvisorySeverity'}, X, Path). -compile({nowarn_unused_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}). 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_uint32/3}). v_type_uint32(N, _Path, _TrUserData) when is_integer(N), 0 =< N, N =< 4294967295 -> ok; v_type_uint32(N, Path, _TrUserData) when is_integer(N) -> mk_type_error({value_out_of_range, uint32, unsigned, 32}, N, Path); v_type_uint32(X, Path, _TrUserData) -> mk_type_error({bad_integer, uint32, unsigned, 32}, X, Path). -compile({nowarn_unused_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_float/3}). v_type_float(N, _Path, _TrUserData) when is_float(N) -> ok; v_type_float(N, _Path, _TrUserData) when is_integer(N) -> ok; v_type_float(infinity, _Path, _TrUserData) -> ok; v_type_float('-infinity', _Path, _TrUserData) -> ok; v_type_float(nan, _Path, _TrUserData) -> ok; v_type_float(X, Path, _TrUserData) -> mk_type_error(bad_float_value, X, Path). -compile({nowarn_unused_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}). 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}). prettify_path([]) -> top_level; prettify_path(PathR) -> string: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, 'Visibility'}, [{'VISIBILITY_PRIVATE', 0}, {'VISIBILITY_PUBLIC', 1}]}, {{enum, 'OverrideAction'}, [{'OVERRIDE_ACTION_ALLOW', 0}, {'OVERRIDE_ACTION_DENY', 1}]}, {{enum, 'RetirementReason'}, [{'RETIRED_OTHER', 0}, {'RETIRED_INVALID', 1}, {'RETIRED_SECURITY', 2}, {'RETIRED_DEPRECATED', 3}, {'RETIRED_RENAMED', 4}]}, {{enum, 'AdvisorySeverity'}, [{'SEVERITY_NONE', 0}, {'SEVERITY_LOW', 1}, {'SEVERITY_MEDIUM', 2}, {'SEVERITY_HIGH', 3}, {'SEVERITY_CRITICAL', 4}]}, {{msg, 'Policy'}, [#{name => repository, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => name, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}, #{name => description, fnum => 3, rnum => 4, type => string, occurrence => optional, opts => []}, #{name => visibility, fnum => 4, rnum => 5, type => {enum, 'Visibility'}, occurrence => required, opts => []}, #{name => repositories, fnum => 5, rnum => 6, type => {msg, 'RepositoryPolicy'}, occurrence => repeated, opts => []}]}, {{msg, 'RepositoryPolicy'}, [#{name => repository, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => restriction, fnum => 2, rnum => 3, type => {msg, 'Restriction'}, occurrence => optional, opts => []}, #{name => overrides, fnum => 3, rnum => 4, type => {msg, 'Override'}, occurrence => repeated, opts => []}]}, {{msg, 'Restriction'}, [#{name => advisory_min_severity, fnum => 1, rnum => 2, type => {enum, 'AdvisorySeverity'}, occurrence => optional, opts => []}, #{name => retirement_reasons, fnum => 2, rnum => 3, type => {enum, 'RetirementReason'}, occurrence => repeated, opts => [packed]}, #{name => cooldown, fnum => 3, rnum => 4, type => string, occurrence => optional, opts => []}]}, {{msg, 'PackageRef'}, [#{name => package, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => requirement, fnum => 2, rnum => 3, type => string, occurrence => optional, opts => []}]}, {{msg, 'Override'}, [#{name => action, fnum => 1, rnum => 2, type => {enum, 'OverrideAction'}, occurrence => required, opts => []}, #{name => ref, fnum => 2, rnum => 3, type => {msg, 'PackageRef'}, occurrence => required, opts => []}]}, {{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 => []}, #{name => advisories, fnum => 4, rnum => 5, type => {msg, 'SecurityAdvisory'}, occurrence => repeated, 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 => []}, #{name => advisory_indexes, fnum => 6, rnum => 7, type => uint32, occurrence => repeated, opts => []}, #{name => published_at, fnum => 7, rnum => 8, type => {msg, 'Timestamp'}, 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, 'SecurityAdvisory'}, [#{name => id, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => summary, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}, #{name => html_url, fnum => 3, rnum => 4, type => string, occurrence => required, opts => []}, #{name => severity, fnum => 4, rnum => 5, type => {enum, 'AdvisorySeverity'}, occurrence => optional, opts => []}, #{name => cvss_score, fnum => 5, rnum => 6, type => float, occurrence => optional, opts => []}, #{name => api_url, fnum => 6, rnum => 7, type => string, occurrence => required, opts => []}, #{name => aliases, fnum => 7, rnum => 8, type => string, occurrence => repeated, 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 => []}]}, {{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() -> ['Policy', 'RepositoryPolicy', 'Restriction', 'PackageRef', 'Override', 'Package', 'Release', 'RetirementStatus', 'SecurityAdvisory', 'Dependency', 'Timestamp']. get_group_names() -> []. get_msg_or_group_names() -> ['Policy', 'RepositoryPolicy', 'Restriction', 'PackageRef', 'Override', 'Package', 'Release', 'RetirementStatus', 'SecurityAdvisory', 'Dependency', 'Timestamp']. get_enum_names() -> ['Visibility', 'OverrideAction', 'RetirementReason', 'AdvisorySeverity']. 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('Policy') -> [#{name => repository, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => name, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}, #{name => description, fnum => 3, rnum => 4, type => string, occurrence => optional, opts => []}, #{name => visibility, fnum => 4, rnum => 5, type => {enum, 'Visibility'}, occurrence => required, opts => []}, #{name => repositories, fnum => 5, rnum => 6, type => {msg, 'RepositoryPolicy'}, occurrence => repeated, opts => []}]; find_msg_def('RepositoryPolicy') -> [#{name => repository, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => restriction, fnum => 2, rnum => 3, type => {msg, 'Restriction'}, occurrence => optional, opts => []}, #{name => overrides, fnum => 3, rnum => 4, type => {msg, 'Override'}, occurrence => repeated, opts => []}]; find_msg_def('Restriction') -> [#{name => advisory_min_severity, fnum => 1, rnum => 2, type => {enum, 'AdvisorySeverity'}, occurrence => optional, opts => []}, #{name => retirement_reasons, fnum => 2, rnum => 3, type => {enum, 'RetirementReason'}, occurrence => repeated, opts => [packed]}, #{name => cooldown, fnum => 3, rnum => 4, type => string, occurrence => optional, opts => []}]; find_msg_def('PackageRef') -> [#{name => package, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => requirement, fnum => 2, rnum => 3, type => string, occurrence => optional, opts => []}]; find_msg_def('Override') -> [#{name => action, fnum => 1, rnum => 2, type => {enum, 'OverrideAction'}, occurrence => required, opts => []}, #{name => ref, fnum => 2, rnum => 3, type => {msg, 'PackageRef'}, occurrence => required, opts => []}]; 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 => []}, #{name => advisories, fnum => 4, rnum => 5, type => {msg, 'SecurityAdvisory'}, occurrence => repeated, 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 => []}, #{name => advisory_indexes, fnum => 6, rnum => 7, type => uint32, occurrence => repeated, opts => []}, #{name => published_at, fnum => 7, rnum => 8, type => {msg, 'Timestamp'}, 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('SecurityAdvisory') -> [#{name => id, fnum => 1, rnum => 2, type => string, occurrence => required, opts => []}, #{name => summary, fnum => 2, rnum => 3, type => string, occurrence => required, opts => []}, #{name => html_url, fnum => 3, rnum => 4, type => string, occurrence => required, opts => []}, #{name => severity, fnum => 4, rnum => 5, type => {enum, 'AdvisorySeverity'}, occurrence => optional, opts => []}, #{name => cvss_score, fnum => 5, rnum => 6, type => float, occurrence => optional, opts => []}, #{name => api_url, fnum => 6, rnum => 7, type => string, occurrence => required, opts => []}, #{name => aliases, fnum => 7, rnum => 8, type => string, occurrence => repeated, 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('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('Visibility') -> [{'VISIBILITY_PRIVATE', 0}, {'VISIBILITY_PUBLIC', 1}]; find_enum_def('OverrideAction') -> [{'OVERRIDE_ACTION_ALLOW', 0}, {'OVERRIDE_ACTION_DENY', 1}]; find_enum_def('RetirementReason') -> [{'RETIRED_OTHER', 0}, {'RETIRED_INVALID', 1}, {'RETIRED_SECURITY', 2}, {'RETIRED_DEPRECATED', 3}, {'RETIRED_RENAMED', 4}]; find_enum_def('AdvisorySeverity') -> [{'SEVERITY_NONE', 0}, {'SEVERITY_LOW', 1}, {'SEVERITY_MEDIUM', 2}, {'SEVERITY_HIGH', 3}, {'SEVERITY_CRITICAL', 4}]; find_enum_def(_) -> error. enum_symbol_by_value('Visibility', Value) -> enum_symbol_by_value_Visibility(Value); enum_symbol_by_value('OverrideAction', Value) -> enum_symbol_by_value_OverrideAction(Value); enum_symbol_by_value('RetirementReason', Value) -> enum_symbol_by_value_RetirementReason(Value); enum_symbol_by_value('AdvisorySeverity', Value) -> enum_symbol_by_value_AdvisorySeverity(Value). enum_value_by_symbol('Visibility', Sym) -> enum_value_by_symbol_Visibility(Sym); enum_value_by_symbol('OverrideAction', Sym) -> enum_value_by_symbol_OverrideAction(Sym); enum_value_by_symbol('RetirementReason', Sym) -> enum_value_by_symbol_RetirementReason(Sym); enum_value_by_symbol('AdvisorySeverity', Sym) -> enum_value_by_symbol_AdvisorySeverity(Sym). enum_symbol_by_value_Visibility(0) -> 'VISIBILITY_PRIVATE'; enum_symbol_by_value_Visibility(1) -> 'VISIBILITY_PUBLIC'. enum_value_by_symbol_Visibility('VISIBILITY_PRIVATE') -> 0; enum_value_by_symbol_Visibility('VISIBILITY_PUBLIC') -> 1. enum_symbol_by_value_OverrideAction(0) -> 'OVERRIDE_ACTION_ALLOW'; enum_symbol_by_value_OverrideAction(1) -> 'OVERRIDE_ACTION_DENY'. enum_value_by_symbol_OverrideAction('OVERRIDE_ACTION_ALLOW') -> 0; enum_value_by_symbol_OverrideAction('OVERRIDE_ACTION_DENY') -> 1. 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. enum_symbol_by_value_AdvisorySeverity(0) -> 'SEVERITY_NONE'; enum_symbol_by_value_AdvisorySeverity(1) -> 'SEVERITY_LOW'; enum_symbol_by_value_AdvisorySeverity(2) -> 'SEVERITY_MEDIUM'; enum_symbol_by_value_AdvisorySeverity(3) -> 'SEVERITY_HIGH'; enum_symbol_by_value_AdvisorySeverity(4) -> 'SEVERITY_CRITICAL'. enum_value_by_symbol_AdvisorySeverity('SEVERITY_NONE') -> 0; enum_value_by_symbol_AdvisorySeverity('SEVERITY_LOW') -> 1; enum_value_by_symbol_AdvisorySeverity('SEVERITY_MEDIUM') -> 2; enum_value_by_symbol_AdvisorySeverity('SEVERITY_HIGH') -> 3; enum_value_by_symbol_AdvisorySeverity('SEVERITY_CRITICAL') -> 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(<<"Policy">>) -> 'Policy'; fqbin_to_msg_name(<<"RepositoryPolicy">>) -> 'RepositoryPolicy'; fqbin_to_msg_name(<<"Restriction">>) -> 'Restriction'; fqbin_to_msg_name(<<"PackageRef">>) -> 'PackageRef'; fqbin_to_msg_name(<<"Override">>) -> 'Override'; fqbin_to_msg_name(<<"Package">>) -> 'Package'; fqbin_to_msg_name(<<"Release">>) -> 'Release'; fqbin_to_msg_name(<<"RetirementStatus">>) -> 'RetirementStatus'; fqbin_to_msg_name(<<"SecurityAdvisory">>) -> 'SecurityAdvisory'; fqbin_to_msg_name(<<"Dependency">>) -> 'Dependency'; fqbin_to_msg_name(<<"Timestamp">>) -> 'Timestamp'; fqbin_to_msg_name(E) -> error({gpb_error, {badmsg, E}}). msg_name_to_fqbin('Policy') -> <<"Policy">>; msg_name_to_fqbin('RepositoryPolicy') -> <<"RepositoryPolicy">>; msg_name_to_fqbin('Restriction') -> <<"Restriction">>; msg_name_to_fqbin('PackageRef') -> <<"PackageRef">>; msg_name_to_fqbin('Override') -> <<"Override">>; msg_name_to_fqbin('Package') -> <<"Package">>; msg_name_to_fqbin('Release') -> <<"Release">>; msg_name_to_fqbin('RetirementStatus') -> <<"RetirementStatus">>; msg_name_to_fqbin('SecurityAdvisory') -> <<"SecurityAdvisory">>; msg_name_to_fqbin('Dependency') -> <<"Dependency">>; msg_name_to_fqbin('Timestamp') -> <<"Timestamp">>; msg_name_to_fqbin(E) -> error({gpb_error, {badmsg, E}}). fqbin_to_enum_name(<<"Visibility">>) -> 'Visibility'; fqbin_to_enum_name(<<"OverrideAction">>) -> 'OverrideAction'; fqbin_to_enum_name(<<"RetirementReason">>) -> 'RetirementReason'; fqbin_to_enum_name(<<"AdvisorySeverity">>) -> 'AdvisorySeverity'; fqbin_to_enum_name(E) -> error({gpb_error, {badenum, E}}). enum_name_to_fqbin('Visibility') -> <<"Visibility">>; enum_name_to_fqbin('OverrideAction') -> <<"OverrideAction">>; enum_name_to_fqbin('RetirementReason') -> <<"RetirementReason">>; enum_name_to_fqbin('AdvisorySeverity') -> <<"AdvisorySeverity">>; 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_policy.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_policy.proto", "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_policy", "mix_hex_pb_package"]. get_msg_containment("mix_hex_pb_policy") -> ['Override', 'PackageRef', 'Policy', 'RepositoryPolicy', 'Restriction']; get_msg_containment("mix_hex_pb_package") -> ['Dependency', 'Package', 'Release', 'RetirementStatus', 'SecurityAdvisory', 'Timestamp']; get_msg_containment(P) -> error({gpb_error, {badproto, P}}). get_pkg_containment("mix_hex_pb_policy") -> undefined; get_pkg_containment("mix_hex_pb_package") -> undefined; get_pkg_containment(P) -> error({gpb_error, {badproto, P}}). get_service_containment("mix_hex_pb_policy") -> []; get_service_containment("mix_hex_pb_package") -> []; get_service_containment(P) -> error({gpb_error, {badproto, P}}). get_rpc_containment("mix_hex_pb_policy") -> []; get_rpc_containment("mix_hex_pb_package") -> []; get_rpc_containment(P) -> error({gpb_error, {badproto, P}}). get_enum_containment("mix_hex_pb_policy") -> ['OverrideAction', 'Visibility']; get_enum_containment("mix_hex_pb_package") -> ['AdvisorySeverity', 'RetirementReason']; get_enum_containment(P) -> error({gpb_error, {badproto, P}}). get_proto_by_msg_name_as_fqbin(<<"Timestamp">>) -> "mix_hex_pb_package"; 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(<<"Override">>) -> "mix_hex_pb_policy"; get_proto_by_msg_name_as_fqbin(<<"PackageRef">>) -> "mix_hex_pb_policy"; get_proto_by_msg_name_as_fqbin(<<"SecurityAdvisory">>) -> "mix_hex_pb_package"; get_proto_by_msg_name_as_fqbin(<<"Dependency">>) -> "mix_hex_pb_package"; get_proto_by_msg_name_as_fqbin(<<"RepositoryPolicy">>) -> "mix_hex_pb_policy"; get_proto_by_msg_name_as_fqbin(<<"Policy">>) -> "mix_hex_pb_policy"; get_proto_by_msg_name_as_fqbin(<<"Restriction">>) -> "mix_hex_pb_policy"; 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(<<"AdvisorySeverity">>) -> "mix_hex_pb_package"; get_proto_by_enum_name_as_fqbin(<<"Visibility">>) -> "mix_hex_pb_policy"; get_proto_by_enum_name_as_fqbin(<<"RetirementReason">>) -> "mix_hex_pb_package"; get_proto_by_enum_name_as_fqbin(<<"OverrideAction">>) -> "mix_hex_pb_policy"; 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.5.1/src/mix_hex_pb_signed.erl000066400000000000000000000551421522402544500172630ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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.5.1/src/mix_hex_pb_versions.erl000066400000000000000000001212651522402544500176620ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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 with_advisories => [integer()] % = 5, 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, B3 = case M of #{retired := F3} -> TrF3 = id(F3, TrUserData), if TrF3 == [] -> B2; true -> e_field_Package_retired(TrF3, B2, TrUserData) end; _ -> B2 end, case M of #{with_advisories := F4} -> TrF4 = id(F4, TrUserData), if TrF4 == [] -> B3; true -> e_field_Package_with_advisories(TrF4, B3, TrUserData) end; _ -> B3 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. e_field_Package_with_advisories(Elems, Bin, TrUserData) when Elems =/= [] -> SubBin = e_pfield_Package_with_advisories(Elems, <<>>, TrUserData), Bin2 = <>, Bin3 = e_varint(byte_size(SubBin), Bin2), <>; e_field_Package_with_advisories([], Bin, _TrUserData) -> Bin. e_pfield_Package_with_advisories([Value | Rest], Bin, TrUserData) -> Bin2 = e_type_int32(id(Value, TrUserData), Bin, TrUserData), e_pfield_Package_with_advisories(Rest, Bin2, TrUserData); e_pfield_Package_with_advisories([], 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), id([], TrUserData), TrUserData). dfp_read_field_def_Package(<<10, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_name(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); dfp_read_field_def_Package(<<18, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_versions(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); dfp_read_field_def_Package(<<26, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_pfield_Package_retired(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); dfp_read_field_def_Package(<<24, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_retired(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); dfp_read_field_def_Package(<<42, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_pfield_Package_with_advisories(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); dfp_read_field_def_Package(<<40, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> d_field_Package_with_advisories(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); dfp_read_field_def_Package(<<>>, 0, 0, _, F@_1, R1, R2, R3, TrUserData) -> #{name => F@_1, versions => lists_reverse(R1, TrUserData), retired => lists_reverse(R2, TrUserData), with_advisories => lists_reverse(R3, TrUserData)}; dfp_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dg_read_field_def_Package(Other, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData). dg_read_field_def_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 32 - 7 -> dg_read_field_def_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); dg_read_field_def_Package(<<0:1, X:7, Rest/binary>>, N, Acc, _, F@_1, F@_2, F@_3, F@_4, TrUserData) -> Key = X bsl N + Acc, case Key of 10 -> d_field_Package_name(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); 18 -> d_field_Package_versions(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); 26 -> d_pfield_Package_retired(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); 24 -> d_field_Package_retired(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); 42 -> d_pfield_Package_with_advisories(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); 40 -> d_field_Package_with_advisories(Rest, 0, 0, 0, F@_1, F@_2, F@_3, F@_4, TrUserData); _ -> case Key band 7 of 0 -> skip_varint_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); 1 -> skip_64_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); 2 -> skip_length_delimited_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); 3 -> skip_group_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData); 5 -> skip_32_Package(Rest, 0, 0, Key bsr 3, F@_1, F@_2, F@_3, F@_4, TrUserData) end end; dg_read_field_def_Package(<<>>, 0, 0, _, F@_1, R1, R2, R3, TrUserData) -> #{name => F@_1, versions => lists_reverse(R1, TrUserData), retired => lists_reverse(R2, TrUserData), with_advisories => lists_reverse(R3, TrUserData)}. d_field_Package_name(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_name(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); d_field_Package_name(<<0:1, X:7, Rest/binary>>, N, Acc, F, _, 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_Package(RestF, 0, 0, F, NewFValue, F@_2, F@_3, F@_4, TrUserData). d_field_Package_versions(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_versions(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); d_field_Package_versions(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, Prev, 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_Package(RestF, 0, 0, F, F@_1, cons(NewFValue, Prev, TrUserData), F@_3, F@_4, TrUserData). d_field_Package_retired(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_retired(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); d_field_Package_retired(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, Prev, F@_4, 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), F@_4, TrUserData). d_pfield_Package_retired(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_pfield_Package_retired(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); d_pfield_Package_retired(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, E, F@_4, 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, F@_4, 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. d_field_Package_with_advisories(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_field_Package_with_advisories(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); d_field_Package_with_advisories(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, 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, F@_3, cons(NewFValue, Prev, TrUserData), TrUserData). d_pfield_Package_with_advisories(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> d_pfield_Package_with_advisories(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); d_pfield_Package_with_advisories(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, E, TrUserData) -> Len = X bsl N + Acc, <> = Rest, NewSeq = d_packed_field_Package_with_advisories(PackedBytes, 0, 0, F, E, TrUserData), dfp_read_field_def_Package(Rest2, 0, 0, F, F@_1, F@_2, F@_3, NewSeq, TrUserData). d_packed_field_Package_with_advisories(<<1:1, X:7, Rest/binary>>, N, Acc, F, AccSeq, TrUserData) when N < 57 -> d_packed_field_Package_with_advisories(Rest, N + 7, X bsl N + Acc, F, AccSeq, TrUserData); d_packed_field_Package_with_advisories(<<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_with_advisories(RestF, 0, 0, F, [NewFValue | AccSeq], TrUserData); d_packed_field_Package_with_advisories(<<>>, 0, 0, _, AccSeq, _) -> AccSeq. skip_varint_Package(<<1:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> skip_varint_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData); skip_varint_Package(<<0:1, _:7, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData). skip_length_delimited_Package(<<1:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData) when N < 57 -> skip_length_delimited_Package(Rest, N + 7, X bsl N + Acc, F, F@_1, F@_2, F@_3, F@_4, TrUserData); skip_length_delimited_Package(<<0:1, X:7, Rest/binary>>, N, Acc, F, F@_1, F@_2, F@_3, F@_4, 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, F@_4, TrUserData). skip_group_Package(Bin, _, Z2, FNum, F@_1, F@_2, F@_3, F@_4, TrUserData) -> {_, Rest} = read_group(Bin, FNum), dfp_read_field_def_Package(Rest, 0, Z2, FNum, F@_1, F@_2, F@_3, F@_4, TrUserData). skip_32_Package(<<_:32, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData). skip_64_Package(<<_:64, Rest/binary>>, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, TrUserData) -> dfp_read_field_def_Package(Rest, Z1, Z2, F, F@_1, F@_2, F@_3, F@_4, 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, S3 = 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, case {PMsg, NMsg} of {#{with_advisories := PFwith_advisories}, #{with_advisories := NFwith_advisories}} -> S3#{with_advisories => 'erlang_++'(PFwith_advisories, NFwith_advisories, TrUserData)}; {_, #{with_advisories := NFwith_advisories}} -> S3#{with_advisories => NFwith_advisories}; {#{with_advisories := PFwith_advisories}, _} -> S3#{with_advisories => PFwith_advisories}; {_, _} -> 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 '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, case M of #{with_advisories := F4} -> if is_list(F4) -> _ = [v_type_int32(Elem, [with_advisories | Path], TrUserData) || Elem <- F4], ok; true -> mk_type_error({invalid_list_of, int32}, F4, [with_advisories | Path]) end; _ -> ok end, lists:foreach(fun (name) -> ok; (versions) -> ok; (retired) -> ok; (with_advisories) -> 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]}, #{name => with_advisories, fnum => 5, rnum => 5, 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]}, #{name => with_advisories, fnum => 5, rnum => 5, 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.5.1/src/mix_hex_registry.erl000066400000000000000000000137121522402544500171760ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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, encode_policy/1, decode_policy/3, build_policy/2, unpack_policy/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. %% @doc %% Builds policy resource. build_policy(Policy, PrivateKey) -> Payload = encode_policy(Policy), zlib:gzip(sign_protobuf(Payload, PrivateKey)). %% @doc %% Unpacks policy resource. unpack_policy(Payload, Repository, Name, PublicKey) -> case decode_and_verify_signed(zlib:gunzip(Payload), PublicKey) of {ok, Policy} -> decode_policy(Policy, Repository, Name); Other -> Other end. %% @private encode_policy(Policy) -> mix_hex_pb_policy:encode_msg(Policy, 'Policy'). %% @private decode_policy(Payload, no_verify, no_verify) -> {ok, mix_hex_pb_policy:decode_msg(Payload, 'Policy')}; decode_policy(Payload, Repository, Name) -> case mix_hex_pb_policy:decode_msg(Payload, 'Policy') of #{repository := Repository, name := Name} = 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.5.1/src/mix_hex_repo.erl000066400000000000000000000273671522402544500163060ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), do not edit manually %% @doc %% Repo API. -module(mix_hex_repo). -export([ get_names/1, get_versions/1, get_package/2, get_policy/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, fingerprint/1, fingerprint_equal/2 ]). %%==================================================================== %% 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 policy resource from the repository. %% %% Like the other resources, the URL is built from `repo_url' (with %% `repo_organization' inserting the `/repos/:org' segment when set), so the %% caller controls the layout through the config rather than this function %% requiring a particular field. %% %% Examples: %% %% ``` %% > Config = (mix_hex_core:default_config())#{repo_organization => <<"myorg">>}, %% > mix_hex_repo:get_policy(Config, <<"strict-prod">>). %% {ok, {200, ..., %% #{repository => <<"myorg">>, %% name => <<"strict-prod">>, %% visibility => 'VISIBILITY_PUBLIC'}}} %% ''' %% @end get_policy(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_policy(Data, repo_name(Config), Name); false -> mix_hex_registry:decode_policy(Data, no_verify, no_verify) end end, get_protobuf(Config, <<"policies/", 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. %% @doc %% Computes a SHA256 fingerprint of a PEM-encoded public key. %% %% Returns a string in the format "SHA256:" followed by base64, which can be used %% to verify public keys out-of-band. %% %% Examples: %% %% ``` %% > mix_hex_repo:fingerprint(PublicKeyPem). %% "SHA256:abc123..." %% ''' %% @end -spec fingerprint(binary()) -> string(). fingerprint(PublicKeyPem) when is_binary(PublicKeyPem) -> [PemEntry] = public_key:pem_decode(PublicKeyPem), PublicKey = public_key:pem_entry_decode(PemEntry), application:ensure_all_started(ssh), ssh:hostkey_fingerprint(sha256, PublicKey). %% @doc %% Compares a PEM-encoded public key against an expected fingerprint. %% %% Uses constant-time comparison to prevent timing attacks. %% %% Examples: %% %% ``` %% > mix_hex_repo:fingerprint_equal(PublicKeyPem, "SHA256:abc123..."). %% true %% ''' %% @end -spec fingerprint_equal(binary(), iodata()) -> boolean(). fingerprint_equal(PublicKeyPem, ExpectedFingerprint) when is_binary(PublicKeyPem) -> ActualFingerprint = fingerprint(PublicKeyPem), constant_time_compare( list_to_binary(ActualFingerprint), iolist_to_binary(ExpectedFingerprint) ). %% @private %% Constant-time comparison to prevent timing attacks. %% Uses crypto:hash_equals/2 on OTP 25+, falls back to manual comparison on older versions. -if(?OTP_RELEASE >= 25). constant_time_compare(A, B) when byte_size(A) =/= byte_size(B) -> false; constant_time_compare(A, B) -> crypto:hash_equals(A, B). -else. constant_time_compare(A, B) when byte_size(A) =:= byte_size(B) -> constant_time_compare(A, B, 0); constant_time_compare(_, _) -> false. constant_time_compare(<>, <>, Acc) -> constant_time_compare(RestA, RestB, Acc bor (X bxor Y)); constant_time_compare(<<>>, <<>>, Acc) -> Acc =:= 0. -endif. %%==================================================================== %% 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.5.1/src/mix_hex_safe_binary_to_term.erl000066400000000000000000000054451522402544500213450ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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.5.1/src/mix_hex_tarball.erl000066400000000000000000001405411522402544500167500ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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, do_decode_metadata/2, 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, 1024 * 1024). -define(METADATA_CHUNK_SIZE, 4096). -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, FilesRoot = maps:get(tarball_files_root, Config, "."), MetadataBinary = encode_metadata(Metadata), case valid_size(MetadataBinary, ?MAX_METADATA_SIZE) of false -> {error, {tarball, {file_too_big, "metadata.config"}}}; true -> case validate_create_files(Files, FilesRoot) of {ok, ValidatedFiles} -> ContentsTarball = create_memory_tarball(ValidatedFiles), 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; {error, _} = Error -> Error 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, FilesRoot = maps:get(tarball_files_root, Config, "."), case validate_create_files(Files, FilesRoot) of {ok, ValidatedFiles} -> UncompressedTarball = create_memory_tarball(ValidatedFiles), 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; {error, _} = Error -> Error 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, {unsafe_path, Name}}) -> io_lib:format("unsafe path in tarball: ~s", [Name]); format_error({tarball, {unsafe_symlink, Name, LinkTarget}}) -> io_lib:format("unsafe symlink in tarball: ~s -> ~s", [Name, LinkTarget]); format_error({tarball, {unsupported_file_type, Name, Type}}) -> io_lib:format("unsupported file type in tarball: ~s (~p)", [Name, Type]); 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}, config := Config} = State) when is_binary(Binary) -> Fields = maps:get(metadata_fields, Config, all), case do_decode_metadata(Binary, Fields) of #{} = Metadata -> maps:put(metadata, normalize_metadata(Metadata), State); Other -> Other end. -ifdef(TEST). do_decode_metadata(Binary) -> do_decode_metadata(Binary, all). -endif. %% @private do_decode_metadata(Binary, all) when is_binary(Binary) -> case decode_metadata_chunked(utf8, Binary, <<>>, [], "", []) of latin1_fallback -> decode_metadata_chunked(latin1, Binary, <<>>, [], "", []); Other -> Other end; do_decode_metadata(Binary, Fields) when is_binary(Binary), is_list(Fields) -> case decode_metadata_streaming(utf8, Binary, <<>>, [], "", [], Fields, start) of latin1_fallback -> decode_metadata_streaming(latin1, Binary, <<>>, [], "", [], Fields, start); Other -> Other end. %% @private %% Streams the metadata.config binary through mix_safe_erl_term:tokens/2 in %% small chunks so we never materialize the whole binary as a char list. %% Each accepted dot-terminated form is parsed and accumulated immediately, %% keeping peak memory at roughly one chunk + one term's tokens + AST. decode_metadata_chunked(Encoding, Binary, IncTail, Cont, Chars, Acc) -> case Chars of [] when Binary =:= <<>>, IncTail =:= <<>> -> flush_metadata_eof(Cont, Acc); [] when Binary =:= <<>>, Encoding =:= utf8 -> %% Trailing bytes that can never form a complete UTF-8 sequence — %% restart the whole decode in latin1 mode rather than spin. latin1_fallback; [] -> case decode_metadata_chunk(Encoding, Binary, IncTail) of {ok, NewChars, NewBinary, NewTail} -> feed_metadata(Encoding, Cont, NewChars, NewBinary, NewTail, Acc); latin1_fallback -> latin1_fallback end; _ -> feed_metadata(Encoding, Cont, Chars, Binary, IncTail, Acc) end. %% @private feed_metadata(Encoding, Cont, Chars, Binary, IncTail, Acc) -> case mix_safe_erl_term:tokens(Cont, Chars) of {more, NewCont} -> decode_metadata_chunked(Encoding, Binary, IncTail, NewCont, "", Acc); {done, {ok, Tokens, _}, RestChars} -> case parse_metadata_term(Tokens) of {ok, Term} -> decode_metadata_chunked( Encoding, Binary, IncTail, [], normalize_rest_chars(RestChars), [Term | Acc] ); {error, _} = Err -> Err end; {done, {eof, _}, _} -> finalize_metadata(Acc); {done, {error, {_, mix_safe_erl_term, Reason}, _}, _} -> {error, {metadata, Reason}} end. %% @private flush_metadata_eof([], Acc) -> finalize_metadata(Acc); flush_metadata_eof(Cont, Acc) -> case mix_safe_erl_term:tokens(Cont, eof) of {done, {eof, _}, _} -> finalize_metadata(Acc); {done, {ok, _Tokens, _}, _} -> {error, {metadata, invalid_terms}}; {done, {error, {_, mix_safe_erl_term, Reason}, _}, _} -> {error, {metadata, Reason}} end. %% @private finalize_metadata([]) -> {error, {metadata, invalid_terms}}; finalize_metadata(Acc) -> try maps:from_list(lists:reverse(Acc)) of Map -> Map catch error:badarg -> {error, {metadata, not_key_value}} end. %% @private parse_metadata_term(Tokens) -> case erl_parse:parse_term(Tokens) of {ok, Term} -> {ok, Term}; {error, _} -> {error, {metadata, invalid_terms}} end. %% @private decode_metadata_chunk(utf8, Binary, IncTail) -> {Chunk, Rest} = take_metadata_chunk(Binary), Combined = case IncTail of <<>> -> Chunk; _ -> <> end, case unicode:characters_to_list(Combined, utf8) of L when is_list(L) -> {ok, L, Rest, <<>>}; {incomplete, L, NewTail} -> {ok, L, Rest, NewTail}; {error, _, _} -> latin1_fallback end; decode_metadata_chunk(latin1, Binary, _IncTail) -> {Chunk, Rest} = take_metadata_chunk(Binary), {ok, binary_to_list(Chunk), Rest, <<>>}. %% @private take_metadata_chunk(Binary) when byte_size(Binary) > ?METADATA_CHUNK_SIZE -> <> = Binary, {Chunk, Rest}; take_metadata_chunk(Binary) -> {Binary, <<>>}. %% @private normalize_rest_chars(eof) -> ""; normalize_rest_chars(L) when is_list(L) -> L. %% @private %% Streams the metadata.config binary through mix_safe_erl_term:token/2 one token %% at a time. Forms whose key is in Fields are accumulated and parsed; forms %% whose key is not in Fields are discarded with only a depth counter held in %% state, so peak memory stays bounded regardless of the unwanted form's size. decode_metadata_streaming(Encoding, Binary, IncTail, Cont, Chars, Acc, Fields, State) -> case Chars of [] when Binary =:= <<>>, IncTail =:= <<>> -> flush_metadata_streaming_eof(Cont, Acc, Fields, State); [] when Binary =:= <<>>, Encoding =:= utf8 -> latin1_fallback; [] -> case decode_metadata_chunk(Encoding, Binary, IncTail) of {ok, NewChars, NewBinary, NewTail} -> feed_metadata_streaming( Encoding, Cont, NewChars, NewBinary, NewTail, Acc, Fields, State ); latin1_fallback -> latin1_fallback end; _ -> feed_metadata_streaming(Encoding, Cont, Chars, Binary, IncTail, Acc, Fields, State) end. %% @private feed_metadata_streaming(Encoding, Cont, Chars, Binary, IncTail, Acc, Fields, State) -> case mix_safe_erl_term:token(Cont, Chars) of {more, NewCont} -> decode_metadata_streaming(Encoding, Binary, IncTail, NewCont, "", Acc, Fields, State); {done, {ok, Token, _}, RestChars} -> case advance_metadata_state(State, Acc, Fields, Token) of {next, NewState, NewAcc} -> decode_metadata_streaming( Encoding, Binary, IncTail, [], normalize_rest_chars(RestChars), NewAcc, Fields, NewState ); {error, _} = Err -> Err end; {done, {eof, _}, _} -> finalize_metadata_streaming(Acc, State); {done, {error, {_, mix_safe_erl_term, Reason}, _}, _} -> {error, {metadata, Reason}} end. %% @private flush_metadata_streaming_eof([], Acc, _Fields, State) -> finalize_metadata_streaming(Acc, State); flush_metadata_streaming_eof(Cont, Acc, Fields, State) -> case mix_safe_erl_term:token(Cont, eof) of {done, {ok, Token, _}, _} -> case advance_metadata_state(State, Acc, Fields, Token) of {next, NewState, NewAcc} -> flush_metadata_streaming_eof([], NewAcc, Fields, NewState); {error, _} = Err -> Err end; {done, {eof, _}, _} -> finalize_metadata_streaming(Acc, State); {done, {error, {_, mix_safe_erl_term, Reason}, _}, _} -> {error, {metadata, Reason}} end. %% @private finalize_metadata_streaming(Acc, start) -> finalize_metadata(Acc); finalize_metadata_streaming([], between) -> #{}; finalize_metadata_streaming(Acc, between) -> finalize_metadata(Acc); finalize_metadata_streaming(_Acc, _State) -> {error, {metadata, invalid_terms}}. %% @private %% State machine for streaming the metadata.config schema. Forms are required %% to be `{<<"key">>, value}.` — anything else is rejected as invalid. %% %% States: start | between | {after_open, Prefix} | {after_left_binary, Prefix} %% | {after_key, KeyChars, Prefix} | {after_right_binary, KeyChars, Prefix} %% | {accumulate, Prefix, Depth} | {skip, Depth} %% %% `start` is the initial position; `between` is the position after a form has %% been completed. Distinguishing them lets empty input return the same %% invalid_terms error as the non-streaming path while a stream that %% successfully skipped every form returns an empty map. advance_metadata_state(Open, Acc, _Fields, {'{', _} = T) when Open =:= start; Open =:= between -> {next, {after_open, [T]}, Acc}; advance_metadata_state({after_open, Prefix}, Acc, _Fields, {'<<', _} = T) -> {next, {after_left_binary, [T | Prefix]}, Acc}; advance_metadata_state({after_left_binary, Prefix}, Acc, _Fields, {string, _, KeyChars} = T) -> {next, {after_key, KeyChars, [T | Prefix]}, Acc}; advance_metadata_state({after_key, KeyChars, Prefix}, Acc, _Fields, {'>>', _} = T) -> {next, {after_right_binary, KeyChars, [T | Prefix]}, Acc}; advance_metadata_state({after_right_binary, KeyChars, Prefix}, Acc, Fields, {',', _} = T) -> case extract_metadata_key(KeyChars) of {ok, Key} -> case lists:member(Key, Fields) of true -> {next, {accumulate, [T | Prefix], 1}, Acc}; false -> {next, {skip, 1}, Acc} end; error -> {error, {metadata, not_key_value}} end; advance_metadata_state({accumulate, Prefix, 0}, Acc, _Fields, {dot, _} = T) -> Tokens = lists:reverse([T | Prefix]), case parse_metadata_term(Tokens) of {ok, Term} -> {next, between, [Term | Acc]}; {error, _} = Err -> Err end; advance_metadata_state({accumulate, _, _}, _Acc, _Fields, {dot, _}) -> {error, {metadata, invalid_terms}}; advance_metadata_state({accumulate, Prefix, Depth}, Acc, _Fields, {Open, _} = T) when Open =:= '{'; Open =:= '[' -> {next, {accumulate, [T | Prefix], Depth + 1}, Acc}; advance_metadata_state({accumulate, Prefix, Depth}, Acc, _Fields, {Close, _} = T) when Close =:= '}'; Close =:= ']' -> {next, {accumulate, [T | Prefix], Depth - 1}, Acc}; advance_metadata_state({accumulate, Prefix, Depth}, Acc, _Fields, T) -> {next, {accumulate, [T | Prefix], Depth}, Acc}; advance_metadata_state({skip, 0}, Acc, _Fields, {dot, _}) -> {next, between, Acc}; advance_metadata_state({skip, _}, _Acc, _Fields, {dot, _}) -> {error, {metadata, invalid_terms}}; advance_metadata_state({skip, Depth}, Acc, _Fields, {Open, _}) when Open =:= '{'; Open =:= '[' -> {next, {skip, Depth + 1}, Acc}; advance_metadata_state({skip, Depth}, Acc, _Fields, {Close, _}) when Close =:= '}'; Close =:= ']' -> {next, {skip, Depth - 1}, Acc}; advance_metadata_state({skip, Depth}, Acc, _Fields, _Token) -> {next, {skip, Depth}, Acc}; advance_metadata_state(_State, _Acc, _Fields, _Token) -> {error, {metadata, not_key_value}}. %% @private extract_metadata_key(KeyChars) -> try list_to_binary(KeyChars) of Key -> {ok, Key} catch error:badarg -> error 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 validate_create_files(Files, FilesRoot) when is_list(Files) -> validate_create_files(Files, FilesRoot, []). validate_create_files([], _FilesRoot, Acc) -> {ok, lists:reverse(Acc)}; validate_create_files([File | Rest], FilesRoot, Acc) -> case validate_create_file(File, FilesRoot) of {ok, ValidatedFile} -> validate_create_files(Rest, FilesRoot, [ValidatedFile | Acc]); {error, _} = Error -> Error end. validate_create_file({Filename, Contents}, _FilesRoot) when is_list(Filename), is_binary(Contents) -> case validate_archive_path(Filename) of ok -> {ok, {Filename, Contents}}; {error, _} = Error -> Error end; validate_create_file(Filename, FilesRoot) when is_list(Filename) -> validate_create_file({Filename, Filename}, FilesRoot); validate_create_file({Filename, AbsFilename}, FilesRoot) when is_list(Filename), is_list(AbsFilename) -> case validate_archive_path(Filename) of ok -> validate_source_file(Filename, AbsFilename, FilesRoot); {error, _} = Error -> Error end. validate_archive_path(Filename) -> case safe_relative_archive_path(Filename) of false -> {error, {tarball, {unsafe_path, Filename}}}; true -> ok end. validate_source_file(ArchiveName, SourcePath, FilesRoot) -> case source_file_paths(SourcePath, FilesRoot) of {ok, DiskPath, RelativePath, Root} -> validate_source_file_root(ArchiveName, DiskPath, RelativePath, Root); outside_root -> {error, {tarball, {unsafe_path, ArchiveName}}} end. source_file_paths(SourcePath, FilesRoot) -> Root = normalize_root(filename:absname(FilesRoot)), case source_relative_path(SourcePath, Root) of {ok, RelativePath} -> {ok, source_disk_path(SourcePath, Root), RelativePath, Root}; outside_root -> outside_root end. normalize_root(Path) -> filename:join(normalize_root_parts(filename:split(Path), [])). normalize_root_parts([], Acc) -> lists:reverse(Acc); normalize_root_parts(["." | Parts], Acc) -> normalize_root_parts(Parts, Acc); normalize_root_parts([".." | Parts], Acc) -> normalize_root_parent(Parts, Acc); normalize_root_parts([Part | Parts], Acc) -> normalize_root_parts(Parts, [Part | Acc]). normalize_root_parent(Parts, [Root] = Acc) -> case filename:pathtype(Root) of relative -> normalize_root_parts(Parts, []); _ -> normalize_root_parts(Parts, Acc) end; normalize_root_parent(Parts, [_Part | Acc]) -> normalize_root_parts(Parts, Acc); normalize_root_parent(Parts, []) -> normalize_root_parts(Parts, []). source_disk_path(SourcePath, Root) -> case filename:pathtype(SourcePath) of absolute -> SourcePath; _ -> filename:join(Root, SourcePath) end. source_relative_path(SourcePath, Root) -> case filename:pathtype(SourcePath) of absolute -> strip_root_path(filename:split(SourcePath), filename:split(Root)); _ -> {ok, SourcePath} end. strip_root_path([], []) -> {ok, "."}; strip_root_path(PathParts, []) -> {ok, filename:join(PathParts)}; strip_root_path([Part | PathParts], [Part | RootParts]) -> strip_root_path(PathParts, RootParts); strip_root_path(_PathParts, _RootParts) -> outside_root. validate_source_file_root(ArchiveName, DiskPath, RelativePath, Root) -> case file:read_link_info(DiskPath, []) of {ok, #file_info{type = Type}} when Type =:= regular; Type =:= directory -> case validate_source_root(ArchiveName, RelativePath, Root) of ok -> {ok, {ArchiveName, DiskPath}}; {error, _} = Error -> Error end; {ok, #file_info{type = symlink}} -> {ok, LinkTarget} = file:read_link(DiskPath), ResolvedTarget = archive_join(archive_dirname(ArchiveName), LinkTarget), case safe_relative_archive_path(ResolvedTarget) of false -> {error, {tarball, {unsafe_symlink, ArchiveName, LinkTarget}}}; true -> case validate_source_root(ArchiveName, RelativePath, Root) of ok -> {ok, {ArchiveName, DiskPath}}; {error, _} = Error -> Error end end; {ok, #file_info{type = Type}} -> {error, {tarball, {unsupported_file_type, ArchiveName, Type}}}; _ -> case validate_source_root(ArchiveName, RelativePath, Root) of ok -> {ok, {ArchiveName, DiskPath}}; {error, _} = Error -> Error end end. validate_source_root(ArchiveName, SourcePath, FilesRoot) -> case filelib:safe_relative_path(SourcePath, FilesRoot) of unsafe -> {error, {tarball, {unsafe_path, ArchiveName}}}; _ -> ok end. safe_relative_archive_path(Path) -> case archive_path_absolute(Path) orelse archive_path_drive(Path) of true -> false; false -> safe_relative_archive_path(archive_path_split(Path), []) end. safe_relative_archive_path([], _Acc) -> true; safe_relative_archive_path(["." | Rest], Acc) -> safe_relative_archive_path(Rest, Acc); safe_relative_archive_path([".." | _Rest], []) -> false; safe_relative_archive_path([".." | Rest], [_ | Acc]) -> safe_relative_archive_path(Rest, Acc); safe_relative_archive_path([_Part | Rest], Acc) -> safe_relative_archive_path(Rest, [ok | Acc]). archive_path_absolute([$/ | _Rest]) -> true; archive_path_absolute([$\\ | _Rest]) -> true; archive_path_absolute(_Path) -> false. archive_path_drive([Drive, $: | _Rest]) when Drive >= $a, Drive =< $z; Drive >= $A, Drive =< $Z -> true; archive_path_drive(_Path) -> false. archive_path_split(Path) -> string:tokens(Path, "/\\"). archive_dirname(Path) -> case archive_path_split(Path) of [] -> "."; [_Name] -> "."; Parts -> string:join(lists:droplast(Parts), "/") end. archive_join(_Dir, Path) when Path =:= [] -> Path; archive_join(_Dir, Path = [$/ | _Rest]) -> Path; archive_join(_Dir, Path = [$\\ | _Rest]) -> Path; archive_join(_Dir, Path = [Drive, $: | _Rest]) when Drive >= $a, Drive =< $z; Drive >= $A, Drive =< $Z -> Path; archive_join(".", Path) -> Path; archive_join(Dir, Path) -> Dir ++ "/" ++ Path. %% @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.5.1/src/mix_safe_erl_term.xrl000066400000000000000000000045051522402544500173140ustar00rootroot00000000000000%% Vendored from hex_core v0.18.0 (d6a6a5a), 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}}. % end_token (not token) lets mix_hex_tarball stream-decode metadata.config % one form at a time via mix_safe_erl_term:tokens/2. \. : {end_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.5.1/test/000077500000000000000000000000001522402544500132655ustar00rootroot00000000000000hex-2.5.1/test/fixtures/000077500000000000000000000000001522402544500151365ustar00rootroot00000000000000hex-2.5.1/test/fixtures/certs/000077500000000000000000000000001522402544500162565ustar00rootroot00000000000000hex-2.5.1/test/fixtures/certs/amazon.der000066400000000000000000000024311522402544500202370ustar00rootroot0000000000000000N/* &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.5.1/test/fixtures/certs/ca.cert.pem000066400000000000000000000035431522402544500203050ustar00rootroot00000000000000-----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.5.1/test/fixtures/certs/server.der000066400000000000000000000020261522402544500202600ustar00rootroot00000000000000000  *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.5.1/test/fixtures/certs/verisign_g3.der000066400000000000000000000027601522402544500211760ustar00rootroot0000000000000000Ԡ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.5.1/test/fixtures/ecto_override/000077500000000000000000000000001522402544500177675ustar00rootroot00000000000000hex-2.5.1/test/fixtures/ecto_override/mix.exs000066400000000000000000000003151522402544500213040ustar00rootroot00000000000000defmodule 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.5.1/test/fixtures/ecto_sql_3_3_2/000077500000000000000000000000001522402544500176345ustar00rootroot00000000000000hex-2.5.1/test/fixtures/ecto_sql_3_3_2/mix.exs000066400000000000000000000003021522402544500211450ustar00rootroot00000000000000defmodule 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.5.1/test/fixtures/ecto_sql_3_3_3/000077500000000000000000000000001522402544500176355ustar00rootroot00000000000000hex-2.5.1/test/fixtures/ecto_sql_3_3_3/mix.exs000066400000000000000000000003021522402544500211460ustar00rootroot00000000000000defmodule Ecto.SQL_3_3_3.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.5.1/test/fixtures/ex_doc/000077500000000000000000000000001522402544500163775ustar00rootroot00000000000000hex-2.5.1/test/fixtures/ex_doc/mix.exs000066400000000000000000000001711522402544500177140ustar00rootroot00000000000000defmodule ExDoc.Fixture.MixProject do use Mix.Project def project do [app: :ex_doc, version: "0.0.1"] end end hex-2.5.1/test/fixtures/has_hex_dep/000077500000000000000000000000001522402544500174055ustar00rootroot00000000000000hex-2.5.1/test/fixtures/has_hex_dep/mix.exs000066400000000000000000000002761522402544500207300ustar00rootroot00000000000000defmodule 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.5.1/test/fixtures/override_with_path/000077500000000000000000000000001522402544500210245ustar00rootroot00000000000000hex-2.5.1/test/fixtures/override_with_path/mix.exs000066400000000000000000000003561522402544500223460ustar00rootroot00000000000000defmodule 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.5.1/test/fixtures/registries/000077500000000000000000000000001522402544500173165ustar00rootroot00000000000000hex-2.5.1/test/fixtures/registries/20200917.ets000066400000000000000000006641121522402544500207510ustar00rootroot00000000000000cXM bWLAhhwidZw nonode@nohostoˮ\xFhwdecentralized_counterswfalsehwread_concurrencywfalsehwwrite_concurrencywfalsehw compressedwfalsehwmemoryb26hwownerXw nonode@nohostehwheirwnonehwnamewElixir.Hex.Devhwsizebehwnodew nonode@nohosthw named_tablewfalsehwtypewsethwkeyposahw protectionw protectedhw major_versionahw minor_versionahw extended_infoj0bWLAhhwretiredmhexpmmplugm1.2.3wnilZbWLAhhwouter_checksummhexpmmphoenixm1.3.0m 5̑l7O2O/]eeSlbWLAhhwdepsmhexpmmphoenixm0.17.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejFbWLAhhw timestampmhexpmmphoenixm1.4.13hhba aha a9a0`bWLAhhwinner_checksummhexpmm db_connectionm1.1.2m (e¤qN"`-v QYɫǨ1bWLAhhwretiredmhexpmmplugm0.12.2wnilBbWLAhhw timestampmhexpmmplugm1.2.5hhba aha a9a0[bWLAhhwouter_checksummhexpmmphoenixm0.2.10m eXNil ?-_0qA $Q"3bWLAhhwretiredmhexpmmphoenixm1.1.1wnilbWLAhhwdepsmhexpmmphoenixm0.5.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmlinguistmlinguistm~> 0.1.2wfalsehmhexpmmplugmplugm~> 0.8.1wfalsehmhexpmmpoisonmpoisonm~> 1.1wfalsej1bWLAhhwretiredmhexpmmplugm0.11.0wnilqbWLAhhwdepsmhexpmm phoenix_htmlm2.4.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsejWbWLAhhwinner_checksummhexpmmplugm1.3.3m پ$7NG sЕIί#0bWLAhhwretiredmhexpmmplugm0.5.3wnil>bWLAhhw timestampmhexpmm connectionhhba aha a9a0bWLAhhwdepsmhexpmm db_connectionm1.1.0lhmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 1.0wtruejbbWLAhhwinner_checksummhexpmm connectionm 1.0.0-rc.1m t$H' X*;֞bYyF'bWLAhhwversionsmhexpmmphoenix_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.3jbWLAhhwdepsmhexpmmpostgrexm 1.0.0-rc.1lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 1.1wfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsej[bWLAhhwinner_checksummhexpmmpostgrexm0.7.0m ;k%W#Ȝ;*)ZbWLAhhwinner_checksummhexpmmphoenixm1.4.2m :P e; T4iMEbWLAhhw timestampmhexpmmphoenixm1.1.6hhba aha a9a05bWLAhhwretiredmhexpmmpostgrexm0.11.1wnilKbWLAhhw timestampmhexpmm phoenix_htmlm2.10.2hhba aha a9a0"bWLAhhwdepsmhexpmmpostgrexm 0.14.0-rc.0lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm ~> 2.0-rc.0wfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsehmhexpmmjasonmjasonm~> 1.0wtruej"bWLAhhwdepsmhexpmmphoenixm 1.4.0-rc.2lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruej8bWLAhhwretiredmhexpmm phoenix_htmlm2.6.0wnil`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.13.2m 4gddے*yz#$\bWLAhhwinner_checksummhexpmmpostgrexm0.15.4m ]i%y5"j5F#^[bWLAhhwouter_checksummhexpmmpostgrexm0.4.2m bŘt/ ԍн6vzʵz,gbWLAhhwdepsmhexpmm phoenix_htmlm2.14.1lhmhexpmmplugmplugm~> 1.5wfalsejabWLAhhwouter_checksummhexpmmphoenix_paramsm0.3.0m \߶E.F_A \<x):bWLAhhw timestampmhexpmmcowboyhhbaaha aa^bWLAhhwinner_checksummhexpmm plug_cryptom1.1.1m K`*BЄq'I5 FZbWLAhhwinner_checksummhexpmmphoenixm0.2.9m (N76eVҫbnK;L}d,bWLAhhwdepsmhexpmmdecimalm1.7.0j0bWLAhhwretiredmhexpmmplugm1.0.0wnil_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.0.1m ߲8MςÝzffSpo&`bWLAhhwouter_checksummhexpmm db_connectionm1.1.1m oxZ#L2* ?~1rF5DZbWLAhhwinner_checksummhexpmmdecimalm1.3.0m Afx`E|Rӣ՗M'}EbWLAhhw timestampmhexpmmphoenixm1.0.2hhba aha a9a0LbWLAhhw timestampmhexpmmpostgrexm 0.14.0-rc.0hhba aha a9a0abWLAhhwinner_checksummhexpmmphoenix_paramsm0.3.0m Kzrv-1 8@FBbWLAhhw timestampmhexpmmplugm1.3.0hhba aha a9a0IbWLAhhw timestampmhexpmm plug_cowboym2.2.0hhba aha a9a0bWLAhhwdepsmhexpmmpostgrexm0.15.1lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 2.1wfalsehmhexpmmdecimalmdecimalm~> 1.5wfalsehmhexpmmjasonmjasonm~> 1.0wtruejbWLAhhwdepsmhexpmmplugm1.5.1lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.3wtruehmhexpmmmimemmimem~> 1.0wfalsejFbWLAhhw timestampmhexpmmpostgrexm0.9.1hhba aha a9a05bWLAhhwretiredmhexpmmpostgrexm0.13.3wnil`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.12.0m y /DGP)%kmn^FbWLAhhw timestampmhexpmmpostgrexm0.9.0hhba aha a9a0`bWLAhhwouter_checksummhexpmm db_connectionm0.1.2m ȫG}:(ieoM8b|B ɖ[bWLAhhwouter_checksummhexpmmphoenixm1.4.13m vZہbl{_QYb`tRv}bWLAhhwdepsmhexpmmphoenixm0.2.8lhmhexpmmex_confmex_confm0.1.2wfalsehmhexpmminflexminflexm0.2.0wfalsehmhexpmmjazzmjazzm0.1.1wfalsehmhexpmmplugmplugm0.4.4wfalsej:bWLAhhwretiredmhexpmmphoenix_paramsm1.1.1wnilWbWLAhhwouter_checksummhexpmmplugm1.2.0m '8êBJm0ʭOh::,bWLAhhwdepsmhexpmmdecimalm1.3.1jWbWLAhhwouter_checksummhexpmmplugm1.1.0m iijK'%.? -ޙ/قKbWLAhhwdepsmhexpmm db_connectionm 1.0.0-rc.5lhmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 1.0.0-beta.3wtruej[bWLAhhwinner_checksummhexpmmpostgrexm0.9.1m Գp1bW.MB/]QF=aZbWLAhhwouter_checksummhexpmmdecimalm1.6.0m $@@s`I*sGHM:bJiLbWLAhhw timestampmhexpmmphoenix_paramsm1.1.3hhba aha a9a0:bWLAhhwretiredmhexpmmphoenix_pubsubm0.0.1wnil_bWLAhhwinner_checksummhexpmmphoenixm 1.3.0-rc.2m SJ%h܂o8Bs E"(ت>bWLAhhwretiredmhexpmm db_connectionm 1.0.0-rc.3wnilZbWLAhhwouter_checksummhexpmmdecimalm1.5.0m  &XUMu_]r?3ɻ9W⡭.6bWLAhhwretiredmhexpmm connectionm1.0.4wnil`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.10.4m 2I1c| p?-Mm8bWLAhhwretiredmhexpmmphoenixm 1.4.0-rc.1wnilbWLAhhwdepsmhexpmm plug_cowboym2.1.3lhmhexpmmcowboymcowboym~> 2.5wfalsehmhexpmmplugmplugm~> 1.7wfalsejWbWLAhhwouter_checksummhexpmmplugm1.7.2m ޘ%o֭ ͈k=e~`kZbWLAhhwinner_checksummhexpmmphoenixm1.5.2m {]lNvA I/jIgXbWLAhhwouter_checksummhexpmmplugm1.10.0m B*'g; {w[-퐏2dA^JabWLAhhwdepsmhexpmmplugm1.0.6lhmhexpmmcowboymcowboym~> 1.0wtruej`bWLAhhwinner_checksummhexpmmpostgrexm 1.0.0-rc.1m &w%!EeFZWbWLAhhwouter_checksummhexpmmplugm1.2.2m ^Dl]R&~,23~KbWLAhhw timestampmhexpmm db_connectionm0.1.8hhba aha a9a03bWLAhhwretiredmhexpmmphoenixm1.4.0wnilbWLAhhwdepsmhexpmmphoenixm1.5.1lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej[bWLAhhwinner_checksummhexpmmphoenixm0.12.0m Bp5 »O+hȠ& F!@ F2,_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.9.0m ne7WuG5FD))%cA:k .35bWLAhhwretiredmhexpmmplugm 1.2.0-rc.0wnilXbWLAhhwinner_checksummhexpmmplugm1.10.4m AѢqSgd[գ}*?9ubWLAhhwdepsmhexpmm db_connectionm2.0.6lhmhexpmm connectionm connectionm~> 1.0.2wfalsej5bWLAhhwretiredmhexpmm telemetrym0.4.2wnilWbWLAhhwouter_checksummhexpmmplugm1.2.3m e”ynw^:݃mrH/-PabWLAhhwdepsmhexpmmplugm1.1.7lhmhexpmmcowboymcowboym~> 1.0wtruej/bWLAhhwdepsmhexpmm connectionm1.0.0jbWLAhwversionabbWLAhhwdepsmhexpmmplugm0.13.0lhmhexpmmcowboymcowboym~> 1.0wtruejZbWLAhhwinner_checksummhexpmmphoenixm1.4.7m 弚Lu R}Zl*<awm,bWLAhhwdepsmhexpmmdecimalm1.4.0j^bWLAhhwinner_checksummhexpmm plug_cowboym2.1.0m Wh<:9Բ[NŎJnj' 0 zbWLAhhwdepsmhexpmm db_connectionm 2.0.0-rc.0lhmhexpmm connectionm connectionm~> 1.0.2wfalsejGbWLAhhw timestampmhexpmmpostgrexm0.11.1hhba aha a9a0"bWLAhhwdepsmhexpmmphoenixm1.3.2lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4wfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0wfalsejBbWLAhhw timestampmhexpmmplugm1.4.3hhba aha a9a0xbWLAhhwversionsmhexpmmmimelm0.0.1m1.0.0m1.0.1m1.1.0m1.2.0m1.3.0m1.3.1m1.4.0j9bWLAhhwretiredmhexpmm db_connectionm2.0.3wnilWbWLAhhwouter_checksummhexpmmplugm1.8.2m TȻ,(#GU(1 VXbWLAhhwouter_checksummhexpmmplugm1.10.1m R%1EII*,bWLAhhwdepsmhexpmmphoenixm1.5.4lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejGbWLAhhw timestampmhexpmm telemetrym0.2.0hhba aha a9a0ZbWLAhhwouter_checksummhexpmmphoenixm1.2.5m S2&īaipHdRα8_bWLAhhwinner_checksummhexpmmphoenixm 1.4.0-rc.0m k]MvF/x[bcjfj8{.ޤ\bWLAhhwinner_checksummhexpmm telemetrym0.2.0m [@ʣ޳-|OUomk\7L#fw!a1wqbWLAhhwdepsmhexpmm phoenix_htmlm2.5.1lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsejWbWLAhhwinner_checksummhexpmmplugm1.6.4m 5aiE/k7vЙs?'G?BbWLAhhw timestampmhexpmmplugm1.0.2hhba aha a9a0CbWLAhhw timestampmhexpmmplugm0.13.1hhba aha a9a0bWLAhhwdepsmhexpmmpostgrexm0.15.4lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 2.1wfalsehmhexpmmdecimalmdecimalm~> 1.5wfalsehmhexpmmjasonmjasonm~> 1.0wtruejabWLAhhwouter_checksummhexpmmphoenix_paramsm1.0.0m  Z޳>77Myd k0bWLAhhwretiredmhexpmmplugm1.3.1wnil,bWLAhhwdepsmhexpmmphoenixm0.1.0jZbWLAhhwinner_checksummhexpmmphoenixm1.2.0m ޹%OLSLߘ )|b]Wb|;fbWLAhhwdepsmhexpmmpostgrexm0.13.0lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 1.1wfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsej3bWLAhhwretiredmhexpmmphoenixm1.1.0wnilabWLAhhwinner_checksummhexpmmphoenix_paramsm1.0.0m LrـݨہJWU6)T:FbWLAhhw timestampmhexpmmphoenixm0.13.0hhba aha a9a0ZbWLAhhwinner_checksummhexpmmphoenixm0.2.3m ^v& G prY 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm6~> 1.4 or ~> 1.3.3 or ~> 1.2.4 or ~> 1.1.8 or ~> 1.0.5wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsej8bWLAhhwretiredmhexpmm phoenix_htmlm2.5.1wnil[bWLAhhwouter_checksummhexpmmpostgrexm0.5.3m inޤh/ U(w@M9 ubWLAhhwdepsmhexpmm db_connectionm2.0.2lhmhexpmm connectionm connectionm~> 1.0.2wfalsej6bWLAhhwretiredmhexpmmjasonm 1.0.0-rc.3wnilLbWLAhhw timestampmhexpmmphoenix_paramsm0.4.1hhba aha a9a0WbWLAhhwouter_checksummhexpmmplugm1.5.1m 1-]yuCSN!nj6bWLAhhwdepsmhexpmm plug_cowboym2.0.0lhmhexpmmcowboymcowboym~> 2.5wfalsehmhexpmmplugmplugm~> 1.7wfalsejbWLAhhwdepsmhexpmm plug_cowboym2.2.2lhmhexpmmcowboymcowboym~> 2.7wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm,~> 1.3.3 or ~> 1.2.4 or ~> 1.1.8 or ~> 1.0.5wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsej_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.9.1m ȽP#4|oA iOTCbWLAhhw timestampmhexpmmplugm0.12.2hhba aha a9a0_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.6.2m i{1HT]hBrX5p- _2,[bWLAhhwinner_checksummhexpmmphoenixm0.13.1m i^`KxiPSளz2R.nۅXbWLAhhwouter_checksummhexpmmplugm0.12.1m 4+`%`bȩ@L)^DF4bWLAhhwretiredmhexpmmphoenixm1.4.10wnil0bWLAhhwretiredmhexpmmplugm1.0.6wnilabWLAhhwinner_checksummhexpmmphoenix_pubsubm0.1.0m zڏAI+Y@W<5y5CbWLAhhw timestampmhexpmmplugm0.12.1hhba aha a9a0EbWLAhhw timestampmhexpmmphoenixm0.6.1hhba aha a9a0ZbWLAhhwouter_checksummhexpmmdecimalm0.2.3m tVX7aCVh`ZV1ւu_bWLAhhwouter_checksummhexpmmphoenixm 1.4.0-rc.1m t4<Q7lWHAw>9bWLAhhwretiredmhexpmm phoenix_htmlm2.13.0wnilcbWLAhhwinner_checksummhexpmm phoenix_htmlm 2.0.0-devm 3{ۼ/"\4s9sHee[}(e`bWLAhhwinner_checksummhexpmm db_connectionm2.2.1m rT@g*bJƓfZbWLAhhwouter_checksummhexpmmdecimalm1.1.1m ?6"/+ 2.5wfalsehmhexpmmplugmplugm~> 1.7wfalsej4bWLAhhwretiredmhexpmmphoenixm0.17.0wnilZbWLAhhwinner_checksummhexpmmphoenixm1.4.5m n ? e?E 7]t5bWLAhhwretiredmhexpmm telemetrym0.4.1wnilZbWLAhhwinner_checksummhexpmmphoenixm1.4.8m -íIc$dQNzS_bWLAhhwinner_checksummhexpmm phoenix_htmlm1.0.0m >ִZPA$<y5PτWDzi GbWLAhhw timestampmhexpmmpostgrexm0.15.1hhba aha a9a05bWLAhhwretiredmhexpmmpostgrexm0.10.0wnilZbWLAhhwinner_checksummhexpmmphoenixm1.1.7m 4ظ ?TL_?85ȭaU]bWLAhhwouter_checksummhexpmmjasonm 1.0.0-rc.2m wv3ԉ ĘDo$bWLAhhwdepsmhexpmmphoenixm0.2.7lhmhexpmmex_confmex_confm0.1.2wfalsehmhexpmminflexminflexm0.2.0wfalsehmhexpmmjazzmjazzm0.1.1wfalsehmhexpmmplugmplugm0.4.4wfalsej,bWLAhhwdepsmhexpmmdecimalm1.2.0jKbWLAhhw timestampmhexpmm phoenix_htmlm2.14.2hhba aha a9a0XbWLAhhwinner_checksummhexpmmplugm1.10.2m y4\}FTTŇd8\wZ,bWLAhhwdepsmhexpmmdecimalm1.1.1j\bWLAhhwinner_checksummhexpmmpostgrexm0.13.5m =)6>CzKm$+BbWLAhhw timestampmhexpmmplugm1.4.1hhba aha a9a0`bWLAhhwouter_checksummhexpmm db_connectionm2.0.1m שZwysJAܿ)2I0Ű\1WbWLAhhwouter_checksummhexpmmplugm1.6.2m q<W&a +UlALu{cbWLAhhwinner_checksummhexpmm phoenix_htmlm 2.4.0-devm 4 }hd2+6V0bWLAhhwretiredmhexpmmplugm1.6.0wnil,bWLAhhwdepsmhexpmmdecimalm0.2.5j_bWLAhhwouter_checksummhexpmmphoenixm 1.4.0-rc.2m &h (0"B}pe:8BbWLAhhwdepsmhexpmmphoenix_paramsm1.0.3lhmhexpmmdecimalmdecimalm>= 1.7.0wfalsehmhexpmmphoenixmphoenixm>= 1.3.3wfalsejEbWLAhhw timestampmhexpmmphoenixm1.2.2hhba aha a9a0WbWLAhhwinner_checksummhexpmmplugm1.1.2m ~$ZVɭ\1L>m ٛHE 1bWLAhhwretiredmhexpmmplugm1.10.4wnil0bWLAhhwretiredmhexpmmplugm1.2.0wnilZbWLAhhwouter_checksummhexpmmphoenixm1.4.0m "ڏe=;;vfÉ=B%̄K`bWLAhhwouter_checksummhexpmm db_connectionm0.2.0m ?MQ%u* j*g;bWLAhhw timestampmhexpmmpoolboyhhba ahaaa)9bWLAhhwretiredmhexpmm db_connectionm2.1.0wnil_bWLAhhwinner_checksummhexpmmdecimalm 1.9.0-rc.0m cfja8{@<Lp}bWLAhhwdepsmhexpmmplugm1.6.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4wtruehmhexpmmmimemmimem~> 1.0wfalsej3bWLAhhwdepsmhexpmmphoenix_pubsubm0.0.1j[bWLAhhwinner_checksummhexpmmpostgrexm0.5.4m <1=wX 1.0wtruehmhexpmmmimemmimem~> 1.0wfalsej`bWLAhhwinner_checksummhexpmm db_connectionm1.1.3m ;FwyYVXf·]M~WZa\bWLAhhwouter_checksummhexpmm telemetrym0.2.0m Nqy]YL?C i .x@#2XbWLAhhwouter_checksummhexpmmplugm0.10.0m a{9 aH5;7KC]ϖ5"Qy!qJ^bWLAhhwouter_checksummhexpmm plug_cowboym2.2.2m #dۭ7SՈ~^Pbf17'ZbWLAhhwouter_checksummhexpmmphoenixm1.0.2m VBSkeѠOU~9V#wk`RgZbWLAhhwouter_checksummhexpmmphoenixm0.4.0m 碆B~@9YUOb*b1bWLAhhwretiredmhexpmmjasonm1.2.2wnilEbWLAhhw timestampmhexpmmphoenixm1.4.4hhba aha a9a0WbWLAhhwinner_checksummhexpmmmimem1.0.0m 0's;;]ȶbˁw1TJV`bWLAhhwouter_checksummhexpmm db_connectionm0.2.5m C[oչ6rd߸i^$Zp/WbWLAhhwinner_checksummhexpmmplugm0.8.1m $0 ;pD/L_awFҐG0bWLAhhwretiredmhexpmmplugm1.6.4wnil:bWLAhhwretiredmhexpmmphoenix_paramsm0.2.2wnil_bWLAhhwinner_checksummhexpmm phoenix_htmlm1.2.1m Sg%pXyOdoa⸻9tl\bWLAhhwinner_checksummhexpmmpostgrexm0.10.0m XimaѩtRqM"M2Տ-IbWLAhhw timestampmhexpmm plug_cryptom1.1.0hhba aha a9a0bWLAhhwdepsmhexpmmplugm1.4.3lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsej)bWLAhhwdepsmhexpmmmimem1.3.0j\bWLAhhwouter_checksummhexpmmpostgrexm0.11.1m mGOd,@d[w#vO(-[LbWLAhhw timestampmhexpmmphoenix_pubsubm1.0.2hhba aha a9a09bWLAhhwretiredmhexpmm db_connectionm0.1.0wnil`bWLAhhwouter_checksummhexpmm db_connectionm2.0.6m h~YY'4 *Em-0f;aZbWLAhhwouter_checksummhexpmmdecimalm1.8.1m z^bWLAhhwinner_checksummhexpmm plug_cowboym2.0.1m טZ̆-DPf\#nQ2AzFbWLAhhw timestampmhexpmmpostgrexm0.8.2hhba aha a9a0bWLAhhwdepsmhexpmmplugm1.2.4lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmmimemmimem~> 1.0wfalsej\bWLAhhwouter_checksummhexpmmplugm 1.5.0-rc.1m A^3\1=ݴGp 0.13 or ~> 1.0wfalsejWbWLAhhwouter_checksummhexpmmmimem1.0.0m ~gG틓\Tzy,OLm}?\9bWLAhhwretiredmhexpmm phoenix_htmlm2.11.2wnil\bWLAhhwinner_checksummhexpmmpostgrexm0.15.5m "Y/0"uAD3/qbWLAhhwdepsmhexpmm phoenix_htmlm2.1.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsejbWLAhhwdepsmhexpmmphoenixm1.4.1lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruejjbWLAhhwdepsmhexpmmpostgrexm0.4.2lhmhexpmmdecimalmdecimalm~> 0.1.2wfalsej0bWLAhhwretiredmhexpmmplugm1.3.5wnilabWLAhhwdepsmhexpmmplugm0.8.4lhmhexpmmcowboymcowboym~> 1.0wtruej0bWLAhhwretiredmhexpmmplugm0.4.3wnilCbWLAhhw timestampmhexpmmplugm1.10.2hhba aha a9a0EbWLAhhw timestampmhexpmmdecimalm1.7.0hhba aha a9a0bWLAhhwdepsmhexpmmplugm1.3.5lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsejbWLAhhwversionsmhexpmm 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.2jWbWLAhhwouter_checksummhexpmmmimem0.0.1m $ ߽#C8Fd7S;:L5C' BbWLAhhw timestampmhexpmmplugm1.1.9hhba aha a9a0;bWLAhhw timestampmhexpmmdecimalhhba aha a9a03bWLAhhwretiredmhexpmmdecimalm1.1.2wnilGbWLAhhw timestampmhexpmmpostgrexm0.15.2hhba aha a9a0abWLAhhwinner_checksummhexpmmphoenix_pubsubm0.0.1m |ļ[kY`dF>NSKtubWLAhhwdepsmhexpmm db_connectionm2.0.4lhmhexpmm connectionm connectionm~> 1.0.2wfalsejdbWLAhhwdepsmhexpmmjasonm1.0.1lhmhexpmmdecimalmdecimalm~> 1.0wtruejKbWLAhhw timestampmhexpmm db_connectionm2.0.2hhba aha a9a0WbWLAhhwouter_checksummhexpmmplugm1.0.2m A› Nm~Q@LÅDyS8bWLAhhwretiredmhexpmm phoenix_htmlm2.9.1wnilabWLAhhwouter_checksummhexpmmpostgrexm 0.14.0-rc.1m =8HMܦBϕwO5r"iEbWLAhhw timestampmhexpmmdecimalm1.1.2hhba aha a9a0ebWLAhhwouter_checksummhexpmm db_connectionm 2.0.0-rc.0m d<9 vl:0v.\dbWLAhhwdepsmhexpmmphoenixm1.4.12lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsej`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.11.1m LEwOJZrBx4iŢgϮabWLAhhwinner_checksummhexpmmphoenix_paramsm0.2.0m S@Abx,ysɸK|Z w$EbWLAhhw timestampmhexpmmphoenixm0.3.1hhba aha a9a0`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.13.1m BϩNޖ3@ٜ4]Om鲳Bdǡd%bWLAhw last_updatehhba aha a$aEbWLAhhw timestampmhexpmmphoenixm1.4.2hhba aha a9a0XbWLAhhwinner_checksummhexpmmplugm0.14.0m KN&ïF9VgD|Az[hbWLAhhwdepsmhexpmmpostgrexm0.8.0lhmhexpmmdecimalmdecimalm~> 1.0wfalsej[bWLAhhwinner_checksummhexpmmphoenixm0.10.0m ,~33gaTyV0A*IXbWLAhhwdepsmhexpmmphoenixm0.2.3lhmhexpmmex_confmex_confm0.1.1wfalsehmhexpmminflexminflexm0.2.0wfalsehmhexpmmjazzmjazzm0.1.0wfalsehmhexpmmplugmplugm0.4.3wfalsejabWLAhhwinner_checksummhexpmmphoenix_paramsm1.0.3m vs8kW1`mM٭2/.+۪3bWLAhhwretiredmhexpmmphoenixm0.7.1wnilFbWLAhhw timestampmhexpmmphoenixm0.17.1hhba aha a9a0WbWLAhhwinner_checksummhexpmmmimem1.1.0m =ǸFQ9b4;( [bWLAhhwouter_checksummhexpmmphoenixm1.4.14m IX%G{eeq [v+XTbWLAhhwdepsmhexpmmpostgrexm0.10.0lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsejUbWLAhhw registry_etagmhexpmm plug_cryptom""2e69c8aae7f0030e224e283fdf6117dc"GbWLAhhw timestampmhexpmmpostgrexm0.14.2hhba aha a9a0obWLAhhwversionsmhexpmm connectionlm 1.0.0-rc.1m1.0.0m1.0.1m1.0.2m1.0.3m1.0.4jZbWLAhhwinner_checksummhexpmmphoenixm0.2.1m e/4QDU(R=7qPDdJ zkZbWLAhhwinner_checksummhexpmmphoenixm0.2.0m lE$ KMb™ua@Eh49bWLAhhwretiredmhexpmm db_connectionm0.1.7wnil\bWLAhhwinner_checksummhexpmm telemetrym0.4.1m 'HHD$GjA|2w(ttRvbWLAhhwretiredmhexpmm plug_cowboym2.2.0twmessagemBroken telemetry supportwreasonwRETIRED_INVALIDZbWLAhhwinner_checksummhexpmmphoenixm1.0.1m .ٝrl#H[;'r9p؂3_bWLAhhwinner_checksummhexpmmphoenixm 1.4.0-rc.1m rZsVAE fKjtbWLAhhwdepsmhexpmm plug_cowboym2.3.0lhmhexpmmcowboymcowboym~> 2.7wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejEbWLAhhw timestampmhexpmmphoenixm1.4.1hhba aha a9a0abWLAhhwouter_checksummhexpmmphoenix_pubsubm1.0.1m J/a?bsBqv˗ Ә`bWLAhhwinner_checksummhexpmm db_connectionm2.0.6m ]yiŵ cHzĪ_9abWLAhhwdepsmhexpmmplugm1.1.6lhmhexpmmcowboymcowboym~> 1.0wtruejbWLAhhwdepsmhexpmmphoenix_paramsm1.0.0lhmhexpmmdecimalmdecimalm>= 1.7.0wfalsehmhexpmmphoenixmphoenixm>= 1.3.3wfalsejZbWLAhhwinner_checksummhexpmmphoenixm1.2.2m 4[RY8ΖlL‰'JDZbWLAhhwinner_checksummhexpmmphoenixm1.2.3m ֧>8YwDXIHbWLAhhwdepsmhexpmmplugm1.2.3lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmmimemmimem~> 1.0wfalsej3bWLAhhwretiredmhexpmmphoenixm0.9.0wnilFbWLAhhw timestampmhexpmmphoenixm0.17.0hhba aha a9a0WbWLAhhwinner_checksummhexpmmmimem1.2.0m x+6 ӴQ@уֈ[ 4:bWLAhhwretiredmhexpmmphoenix_pubsubm1.0.0wnilPbWLAhhw timestampmhexpmm db_connectionm 1.0.0-rc.1hhba aha a9a0EbWLAhhw timestampmhexpmmphoenixm0.9.0hhba aha a9a0ZbWLAhhwouter_checksummhexpmmphoenixm1.0.4m Y_:oTIj- {O5&foiWbWLAhhwouter_checksummhexpmmmimem1.3.0m ^(`2j` Ɠݫ%y{ffS(`bWLAhhwinner_checksummhexpmm db_connectionm0.2.1m CaC,ʔ'-ŀҁ LabWLAhhwinner_checksummhexpmmphoenix_pubsubm1.1.1m fhׇ$z_i̘Q6 <:bWLAhhwretiredmhexpmmphoenix_paramsm1.0.0wnilBbWLAhhw timestampmhexpmmplugm1.8.1hhba aha a9a05bWLAhhwretiredmhexpmmpostgrexm0.15.3wnil7bWLAhhwretiredmhexpmm plug_cryptom1.1.0wnilgbWLAhhwdepsmhexpmm phoenix_htmlm2.11.1lhmhexpmmplugmplugm~> 1.5wfalsejWbWLAhhwouter_checksummhexpmmplugm1.0.4m ,翈QEfss0Lӛ[QNbWLAhhw timestampmhexpmm phoenix_htmlm 2.7.0-devhhba aha a9a00bWLAhhwretiredmhexpmmplugm1.7.0wnil_bWLAhhwouter_checksummhexpmmphoenixm 1.5.0-rc.0m 7}ʋNEBt_y e%gf\bWLAhhwouter_checksummhexpmmpostgrexm0.14.1m Z!HH _0( ޲;[bWLAhhwinner_checksummhexpmmphoenixm0.11.0m `3dLڜ,\O,pO@ZbWLAhhwouter_checksummhexpmmdecimalm1.0.1m dk+5~ kƿF|xwtabWLAhhwdepsmhexpmmplugm1.0.2lhmhexpmmcowboymcowboym~> 1.0wtruejLbWLAhhw timestampmhexpmmphoenix_pubsubm0.0.1hhba aha a9a0ZbWLAhhwouter_checksummhexpmmphoenixm1.1.4m We#ΒB;C?DpQ YzabWLAhhwouter_checksummhexpmmphoenix_paramsm0.4.2m '͓aoD&ZŶz`)}tkKZbWLAhhwinner_checksummhexpmmphoenixm1.0.5m E)PQ!jflW1)dfbWLAhhwdepsmhexpmm phoenix_htmlm2.9.1lhmhexpmmplugmplugm~> 1.0wfalsejEbWLAhhw timestampmhexpmmdecimalm0.2.2hhba aha a9a0^bWLAhhwouter_checksummhexpmm plug_cowboym2.2.1m ;C$F (nz b䎷#YTh ]޶pbWLAhhwdepsmhexpmmphoenix_paramsm0.1.2lhmhexpmmphoenixmphoenixm~> 1.3.3wfalsejWbWLAhhwouter_checksummhexpmmmimem1.0.1m ^m r,)G bWLAhhwdepsmhexpmm db_connectionm 1.0.0-rc.0lhmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 1.0.0-beta.2wtruejZbWLAhhwinner_checksummhexpmmdecimalm0.2.3m r}$޿>4m\lZ$7bWLAhhwretiredmhexpmm plug_cowboym2.1.3wnilBbWLAhhw timestampmhexpmmplugm0.9.0hhba aha a9a0dbWLAhhwdepsmhexpmmphoenixm1.4.16lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsej`bWLAhhwinner_checksummhexpmm db_connectionm2.0.4m ָ}+Sˬ2)cve=h8[bWLAhhwouter_checksummhexpmmpostgrexm0.5.0m -8;lŜa 8j[(:bWLAhhwretiredmhexpmmphoenix_paramsm1.0.1wnilZbWLAhhwinner_checksummhexpmmdecimalm1.2.0m F)`q(.W{Gkkh'}M dcRiK 9bWLAhhwretiredmhexpmm phoenix_htmlm2.13.1wnil`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.13.0m ;^lYz ?GALV,A bWLAhhwdepsmhexpmmphoenixm0.10.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm ~> 0.11.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsehmhexpmmpoolboympoolboym~> 1.4.2wtruej`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.11.0m 6h+2INp#9:@7}~Ne h_)\bWLAhhwinner_checksummhexpmmpostgrexm0.13.2m +ƥEj'L ]'HA^]u]NbWLAhhwdepsmhexpmmphoenixm0.9.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm ~> 0.10.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsehmhexpmmpoolboympoolboym~> 1.4.2wtruejGbWLAhhw timestampmhexpmmplugm 1.5.0-rc.2hhba aha a9a0WbWLAhhwouter_checksummhexpmmplugm1.3.0m RD5~mVQ5)_4E3-jbWLAhhwdepsmhexpmmpostgrexm0.5.4lhmhexpmmdecimalmdecimalm~> 0.2.3wfalsejBbWLAhhw timestampmhexpmmplugm0.8.4hhba aha a9a0EbWLAhhw timestampmhexpmmphoenixm1.1.0hhba aha a9a0\bWLAhhwinner_checksummhexpmmpostgrexm0.13.4m 1TQچjEm1Щ#nZA8bWLAhhwretiredmhexpmm phoenix_htmlm1.1.0wnilubWLAhhwdepsmhexpmm phoenix_htmlm1.0.1lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0wfalsejJbWLAhhw timestampmhexpmm phoenix_htmlm1.2.1hhba aha a9a0pbWLAhhwdepsmhexpmmphoenix_paramsm0.2.1lhmhexpmmphoenixmphoenixm>= 1.3.3wfalsejbWLAhhwdepsmhexpmmpostgrexm0.13.2lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 1.1wfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsejEbWLAhhw timestampmhexpmmdecimalm1.0.1hhba aha a9a0WbWLAhhwouter_checksummhexpmmplugm1.1.2m %XB=!UG^5EE*dTZbWLAhhwouter_checksummhexpmmdecimalm0.1.2m Jޗ֊YJ AJ'm\bWLAhhwouter_checksummhexpmm telemetrym0.4.0m |1p| 0-Ow?AWbWLAhhwinner_checksummhexpmmplugm1.3.1m FuB93pL^?B`dūUWbWLAhhwinner_checksummhexpmmplugm1.1.4m .B n[1vMoaB+Ű.ΙKbWLAhhw timestampmhexpmm db_connectionm2.1.1hhba aha a9a0bWLAhhwdepsmhexpmm db_connectionm0.1.1lhmhexpmmbackoffmbackoffm~> 1.0wfalsehmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 0.7wtruejWbWLAhhwouter_checksummhexpmmplugm1.1.1m  E!/&uW><fJ]pqZbWLAhhwinner_checksummhexpmmdecimalm1.1.1m [g1ͬoyo(]nVnv8bWLAhhwretiredmhexpmmphoenixm 1.2.0-rc.0wnilXbWLAhhwinner_checksummhexpmmplugm0.11.3m v;X5!6חtwI`+?WJ1bWLAhhwretiredmhexpmmplugm1.10.2wnil4bWLAhhwretiredmhexpmmpostgrexm0.5.0wnil_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.3.1m ?in`"yԡ_n}fo0-B[bWLAhhwinner_checksummhexpmmphoenixm0.16.0m I~TY$G$nc; aZ%w~(8SEbWLAhhw timestampmhexpmmphoenixm1.3.4hhba aha a9a08bWLAhhwretiredmhexpmmphoenixm 1.4.0-rc.3wnilbWLAhhwdepsmhexpmm db_connectionm0.1.5lhmhexpmmbackoffmbackoffm~> 1.0wfalsehmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 0.7wtruej:bWLAhhwretiredmhexpmmphoenix_pubsubm1.1.1wnil`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.10.1m H%[6id~^d hh3bWLAhhwretiredmhexpmmphoenixm1.1.2wnil.bWLAhhwdepsmhexpmm telemetrym0.4.2j3bWLAhhwretiredmhexpmmphoenixm1.2.4wnil^bWLAhhwinner_checksummhexpmm plug_cryptom1.1.2m чW,mq6)%򵀡VB0'^bWLAhhwouter_checksummhexpmm plug_cryptom1.1.2m k`[oԜ7Lj>j(;["ֵBbWLAhhw timestampmhexpmmplugm1.5.1hhba aha a9a0KbWLAhhw timestampmhexpmm phoenix_htmlm2.13.1hhba aha a9a0[bWLAhhwouter_checksummhexpmmphoenixm0.17.0m ,}ce1::lyw)1ypbWLAhhwdepsmhexpmmphoenix_paramsm0.1.1lhmhexpmmphoenixmphoenixm~> 1.3.3wfalsejZbWLAhhwinner_checksummhexpmmdecimalm1.8.1m ?_4( 57@)Nޅ3SoEh٬<KbWLAhhw timestampmhexpmm db_connectionm0.1.3hhba aha a9a0]bWLAhhwouter_checksummhexpmm connectionm1.0.3m nH +$}Z'|X3OirGbWLAhhw timestampmhexpmmpostgrexm0.12.1hhba aha a9a0JbWLAhhw timestampmhexpmmphoenixm 1.3.0-rc.2hhba aha a9a0JbWLAhhw timestampmhexpmm phoenix_htmlm2.4.0hhba aha a9a0bWLAhhwdepsmhexpmm db_connectionm1.1.3lhmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 1.0wtruejKbWLAhhw timestampmhexpmm db_connectionm2.0.6hhba aha a9a0"bWLAhhwdepsmhexpmmphoenixm1.3.4lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4wfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0wfalsejZbWLAhhwouter_checksummhexpmmdecimalm1.4.1m #{RNUV]JrV&hL{ʀ_;XbWLAhhwinner_checksummhexpmmjasonm1.0.0m |#!Aw&ZqoXbWLAhhwouter_checksummhexpmmjasonm1.2.0m gGWyL:>N;|cNNJYu._bWLAhhwouter_checksummhexpmm phoenix_htmlm2.4.0m "s3f?r&Yg~?f׹KWbWLAhhwouter_checksummhexpmmplugm1.6.1m s?2IC/JbWLAhhw timestampmhexpmm phoenix_htmlm2.7.0hhba aha a9a0BbWLAhhw timestampmhexpmmplugm0.8.3hhba aha a9a00bWLAhhwretiredmhexpmmplugm1.2.5wnilbWLAhhwdepsmhexpmmphoenixm1.5.2lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.0.0m JnPp~ղi>TCHYF>!\5bWLAhhwdepsmhexpmmpostgrexm0.15.0lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 2.1wfalsehmhexpmmdecimalmdecimalm~> 1.5wfalsehmhexpmmjasonmjasonm~> 1.0wtruej7bWLAhhwretiredmhexpmm plug_cowboym2.0.1wnil>bWLAhhwretiredmhexpmm db_connectionm 1.0.0-rc.1wnilGbWLAhhw timestampmhexpmmplugm 1.5.0-rc.0hhba aha a9a0[bWLAhhwinner_checksummhexpmmphoenixm0.13.0m FN6"K]m/6Mݛ%KbWLAhhw timestampmhexpmm phoenix_htmlm2.10.3hhba aha a9a0FbWLAhhw timestampmhexpmmpostgrexm0.7.0hhba aha a9a0[bWLAhhwinner_checksummhexpmmpostgrexm0.5.3m i'Jz5 APp@l/s+OEbWLAhhw timestampmhexpmmphoenixm1.1.4hhba aha a9a04bWLAhhwretiredmhexpmmphoenixm0.16.1wnilWbWLAhhwouter_checksummhexpmmplugm1.0.5m -Ьh80"w=-wZbWLAhhwouter_checksummhexpmmphoenixm1.2.1m ;f`5 nz 1.0wtruehmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejKbWLAhhw timestampmhexpmm db_connectionm2.2.0hhba aha a9a0fbWLAhhwdepsmhexpmm phoenix_htmlm2.6.1lhmhexpmmplugmplugm~> 1.0wfalsejBbWLAhhw timestampmhexpmmplugm1.4.4hhba aha a9a0JbWLAhhw timestampmhexpmm phoenix_htmlm1.3.0hhba aha a9a0XbWLAhhwinner_checksummhexpmmplugm0.11.0m ΰ`P&o@dS5IbWLAhhw timestampmhexpmm plug_cowboym2.1.2hhba aha a9a0ZbWLAhhwinner_checksummhexpmmphoenixm1.1.2m kl`ٛ D3x e&Qm4|}WbWLAhhwouter_checksummhexpmmplugm1.7.0m ~ƫjxXoDm* \bWLAhhwinner_checksummhexpmm telemetrym0.4.0m 9˄L)5sm U84GbWLAhhw timestampmhexpmmpostgrexm0.13.1hhba aha a9a0[bWLAhhwinner_checksummhexpmmphoenixm0.2.11m -bboW8v[xhFbWLAhhw timestampmhexpmmphoenixm0.14.0hhba aha a9a0fbWLAhhwdepsmhexpmm phoenix_htmlm2.7.0lhmhexpmmplugmplugm~> 1.0wfalsej`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.14.1m }R* G DI fXbWLAhhwinner_checksummhexpmmjasonm1.2.1m (%/F>Kˍ spDZbWLAhhwinner_checksummhexpmmdecimalm2.0.0m LlWCG/By+VB֗ZbWLAhhwouter_checksummhexpmmphoenixm1.1.7m MRVA1,OO'kT)1qbWLAhhwdepsmhexpmm phoenix_htmlm2.6.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsej9bWLAhhwretiredmhexpmm phoenix_htmlm2.10.3wnilJbWLAhhw timestampmhexpmmphoenixm 1.4.0-rc.2hhba aha a9a05bWLAhhwretiredmhexpmmplugm 1.5.0-rc.1wnilGbWLAhhw timestampmhexpmmpostgrexm0.15.4hhba aha a9a0\bWLAhhwouter_checksummhexpmmpostgrexm0.13.4m u|I?7|U^ufX>@D{s2ebWLAhhwouter_checksummhexpmm db_connectionm 1.0.0-rc.5m c˝([eͱbWXfXX?Λ_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.4.0m ;ݙ\_^$HENf ^wQ5abWLAhhwouter_checksummhexpmmphoenix_paramsm1.1.1m Ym{DG:.oٙ=壈 jbWLAhhwdepsmhexpmm phoenix_htmlm 2.7.0-devlhmhexpmmplugmplugm~> 1.0wfalsejLbWLAhhw timestampmhexpmmphoenix_paramsm0.1.2hhba aha a9a0,bWLAhhwdepsmhexpmmdecimalm1.1.0jXbWLAhhw registry_etagmhexpmmphoenix_paramsm""389651a1f33cf6831d08446192a18010"abWLAhhwdepsmhexpmmplugm1.1.0lhmhexpmmcowboymcowboym~> 1.0wtruejhbWLAhhwdepsmhexpmmpostgrexm0.9.0lhmhexpmmdecimalmdecimalm~> 1.0wfalsej`bWLAhhwouter_checksummhexpmm db_connectionm2.0.4m RKK eP3$#WbWLAhhwinner_checksummhexpmmplugm1.0.0m ߶ 3]gN{a!<0`bWLAhhwinner_checksummhexpmm db_connectionm0.1.2m ~r ~i`%&Sy!Lm<abWLAhhwouter_checksummhexpmmphoenix_paramsm1.0.3m Yf(D>Y/ y/$~SEbWLAhhw timestampmhexpmmphoenixm0.2.7hhba aha a9a0KbWLAhhw timestampmhexpmm db_connectionm0.1.2hhba aha a9a0FbWLAhhw timestampmhexpmmphoenixm1.4.11hhba aha a9a0`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.13.3m ӗ1TWm%u_U7%gbWLAhhwdepsmhexpmmphoenixm1.0.6lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0.5wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsej[bWLAhhwinner_checksummhexpmmpostgrexm0.5.2m b_Do`7DND+@=TZbWLAhhwouter_checksummhexpmmphoenixm1.4.9m 3&oUu xOՔ_tz읰.EbWLAhhw timestampmhexpmmphoenixm1.0.4hhba aha a9a08bWLAhhwretiredmhexpmm phoenix_htmlm2.3.0wnilWbWLAhhwinner_checksummhexpmmplugm1.6.0m 8Lb->$rE[QTKwSo8bWLAhhwretiredmhexpmmphoenixm 1.3.0-rc.1wnil_bWLAhhwouter_checksummhexpmmphoenixm 1.3.0-rc.0m 5PN 1.0.2wfalsej$bWLAhhwdepsmhexpmmphoenixm 1.4.0-rc.0lhmhexpmmcowboymcowboym~> 1.0 or ~> 2.3wtruehmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.6.2 or ~> 1.7wfalsej1bWLAhhwretiredmhexpmmplugm1.10.0wnilEbWLAhhw timestampmhexpmmphoenixm1.0.3hhba aha a9a0FbWLAhhw timestampmhexpmmphoenixm0.2.10hhba aha a9a0GbWLAhhw timestampmhexpmmpostgrexm0.15.0hhba aha a9a0`bWLAhhwinner_checksummhexpmm db_connectionm0.1.6m f _`==o_Frp 49ӣp8bWLAhhwretiredmhexpmm phoenix_htmlm2.1.2wnilbWLAhhwdepsmhexpmm plug_cowboym2.1.1lhmhexpmmcowboymcowboym~> 2.5wfalsehmhexpmmplugmplugm~> 1.7wfalsej9bWLAhhwretiredmhexpmm db_connectionm0.1.6wnilFbWLAhhw timestampmhexpmmpostgrexm0.8.4hhba aha a9a04bWLAhhwretiredmhexpmmphoenixm0.10.0wnilWbWLAhhwouter_checksummhexpmmplugm0.8.3m L(G/1hMKKz5bWLAhhwretiredmhexpmmpostgrexm0.13.5wnilcbWLAhhwdepsmhexpmmplugm0.5.3lhmhexpmmcowboymcowboym~> 1.0.0wtruejXbWLAhhwouter_checksummhexpmmjasonm1.0.1m QQjL Y= hv%W4 hbWLAhhwdepsmhexpmmpostgrexm0.8.2lhmhexpmmdecimalmdecimalm~> 1.0wfalsej8bWLAhhwretiredmhexpmmphoenixm 1.3.0-rc.3wnil_bWLAhhwinner_checksummhexpmm phoenix_htmlm1.1.0m UL-\^'u2˖ut(KbWLAhhw timestampmhexpmm phoenix_htmlm2.11.1hhba aha a9a0ZbWLAhhwinner_checksummhexpmmphoenixm1.0.3m wafnQ+èzh $\FbWLAhhw timestampmhexpmmphoenixm1.4.17hhba aha a9a0bWLAhhwdepsmhexpmmpostgrexm0.14.1lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 2.0wfalsehmhexpmmdecimalmdecimalm~> 1.5wfalsehmhexpmmjasonmjasonm~> 1.0wtruejWbWLAhhwinner_checksummhexpmmplugm0.5.2m |"Wž/1޹\T5 J0bWLAhhwretiredmhexpmmplugm1.4.0wnilbWLAhhwdepsmhexpmmplugm1.6.4lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4wtruehmhexpmmmimemmimem~> 1.0wfalsej0bWLAhhwretiredmhexpmmplugm1.1.7wnilbWLAhhwdepsmhexpmmphoenixm1.2.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.1wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsej`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.13.2m |Έ`}+R'1OkA4WXbWLAhhwinner_checksummhexpmmjasonm1.0.1m d6K<p3T]s.g\bWLAhhwouter_checksummhexpmmplugm 1.4.0-rc.0m \u6q ]v8' ln*AM}~WbWLAhhwinner_checksummhexpmmplugm1.1.3m ,Cp~imL`ZUbWLAhhwversionsmhexpmmphoenixlcm0.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.4j1bWLAhhwretiredmhexpmmplugm0.13.1wnilXbWLAhhw registry_etagmhexpmmphoenix_pubsubm""9c40253b1ea746f7a666b077918a909f"EbWLAhhw timestampmhexpmmdecimalm1.8.1hhba aha a9a0WbWLAhhwouter_checksummhexpmmplugm0.4.3m &[ }yv6[A.q4bWLAhhwretiredmhexpmmphoenixm0.13.0wnil[bWLAhhwouter_checksummhexpmmpostgrexm0.8.2m Ac#=B0YO1uдfQ]L=ߨ3bWLAhhwdepsmhexpmmphoenix_pubsubm1.1.0j/bWLAhhwdepsmhexpmm connectionm1.0.3j6bWLAhhwretiredmhexpmm connectionm1.0.3wnilFbWLAhhw timestampmhexpmmpostgrexm0.8.1hhba aha a9a0EbWLAhhw timestampmhexpmmphoenixm0.2.0hhba aha a9a0bWLAhhwdepsmhexpmmphoenixm1.0.3lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsej,bWLAhhwdepsmhexpmmdecimalm1.4.1jXbWLAhhwouter_checksummhexpmmplugm0.11.0m "89fأXI?J\sT68EhGWbWLAhhwouter_checksummhexpmmplugm0.4.1m Hd,dÎY7qrwܩ:bWLAhhwdepsmhexpmmphoenixm0.7.2lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 0.9.0wfalsehmhexpmmpoisonmpoisonm~> 1.2wfalsejbWLAhhwdepsmhexpmmphoenixm1.2.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.1wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsej_bWLAhhwouter_checksummhexpmm phoenix_htmlm1.0.0m Eg^-"t"ޟR ʻu;ɒXw\(FbWLAhhw timestampmhexpmmphoenixm1.4.14hhba aha a9a0ZbWLAhhwouter_checksummhexpmmphoenixm1.1.6m %Gd1D p4~ M_$#5* )4`bWLAhhwouter_checksummhexpmmpostgrexm 1.0.0-rc.1m SB!J1DLtz+v~O-$f|>o3WbWLAhhwouter_checksummhexpmmplugm1.4.1m ʕ'PpL]MLH,kOFbWLAhhwdepsmhexpmmphoenixm1.2.3lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm6~> 1.4 or ~> 1.3.3 or ~> 1.2.4 or ~> 1.1.8 or ~> 1.0.5wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsejCbWLAhhw timestampmhexpmmjasonm1.0.1hhba aha a9a0bWLAhhwdepsmhexpmmpostgrexm0.11.0lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 0.2wfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsej`bWLAhhwinner_checksummhexpmm db_connectionm0.1.5m ^/ :af@<э|Ot*\5EbWLAhhw timestampmhexpmmphoenixm1.0.1hhba aha a9a0]bWLAhhwouter_checksummhexpmm connectionm1.0.4m JPɾ": qQ\ @&8%[bWLAhhwinner_checksummhexpmmpostgrexm0.4.2m gt\Fl0={ 7bWLAhhwretiredmhexpmm plug_cowboym2.0.2wnilZbWLAhhwinner_checksummhexpmmdecimalm1.5.0m C:6C=P)e<7of[CיchHeh5bWLAhhwretiredmhexpmmplugm 1.4.0-rc.0wnil_bWLAhhwouter_checksummhexpmmdecimalm 1.9.0-rc.0m fuEsW\^KE0|dX< G`bWLAhhwinner_checksummhexpmm db_connectionm2.0.2m D Q ܠFE@5CH(ʤ3IbWLAhhw timestampmhexpmm plug_cowboym2.1.1hhba aha a9a0abWLAhhwouter_checksummhexpmmphoenix_paramsm0.4.0m W؟}tE+qǹnԾXp&/'p[bWLAhhwinner_checksummhexpmmpostgrexm0.8.0m g]MJEJ`j컆E&З4X WbWLAhhwinner_checksummhexpmmplugm1.0.6m dpBcOfXeyu6LCelI`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.11.0m m[\VB3x2ba~Weڡl83bWLAhhwretiredmhexpmmphoenixm0.6.1wnilZbWLAhhwouter_checksummhexpmmdecimalm1.8.0m RiNn`PsLfXO[}1xfcEbWLAhhw timestampmhexpmmdecimalm0.2.3hhba aha a9a0GbWLAhhw timestampmhexpmmpostgrexm0.13.5hhba aha a9a0GbWLAhhw timestampmhexpmmpostgrexm0.14.3hhba aha a9a04bWLAhhwretiredmhexpmmphoenixm0.15.0wnilWbWLAhhwinner_checksummhexpmmplugm0.8.0m aC-N`|~<>&ҸEX.ęKbWLAhhw timestampmhexpmm db_connectionm0.1.4hhba aha a9a04bWLAhhwretiredmhexpmmpostgrexm0.5.2wnilBbWLAhhw timestampmhexpmmplugm1.2.2hhba aha a9a0bWLAhhwdepsmhexpmmphoenixm1.4.3lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruej7bWLAhhwretiredmhexpmm plug_cryptom1.1.2wnil\bWLAhhwouter_checksummhexpmmpostgrexm0.15.5m %Z+'03(ڢaC9bWLAhhwretiredmhexpmm db_connectionm0.2.1wnil.bWLAhhwdepsmhexpmm telemetrym0.1.0jEbWLAhhw timestampmhexpmmphoenixm1.4.3hhba aha a9a0bWLAhhwdepsmhexpmmphoenix_paramsm1.1.0lhmhexpmmdecimalmdecimalm>= 1.7.0wfalsehmhexpmmphoenixmphoenixm>= 1.3.3wfalsej`bWLAhhwouter_checksummhexpmm db_connectionm0.2.1m #4@xkLtƳ6@F-JbWLAhhw timestampmhexpmmphoenixm 1.4.0-rc.3hhba aha a9a0`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.13.4m =nFd$JI' P*}mء[OEbWLAhhw timestampmhexpmmphoenixm0.7.0hhba aha a9a0WbWLAhhwinner_checksummhexpmmplugm1.1.6m '3Y8|(71'3PfZbWLAhhwouter_checksummhexpmmphoenixm0.2.8m YyT%#E*i| nfq;ЍނXbWLAhhwinner_checksummhexpmmplugm0.13.0m /:.Gűw υIl4K\bWLAhhwinner_checksummhexpmmpostgrexm0.13.1m {sH*ۋMpF2Q#RkUg0cbWLAhhwdepsmhexpmmplugm1.4.5lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsej[bWLAhhwouter_checksummhexpmmpostgrexm0.8.1m #K4D}C{&o 2Ui^bWLAhhwinner_checksummhexpmm plug_cryptom1.1.0m HCՐbOH%oGU]ɇT.XbWLAhhwinner_checksummhexpmmplugm1.10.1m jm-XY;T ҇@ZbWLAhhwinner_checksummhexpmmphoenixm1.4.3m Jd7,4rK֑Rv7]g}BbWLAhhw timestampmhexpmmplugm1.0.1hhba aha a9a0_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.6.2m J^X OLiP>0[(D9%>1bWLAhhwretiredmhexpmmplugm0.11.3wnilXbWLAhhwinner_checksummhexpmmjasonm1.2.2m CpѪΐi\Vx?R[bWLAhhwinner_checksummhexpmmpostgrexm0.8.2m u?. {'f1&A37 +EbWLAhhw timestampmhexpmmphoenixm1.3.1hhba aha a9a0HbWLAhhw timestampmhexpmm connectionm1.0.1hhba aha a9a0ZbWLAhhwouter_checksummhexpmmphoenixm1.3.1m e+a!= ETsyFML2hX2(CbWLAhhw timestampmhexpmmjasonm1.2.0hhba aha a9a0XbWLAhhwouter_checksummhexpmmplugm0.14.0m qh5Y#oAf]h0=_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.9.2m 7` Co񚮘dbw:[.`BbWLAhhw timestampmhexpmmplugm1.0.4hhba aha a9a00bWLAhhwretiredmhexpmmmimem1.3.0wnilWbWLAhhwouter_checksummhexpmmplugm1.6.4m iE;2 0 tH`:bWLAhhwretiredmhexpmmphoenix_paramsm1.1.0wnil7bWLAhhwretiredmhexpmm plug_cowboym1.0.0wnilbWLAhhwdepsmhexpmm plug_cowboym2.0.1lhmhexpmmcowboymcowboym~> 2.5wfalsehmhexpmmplugmplugm~> 1.7wfalsej`bWLAhhwouter_checksummhexpmm db_connectionm2.1.1m Zr- L=3\)^KHIZG`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.10.0m iFM|S“4'QA8 JWbWLAhhwinner_checksummhexpmmplugm0.5.1m X)],{/xL5_RxP)WbWLAhhwinner_checksummhexpmmplugm1.1.5m VEpAZrS!'Z˒7Bn'0bWLAhhwretiredmhexpmmplugm1.2.6wnilbWLAhhwdepsmhexpmmphoenixm0.17.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsej\bWLAhhwinner_checksummhexpmmpostgrexm0.14.3m WT8ߖBxp vAwe4Z,bWLAhhwdepsmhexpmmdecimalm2.0.0jgbWLAhhwdepsmhexpmm phoenix_htmlm2.10.5lhmhexpmmplugmplugm~> 1.0wfalsej\bWLAhhwinner_checksummhexpmmpostgrexm0.13.0m GrYUȃ $.佝- _bWLAhhwouter_checksummhexpmm phoenix_htmlm2.7.0m h׫~~څ1m%s{q ORNwbWLAhhwdepsmhexpmmpostgrexm0.15.5lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 2.1wfalsehmhexpmmdecimalmdecimalm~> 1.5wfalsehmhexpmmjasonmjasonm~> 1.0wtruejZbWLAhhwouter_checksummhexpmmphoenixm1.5.0m ƕr9K}bc%P;K Ծ609bWLAhhwdepsmhexpmmphoenixm0.8.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 0.9.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejLbWLAhhw timestampmhexpmmphoenix_paramsm1.0.0hhba aha a9a0ZbWLAhhwouter_checksummhexpmmphoenixm0.7.2m leuevoLGgұzo9bWLAhhwretiredmhexpmm db_connectionm2.2.0wnil\bWLAhhwinner_checksummhexpmmpostgrexm0.15.0m SI>B/y\:B{LcWbWLAhhwinner_checksummhexpmmplugm1.4.1m .g} 1.5wfalsejZbWLAhhwouter_checksummhexpmmphoenixm1.0.1m s$DeOă&x Uk4AubWLAhhwdepsmhexpmm db_connectionm2.1.0lhmhexpmm connectionm connectionm~> 1.0.2wfalsej0bWLAhhwretiredmhexpmmplugm1.0.5wnilabWLAhhwdepsmhexpmmplugm1.1.3lhmhexpmmcowboymcowboym~> 1.0wtruejEbWLAhhw timestampmhexpmmphoenixm0.2.5hhba aha a9a00bWLAhhwretiredmhexpmmmimem1.3.1wnilEbWLAhhw timestampmhexpmmdecimalm0.2.5hhba aha a9a0_bWLAhhwouter_checksummhexpmmphoenixm 1.2.0-rc.0m ߒҏ&=b Oֵ> #iZn`ZSakPZbWLAhhwouter_checksummhexpmmdecimalm1.7.0m wvPZՊOW\_`e],bWLAhhwdepsmhexpmmdecimalm1.0.1jZbWLAhhwinner_checksummhexpmmphoenixm0.4.0m *S]PoH39B;(X5uhj5bWLAhhwretiredmhexpmmpostgrexm0.15.4wnil[bWLAhhwouter_checksummhexpmmphoenixm0.16.0m !,@X _F(7bWLAhhwretiredmhexpmm plug_cowboym2.3.0wnilWbWLAhhwinner_checksummhexpmmplugm1.6.2m j{һm]ݕp ΈSQ"KَIbWLAhhw timestampmhexpmm plug_cowboym2.1.0hhba aha a9a0WbWLAhhwinner_checksummhexpmmplugm1.0.2m 4)ȏưMq/gV@Lꗦ1:Iʨ#n[bWLAhhwinner_checksummhexpmmpostgrexm0.5.1m ]]<$Ă0g_||bWLAhhwdepsmhexpmmpostgrexm0.13.3lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 1.1wfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsejXbWLAhhwouter_checksummhexpmmplugm1.10.2m xGgﳹ%p^f;ō*ؚZ0bWLAhhwretiredmhexpmmplugm1.3.6wnil`bWLAhhwouter_checksummhexpmm db_connectionm2.1.0m MނBƗg`z" [ubWLAhhwdepsmhexpmm phoenix_htmlm1.2.1lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0wfalsejWbWLAhhwinner_checksummhexpmmplugm1.8.0m &٬'(&+x )oCwLbWLAhhw timestampmhexpmmphoenix_pubsubm0.1.0hhba aha a9a0bWLAhhwdepsmhexpmmpostgrexm0.14.3lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 2.0wfalsehmhexpmmdecimalmdecimalm~> 1.5wfalsehmhexpmmjasonmjasonm~> 1.0wtruejBbWLAhhw timestampmhexpmmplugm1.3.4hhba aha a9a0dbWLAhhwdepsmhexpmmjasonm1.0.0lhmhexpmmdecimalmdecimalm~> 1.0wtruejNbWLAhhw registry_etagmhexpmmplugm""a35d954d250109e64532cce56060f54b"gbWLAhhwdepsmhexpmm phoenix_htmlm2.11.2lhmhexpmmplugmplugm~> 1.5wfalsejBbWLAhhw timestampmhexpmmplugm1.7.1hhba aha a9a09bWLAhhwretiredmhexpmm phoenix_htmlm2.13.3wnil_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.6.1m }S= zn$Ÿf0I["7 R}ebWLAhhwinner_checksummhexpmm db_connectionm 2.0.0-rc.0m $Fo'~PxMeR:ҿ}/[bWLAhhwouter_checksummhexpmmpostgrexm0.8.3m @LMP'`Rۄ+O:OybWLAhhwdepsmhexpmm db_connectionm0.1.7lhmhexpmmbackoffmbackoffm~> 1.0wfalsehmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 0.7wtruejcbWLAhhwdepsmhexpmmplugm0.7.0lhmhexpmmcowboymcowboym~> 1.0.0wtruej9bWLAhhwretiredmhexpmm phoenix_htmlm2.12.0wnil]bWLAhhwinner_checksummhexpmm connectionm1.0.2m mAAfL.ĸu0iG(/0xWbWLAhhwinner_checksummhexpmmplugm0.6.0m b#C(lm6 t{-bWLAhhwdepsmhexpmm db_connectionm0.1.2lhmhexpmmbackoffmbackoffm~> 1.0wfalsehmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 0.7wtruejBbWLAhhw timestampmhexpmmplugm1.0.0hhba aha a9a0WbWLAhhwinner_checksummhexpmmplugm1.2.1m ܇ Te N+o>&t'bWLAhhwdepsmhexpmmplugm1.10.2lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej4bWLAhhwretiredmhexpmmphoenixm0.14.0wnilZbWLAhhwouter_checksummhexpmmdecimalm1.4.0m vqN/q5 :=8K-zEFq#bWLAhhwdepsmhexpmmplugm1.3.1lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsejWbWLAhhwouter_checksummhexpmmplugm1.6.0m vΛ6ѝ\U^/À.HZbWLAhhwouter_checksummhexpmmphoenixm1.4.7m S>ke5qN4'ՠ$]my'bWLAhhwdepsmhexpmmphoenixm 1.3.0-rc.2lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.3.2 or ~> 1.4wfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0wfalsej0bWLAhhwretiredmhexpmmplugm1.1.0wnil.bWLAhhwversionsmhexpmm 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.2jBbWLAhhw timestampmhexpmmplugm1.9.0hhba aha a9a0bWLAhhwdepsmhexpmmphoenixm0.2.4lhmhexpmmex_confmex_confm0.1.1wfalsehmhexpmminflexminflexm0.2.0wfalsehmhexpmmplugmplugm0.4.3wfalsejbWLAhhwdepsmhexpmmphoenixm1.1.8lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0.5wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsej_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.6.0m =cEGw=v2ztUi3bWLAhhwretiredmhexpmmdecimalm1.7.0wnilFbWLAhhw timestampmhexpmmphoenixm0.15.0hhba aha a9a0BbWLAhhw timestampmhexpmmmimem1.0.1hhba aha a9a03bWLAhhwretiredmhexpmmphoenixm1.4.2wnilZbWLAhhwouter_checksummhexpmmdecimalm1.9.0m 45h֒>u[] o}BbWLAhhw timestampmhexpmmmimem1.0.0hhba aha a9a0CbWLAhhw timestampmhexpmmplugm0.11.2hhba aha a9a0HbWLAhhw timestampmhexpmmjasonm 1.0.0-rc.2hhba aha a9a00bWLAhhwretiredmhexpmmplugm0.8.0wnilFbWLAhhw timestampmhexpmmphoenixm1.4.10hhba aha a9a04bWLAhhwretiredmhexpmmphoenixm0.2.11wnil3bWLAhhwretiredmhexpmmphoenixm0.2.1wnilWbWLAhhwinner_checksummhexpmmplugm0.7.0m ;\W/ppe5ȖL{dhLbWLAhhw timestampmhexpmmphoenix_pubsubm1.1.0hhba aha a9a00bWLAhhwretiredmhexpmmplugm1.2.1wnilFbWLAhhw timestampmhexpmmpostgrexm0.8.0hhba aha a9a03bWLAhhwretiredmhexpmmphoenixm0.2.0wnilWbWLAhhwinner_checksummhexpmmplugm0.4.4m ÃnOWs˧'S/M)WW)bWLAhhwdepsmhexpmmplugm0.5.2j)bWLAhhwdepsmhexpmmmimem1.0.1j0bWLAhhwretiredmhexpmmplugm0.4.4wnilXbWLAhhwinner_checksummhexpmmplugm0.11.1m +.W)yyڴ8w1q.o4&8bWLAhhwretiredmhexpmm phoenix_htmlm1.2.0wnilZbWLAhhwouter_checksummhexpmmphoenixm1.1.0m Q>Ob S.- _bWLAhhwouter_checksummhexpmm phoenix_htmlm2.6.1m nIDb$ݲ޺`Ao>!5Ù~ PbWLAhhw timestampmhexpmm db_connectionm 1.0.0-rc.3hhba aha a9a00bWLAhhwretiredmhexpmmplugm1.9.0wnilEbWLAhhw timestampmhexpmmphoenixm0.2.9hhba aha a9a0KbWLAhhw timestampmhexpmm phoenix_htmlm2.11.2hhba aha a9a0WbWLAhhwouter_checksummhexpmmplugm1.3.4m bˠ*^MujXR:_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.1.2m gKHuE?zDFx9 7to6KbWLAhhw timestampmhexpmm db_connectionm0.1.1hhba aha a9a0`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.10.2m IclL^>5,uaB1?*0bWLAhhwretiredmhexpmmplugm1.2.4wnilibWLAhhwdepsmhexpmmjasonm 1.0.0-rc.2lhmhexpmmdecimalmdecimalm~> 1.0wtruej[bWLAhhwinner_checksummhexpmmphoenixm1.4.13m g'֛QqT`OJ?a46VWbWLAhhwinner_checksummhexpmmplugm1.5.0m "K%IREgpgIF [\bWLAhhwinner_checksummhexpmmpostgrexm0.14.1m c$}JZֹW-Aj&;͊.IQbWLAhhw registry_etagmhexpmmphoenixm""584b1d4ed74a3b1ef299dc3546ce0746"\bWLAhhwinner_checksummhexpmmpostgrexm0.15.3m Xh|zbL͹~s񹀙ttjpbWLAhhwdepsmhexpmmphoenix_paramsm0.3.0lhmhexpmmphoenixmphoenixm>= 1.3.3wfalsejebWLAhhwinner_checksummhexpmm db_connectionm 1.0.0-rc.0m ޽OLk;L l9c-ĵ4bWLAhhwretiredmhexpmmpostgrexm0.8.2wnilBbWLAhhw timestampmhexpmmplugm0.4.1hhba aha a9a0,bWLAhhwdepsmhexpmmdecimalm0.1.1j0bWLAhhwretiredmhexpmmplugm1.4.4wnil]bWLAhhwouter_checksummhexpmmjasonm 1.0.0-rc.1m Iл;_,n4%G)JbWLAhhw timestampmhexpmm phoenix_htmlm1.0.1hhba aha a9a0ZbWLAhhwinner_checksummhexpmmphoenixm1.1.3m ZK`c`o^݆ p*<,azl\bWLAhhwinner_checksummhexpmmpostgrexm0.14.2m fY⍒C$6ʹ*y6I$.;x3bWLAhhwretiredmhexpmmphoenixm1.0.4wnilEbWLAhhw timestampmhexpmmphoenixm0.2.8hhba aha a9a0ZbWLAhhwinner_checksummhexpmmdecimalm1.7.0m 0ֵ,TfcsYPߞf S_~ŪbWLAhhwdepsmhexpmmphoenix_paramsm1.1.2lhmhexpmmdecimalmdecimalm~> 2.0wfalsehmhexpmmphoenixmphoenixm~> 1.3wfalsejcbWLAhhwouter_checksummhexpmm phoenix_htmlm 2.0.0-devm 0WyʀZPlϠG!ȪZbWLAhhwouter_checksummhexpmmphoenixm0.5.0m /ߌͥB( P fk.O\bWLAhhwinner_checksummhexpmm telemetrym0.1.0m s 1 3|m*c8y+A<EbWLAhhw timestampmhexpmmphoenixm0.8.0hhba aha a9a0cbWLAhhwdepsmhexpmmplugm0.6.0lhmhexpmmcowboymcowboym~> 1.0.0wtruejZbWLAhhwinner_checksummhexpmmphoenixm0.3.0m |&QP?Xߛ*, b\^G>&݌ZbWLAhhwinner_checksummhexpmmphoenixm1.3.0m LJzo PgA]$>ʼnXbWLAhhwouter_checksummhexpmmjasonm1.2.1m YW`Z`A@Dqoi`bWLAhhwouter_checksummhexpmm db_connectionm2.0.3m =yY 7uqtY**ͅI4&PbWLAhhwdepsmhexpmmphoenixm 1.2.0-rc.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmmimemmimem~> 1.0wfalsehmhexpmmphoenix_pubsubmphoenix_pubsubm ~> 1.0.0-rcwfalsehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsejbWLAhhwdepsmhexpmmphoenix_paramsm0.4.0lhmhexpmmdecimalmdecimalm>= 1.7.0wfalsehmhexpmmphoenixmphoenixm>= 1.3.3wfalsej`bWLAhhwouter_checksummhexpmm db_connectionm0.1.7m 2{m2FвIjyl{kMMz5bWLAhhwretiredmhexpmmpostgrexm0.13.4wnil3bWLAhhwdepsmhexpmmphoenix_pubsubm1.0.1j/bWLAhhwdepsmhexpmm connectionm1.0.1jZbWLAhhwinner_checksummhexpmmdecimalm1.4.0m eqjSӦEf(Ze.J35bWLAhhwretiredmhexpmm telemetrym0.4.0wnilXbWLAhhwouter_checksummhexpmmplugm0.11.3m O0% Ԣq\%V#4^Ie]bWLAhhwinner_checksummhexpmmjasonm 1.0.0-rc.2m *]f?v7@.FV)^bWLAhhwouter_checksummhexpmm plug_cowboym1.0.0m BzD$eE"c2u@JaJbWLAhhw timestampmhexpmmphoenixm 1.2.0-rc.1hhba aha a9a03bWLAhhwretiredmhexpmmdecimalm1.8.0wnilubWLAhhwdepsmhexpmm db_connectionm2.2.1lhmhexpmm connectionm connectionm~> 1.0.2wfalsej9bWLAhhwretiredmhexpmm db_connectionm2.2.1wnil9bWLAhhwretiredmhexpmm db_connectionm2.0.2wnilWbWLAhhwouter_checksummhexpmmplugm1.3.1m b\P1Yu.e34hDJT<bWLAhhwdepsmhexpmmplugm1.7.0lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0wfalsej[bWLAhhwouter_checksummhexpmmpostgrexm0.5.4m A㔬 N]1P$LԲJyd<@:bWLAhhwretiredmhexpmmphoenix_pubsubm0.1.0wnilEbWLAhhw timestampmhexpmmphoenixm0.3.0hhba aha a9a0:bWLAhhwretiredmhexpmmpostgrexm 0.14.0-rc.1wnil4bWLAhhwretiredmhexpmmpostgrexm0.7.0wnilpbWLAhhwdepsmhexpmmphoenix_paramsm0.2.2lhmhexpmmphoenixmphoenixm>= 1.3.3wfalsej9bWLAhhwretiredmhexpmm db_connectionm1.1.0wnilabWLAhhwinner_checksummhexpmmpostgrexm 0.14.0-rc.1m %P_Y wD;AcLBbWLAhhw timestampmhexpmmplugm1.5.0hhba aha a9a0JbWLAhhw timestampmhexpmmdecimalm 2.0.0-rc.0hhba aha a9a0\bWLAhhwinner_checksummhexpmmplugm 1.2.0-rc.0m Yqr}Ђc.4wp[bWLAhhwinner_checksummhexpmmphoenixm0.16.1m 6F''-apJBo1tdq:\bWLAhhwouter_checksummhexpmmpostgrexm0.13.2m Y)'y6F"O'9 0.2.1wfalsejBbWLAhhw timestampmhexpmmplugm1.1.0hhba aha a9a0bWLAhhwdepsmhexpmmplugm1.3.6lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsejZbWLAhhwinner_checksummhexpmmphoenixm1.2.4m ArG^!j^AuH 9 .c[`bWLAhhwinner_checksummhexpmm db_connectionm1.0.0m c>R TjfM4# KYl!Y,֤ [bWLAhhwouter_checksummhexpmmpostgrexm0.6.0m *98R:,7z U>DCPbWLAhhw timestampmhexpmm db_connectionm 2.0.0-rc.0hhba aha a9a0/bWLAhhwdepsmhexpmm connectionm1.0.2j^bWLAhhwinner_checksummhexpmm plug_cowboym2.1.2m ݵR8ÎD.'2F]|Gŀ^\bWLAhhwinner_checksummhexpmmpostgrexm0.11.0m ۙt*Wml'E- 6`B'bWLAhhwdepsmhexpmmphoenixm 1.3.0-rc.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.3.2 or ~> 1.4wfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0wfalsej8bWLAhhwretiredmhexpmmphoenixm 1.5.0-rc.0wnil3bWLAhhwretiredmhexpmmdecimalm0.2.0wnilKbWLAhhw timestampmhexpmm phoenix_htmlm2.13.3hhba aha a9a0,bWLAhhwdepsmhexpmmdecimalm0.2.1j[bWLAhhwouter_checksummhexpmmphoenixm0.10.0m @` L)-%T)bWLAhhwdepsmhexpmmplugm0.5.0jhbWLAhhwdepsmhexpmmpostgrexm0.9.1lhmhexpmmdecimalmdecimalm~> 1.0wfalsej_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.8.0m V -*J= z%߻!CbWLAhhw timestampmhexpmmjasonm1.2.1hhba aha a9a0bWLAhhwdepsmhexpmmphoenixm0.13.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm>= 0.12.2 and < 2.0.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsehmhexpmmpoolboympoolboym~> 1.5.1 or ~> 1.6wtruejZbWLAhhwinner_checksummhexpmmphoenixm1.5.4m ʜ`Ic _%[ g~p?4bWLAhhwretiredmhexpmmpostgrexm0.8.1wnil9bWLAhhwretiredmhexpmm db_connectionm0.1.5wnilZbWLAhhwouter_checksummhexpmmphoenixm1.1.5m ebHm O^<<9Ubj}še]HabWLAhhwouter_checksummhexpmmphoenix_paramsm0.2.2m FlXGִ]7ZbWLAhhwouter_checksummhexpmmphoenixm0.2.1m +glXOptDt hT9ky[9{-WbWLAhhwouter_checksummhexpmmplugm1.7.1m ڥ ċl2 b|uUM^[bWLAhhwinner_checksummhexpmmphoenixm0.14.0m q;{;  )=oR b/fbWLAhhwdepsmhexpmm phoenix_htmlm2.8.0lhmhexpmmplugmplugm~> 1.0wfalsej3bWLAhhwretiredmhexpmmdecimalm1.9.0wniljbWLAhhwdepsmhexpmmpostgrexm0.5.5lhmhexpmmdecimalmdecimalm~> 0.2.3wfalsejEbWLAhhw timestampmhexpmmphoenixm1.1.8hhba aha a9a0bWLAhhwdepsmhexpmm plug_cowboym1.0.0lhmhexpmmcowboymcowboym~> 1.0wfalsehmhexpmmplugmplugm~> 1.7wfalsejEbWLAhhw timestampmhexpmmdecimalm1.6.0hhba aha a9a0WbWLAhhwinner_checksummhexpmmplugm1.3.0m n+?JBE(W Dk̒Dz9bWLAhhwretiredmhexpmm db_connectionm0.2.4wnilZbWLAhhwinner_checksummhexpmmphoenixm0.6.1m 0Y 9cQ#hư(* qbWLAhhwdepsmhexpmmpostgrexm0.14.2lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 2.0wfalsehmhexpmmdecimalmdecimalm~> 1.5wfalsehmhexpmmjasonmjasonm~> 1.0wtruej3bWLAhhwretiredmhexpmmphoenixm0.2.3wnilLbWLAhhw timestampmhexpmmphoenix_paramsm1.0.1hhba aha a9a03bWLAhhwretiredmhexpmmphoenixm0.6.0wnil[bWLAhhwouter_checksummhexpmmpostgrexm0.5.1m taj6[Vn3JMvbWLAhhwdepsmhexpmmphoenixm0.4.1lhmhexpmmcowboymcowboym~> 1.0.0wtruehmhexpmmlinguistmlinguistm~> 0.1.2wfalsehmhexpmmplugmplugm0.7.0wfalsehmhexpmmpoisonmpoisonm~> 1.1.0wfalsejgbWLAhhwdepsmhexpmm phoenix_htmlm2.10.4lhmhexpmmplugmplugm~> 1.0wfalsejabWLAhhwinner_checksummhexpmmpostgrexm 0.14.0-rc.0m )Wec( y*F&N9FbWLAhhw timestampmhexpmmpostgrexm0.5.5hhba aha a9a03bWLAhhwretiredmhexpmmphoenixm1.3.1wnil`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.10.5m Ot"D s*VlN3|(O ebWLAhhwinner_checksummhexpmm db_connectionm 1.0.0-rc.2m Ų2Qm~G=qe-gXbWLAhhwversionsmhexpmmjasonl 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`bWLAhhwinner_checksummhexpmm db_connectionm0.1.0m ҇4gMbzYw|} babWLAhhwdepsmhexpmmplugm0.8.2lhmhexpmmcowboymcowboym~> 1.0wtruejabWLAhhwdepsmhexpmmplugm1.0.4lhmhexpmmcowboymcowboym~> 1.0wtruejabWLAhhwouter_checksummhexpmmphoenix_pubsubm1.1.2m ig)tѕR@i8CbWLAhhw timestampmhexpmmjasonm1.1.2hhba aha a9a0WbWLAhhwouter_checksummhexpmmplugm0.8.0m s1eL>:cv6-+KbWLAhhw timestampmhexpmm db_connectionm0.1.7hhba aha a9a0EbWLAhhw timestampmhexpmmphoenixm1.2.3hhba aha a9a0WbWLAhhwouter_checksummhexpmmplugm0.5.1m '#ryNP9Xjk_C.j/bWLAhhwdepsmhexpmmphoenixm1.0.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejubWLAhhwdepsmhexpmm db_connectionm2.0.1lhmhexpmm connectionm connectionm~> 1.0.2wfalsej`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.11.2m /T06!lEZ n'BbWLAhhw timestampmhexpmmplugm1.1.3hhba aha a9a0[bWLAhhwouter_checksummhexpmmphoenixm1.4.10m %jס@)p6=|pnd"t_bWLAhhwouter_checksummhexpmm phoenix_htmlm1.4.0m jJZ:bxhk ~RsXI֟kL`bWLAhhwouter_checksummhexpmm db_connectionm1.0.0m mcVe/g^$Z=ٌ" /[x"3bWLAhhwretiredmhexpmmphoenixm0.5.0wnil0bWLAhhwretiredmhexpmmplugm1.4.3wnilZbWLAhhwinner_checksummhexpmmdecimalm1.4.1m Ps""|* ږ[ p8bWLAhhwretiredmhexpmm phoenix_htmlm2.6.2wnil`bWLAhhwouter_checksummhexpmm db_connectionm2.0.2m {YCbGv [bWLAhhwinner_checksummhexpmmpostgrexm0.8.4m 4Ma W`7ℋ*Z-Ⴛ\ڧ,E5MbWLAhhw timestampmhexpmm connectionm 1.0.0-rc.1hhba aha a9a0CbWLAhhw timestampmhexpmmplugm0.14.0hhba aha a9a0:bWLAhhwretiredmhexpmmphoenix_pubsubm1.1.0wnil:bWLAhhw timestampmhexpmmpoisonhhba aha a+abWLAhhwdepsmhexpmmphoenixm1.1.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejbWLAhhwdepsmhexpmmphoenixm1.4.2lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruejWbWLAhhwinner_checksummhexpmmmimem1.3.0m ^EP O`J4Ǣ QQ`bWLAhhwouter_checksummhexpmm db_connectionm0.1.5m uXһ >>ʃjGmoۢMc3bWLAhhwretiredmhexpmmphoenixm1.4.8wnilWbWLAhhwinner_checksummhexpmmplugm0.5.3m ͢Q:ID+Y1g%8RMJbWLAhhw timestampmhexpmm phoenix_htmlm2.9.2hhba aha a9a0KbWLAhhw timestampmhexpmm db_connectionm1.1.2hhba aha a9a0)bWLAhhwdepsmhexpmmplugm0.4.4j;bWLAhhw timestampmhexpmmphoenixhhba aha a9a0JbWLAhhw timestampmhexpmmphoenixm 1.5.0-rc.0hhba aha a9a0gbWLAhhwdepsmhexpmm phoenix_htmlm2.10.2lhmhexpmmplugmplugm~> 1.0wfalsej[bWLAhhwinner_checksummhexpmmpostgrexm0.5.5m d : .υ!ȗ6!̫TbWLAhhw registry_etagmhexpmm connectionm""cb8397ee262701f54669d854dd7531e6"WbWLAhhwinner_checksummhexpmmplugm1.0.4m ׼fFOshM$L3m_bWLAhhwinner_checksummhexpmmphoenixm 1.3.0-rc.1m KH#L^Xeu7bWLAhhwdepsmhexpmmplugm 1.2.0-rc.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmmimemmimem~> 1.0wfalsejLbWLAhhw timestampmhexpmmphoenix_paramsm1.1.0hhba aha a9a0ZbWLAhhwinner_checksummhexpmmphoenixm0.5.0m tlAe-8æ%Z6@*LbWLAhhw timestampmhexpmmphoenix_paramsm0.2.0hhba aha a9a0bWLAhhwdepsmhexpmmpostgrexm0.13.5lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 1.1wfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsej0bWLAhhwretiredmhexpmmplugm0.5.2wnilZbWLAhhwinner_checksummhexpmmphoenixm1.3.1m ܟ(``Kۓ L𝳁s3bbWLAhhwversionsmhexpmm 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.0j3bWLAhhwretiredmhexpmmphoenixm1.4.1wnil`bWLAhhwouter_checksummhexpmm db_connectionm0.1.8m ԋj<@kU- 1&B[\bWLAhhwouter_checksummhexpmmpostgrexm0.12.0m j^Z'ld R2!ҙD4bWLAhhwretiredmhexpmmphoenixm1.4.16wnilKbWLAhhw timestampmhexpmm phoenix_htmlm2.12.0hhba aha a9a04bWLAhhwretiredmhexpmmphoenixm0.16.0wnil\bWLAhhwouter_checksummhexpmm telemetrym0.4.2m -j m{XRr.+#b {*abWLAhhwinner_checksummhexpmmphoenix_paramsm1.1.3m 2{kJkf c}u1{ j9bWLAhhwretiredmhexpmm db_connectionm2.0.0wnilGbWLAhhw timestampmhexpmmpostgrexm0.15.3hhba aha a9a00bWLAhhwretiredmhexpmmplugm1.5.0wnilObWLAhhw registry_etagmhexpmmjasonm""c7ccb0b0d3701498a0a717862ef103b1"WbWLAhhwinner_checksummhexpmmplugm1.5.1m [϶5^LG0?l)ҧoASeZbWLAhhwinner_checksummhexpmmdecimalm0.2.1m U%qs]A27M_bWLAhhwouter_checksummhexpmmphoenixm 1.2.0-rc.1m _ʨul! ifʂA,WbWLAhhwouter_checksummhexpmmplugm1.0.1m Lq ۖ}|e7;d0bWLAhhwretiredmhexpmmplugm0.4.1wnil)bWLAhhwdepsmhexpmmmimem1.3.1jbWLAhhwdepsmhexpmmphoenixm0.4.0lhmhexpmmcowboymcowboym~> 1.0.0wtruehmhexpmmlinguistmlinguistm~> 0.1.2wfalsehmhexpmmplugmplugm0.7.0wfalsehmhexpmmpoisonmpoisonm~> 1.0.1wfalsej:bWLAhhwretiredmhexpmmphoenix_pubsubm1.1.2wnilbWLAhhwdepsmhexpmm db_connectionm0.2.0lhmhexpmmbackoffmbackoffm1.1.1wfalsehmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 0.7wtruejZbWLAhhwinner_checksummhexpmmphoenixm0.6.0m wކM` 1.0wfalsehmhexpmm db_connectionm db_connectionm ~> 2.0-rc.0wfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsehmhexpmmjasonmjasonm~> 1.0wtruejZbWLAhhwouter_checksummhexpmmphoenixm1.4.1m [t=] xU&M"bWLAhhwdepsmhexpmmphoenixm1.3.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4wfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0wfalsejBbWLAhhw timestampmhexpmmplugm0.8.1hhba aha a9a0XbWLAhhwouter_checksummhexpmmplugm0.13.1m P|u>p;y%N}퐉EwI BbWLAhhw timestampmhexpmmplugm1.3.2hhba aha a9a0@bWLAhhw timestampmhexpmm phoenix_htmlhhba aha a9a08bWLAhhw timestampmhexpmmmimehhba aha a9a0EbWLAhhw timestampmhexpmmphoenixm1.3.2hhba aha a9a0GbWLAhhw timestampmhexpmmpostgrexm0.11.0hhba aha a9a0ZbWLAhhwouter_checksummhexpmmphoenixm0.8.0m dgz1R,GdSv*>_bWLAhhwinner_checksummhexpmm phoenix_htmlm1.4.0m 9܍u7| ?׮:jzbbWLAhhwdepsmhexpmmplugm0.11.2lhmhexpmmcowboymcowboym~> 1.0wtruejWbWLAhhwouter_checksummhexpmmplugm0.4.2m Anۅm 4 u4:bWLAhhwretiredmhexpmmphoenix_pubsubm1.0.2wnilWbWLAhhwouter_checksummhexpmmplugm1.5.0m ͜\c~:ɿPtovͲױΓԠ.RjbWLAhhwdepsmhexpmmpostgrexm0.5.0lhmhexpmmdecimalmdecimalm~> 0.2.0wfalsej4bWLAhhwretiredmhexpmmpostgrexm0.5.4wnilZbWLAhhwouter_checksummhexpmmphoenixm1.0.3m e^c hwK>r6)BdڙabWLAhhwouter_checksummhexpmmphoenix_pubsubm0.0.1m SieZc6@1oW: ^bWLAhhwouter_checksummhexpmm plug_cowboym2.0.0m ?IbЙ$m|/.j) ns:tbWLAhhwdepsmhexpmmpostgrexm0.11.2lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm ~> 1.0-rcwfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsejWbWLAhhwouter_checksummhexpmmplugm1.0.6m mK-7e ͦS']bWLAhhwinner_checksummhexpmm connectionm1.0.4m "w>f%Jaj}YvZbWLAhhwouter_checksummhexpmmphoenixm1.3.3m (& 1A E-;4sN0]J+XbWLAhhwinner_checksummhexpmmplugm0.12.1m X"!崫*x/ӡ~(#_bWLAhhwouter_checksummhexpmm phoenix_htmlm1.0.1m /+P3)׾Jabʶ$§O5oIbWLAhhw timestampmhexpmm plug_cryptom1.1.1hhba aha a9a0JbWLAhhw timestampmhexpmm phoenix_htmlm1.2.0hhba aha a9a0`bWLAhhwinner_checksummhexpmm db_connectionm2.0.0m ⌇5)U]dV=4 M^ZbWLAhhwouter_checksummhexpmmdecimalm0.2.1m v2 T80W'u̶J2UjWz cc "EbWLAhhw timestampmhexpmmdecimalm1.8.0hhba aha a9a0ZbWLAhhwouter_checksummhexpmmphoenixm1.4.8m 9e" MrJgjt--0bWLAhhwdepsmhexpmm plug_cryptom1.0.0jabWLAhhwdepsmhexpmmplugm1.1.5lhmhexpmmcowboymcowboym~> 1.0wtruej`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.13.3m )/%_LToS`·к38gZbWLAhhwinner_checksummhexpmmphoenixm1.1.6m {fi/Z0ڷ=b(ս*BkGKbWLAhhw timestampmhexpmm phoenix_htmlm2.11.0hhba aha a9a0abWLAhhwinner_checksummhexpmmpostgrexm 0.13.0-rc.0m cЛ $>Rir-.+f.)9;F9bWLAhhwretiredmhexpmm phoenix_htmlm2.10.5wnil3bWLAhhwretiredmhexpmmphoenixm1.4.4wnilWbWLAhhw registry_etagmhexpmm db_connectionm""84fb07dffdadbc16b002d1285963d731"WbWLAhhwinner_checksummhexpmmplugm0.8.3m ˯eF[q|C8S>!VTEbWLAhhw timestampmhexpmmphoenixm0.7.2hhba aha a9a0ZbWLAhhwinner_checksummhexpmmdecimalm1.1.0m 33s/}z_ 0-g\ʁ*c>rGbWLAhhw timestampmhexpmmplugm 1.2.0-rc.0hhba aha a9a0bWLAhhwdepsmhexpmmphoenixm0.11.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm ~> 0.11.3wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsehmhexpmmpoolboympoolboym~> 1.4.2wtruej]bWLAhhwouter_checksummhexpmm connectionm1.0.0m JLj"A 1.0wtruehmhexpmmplugmplugm ~> 0.12.1wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsehmhexpmmpoolboympoolboym~> 1.5.1wtruej3bWLAhhwretiredmhexpmmdecimalm1.6.0wnilhbWLAhhwdepsmhexpmmpostgrexm0.8.4lhmhexpmmdecimalmdecimalm~> 1.0wfalsejbWLAhhwdepsmhexpmmplugm1.2.6lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmmimemmimem~> 1.0wfalsej5bWLAhhwretiredmhexpmmplugm 1.5.0-rc.2wnilbWLAhhwdepsmhexpmmphoenixm0.13.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm>= 0.12.2 and < 2.0.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsehmhexpmmpoolboympoolboym~> 1.5.1 or ~> 1.6wtruejbWLAhhwdepsmhexpmmphoenixm1.4.5lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruejZbWLAhhwouter_checksummhexpmmphoenixm0.2.9m x*;D4Dc~| d]CbWLAhhw timestampmhexpmmplugm1.10.0hhba aha a9a0WbWLAhhwinner_checksummhexpmmplugm1.8.2m ڤ I9@wa\;Y t@5bWLAhhwretiredmhexpmm telemetrym0.3.0wnilBbWLAhhw timestampmhexpmmmimem0.0.1hhba aha a9a0bWLAhhwdepsmhexpmmphoenixm0.6.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmlinguistmlinguistm~> 0.1.4wfalsehmhexpmmplugmplugm~> 0.8.4wfalsehmhexpmmpoisonmpoisonm~> 1.2wfalsej0bWLAhhwretiredmhexpmmmimem1.0.1wnil0bWLAhhwretiredmhexpmmplugm1.0.4wnil_bWLAhhwouter_checksummhexpmm phoenix_htmlm1.2.0m QR«`ǭ}10R~2*NЇlv\bWLAhhwouter_checksummhexpmmpostgrexm0.10.0m fOzg#rpzLv^`&3WGbWLAhhw timestampmhexpmm telemetrym0.1.0hhba aha a9a0[bWLAhhwouter_checksummhexpmmphoenixm1.4.17m :]z=vR_n{7cԮ3bWLAhhwretiredmhexpmmdecimalm1.4.0wnil3bWLAhhwdepsmhexpmmphoenix_pubsubm2.0.0j\bWLAhhwinner_checksummhexpmmplugm 1.5.0-rc.2m ; _A2heu=fog%KbWLAhhw timestampmhexpmm db_connectionm2.0.4hhba aha a9a09bWLAhhwretiredmhexpmm db_connectionm2.0.1wnilWbWLAhhwinner_checksummhexpmmplugm1.1.8m ۋmyRDjp2CabWLAhhwdepsmhexpmmplugm1.1.1lhmhexpmmcowboymcowboym~> 1.0wtruej`bWLAhhwouter_checksummhexpmm db_connectionm0.2.3m haԂBfAUEW,+JI oXbWLAhhwinner_checksummhexpmmjasonm1.1.1m ̸@߰o/56MH#X^JbWLAhhw timestampmhexpmm phoenix_htmlm2.6.2hhba aha a9a08bWLAhhwretiredmhexpmm phoenix_htmlm2.2.0wnilBbWLAhhw timestampmhexpmmplugm1.1.1hhba aha a9a0WbWLAhhwinner_checksummhexpmmplugm1.4.3m #mw{f*o6jIIJWbWLAhhwinner_checksummhexpmmplugm1.2.2m Ͻ!L?퍏K͛ubWLAhhwdepsmhexpmm db_connectionm2.0.3lhmhexpmm connectionm connectionm~> 1.0.2wfalsejbWLAhhwdepsmhexpmmphoenixm1.1.6lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsejfbWLAhhwouter_checksummhexpmmphoenix_pubsubm 1.0.0-rc.0m v\f_vM^Z"q9ۏ ;W_vbWLAhhwretiredmhexpmm plug_cowboym2.2.2twmessagemBroken telemetry supportwreasonwRETIRED_INVALID3bWLAhhwdepsmhexpmmphoenix_pubsubm1.1.1jbWLAhhwdepsmhexpmmplugm1.2.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmmimemmimem~> 1.0wfalsejabWLAhhwouter_checksummhexpmmphoenix_pubsubm1.1.1m ؐmQayܪ2DV'ŇVxulEbWLAhhw timestampmhexpmmphoenixm0.4.1hhba aha a9a00bWLAhhwretiredmhexpmmplugm1.6.3wnil_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.9.3m Z!"C$/TOx0OKLC% XX0bWLAhhwretiredmhexpmmplugm1.1.8wnilEbWLAhhw timestampmhexpmmdecimalm1.0.0hhba aha a9a0ZbWLAhhwinner_checksummhexpmmphoenixm0.1.0m Hyr M{=w-bWLAhhwdepsmhexpmm db_connectionm0.1.3lhmhexpmmbackoffmbackoffm~> 1.0wfalsehmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 0.7wtruej\bWLAhhwinner_checksummhexpmmpostgrexm0.15.1m #4pA[ڼƆ i%,o;[G/^bWLAhhwouter_checksummhexpmm plug_cowboym2.0.2m DSݢiu| Mq 1ubWLAhhwdepsmhexpmm db_connectionm2.0.0lhmhexpmm connectionm connectionm~> 1.0.2wfalsej8bWLAhhwretiredmhexpmmphoenixm 1.4.0-rc.2wnilWbWLAhhwinner_checksummhexpmmplugm0.4.2m ¤X {p&ʜ`bWLAhhwinner_checksummhexpmm db_connectionm0.2.4m Q\?{ffCx6mC(T"bWLAhhwdepsmhexpmmphoenixm1.3.3lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4wfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0wfalsejWbWLAhhwouter_checksummhexpmmplugm1.8.0m L [8Ɵ*x}kTot$KbWLAhhw timestampmhexpmm db_connectionm0.1.5hhba aha a9a0EbWLAhhw timestampmhexpmmphoenixm0.2.4hhba aha a9a0bWLAhhwdepsmhexpmmphoenix_paramsm1.1.3lhmhexpmmdecimalmdecimalm~> 1.8wfalsehmhexpmmphoenixmphoenixm~> 1.3wfalsejWbWLAhhwinner_checksummhexpmmplugm1.4.5m {sS&,^ ]$ '=ZbWLAhhwinner_checksummhexpmmdecimalm0.1.2m iڠV;j\åiTYjFbWLAhhw timestampmhexpmmphoenixm1.4.12hhba aha a9a0dbWLAhhwdepsmhexpmmphoenixm1.4.14lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsejWbWLAhhwinner_checksummhexpmmplugm1.2.5m |؋f4Bf w{їSbWLAhhw registry_etagmhexpmm telemetrym""fd930643205853c2c965911bb8aa90f7"cbWLAhhwdepsmhexpmmphoenixm1.4.7lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsejbWLAhhwdepsmhexpmmphoenixm0.6.2lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmlinguistmlinguistm~> 0.1.4wfalsehmhexpmmplugmplugm~> 0.8.4wfalsehmhexpmmpoisonmpoisonm~> 1.2wfalsejZbWLAhhwinner_checksummhexpmmphoenixm1.0.4m /(L#:ߋkK2}?LJnZbWLAhhwouter_checksummhexpmmphoenixm0.6.0m \&9&)!cigN{Ezx9HbWLAhhw timestampmhexpmm connectionm1.0.3hhba aha a9a09bWLAhhwretiredmhexpmm db_connectionm0.1.2wnil4bWLAhhwretiredmhexpmmphoenixm1.4.12wnilXbWLAhhwouter_checksummhexpmmjasonm1.0.0m l@eLc# H;g͠y8bWLAhhwretiredmhexpmm phoenix_htmlm1.0.1wnilGbWLAhhw timestampmhexpmmpostgrexm0.12.2hhba aha a9a0CbWLAhhw timestampmhexpmmplugm0.13.0hhba aha a9a0ZbWLAhhwinner_checksummhexpmmphoenixm1.3.3m _9災Dj,y|XUf3bWLAhhwretiredmhexpmmphoenixm0.1.0wnil[bWLAhhwinner_checksummhexpmmphoenixm1.4.11m b÷lz>XPڧǦ K 3bWLAhhwretiredmhexpmmphoenixm1.5.3wnil\bWLAhhwouter_checksummhexpmmpostgrexm0.14.0m mO 1~SpR3≵u/P3bWLAhhwretiredmhexpmmdecimalm0.1.2wnil3bWLAhhwretiredmhexpmmphoenixm0.2.7wnilbWLAhhwdepsmhexpmmphoenix_paramsm0.4.1lhmhexpmmdecimalmdecimalm>= 1.7.0wfalsehmhexpmmphoenixmphoenixm>= 1.3.3wfalsej4bWLAhhwretiredmhexpmmphoenixm0.2.10wnil_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.5.0m rJPwO`.; Sk ۩ ]-A?WbWLAhhwinner_checksummhexpmmplugm1.7.0m ͌U17 HuJ%#nbWLAhhwdepsmhexpmmpostgrexm0.12.1lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm ~> 1.0-rc.4wfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsejZbWLAhhwouter_checksummhexpmmphoenixm0.6.2m ATg98TA^eVAUbWLAhhw registry_etagmhexpmm plug_cowboym""d9ec1d3801c046c701fa55c601b67a33"bWLAhhwdepsmhexpmmplugm1.7.1lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0wfalsej)bWLAhhwdepsmhexpmmplugm0.4.1jKbWLAhhw timestampmhexpmm db_connectionm0.1.6hhba aha a9a0ibWLAhhwdepsmhexpmmjasonm 1.0.0-rc.3lhmhexpmmdecimalmdecimalm~> 1.0wtruejKbWLAhhw timestampmhexpmm db_connectionm2.2.2hhba aha a9a0WbWLAhhwouter_checksummhexpmmmimem1.2.0m MpB `UQ`\([+0bWLAhhwretiredmhexpmmplugm1.8.0wnil_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.5.0m D7zp6 . .cKkXbWLAhhwdepsmhexpmmphoenixm1.1.9lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0.5wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsejbWLAhhwdepsmhexpmmphoenixm0.2.10lhmhexpmmex_confmex_confm0.1.2wfalsehmhexpmminflexminflexm0.2.3wfalsehmhexpmmjazzmjazzm0.1.1wfalsehmhexpmmplugmplugm0.5.0wfalsej\bWLAhhwouter_checksummhexpmmpostgrexm0.15.3m G7bG< b0~QOs ,( `l\bWLAhhwouter_checksummhexpmmpostgrexm0.13.3m s)S*q',=u9 OAbWLAhhw timestampmhexpmm db_connectionhhba aha a9a0dbWLAhhwdepsmhexpmmjasonm1.1.0lhmhexpmmdecimalmdecimalm~> 1.0wtruej0bWLAhhwretiredmhexpmmplugm1.8.3wniltm~Ļsb`&x ew^bWLAhhwouter_checksummhexpmm plug_cowboym2.1.0m lѽM?G}gaSՍ@CzF20bWLAhhwretiredmhexpmmplugm0.4.2wnil\bWLAhhwinner_checksummhexpmmpostgrexm0.14.0m Vc93=5"w&퉂q2bWLAhhwdepsmhexpmmphoenix_paramsm1.1.1lhmhexpmmdecimalmdecimalm>= 1.7.0wfalsehmhexpmmphoenixmphoenixm>= 1.3.3wfalsejEbWLAhhw timestampmhexpmmphoenixm1.5.4hhba aha a9a01bWLAhhwretiredmhexpmmplugm0.10.0wnil`bWLAhhwouter_checksummhexpmm db_connectionm2.2.2m d*@بZo+'$%Ӄq6 0bWLAhhwretiredmhexpmmplugm1.0.1wnil:bWLAhhwretiredmhexpmmphoenix_paramsm0.4.2wnilEbWLAhhw timestampmhexpmmdecimalm1.1.0hhba aha a9a0JbWLAhhw timestampmhexpmm phoenix_htmlm2.6.1hhba aha a9a0_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.3.0m 'կ z9X>b+Qb: ڨ`( yEbWLAhhw timestampmhexpmmphoenixm1.1.2hhba aha a9a0EbWLAhhw timestampmhexpmmphoenixm0.2.6hhba aha a9a0gbWLAhhwdepsmhexpmm phoenix_htmlm2.14.0lhmhexpmmplugmplugm~> 1.5wfalsejEbWLAhhw timestampmhexpmmphoenixm0.7.1hhba aha a9a03bWLAhhwretiredmhexpmmdecimalm0.2.1wnilWbWLAhhwinner_checksummhexpmmplugm1.3.5m up*afm,T˺及Y ;p1ZbWLAhhwouter_checksummhexpmmphoenixm1.0.0m s/L!p_1iRV䒥WWbWLAhhwouter_checksummhexpmmmimem1.4.0m uB"?p#KjGO?jzObWLAhhwdepsmhexpmmphoenixm1.0.5lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejbWLAhhwdepsmhexpmmphoenixm0.2.6lhmhexpmmex_confmex_confm0.1.2wfalsehmhexpmminflexminflexm0.2.0wfalsehmhexpmmjazzmjazzm0.1.1wfalsehmhexpmmplugmplugm0.4.4wfalsejKbWLAhhw timestampmhexpmm phoenix_htmlm2.10.1hhba aha a9a0BbWLAhhw timestampmhexpmmplugm0.7.0hhba aha a9a0ZbWLAhhwinner_checksummhexpmmphoenixm0.7.0m *~t@ `fNV][:;MY0bWLAhhwretiredmhexpmmplugm1.2.2wnil3bWLAhhwretiredmhexpmmphoenixm0.7.2wnilKbWLAhhw timestampmhexpmm db_connectionm1.1.1hhba aha a9a0bbWLAhhwdepsmhexpmmplugm0.13.1lhmhexpmmcowboymcowboym~> 1.0wtruej3bWLAhhwdepsmhexpmmphoenix_pubsubm1.0.2jBbWLAhhw timestampmhexpmmplugm1.4.0hhba aha a9a0bWLAhhwdepsmhexpmm db_connectionm 1.0.0-rc.4lhmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 1.0.0-beta.3wtruejqbWLAhhwdepsmhexpmm phoenix_htmlm2.5.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsej3bWLAhhwretiredmhexpmmphoenixm1.2.3wnil3bWLAhhwretiredmhexpmmdecimalm1.5.0wnilFbWLAhhw timestampmhexpmmphoenixm0.12.0hhba aha a9a0WbWLAhhwinner_checksummhexpmmplugm1.9.0m |N&"3Gs$Ë{F}Y$:ZJ[bWLAhhwinner_checksummhexpmmphoenixm1.4.17m |ު}`݌"ChI@;8dbWLAhhwdepsmhexpmmphoenixm1.4.13lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsej)bWLAhhwdepsmhexpmmmimem1.1.0jEbWLAhhw timestampmhexpmmdecimalm1.4.0hhba aha a9a09bWLAhhwretiredmhexpmm db_connectionm2.0.6wnil6bWLAhhwretiredmhexpmmjasonm 1.0.0-rc.1wnil0bWLAhhwretiredmhexpmmplugm1.8.1wnilWbWLAhhwouter_checksummhexpmmmimem1.1.0m 3 ګVh\ã8)'( _MZbWLAhhwouter_checksummhexpmmdecimalm1.0.0m T ! oC d0'${ՓBtPbWLAhhw timestampmhexpmm db_connectionm 1.0.0-rc.4hhba aha a9a0GbWLAhhw timestampmhexpmmpostgrexm0.15.5hhba aha a9a0bWLAhhwdepsmhexpmmphoenixm1.5.3lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej_bWLAhhwinner_checksummhexpmm phoenix_htmlm1.0.1m wcC$f-fZ$ mK]d!8Z1xbWLAhhwversionsmhexpmmphoenix_pubsubl m0.0.1m0.1.0m 1.0.0-rc.0m1.0.0m1.0.1m1.0.2m1.1.0m1.1.1m1.1.2m2.0.0jZbWLAhhwinner_checksummhexpmmphoenixm1.0.2m ] 2EmUah⽶GzC2f0bWLAhhwretiredmhexpmmmimem0.0.1wnilgbWLAhhwdepsmhexpmm phoenix_htmlm2.13.0lhmhexpmmplugmplugm~> 1.5wfalsejKbWLAhhw timestampmhexpmm db_connectionm2.0.1hhba aha a9a0?bWLAhhw timestampmhexpmm plug_cowboyhhba aha a9a06bWLAhhwretiredmhexpmmjasonm 1.0.0-rc.2wnilgbWLAhhwdepsmhexpmm phoenix_htmlm2.10.3lhmhexpmmplugmplugm~> 1.0wfalsejJbWLAhhw timestampmhexpmm phoenix_htmlm2.1.1hhba aha a9a0WbWLAhhwinner_checksummhexpmmplugm1.4.0m '^f^Bl% $m_E;l }dbWLAhhwdepsmhexpmmjasonm1.1.1lhmhexpmmdecimalmdecimalm~> 1.0wtruejZbWLAhhwouter_checksummhexpmmphoenixm1.2.0m l@CF/l,86B"D^xQ_UnZbWLAhhwinner_checksummhexpmmdecimalm1.3.1m { 1.0wtruejFbWLAhhw timestampmhexpmmpostgrexm0.5.2hhba aha a9a00bWLAhhwretiredmhexpmmplugm0.8.4wnil3bWLAhhwretiredmhexpmmphoenixm1.0.5wnilebWLAhhwouter_checksummhexpmm db_connectionm 1.0.0-rc.2m 3ěV2igIhjl`l*>U&>bWLAhhwretiredmhexpmm db_connectionm 1.0.0-rc.0wnil_bWLAhhwinner_checksummhexpmmphoenixm 1.5.0-rc.0m 8;njxPZh>"Ξǣ,\bWLAhhwinner_checksummhexpmmplugm 1.5.0-rc.0m f!T`B sU-m(LebWLAhhwouter_checksummhexpmm db_connectionm 1.0.0-rc.0m NHtQ9T{;1EmKbWLAhhw timestampmhexpmm phoenix_htmlm2.13.4hhba aha a9a03bWLAhhwretiredmhexpmmphoenixm1.4.9wnil_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.5.1m |] ^7d"> Q)`WbWLAhhwouter_checksummhexpmmplugm1.1.3m 0c倉w<\zQY/OZ~N3o_bWLAhhwouter_checksummhexpmmphoenixm 1.3.0-rc.1m v,TOƿ<~RS7=bebWLAhhwinner_checksummhexpmm db_connectionm 1.0.0-rc.3m ζp0q m5{f  m}2"l83bWLAhhwretiredmhexpmmphoenixm1.1.4wnil1bWLAhhwretiredmhexpmmplugm0.12.0wnilBbWLAhhw timestampmhexpmmplugm1.2.3hhba aha a9a0bbWLAhhwdepsmhexpmmplugm0.12.1lhmhexpmmcowboymcowboym~> 1.0wtruejBbWLAhhw timestampmhexpmmplugm1.1.8hhba aha a9a0QbWLAhhw registry_etagmhexpmmdecimalm""4657f6f92e3c01945c9c9b06b6fd850e"`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.14.0m Ƽ(_Z=,XiB{eZ:bWLAhhwretiredmhexpmmphoenix_paramsm0.4.0wnil0bWLAhhwretiredmhexpmmplugm1.1.4wnilWbWLAhhwversionsmhexpmm plug_cryptolm1.0.0m1.1.0m1.1.1m1.1.2j_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.9.0m ŔEtYLjU yL{D+XbWLAhhwouter_checksummhexpmmplugm0.12.2m ncoŸ> iv,i7n|-JbWLAhhw timestampmhexpmm phoenix_htmlm2.8.0hhba aha a9a0JbWLAhhw timestampmhexpmm phoenix_htmlm2.1.0hhba aha a9a0bWLAhhwdepsmhexpmmplugm1.5.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.1wtruehmhexpmmmimemmimem~> 1.0wfalsej`bWLAhhwinner_checksummhexpmm db_connectionm0.1.7m !@֥{;wvhqB\ՎՃ*8bWLAhhwretiredmhexpmm phoenix_htmlm2.4.0wnilbWLAhhwdepsmhexpmmplugm1.3.4lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsej4bWLAhhwretiredmhexpmmpostgrexm0.9.0wnilZbWLAhhwinner_checksummhexpmmdecimalm0.2.2m b^BJ;@>ҜLz:K WbWLAhhwinner_checksummhexpmmplugm1.1.9m r!gкgfk8邗#3(7$s1bWLAhhwretiredmhexpmmplugm0.13.0wnil4bWLAhhwretiredmhexpmmpostgrexm0.9.1wnilWbWLAhhwinner_checksummhexpmmplugm1.8.3m ym.aNL Be ZhDx4bWLAhhwretiredmhexpmmphoenixm1.4.15wnil3bWLAhhwretiredmhexpmmdecimalm2.0.0wnilBbWLAhhw timestampmhexpmmplugm1.3.5hhba aha a9a0abWLAhhwouter_checksummhexpmmphoenix_pubsubm1.1.0m B.*ҞKna߄*cz;e,dbWLAhhwdepsmhexpmmphoenixm1.4.10lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsejJbWLAhhw timestampmhexpmmphoenixm 1.2.0-rc.0hhba aha a9a0XbWLAhhwouter_checksummhexpmmjasonm1.1.1m cEϬ2^4gry:42•%#LIbWLAhhw timestampmhexpmm plug_cowboym1.0.0hhba aha a9a0_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.1.1m /q?e*;155~`N*mBbWLAhhw timestampmhexpmmplugm1.6.2hhba aha a9a0abWLAhhwinner_checksummhexpmmphoenix_paramsm1.1.0m L|a0ys~+a#oEo6W,-@_bWLAhhwinner_checksummhexpmmphoenixm 1.4.0-rc.3m >Y$z^-c㬭}GknډA)g^bWLAhhwouter_checksummhexpmm plug_cryptom1.0.0m sh/AL]oi;tNXwYGbWLAhhw timestampmhexpmmpostgrexm0.13.4hhba aha a9a0ZbWLAhhwouter_checksummhexpmmphoenixm0.3.0m syeݞ 5aREx#7vK"bWLAhhwdepsmhexpmmplugm 1.5.0-rc.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.1wtruehmhexpmmmimemmimem~> 1.0wfalsejNbWLAhhw timestampmhexpmm phoenix_htmlm 2.0.0-devhhba aha a9a0EbWLAhhw timestampmhexpmmdecimalm0.2.0hhba aha a9a0;bWLAhhwretiredmhexpmm connectionm 1.0.0-rc.1wnil_bWLAhhwinner_checksummhexpmmphoenixm 1.3.0-rc.0m Ni!{19n}o⾊шùG'bWLAhhwdepsmhexpmmphoenixm 1.3.0-rc.3lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.3.2 or ~> 1.4wfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0wfalsejqbWLAhhwdepsmhexpmm phoenix_htmlm2.1.2lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsej3bWLAhhwretiredmhexpmmphoenixm1.0.6wnil1bWLAhhwretiredmhexpmmjasonm1.1.1wnilIbWLAhhw timestampmhexpmm plug_cryptom1.1.2hhba aha a9a03bWLAhhwretiredmhexpmmphoenixm1.3.3wnil)bWLAhhwdepsmhexpmmmimem0.0.1jJbWLAhhw timestampmhexpmm phoenix_htmlm2.5.0hhba aha a9a0ZbWLAhhwinner_checksummhexpmmphoenixm1.4.9m tm t3M=<ʱud5C4Ay.ftGbWLAhhw timestampmhexpmmplugm 1.4.0-rc.0hhba aha a9a0ZbWLAhhwouter_checksummhexpmmphoenixm1.2.4m ]ͤr GM(\rn\qV[9bWLAhhwretiredmhexpmm phoenix_htmlm2.11.0wnilKbWLAhhw timestampmhexpmm phoenix_htmlm2.10.0hhba aha a9a0WbWLAhhwouter_checksummhexpmmplugm1.4.3m zLe5"e۹zFk6LaHbWLAhhw timestampmhexpmmjasonm 1.0.0-rc.3hhba aha a9a0bWLAhhwdepsmhexpmmphoenixm0.6.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmlinguistmlinguistm~> 0.1.3wfalsehmhexpmmplugmplugm~> 0.8.2wfalsehmhexpmmpoisonmpoisonm~> 1.2wfalsej9bWLAhhwretiredmhexpmm phoenix_htmlm2.13.4wnilFbWLAhhw timestampmhexpmmpostgrexm0.4.2hhba aha a9a0BbWLAhhw timestampmhexpmmphoenix_paramshhba aha a9a03bWLAhhwretiredmhexpmmphoenixm1.5.4wnilubWLAhhwdepsmhexpmm phoenix_htmlm1.4.0lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0wfalsej`bWLAhhwinner_checksummhexpmm db_connectionm0.2.0m JDM*sǰq̎j hWbWLAhhwinner_checksummhexpmmplugm1.2.4m e)Do=."'vu=HϾ9bWLAhhwretiredmhexpmm db_connectionm0.2.5wnilbWLAhhwdepsmhexpmmplugm1.9.0lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0wfalsehmhexpmm telemetrym telemetrym~> 0.4wtruejWbWLAhhwouter_checksummhexpmmplugm0.8.1m Pߙ%5s}_ݡD/TD]bWLAhhwdepsmhexpmmpostgrexm 1.0.0-rc.0lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 1.1wfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsejZbWLAhhwouter_checksummhexpmmdecimalm1.1.0m  }9 ,SHmga8-k!bWLAhhwdepsmhexpmmplugm1.2.2lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmmimemmimem~> 1.0wfalsejEbWLAhhw timestampmhexpmmdecimalm0.1.2hhba aha a9a0bWLAhhwdepsmhexpmmphoenixm1.1.5lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsejabWLAhhwouter_checksummhexpmmphoenix_pubsubm0.1.0m ktp\5Ikz@}9N\ҡVb9^bWLAhhwinner_checksummhexpmm plug_cowboym2.0.2m `UhhHKncҺڔIxA3w*l_bWLAhhwouter_checksummhexpmm phoenix_htmlm1.3.0m E~<&[_^ N9bWLAhhwretiredmhexpmm phoenix_htmlm2.10.1wnil3bWLAhhwretiredmhexpmmphoenixm0.2.6wnil3bWLAhhwdepsmhexpmmphoenix_pubsubm1.1.2jBbWLAhhw timestampmhexpmmplugm1.3.6hhba aha a9a01bWLAhhwretiredmhexpmmplugm1.10.1wnil`bWLAhhwouter_checksummhexpmm db_connectionm1.1.2m U^§Y2UŸfxCFchwk0f֧bWLAhhwdepsmhexpmmplugm1.8.3lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0wfalsehmhexpmm telemetrym telemetrym~> 0.4wtruejEbWLAhhw timestampmhexpmmphoenixm1.1.5hhba aha a9a0`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.14.0m 0xmra838aQoI?bWLAhhwdepsmhexpmm db_connectionm 1.0.0-rc.1lhmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 1.0.0-beta.2wtruejXbWLAhhwinner_checksummhexpmmplugm0.12.0m =o.?Eg b,?(y3WbWLAhhwinner_checksummhexpmmplugm0.5.0m r"cuM>͖ x7ɮ4|)uR#vt:bWLAhhwretiredmhexpmmphoenix_pubsubm2.0.0wnil0bWLAhhwretiredmhexpmmmimem1.1.0wnil'bWLAhhwdepsmhexpmmphoenixm 1.3.0-rc.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.3.2 or ~> 1.4wfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0wfalsej3bWLAhhwretiredmhexpmmphoenixm0.2.8wnil3bWLAhhwretiredmhexpmmdecimalm1.0.0wnil,bWLAhhwdepsmhexpmmdecimalm1.0.0jWbWLAhhwinner_checksummhexpmmplugm1.2.3m P W8r^ @&sS3,Us0bWLAhhwretiredmhexpmmplugm1.4.2wnilZbWLAhhwouter_checksummhexpmmdecimalm0.2.5m .-h̆ lok$+FXnYGabWLAhhwouter_checksummhexpmmphoenix_paramsm0.1.0m njΚliC"B0 CEHʺZbWLAhhwouter_checksummhexpmmphoenixm1.1.2m dƼmd/(VYZ#DbWLAhhwdepsmhexpmmpostgrexm0.11.1lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 0.2wfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsejbbWLAhhwdepsmhexpmmplugm0.11.0lhmhexpmmcowboymcowboym~> 1.0wtruejXbWLAhhwinner_checksummhexpmmplugm0.11.2m Җ\7İԩ _e_O`*Z,bWLAhhwdepsmhexpmmdecimalm0.2.2jcbWLAhhwdepsmhexpmmphoenixm1.4.9lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsejfbWLAhhwdepsmhexpmm phoenix_htmlm2.9.2lhmhexpmmplugmplugm~> 1.0wfalsej:bWLAhhwretiredmhexpmmphoenix_paramsm1.1.3wnilEbWLAhhw timestampmhexpmmphoenixm1.0.6hhba aha a9a04bWLAhhwretiredmhexpmmphoenixm1.4.17wnilCbWLAhhw timestampmhexpmmplugm1.10.4hhba aha a9a0\bWLAhhwouter_checksummhexpmm telemetrym0.1.0m <1bhxAO\"BMPCD$[bWLAhhwouter_checksummhexpmmphoenixm0.13.0m 2W@ 1.0wtruehmhexpmmmimemmimem~> 1.0wfalsej4bWLAhhwretiredmhexpmmpostgrexm0.5.3wnilGbWLAhhw timestampmhexpmmpostgrexm0.13.2hhba aha a9a0 bWLAhhwdepsmhexpmmphoenixm 1.2.0-rc.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm ~> 1.0.0-rcwfalsehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsejzbWLAhhwretiredmhexpmmpostgrexm 1.0.0-rc.1twmessagemSuperseded by v0.13.0-rc.0wreasonwRETIRED_INVALIDZbWLAhhwinner_checksummhexpmmphoenixm0.8.0m ҡ:l $,I 2aEӰ|KWbWLAhhwinner_checksummhexpmmplugm1.8.1m py9pߠ~L֫X qL:gbWLAhhwdepsmhexpmm phoenix_htmlm2.13.3lhmhexpmmplugmplugm~> 1.5wfalsejabWLAhhwouter_checksummhexpmmphoenix_paramsm0.1.1m p> ]P_eT )# 007bWLAhhwretiredmhexpmm plug_cryptom1.0.0wnil3bWLAhhwretiredmhexpmmphoenixm0.4.0wnil`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.10.4m vٙ84*F"\h402i gZbWLAhhwinner_checksummhexpmmdecimalm0.2.0m [.ٙ)b9*O^GLzb}F^bWLAhhwinner_checksummhexpmm plug_cowboym1.0.0m .*}4 tm3_EX0=h' nzdbWLAhhwdepsmhexpmmjasonm1.2.0lhmhexpmmdecimalmdecimalm~> 1.0wtruej9bWLAhhwretiredmhexpmm db_connectionm0.1.3wnil\bWLAhhwinner_checksummhexpmm telemetrym0.4.2m (ɒE^s"M;kb_#:sPXps/E1bWLAhhwretiredmhexpmmplugm0.11.2wnilZbWLAhhwouter_checksummhexpmmphoenixm1.1.3m Wv pwd6Bf&Ͱp`R JZbWLAhhwouter_checksummhexpmmphoenixm0.3.1m YHy7^6aM+VSN%`bWLAhhwinner_checksummhexpmm db_connectionm2.1.1m .Nnfx~ I((Lj`bWLAhhwouter_checksummhexpmm db_connectionm2.2.0m k+ovnojbWLAhhwdepsmhexpmmplugm1.3.2lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsej3bWLAhhwretiredmhexpmmdecimalm1.4.1wnil[bWLAhhwinner_checksummhexpmmpostgrexm0.8.1m O羸7u;:N_QⰏ1bWLAhhwretiredmhexpmmjasonm1.0.0wnil1bWLAhhwretiredmhexpmmplugm0.14.0wnilCbWLAhhwversionsmhexpmmdecimallm0.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.0j3bWLAhhwretiredmhexpmmphoenixm1.2.1wnilLbWLAhhw timestampmhexpmmphoenix_paramsm0.1.1hhba aha a9a0bWLAhhwdepsmhexpmmplugm1.10.1lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wtruejFbWLAhhw timestampmhexpmmpostgrexm0.6.0hhba aha a9a0JbWLAhhw timestampmhexpmm phoenix_htmlm1.4.0hhba aha a9a00bWLAhhwretiredmhexpmmmimem1.4.0wnil3bWLAhhwretiredmhexpmmphoenixm0.4.1wnilKbWLAhhw timestampmhexpmm db_connectionm1.0.0hhba aha a9a0gbWLAhhwdepsmhexpmm phoenix_htmlm2.10.0lhmhexpmmplugmplugm~> 1.0wfalsej\bWLAhhwouter_checksummhexpmmpostgrexm0.15.4m 0euG7}ÿq|3?:=! NbWLAhhw timestampmhexpmm phoenix_htmlm 2.4.0-devhhba aha a9a09bWLAhhwretiredmhexpmm db_connectionm2.0.4wnil`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.13.0m kyi,(vS؜'miwu}oaKZLbWLAhhw timestampmhexpmmphoenix_paramsm1.0.3hhba aha a9a0^bWLAhhwouter_checksummhexpmm plug_cowboym2.1.2m }r%ΆZ#~mopAt %k>c"p3bWLAhhwretiredmhexpmmdecimalm0.2.5wnil`bWLAhhwinner_checksummhexpmm db_connectionm0.1.1m T@EWB*tQ]!-Ι)bWLAhhwdepsmhexpmm plug_cowboym2.2.1lhmhexpmmcowboymcowboym~> 2.7wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej5bWLAhhwretiredmhexpmmpostgrexm0.11.0wnilWbWLAhhwinner_checksummhexpmmplugm1.6.3m C3{*/c5i~LSH,^WbWLAhhwouter_checksummhexpmmplugm1.1.9m SPi]I\+B*'3$BbWLAhhw timestampmhexpmmplugm1.2.4hhba aha a9a07bWLAhhwretiredmhexpmm plug_cowboym2.0.0wnil_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.2.0m ҩ7|˷D/!:P0ؠ ^bWLAhhwouter_checksummhexpmm plug_cryptom1.1.0m ͡ 3%eu|%ʮȧ-l=9bWLAhhwretiredmhexpmm db_connectionm0.2.3wnilZbWLAhhwinner_checksummhexpmmdecimalm1.9.0m 12q$,QK =MjbWLAhhwdepsmhexpmmpostgrexm0.5.1lhmhexpmmdecimalmdecimalm~> 0.2.1wfalsejdbWLAhhwdepsmhexpmmphoenixm1.4.15lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsejabWLAhhwinner_checksummhexpmmphoenix_pubsubm1.0.1m b7|Kչ .: Nj[bWLAhhwouter_checksummhexpmmphoenixm0.12.0m -V>dOva1MSdUC~68 FbWLAhhw timestampmhexpmmphoenixm0.2.11hhba aha a9a0:bWLAhhwretiredmhexpmmphoenix_paramsm0.1.0wnil[bWLAhhwouter_checksummhexpmmphoenixm0.2.11m I0BDaT>@y dƀ3bWLAhhwretiredmhexpmmdecimalm1.3.1wnilCbWLAhhw timestampmhexpmmjasonm1.1.0hhba aha a9a0BbWLAhhw timestampmhexpmmplugm1.8.0hhba aha a9a01bWLAhhwdepsmhexpmmdecimalm 1.9.0-rc.0jEbWLAhhw timestampmhexpmmphoenixm1.1.9hhba aha a9a0^bWLAhhwouter_checksummhexpmm plug_cowboym2.1.1m O W[9DXm}rLs}WbWLAhhwouter_checksummhexpmmplugm1.0.0m YC1S [(R-7Vc ,bWLAhhwdepsmhexpmmdecimalm1.8.1j\bWLAhhwinner_checksummhexpmmpostgrexm0.13.3m wϲMDZr$3YaDo%51h+\bWLAhhwinner_checksummhexpmm telemetrym0.3.0m <Gqc <"f" :*:jguJsGbWLAhhw timestampmhexpmm telemetrym0.4.2hhba aha a9a0abWLAhhwouter_checksummhexpmmphoenix_paramsm0.4.1m Ue)2/Tyr;_,:bg$MIN`M[bWLAhhwinner_checksummhexpmmphoenixm1.4.15m \90j3RβV)bX+Y(WbWLAhhwouter_checksummhexpmmplugm1.4.2m P@`%ܧDd[͑Z1"? [BbWLAhhw timestampmhexpmmplugm1.3.1hhba aha a9a0ZbWLAhhwinner_checksummhexpmmphoenixm1.5.0m YόsJ0W6eIaij ĮBZbWLAhhwouter_checksummhexpmmphoenixm0.2.3m o, B)| X0pi)bWLAhhwdepsmhexpmmplugm0.4.2jZbWLAhhwouter_checksummhexpmmphoenixm1.2.2m vLJUԾ=er* 1.0wtruej9bWLAhhwretiredmhexpmm phoenix_htmlm2.14.0wnil8bWLAhhwretiredmhexpmm phoenix_htmlm2.0.1wnil]bWLAhhwouter_checksummhexpmmjasonm 1.0.0-rc.3m tE;#:˛V83;jWD+lE,ZbWLAhhwinner_checksummhexpmmphoenixm1.3.2m *QpkNn~ˊ,q痍\sx3bWLAhhwretiredmhexpmmphoenixm1.1.6wnildbWLAhhwdepsmhexpmmphoenixm1.4.11lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsej_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.1.0m ud*7kH|K~9dƀibWLAhhwdepsmhexpmmjasonm 1.0.0-rc.1lhmhexpmmdecimalmdecimalm~> 1.0wtruej^bWLAhhwouter_checksummhexpmm plug_cowboym2.0.1m s nh4jQQ@! 1.0wtruej`bWLAhhwouter_checksummhexpmm db_connectionm0.1.0m rpOH<]+4o!HBr,M(\bWLAhhwinner_checksummhexpmmpostgrexm0.11.2m U5<\mn%VӞ'FmߴB<4`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.10.1m ,ƭwLx@1D>Qy!¦ZbWLAhhwinner_checksummhexpmmphoenixm1.0.0m QyՉy ecoN*LbWLAhhw timestampmhexpmmphoenix_paramsm0.3.0hhba aha a9a0abWLAhhwdepsmhexpmmplugm1.0.5lhmhexpmmcowboymcowboym~> 1.0wtruejBbWLAhhw timestampmhexpmmplugm1.6.0hhba aha a9a0bWLAhhwdepsmhexpmmphoenix_paramsm0.4.2lhmhexpmmdecimalmdecimalm>= 1.7.0wfalsehmhexpmmphoenixmphoenixm>= 1.3.3wfalsejEbWLAhhw timestampmhexpmmphoenixm1.5.3hhba aha a9a0ZbWLAhhwouter_checksummhexpmmphoenixm0.4.1m 'Y Kj]MunLT܋Yg1bWLAhhwretiredmhexpmmjasonm1.0.1wnil\bWLAhhwinner_checksummhexpmmpostgrexm0.12.0m 'h|<"fWwK"5WbWLAhhwinner_checksummhexpmmplugm1.0.5m $W .:ѵȫ2RU)wJZbWLAhhwouter_checksummhexpmmphoenixm0.2.0m D7܆qO6ǁ79*0]P쿺sEiZbWLAhhwouter_checksummhexpmmdecimalm0.2.0m &EQ{-D; Mʝ4$bWLAhhwdepsmhexpmmpostgrexm0.14.0lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 2.0wfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsehmhexpmmjasonmjasonm~> 1.0wtruej$bWLAhhwdepsmhexpmmphoenixm 1.4.0-rc.1lhmhexpmmcowboymcowboym~> 1.0 or ~> 2.5wtruehmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.6.4 or ~> 1.7wfalsej?bWLAhhw timestampmhexpmm plug_cryptohhba aha a9a0WbWLAhhwouter_checksummhexpmmplugm1.1.8m -nOWz?~iW6F1؀,J[bWLAhhwouter_checksummhexpmmphoenixm0.11.0m @7#&[vnf"ď?O#y*.bWLAhhwdepsmhexpmm telemetrym0.4.1j5bWLAhhwretiredmhexpmmplugm 1.5.0-rc.0wnilbWLAhhwdepsmhexpmm db_connectionm1.1.1lhmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 1.0wtruej5bWLAhhwretiredmhexpmmpostgrexm0.15.2wnilbWLAhhwdepsmhexpmmplugm1.4.2lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsejabWLAhhwouter_checksummhexpmmphoenix_paramsm1.1.3m 7"R,ݮ80 j+>_|_bWLAhhwinner_checksummhexpmmphoenixm 1.4.0-rc.2m _ 4a KCGE(/gͮoYabWLAhhwinner_checksummhexpmmphoenix_paramsm0.2.2m 8n]ͨ րB0|z)-vgU탻M3bWLAhhwretiredmhexpmmdecimalm1.8.1wnilbWLAhhwdepsmhexpmmphoenixm1.1.2lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejbWLAhhwdepsmhexpmmpostgrexm0.15.2lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 2.1wfalsehmhexpmmdecimalmdecimalm~> 1.5wfalsehmhexpmmjasonmjasonm~> 1.0wtruejbWLAhhwdepsmhexpmm db_connectionm0.1.6lhmhexpmmbackoffmbackoffm~> 1.0wfalsehmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 0.7wtruej0bWLAhhwretiredmhexpmmplugm1.7.2wnil^bWLAhhwinner_checksummhexpmm plug_cowboym2.3.0m P\<Pj7WP2f#ݩcbWLAhhwouter_checksummhexpmm phoenix_htmlm 2.7.0-devm OhnV.m%VH;Dyd ZBbWLAhhw timestampmhexpmmplugm1.8.3hhba aha a9a0WbWLAhhwinner_checksummhexpmmplugm1.6.1m /b=P Ϙ Fi~)4XnlPbWLAhhw timestampmhexpmm db_connectionm 1.0.0-rc.0hhba aha a9a0FbWLAhhw timestampmhexpmmphoenixm0.13.1hhba aha a9a0"bWLAhhwdepsmhexpmmphoenixm 1.4.0-rc.3lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruejIbWLAhhw timestampmhexpmm plug_cowboym2.2.2hhba aha a9a0_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.9.1m pqm e=EFxenX".Hi6DX?WbWLAhhwinner_checksummhexpmmmimem1.0.1m Ó$v}6'qbCM$zQWbWLAhhwinner_checksummhexpmmplugm1.1.0m iHI Pjy=W%9bWLAhhwretiredmhexpmm phoenix_htmlm2.13.2wnil0bWLAhhwretiredmhexpmmplugm1.1.5wnilebWLAhhwinner_checksummhexpmm db_connectionm 1.0.0-rc.5m zhf|/uƜ*F7 abWLAhhwouter_checksummhexpmmphoenix_paramsm1.1.2m ; 왐UWo*imv_bWLAhhwinner_checksummhexpmmphoenixm 1.2.0-rc.0m ?uF&ːIbJZ} |3ҋ:bWLAhhwretiredmhexpmmphoenix_paramsm0.2.0wnilZbWLAhhwinner_checksummhexpmmphoenixm1.1.1m XH.!ޜnT-cI!_aG,0 C$bWLAhhwdepsmhexpmmplugm1.8.1lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0wfalsehmhexpmm telemetrym telemetrym~> 0.4wtruejEbWLAhhw timestampmhexpmmdecimalm1.9.0hhba aha a9a0[bWLAhhwouter_checksummhexpmmphoenixm0.16.1m 8NP ޱ7|zɚ`bWLAhhwinner_checksummhexpmm db_connectionm0.1.4m YUjMxG'*ZZ.8>JbWLAhhw timestampmhexpmmphoenixm 1.3.0-rc.1hhba aha a9a0EbWLAhhw timestampmhexpmmphoenixm0.2.1hhba aha a9a06bWLAhhwretiredmhexpmm connectionm1.0.0wnil3bWLAhhwretiredmhexpmmphoenixm1.0.2wnilBbWLAhhw timestampmhexpmmplugm1.1.7hhba aha a9a0LbWLAhhw timestampmhexpmmphoenix_paramsm0.2.1hhba aha a9a0_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.3.0m (&M"$& $bn 2q>/HbWLAhhw timestampmhexpmm connectionm1.0.4hhba aha a9a0:bWLAhhwretiredmhexpmmphoenix_paramsm1.1.2wnilbbWLAhhwdepsmhexpmmplugm0.14.0lhmhexpmmcowboymcowboym~> 1.0wtruej`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.10.0m .8CN҄W~`s}") .U<=mbWLAhhwdepsmhexpmm db_connectionm0.1.0lhmhexpmmbackoffmbackoffm~> 1.0wfalsehmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wfalsehmhexpmmsbrokermsbrokerm~> 0.7wfalsejQbWLAhhw timestampmhexpmmphoenix_pubsubm 1.0.0-rc.0hhba aha a9a0JbWLAhhw timestampmhexpmm phoenix_htmlm2.3.1hhba aha a9a03bWLAhhwretiredmhexpmmdecimalm0.2.3wnil`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.10.5m O82cηF|IUTu yQ`^E\bWLAhhwouter_checksummhexpmmpostgrexm0.12.2m YwB(N)0*$}ZbWLAhhwinner_checksummhexpmmphoenixm1.0.6m ד kK[=rEMWhKbWLAhhw timestampmhexpmm db_connectionm0.2.2hhba aha a9a0EbWLAhhw timestampmhexpmmphoenixm1.4.5hhba aha a9a0XbWLAhhwouter_checksummhexpmmjasonm1.1.0m #aJc5o( ǫh_QЄR68l0bWLAhhwretiredmhexpmmplugm1.6.2wnilZbWLAhhwouter_checksummhexpmmphoenixm0.2.6m xJlӐ,ϔMwYe`?bWLAhhwretiredmhexpmm plug_cryptom1.1.1twmessagem/Wrong default value is used for salt on encryptwreasonwRETIRED_INVALID\bWLAhhwinner_checksummhexpmmpostgrexm0.11.1m XѪVBsbETϸ7rkjJqbWLAhhwdepsmhexpmm phoenix_htmlm2.1.1lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsejbWLAhhwdepsmhexpmm plug_cowboym2.0.2lhmhexpmmcowboymcowboym~> 2.5wfalsehmhexpmmplugmplugm~> 1.7wfalsej`bWLAhhwouter_checksummhexpmm db_connectionm2.0.5m x PCw tއ JPNϟ zFYfOJbWLAhhw timestampmhexpmm phoenix_htmlm1.0.0hhba aha a9a0FbWLAhhw timestampmhexpmmpostgrexm0.5.3hhba aha a9a0WbWLAhhwouter_checksummhexpmmplugm1.4.4m _~AVƐ:݈x53,/Cm:T8bWLAhhwretiredmhexpmm phoenix_htmlm1.3.0wnil]bWLAhhwinner_checksummhexpmmjasonm 1.0.0-rc.3m RHr `D>PŕQOˣfƔ~abWLAhhwinner_checksummhexpmmphoenix_pubsubm1.0.2m Rx^ QQXR?4EbWLAhhw timestampmhexpmmphoenixm1.0.0hhba aha a9a0[bWLAhhwouter_checksummhexpmmphoenixm0.17.1m L+bP"n tߔҋۑqbWLAhhwdepsmhexpmm phoenix_htmlm2.2.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsejabWLAhhwouter_checksummhexpmmphoenix_pubsubm1.0.0m ,{?\MfVI$iqrEbWLAhhw timestampmhexpmmphoenixm1.2.4hhba aha a9a0WbWLAhhwouter_checksummhexpmmplugm0.8.4m "5 3T[ۼ~`2p2(5_FbWLAhhw timestampmhexpmmphoenixm0.11.0hhba aha a9a0BbWLAhhw timestampmhexpmmplugm1.6.4hhba aha a9a0bWLAhhwdepsmhexpmmphoenixm0.3.0lhmhexpmmex_confmex_confm0.1.2wfalsehmhexpmminflexminflexm0.2.4wfalsehmhexpmmjazzmjazzm0.1.2wfalsehmhexpmmplugmplugm0.5.1wfalsej0bWLAhhwretiredmhexpmmplugm0.9.0wnilZbWLAhhwinner_checksummhexpmmphoenixm0.7.1m }x3V%1,TY9 L~*bWLAhhwdepsmhexpmm db_connectionm 1.0.0-rc.2lhmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 1.0.0-beta.2wtruej1bWLAhhwretiredmhexpmmjasonm1.1.2wnilbWLAhhwdepsmhexpmmplugm1.10.0lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wtruejbWLAhhwdepsmhexpmmphoenixm0.2.0lhmhexpmmex_confmex_confm0.1.1wfalsehmhexpmmplugmplugm0.4.2wfalsej\bWLAhhwouter_checksummhexpmmpostgrexm0.15.1m A {ׇ lX3}dbY=XbWLAhhwouter_checksummhexpmmplugm1.10.3m z*3jDKʻ8l s98bWLAhhwretiredmhexpmm phoenix_htmlm2.3.1wnil0bWLAhhwretiredmhexpmmplugm1.3.2wnilZbWLAhhwouter_checksummhexpmmdecimalm0.2.4m eBB:QN.cGJ9@5"KbWLAhhw timestampmhexpmm db_connectionm0.2.5hhba aha a9a04bWLAhhwretiredmhexpmmpostgrexm0.8.4wnilZbWLAhhwinner_checksummhexpmmdecimalm0.2.5m @[x:`&'?ԀC{Xk4NêabWLAhhwdepsmhexpmmplugm1.1.8lhmhexpmmcowboymcowboym~> 1.0wtruej`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.12.0m KfUd=6UFֵSL+Fv"0bWLAhhwretiredmhexpmmplugm0.8.2wnilKbWLAhhw timestampmhexpmm phoenix_htmlm2.10.5hhba aha a9a0HbWLAhhw timestampmhexpmm connectionm1.0.0hhba aha a9a0ubWLAhhwdepsmhexpmm db_connectionm2.0.5lhmhexpmm connectionm connectionm~> 1.0.2wfalsejbWLAhhwdepsmhexpmm db_connectionm1.0.0lhmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 1.0wtruej5bWLAhhwretiredmhexpmmpostgrexm0.14.2wnil_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.3.1m 7i8x }6JyHhk0bWLAhhwretiredmhexpmmmimem1.2.0wnilLbWLAhhw timestampmhexpmmpostgrexm 0.14.0-rc.1hhba aha a9a03bWLAhhwdepsmhexpmmphoenix_pubsubm1.0.0jZbWLAhhwouter_checksummhexpmmphoenixm0.2.7m :=ќxלFaG5=Z;@vB9ϕpbWLAhhwdepsmhexpmmphoenix_paramsm0.2.0lhmhexpmmphoenixmphoenixm~> 1.3.3wfalsejGbWLAhhw timestampmhexpmmpostgrexm0.11.2hhba aha a9a0bWLAhhwdepsmhexpmmphoenixm0.2.2lhmhexpmmex_confmex_confm0.1.1wfalsehmhexpmminflexminflexm0.2.0wfalsehmhexpmmjazzmjazzm0.1.0wfalsehmhexpmmplugmplugm0.4.2wfalsej 1.0 or ~> 2.0wtruej[bWLAhhwinner_checksummhexpmmphoenixm0.15.0m JRlá=z^J@"!K`bWLAhhwinner_checksummhexpmm db_connectionm2.1.0m ./bĐkTM " 50CyEbWLAhhw timestampmhexpmmphoenixm1.4.7hhba aha a9a0`bWLAhhwouter_checksummhexpmm db_connectionm1.1.3m _  Pb_Q7y_*-P3bWLAhhwretiredmhexpmmdecimalm1.1.0wnilJbWLAhhw timestampmhexpmmphoenixm 1.4.0-rc.0hhba aha a9a0BbWLAhhw timestampmhexpmmplugm1.8.2hhba aha a9a0BbWLAhhw timestampmhexpmmplugm1.1.6hhba aha a9a0BbWLAhhw timestampmhexpmmplugm0.4.2hhba aha a9a0bWLAhhwdepsmhexpmmphoenixm0.16.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 0.14 or ~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsej]bWLAhhwinner_checksummhexpmm connectionm1.0.1m X)Q:4B1ӟ,RBUYy첊V\ _bWLAhhwouter_checksummhexpmmphoenixm 1.4.0-rc.3m SJjJDv.8[˭FOn>35KbWLAhhw timestampmhexpmm phoenix_htmlm2.14.1hhba aha a9a0XbWLAhhwinner_checksummhexpmmplugm1.10.3m ξv7u9jܠiN4n?Bƒ/bWLAhhwdepsmhexpmm connectionm1.0.4j[bWLAhhwinner_checksummhexpmmphoenixm0.17.1m xG.rgk.W%Y49+kY`bWLAhhwouter_checksummhexpmm db_connectionm2.0.0m jE//<ьήVv ZbWLAhhwinner_checksummhexpmmphoenixm1.5.1m eƒǐ an)4uPbWLAhhw timestampmhexpmm db_connectionm 1.0.0-rc.2hhba aha a9a0,bWLAhhwdepsmhexpmmdecimalm0.2.3jZbWLAhhwouter_checksummhexpmmphoenixm1.1.1m Iu꿣 CMMbK[bWLAhhwinner_checksummhexpmmphoenixm1.4.10m aJTUb)M"7-(#4ez$(U>bWLAhhwretiredmhexpmm db_connectionm 1.0.0-rc.5wnil\bWLAhhwinner_checksummhexpmmpostgrexm0.15.2m -4=MԊ*5V;(ZZbWLAhhwinner_checksummhexpmmphoenixm0.2.8m _3uDj*EЏ%e I}=cWbWLAhhwouter_checksummhexpmmplugm1.1.6m #d):4p]mϤ\5bWLAhhwretiredmhexpmmpostgrexm0.11.2wnilZbWLAhhwinner_checksummhexpmmphoenixm0.3.1m wg!m m~!SPbJXig0a+bWLAhhwdepsmhexpmmphoenixm0.15.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm>= 0.13.1 and < 2.0.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsej,bWLAhhwdepsmhexpmmdecimalm0.2.0jZbWLAhhwouter_checksummhexpmmphoenixm1.4.4m DEL@3aho9O3i^t7mZ;y ~EbWLAhhw timestampmhexpmmdecimalm1.5.0hhba aha a9a03bWLAhhwretiredmhexpmmphoenixm0.2.5wnil8bWLAhhwretiredmhexpmm phoenix_htmlm2.5.0wnilZbWLAhhwinner_checksummhexpmmdecimalm1.1.2m yie{-S{Q<Қ+HkQlBS \bWLAhhwouter_checksummhexpmm telemetrym0.4.1m G88.6]g`,V m)ތXH^bWLAhhwinner_checksummhexpmm plug_cowboym2.2.2m z ]盒2ĔvLȐ\bWLAhhwinner_checksummhexpmmpostgrexm0.12.1m /F:D/B~;΁ |bWLAhhwdepsmhexpmmplugm1.4.4lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsej8bWLAhhwretiredmhexpmm phoenix_htmlm2.6.1wnil[bWLAhhwouter_checksummhexpmmpostgrexm0.8.0m (Ӂnz(i$Te&H 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsej`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.10.3m C-7.zmYxCDQA"75bbWLAhhwdepsmhexpmmplugm0.12.2lhmhexpmmcowboymcowboym~> 1.0wtruejJbWLAhhw timestampmhexpmm phoenix_htmlm2.9.3hhba aha a9a0:bWLAhhwretiredmhexpmmphoenix_paramsm1.0.3wnil]bWLAhhwinner_checksummhexpmm connectionm1.0.0m ʀdtMu;L_o&Ii)ږBbWLAhhw timestampmhexpmmplugm1.4.2hhba aha a9a0_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.5.1m cSE\&m'BipgB_\bWLAhhwouter_checksummhexpmmpostgrexm0.14.3m M-vM!56FgWbWLAhhwouter_checksummhexpmmplugm1.1.4m !&=zq(H#10rVƨTmݔ%MEbWLAhhw timestampmhexpmmphoenixm1.4.8hhba aha a9a0>bWLAhhwretiredmhexpmm db_connectionm 1.0.0-rc.4wnilJbWLAhhw timestampmhexpmm phoenix_htmlm2.0.0hhba aha a9a0ZbWLAhhwinner_checksummhexpmmdecimalm1.0.1m Jćs1K܃ǒ+l abWLAhhwouter_checksummhexpmmphoenix_paramsm1.1.0m ; 5(XZRܫDVǓ V3bWLAhhwretiredmhexpmmphoenixm1.0.1wnil0bWLAhhwretiredmhexpmmplugm0.8.3wnil[bWLAhhwouter_checksummhexpmmpostgrexm0.5.2m L~ĵT9 &V\bWLAhhwouter_checksummhexpmmplugm 1.2.0-rc.0m ~Ie -JizŅkwiC iBbWLAhhw timestampmhexpmmmimem1.2.0hhba aha a9a0IbWLAhhw timestampmhexpmm plug_cowboym2.0.1hhba aha a9a0`bWLAhhwinner_checksummhexpmmpostgrexm 1.0.0-rc.0m OOnUа|k-=mS,}=KBWbWLAhhwinner_checksummhexpmmmimem0.0.1m s3ާj2Ӭeěs!>&4bWLAhhwretiredmhexpmmphoenixm1.4.11wnilKbWLAhhw timestampmhexpmmpostgrexm 1.0.0-rc.1hhba aha a9a0;bWLAhhw timestampmhexpmmsbrokerhhba ahaaa)abWLAhhwinner_checksummhexpmmphoenix_paramsm0.4.2m mREM ms$;\bWLAhhwouter_checksummhexpmmpostgrexm0.11.2m <$<ʎ sk{aJFQ$uBSbWLAhhwdepsmhexpmmphoenixm0.2.9lhmhexpmmex_confmex_confm0.1.2wfalsehmhexpmminflexminflexm0.2.0wfalsehmhexpmmjazzmjazzm0.1.1wfalsehmhexpmmplugmplugm0.5.0wfalsej>bWLAhhwretiredmhexpmm db_connectionm 1.0.0-rc.2wnil0bWLAhhwretiredmhexpmmplugm0.8.1wnil3bWLAhhwretiredmhexpmmphoenixm1.1.9wnilWbWLAhhwinner_checksummhexpmmplugm1.7.2m ׷u^ }D"7.`bWLAhhwouter_checksummhexpmm db_connectionm0.1.3m _0}4ٰH>X5!qK6bWLAhhwretiredmhexpmm connectionm1.0.2wnilZbWLAhhwouter_checksummhexpmmphoenixm1.4.2m Q8|YΥS*rUŨa,AB|F|kMKX_bWLAhhwinner_checksummhexpmmdecimalm 2.0.0-rc.0m ĸK%5^(D4Uh*/R%qׁ ϕ_>BbWLAhhw timestampmhexpmmplugm1.6.3hhba aha a9a0"bWLAhhwdepsmhexpmmphoenixm1.3.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4wfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0wfalsejBbWLAhhw timestampmhexpmmplugm1.0.5hhba aha a9a0XbWLAhhwouter_checksummhexpmmplugm0.12.0m LeEo448W }L<NJ9bWLAhhwretiredmhexpmm db_connectionm2.2.2wnilfbWLAhhwdepsmhexpmm phoenix_htmlm2.9.3lhmhexpmmplugmplugm~> 1.0wfalsejZbWLAhhwouter_checksummhexpmmphoenixm1.4.6m oI uqqYnb>:bWLAhhwretiredmhexpmmphoenix_paramsm0.2.1wnilLbWLAhhw timestampmhexpmmphoenix_pubsubm1.0.1hhba aha a9a05bWLAhhwretiredmhexpmmpostgrexm0.14.1wnilbWLAhhwdepsmhexpmmpostgrexm0.12.2lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 1.0wfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsejWbWLAhhwouter_checksummhexpmmplugm1.1.5m phq=fLKΪP4@P1cM=EbWLAhhw timestampmhexpmmphoenixm1.2.5hhba aha a9a0FbWLAhhw timestampmhexpmmphoenixm1.4.16hhba aha a9a01bWLAhhwretiredmhexpmmplugm1.10.3wnilhbWLAhhwdepsmhexpmmpostgrexm0.8.1lhmhexpmmdecimalmdecimalm~> 1.0wfalsejpbWLAhhwdepsmhexpmmphoenix_paramsm0.1.0lhmhexpmmphoenixmphoenixm~> 1.3.3wfalsej4bWLAhhwretiredmhexpmmpostgrexm0.5.5wnil1bWLAhhwretiredmhexpmmplugm0.12.1wnilBbWLAhhw timestampmhexpmmplugm1.1.4hhba aha a9a08bWLAhhwdepsmhexpmmphoenix_pubsubm 1.0.0-rc.0j,bWLAhhwdepsmhexpmmdecimalm0.2.4jbWLAhhwdepsmhexpmmpostgrexm0.15.3lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 2.1wfalsehmhexpmmdecimalmdecimalm~> 1.5wfalsehmhexpmmjasonmjasonm~> 1.0wtruejJbWLAhhw timestampmhexpmm phoenix_htmlm2.2.0hhba aha a9a0XbWLAhhwouter_checksummhexpmmplugm0.11.1m M}O໿Fk΍]/]/9ݸ3bWLAhhwretiredmhexpmmphoenixm1.4.3wnilZbWLAhhwinner_checksummhexpmmphoenixm0.9.0m cmHZ^j:x:_h[EKbWLAhhw timestampmhexpmm db_connectionm2.0.0hhba aha a9a08bWLAhhwretiredmhexpmm phoenix_htmlm2.9.0wnilbWLAhhwdepsmhexpmmplugm1.8.2lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0wfalsehmhexpmm telemetrym telemetrym~> 0.4wtruejfbWLAhhwdepsmhexpmm phoenix_htmlm2.6.2lhmhexpmmplugmplugm~> 1.0wfalsejJbWLAhhw timestampmhexpmm phoenix_htmlm2.9.1hhba aha a9a0abWLAhhwinner_checksummhexpmmphoenix_paramsm0.1.2m ,̏v!펙<:"lMzov̡mfKbWLAhhw timestampmhexpmmpostgrexm 1.0.0-rc.0hhba aha a9a0EbWLAhhw timestampmhexpmmphoenixm0.6.0hhba aha a9a0gbWLAhhwdepsmhexpmm phoenix_htmlm2.12.0lhmhexpmmplugmplugm~> 1.5wfalsejWbWLAhhwinner_checksummhexpmmplugm1.2.0m IkcJI׀:gΜ෹% Y 0bWLAhhwretiredmhexpmmplugm1.0.2wnildbWLAhhwdepsmhexpmmjasonm1.2.1lhmhexpmmdecimalmdecimalm~> 1.0wtruejGbWLAhhw timestampmhexpmmpostgrexm0.13.3hhba aha a9a0BbWLAhhw timestampmhexpmmplugm0.6.0hhba aha a9a0WbWLAhhwouter_checksummhexpmmplugm0.8.2m ɗg)&'(|VVԃ7 _bWLAhhwouter_checksummhexpmm phoenix_htmlm2.2.0m tO$w^WGwLUcՌAZbWLAhhwinner_checksummhexpmmphoenixm1.1.9m Gւ52(t,[֛A}9ytY_XbWLAhhwinner_checksummhexpmmjasonm1.1.2m =g#&LqTVM=!d`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.14.2m r?Hd0P}ٜ lwRFO=V>`bWLAhhwouter_checksummhexpmm db_connectionm0.2.2m 8.ߖc0 uMk6ZbWLAhhwouter_checksummhexpmmphoenixm1.3.2m Cn [N24z9Or|Ex۶ZbWLAhhwinner_checksummhexpmmdecimalm1.6.0m Mn]CpӔ42_Ӻp0Y bWLAhhwdepsmhexpmmphoenixm1.1.3lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsejWbWLAhhwouter_checksummhexpmmplugm1.2.5m >m?ªc`p\ѥLEubWLAhhwdepsmhexpmmplugm 1.4.0-rc.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsejabWLAhhwinner_checksummhexpmmphoenix_paramsm0.1.1m Nl̒uJ8}]õrt,mbWLAhhwdepsmhexpmmplugm1.7.2lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0wfalsejJbWLAhhw timestampmhexpmmphoenixm 1.4.0-rc.1hhba aha a9a0BbWLAhhw timestampmhexpmmplugm1.1.5hhba aha a9a03bWLAhhwretiredmhexpmmphoenixm1.5.0wnilWbWLAhhwouter_checksummhexpmmplugm1.1.7m ֵePvEb}DزVAubWLAhhwdepsmhexpmm phoenix_htmlm1.1.0lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0wfalsej4bWLAhhwretiredmhexpmmpostgrexm0.8.0wnilZbWLAhhwouter_checksummhexpmmdecimalm1.2.0m t"HD2HDM_dшZbWLAhhwouter_checksummhexpmmphoenixm0.7.1m 7R8CIY<+h Aa&G:bWLAhhwretiredmhexpmmphoenix_pubsubm1.0.1wnilabWLAhhwinner_checksummhexpmmphoenix_pubsubm1.1.2m Il0;.n[::75KM*Z>KbWLAhhw timestampmhexpmm db_connectionm1.1.0hhba aha a9a0[bWLAhhwouter_checksummhexpmmphoenixm0.13.1m Z. MeC9fԮZB_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.9.2m MI?V`1= f[0g7iqKNoWbWLAhhwouter_checksummhexpmmplugm0.4.4m 1-W3SXd#_Y:[bWLAhhwinner_checksummhexpmmpostgrexm0.8.3m rQZi񏃡Sb>L%JEmʪ(h2IbWLAhhw timestampmhexpmm plug_cowboym2.2.1hhba aha a9a0ubWLAhhwdepsmhexpmm phoenix_htmlm1.0.0lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0wfalsejXbWLAhhwinner_checksummhexpmmplugm0.10.0m D?K/Fr"E*ScˋZbWLAhhwinner_checksummhexpmmphoenixm0.7.2m HN!3rWDvQ b{wf;bWLAhhwdepsmhexpmmplugm 1.5.0-rc.1lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.1wtruehmhexpmmmimemmimem~> 1.0wfalsejNbWLAhhw registry_etagmhexpmmmimem""caa3030c95a211515c73c7c786aa1110"1bWLAhhwretiredmhexpmmjasonm1.2.0wnil7bWLAhhwretiredmhexpmm plug_cowboym2.1.2wnil,bWLAhhwdepsmhexpmmdecimalm1.6.0j3bWLAhhwretiredmhexpmmphoenixm1.2.2wnilbbWLAhhwdepsmhexpmmplugm0.12.0lhmhexpmmcowboymcowboym~> 1.0wtruej8bWLAhhwretiredmhexpmm phoenix_htmlm2.9.2wnil^bWLAhhwouter_checksummhexpmm plug_cowboym2.3.0m YZp?Vhp!u̬ۏ\~WbWLAhhwinner_checksummhexpmmplugm1.4.2m ğ :?a`j!UabWLAhhwouter_checksummhexpmmphoenix_paramsm0.2.0m l: CpsmՊ`Git>LBbWLAhhw timestampmhexpmmplugm0.5.1hhba aha a9a0,bWLAhhwdepsmhexpmmdecimalm0.1.2jbWLAhhwdepsmhexpmmphoenixm1.1.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsej5bWLAhhwretiredmhexpmmpostgrexm0.14.0wnil0bWLAhhwretiredmhexpmmplugm1.8.2wnil`bWLAhhwinner_checksummhexpmm db_connectionm0.2.2m v I<^Ο+*(mܽ4cbWLAhhwinner_checksummhexpmm phoenix_htmlm 2.7.0-devm <|nKx;&y-0GYabWLAhhwouter_checksummhexpmmphoenix_paramsm0.1.2m 1R? ŲG)fqm!zbEbWLAhhw timestampmhexpmmphoenixm1.4.9hhba aha a9a0bWLAhhwdepsmhexpmmplugm1.3.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsej`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.14.2m X%] |/l\ғpE/3bWLAhhwretiredmhexpmmphoenixm0.6.2wnilhbWLAhhwdepsmhexpmmpostgrexm0.8.3lhmhexpmmdecimalmdecimalm~> 1.0wfalsej`bWLAhhwinner_checksummhexpmm db_connectionm0.1.8m 0}Ա_IU{g]qu>]bWLAhhwdepsmhexpmmplugm1.8.0lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0wfalsehmhexpmm telemetrym telemetrym~> 0.4wtruejbWLAhhwdepsmhexpmmplugm1.6.1lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4wtruehmhexpmmmimemmimem~> 1.0wfalsej0bWLAhhwdepsmhexpmm plug_cryptom1.1.0jWbWLAhhwouter_checksummhexpmmplugm0.7.0m \ꬽVSOy#M96s-vyXgabWLAhhwouter_checksummhexpmmpostgrexm 0.13.0-rc.0m &[ضY H5, "\bWLAhhwouter_checksummhexpmm telemetrym0.3.0m c}11b!1 Z6h!/r9bWLAhhwretiredmhexpmm db_connectionm0.1.4wnil3bWLAhhwretiredmhexpmmphoenixm1.0.3wnilBbWLAhhw timestampmhexpmmplugm1.3.3hhba aha a9a03bWLAhhwretiredmhexpmmdecimalm0.1.1wnilBbWLAhhw timestampmhexpmmmimem1.1.0hhba aha a9a0XbWLAhhwouter_checksummhexpmmplugm0.11.2m 0Hи#ƢJ-Pr4WbWLAhhwinner_checksummhexpmmplugm1.2.6m a6ՀEʫz:( :$"JWbWLAhhwouter_checksummhexpmmplugm0.5.3m E}A[; 1.0wfalsehmhexpmm db_connectionm db_connectionm ~> 1.0-rc.4wfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsejbWLAhhwdepsmhexpmm db_connectionm0.1.4lhmhexpmmbackoffmbackoffm~> 1.0wfalsehmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 0.7wtruejabWLAhhwinner_checksummhexpmmphoenix_paramsm0.1.0m Ka$Nt,68M2r[bWLAhhwouter_checksummhexpmmphoenixm0.14.0m حTx&y/ݝ988bWLAhhwretiredmhexpmm phoenix_htmlm1.2.1wnilbWLAhhwdepsmhexpmmphoenixm0.2.5lhmhexpmmex_confmex_confm0.1.1wfalsehmhexpmminflexminflexm0.2.0wfalsehmhexpmmplugmplugm0.4.4wfalsej_bWLAhhwinner_checksummhexpmmphoenixm 1.3.0-rc.3m 7 9 5r}F!Yj 0D0bWLAhhwdepsmhexpmm plug_cryptom1.1.2j8bWLAhhw timestampmhexpmmplughhba aha a9a0[bWLAhhwouter_checksummhexpmmphoenixm1.4.12m X3mw1*=o4%F0[bWLAhhwouter_checksummhexpmmpostgrexm0.9.1m OEG4=zQsW0T%12HYZhbWLAhhwdepsmhexpmmpostgrexm0.7.0lhmhexpmmdecimalmdecimalm~> 1.0wfalsejKbWLAhhw timestampmhexpmm db_connectionm0.1.0hhba aha a9a0[bWLAhhwinner_checksummhexpmmphoenixm1.4.14m ڭh\@WF>ˇj슶ޢBL$AN8WbWLAhhwinner_checksummhexpmmplugm1.7.1m er.t%O6N >XbWLAhhwouter_checksummhexpmmplugm0.13.0m +oĦ%W{US1o":gL_abWLAhhwinner_checksummhexpmmphoenix_paramsm0.2.1m ftӓ~_2G0WY_ 9a _bWLAhhwouter_checksummhexpmm phoenix_htmlm1.2.1m N ĪI,"sin ]}[bWLAhhwouter_checksummhexpmmpostgrexm0.8.4m .X%(Xf$^bWLAhhwinner_checksummhexpmm plug_cowboym2.1.3m 8>k߂aa2D\ܶ3BbWLAhhw timestampmhexpmmplugm0.8.0hhba aha a9a0dbWLAhhwdepsmhexpmmphoenixm1.4.17lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsejWbWLAhhwouter_checksummhexpmmplugm1.8.3m K8-ĄI**w 3N'43bWLAhhwretiredmhexpmmphoenixm1.1.8wnil8bWLAhhwretiredmhexpmm phoenix_htmlm1.4.0wnilgbWLAhhwdepsmhexpmm phoenix_htmlm2.13.2lhmhexpmmplugmplugm~> 1.5wfalsejZbWLAhhwinner_checksummhexpmmphoenixm1.1.8m %J&RG ʩB).A-6T\_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.1.1m *Zbu6n4"0A(uTW70BbWLAhhw timestampmhexpmmphoenix_pubsubhhba aha a9a0ubWLAhhwdepsmhexpmm phoenix_htmlm 2.4.0-devlhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsej8bWLAhhwretiredmhexpmm phoenix_htmlm2.9.3wnil_bWLAhhwouter_checksummhexpmmdecimalm 2.0.0-rc.0m %^,>8W$d. 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruejCbWLAhhw timestampmhexpmmjasonm1.2.2hhba aha a9a0IbWLAhhw timestampmhexpmm plug_cryptom1.0.0hhba aha a9a0[bWLAhhwinner_checksummhexpmmphoenixm1.4.12m oZ+6I\VA2dc~w39]lbWLAhhwdepsmhexpmmpostgrexm 0.13.0-rc.0lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 1.1wfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsejLbWLAhhw timestampmhexpmmphoenix_paramsm1.1.2hhba aha a9a0EbWLAhhw timestampmhexpmmphoenixm1.5.2hhba aha a9a08bWLAhhwretiredmhexpmm phoenix_htmlm2.1.1wnilCbWLAhhw timestampmhexpmmplugm0.11.0hhba aha a9a0`bWLAhhwinner_checksummhexpmm db_connectionm2.0.3m Co,gQČy99+ 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej3bWLAhhwretiredmhexpmmphoenixm0.2.2wnilWbWLAhhwinner_checksummhexpmmplugm1.0.3m LQp\!Svx$3bWLAhhwretiredmhexpmmdecimalm0.2.2wnil`bWLAhhwinner_checksummhexpmm db_connectionm2.0.5m ݲga+}&MӐgBm\$I2ZbWLAhhwouter_checksummhexpmmphoenixm0.6.1m ύK lt~/S3ۗhN1b_bWLAhhwouter_checksummhexpmmphoenixm 1.4.0-rc.0m a0>Yc$ph-6`g^T[bWLAhhwouter_checksummhexpmmphoenixm1.4.15m dv#f mW7JܻiI5bWLAhhwretiredmhexpmmpostgrexm0.15.0wnilbWLAhhwdepsmhexpmm db_connectionm0.2.2lhmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 0.7wtruejLbWLAhhw timestampmhexpmmphoenix_pubsubm2.0.0hhba aha a9a03bWLAhhwretiredmhexpmmphoenixm1.1.7wnilabWLAhhwdepsmhexpmmplugm1.0.0lhmhexpmmcowboymcowboym~> 1.0wtruej3bWLAhhwretiredmhexpmmphoenixm1.5.1wnilWbWLAhhwinner_checksummhexpmmplugm1.3.2m غ.,}i!YdU Uac-ZbWLAhhwouter_checksummhexpmmdecimalm0.1.1m ZA57/P,#n^}[WBxWbWLAhhwouter_checksummhexpmmplugm1.2.6m P;hQ>y4Ԇ-.rQL$"4EbWLAhhw timestampmhexpmmphoenixm1.0.5hhba aha a9a0FbWLAhhw timestampmhexpmmphoenixm0.16.1hhba aha a9a0WbWLAhhwouter_checksummhexpmmmimem1.3.1m lvj  1Lc KζE8duEbWLAhhw timestampmhexpmmdecimalm2.0.0hhba aha a9a0WbWLAhhwouter_checksummhexpmmplugm0.5.0m CQ rU!w*q LkSצobWLAhhwversionsmhexpmmpostgrexl,m0.4.2m0.5.0m0.5.1m0.5.2m0.5.3m0.5.4m0.5.5m0.6.0m0.7.0m0.8.0m0.8.1m0.8.2m0.8.3m0.8.4m0.9.0m0.9.1m0.10.0m0.11.0m0.11.1m0.11.2m0.12.0m0.12.1m0.12.2m 0.13.0-rc.0m0.13.0m0.13.1m0.13.2m0.13.3m0.13.4m0.13.5m 0.14.0-rc.0m 0.14.0-rc.1m0.14.0m0.14.1m0.14.2m0.14.3m0.15.0m0.15.1m0.15.2m0.15.3m0.15.4m0.15.5m 1.0.0-rc.0m 1.0.0-rc.1jabWLAhhwinner_checksummhexpmmphoenix_paramsm1.1.1m AY`E_L x 5 WbWLAhhwinner_checksummhexpmmplugm0.4.3m }.sD= 1.7.0wfalsehmhexpmmphoenixmphoenixm>= 1.3.3wfalsejEbWLAhhw timestampmhexpmmdecimalm0.2.4hhba aha a9a0CbWLAhhw timestampmhexpmmplugm0.11.1hhba aha a9a0EbWLAhhw timestampmhexpmmphoenixm1.2.0hhba aha a9a0JbWLAhhw timestampmhexpmm phoenix_htmlm2.5.1hhba aha a9a0`bWLAhhwinner_checksummhexpmm db_connectionm1.1.1m F[\6uɠR`2L90bWLAhhwretiredmhexpmmplugm1.6.1wnilEbWLAhhw timestampmhexpmmphoenixm1.3.0hhba aha a9a0IbWLAhhw timestampmhexpmm plug_cowboym2.0.0hhba aha a9a03bWLAhhwretiredmhexpmmphoenixm0.2.4wnilXbWLAhhwinner_checksummhexpmmplugm0.12.2m Af+ ^@ hpܥ{$iz\bWLAhhwouter_checksummhexpmmplugm 1.5.0-rc.2m 8TUHlH̖"4jaL_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.1.2m {*cebWLAhhwinner_checksummhexpmm db_connectionm 1.0.0-rc.1m Mۊn@Zқ@pI*"ϲPabWLAhhwouter_checksummhexpmmphoenix_pubsubm2.0.0m -O&wKPYuCSPqBbWLAhhw timestampmhexpmmplugm0.8.2hhba aha a9a0bWLAhhwdepsmhexpmm plug_cowboym2.1.2lhmhexpmmcowboymcowboym~> 2.5wfalsehmhexpmmplugmplugm~> 1.7wfalsej_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.7.0m . D4 .C jYg|Yh5T8ՒB XbWLAhhwinner_checksummhexpmmplugm0.13.1m GA2wY!^asoK/}bWLAhhwdepsmhexpmmphoenixm 1.5.0-rc.0lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.1wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejbWLAhhwdepsmhexpmmphoenixm1.0.4lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejbWLAhhwdepsmhexpmmplugm1.6.2lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4wtruehmhexpmmmimemmimem~> 1.0wfalsejabWLAhhwdepsmhexpmmplugm1.1.4lhmhexpmmcowboymcowboym~> 1.0wtruejZbWLAhhwinner_checksummhexpmmphoenixm1.2.1m mŒ$VV5E6M^^AE@uUEh0bWLAhhwretiredmhexpmmplugm1.5.1wnilWbWLAhhwouter_checksummhexpmmplugm1.4.0m p>PPSm mɆEubWLAhhwdepsmhexpmm phoenix_htmlm1.3.0lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0wfalsejCbWLAhhw timestampmhexpmmplugm0.10.0hhba aha a9a03bWLAhhwretiredmhexpmmphoenixm1.1.3wnilWbWLAhhwinner_checksummhexpmmmimem1.3.1m 01u \w=R=sxM<abWLAhhwdepsmhexpmmplugm0.9.0lhmhexpmmcowboymcowboym~> 1.0wtruej3bWLAhhwretiredmhexpmmphoenixm1.2.5wnil[bWLAhhwouter_checksummhexpmmphoenixm1.4.16m l2S'7A<u%Wm\$ZbWLAhhwinner_checksummhexpmmphoenixm0.2.5m ",Ҍ5 L<ؼ߬38bWLAhhwretiredmhexpmmphoenixm 1.4.0-rc.0wnil9bWLAhhwretiredmhexpmm phoenix_htmlm2.10.4wnilubWLAhhwdepsmhexpmm db_connectionm2.2.2lhmhexpmm connectionm connectionm~> 1.0.2wfalsej 1.5wfalsejGbWLAhhw timestampmhexpmmpostgrexm0.10.0hhba aha a9a01bWLAhhwretiredmhexpmmplugm0.11.1wnilBbWLAhhw timestampmhexpmmplugm1.6.1hhba aha a9a0KbWLAhhw timestampmhexpmm phoenix_htmlm2.10.4hhba aha a9a0qbWLAhhwdepsmhexpmm phoenix_htmlm2.3.1lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsejabWLAhhwdepsmhexpmmplugm1.0.3lhmhexpmmcowboymcowboym~> 1.0wtruejFbWLAhhw timestampmhexpmmphoenixm1.4.15hhba aha a9a0ZbWLAhhwinner_checksummhexpmmphoenixm1.2.5m Z_"Y^#*evH#bWLAhhwdepsmhexpmm db_connectionm0.1.8lhmhexpmmbackoffmbackoffm1.1.1wfalsehmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 0.7wtruej^bWLAhhwinner_checksummhexpmm plug_cryptom1.0.0m 4?$b')ԥ- }M3bWLAhhwretiredmhexpmmphoenixm1.5.2wnil`bWLAhhwinner_checksummhexpmm db_connectionm1.1.0m /{XM َV z ڶ&3TͳcbWLAhhwouter_checksummhexpmm phoenix_htmlm 2.4.0-devm 0a/@/։ꠦ=;AjabWLAhhwinner_checksummhexpmmphoenix_paramsm0.4.1m YFG=1}7ӷtXHS8bWLAhhwretiredmhexpmm phoenix_htmlm1.0.0wnilZbWLAhhwinner_checksummhexpmmdecimalm1.8.0m F. _ ţB~5HZes4.abWLAhhwouter_checksummhexpmmphoenix_paramsm0.2.1m k<#jwDIdNgFO70bWLAhhwretiredmhexpmmplugm1.7.1wnilWbWLAhhwinner_checksummhexpmmplugm1.1.7m @MMerFL/YH"[4bWLAhhwretiredmhexpmmpostgrexm0.4.2wnil\bWLAhhwinner_checksummhexpmmplugm 1.5.0-rc.1m r>%f-k,\a$B/9&GbWLAhhw timestampmhexpmm telemetrym0.4.1hhba aha a9a0BbWLAhhw timestampmhexpmmplugm1.1.2hhba aha a9a03bWLAhhwretiredmhexpmmphoenixm0.3.0wnil,bWLAhhwdepsmhexpmmdecimalm1.1.2j6bWLAhhwretiredmhexpmm connectionm1.0.1wnilbWLAhhwdepsmhexpmm db_connectionm0.2.3lhmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 0.7wtruej0bWLAhhwretiredmhexpmmplugm1.0.3wnilJbWLAhhw timestampmhexpmm phoenix_htmlm1.1.0hhba aha a9a0qbWLAhhwdepsmhexpmm phoenix_htmlm2.3.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsej\bWLAhhwinner_checksummhexpmmplugm 1.4.0-rc.0m Solm$a63;3r~[ bWLAhhwdepsmhexpmmpostgrexm0.13.1lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 1.1wfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsejEbWLAhhw timestampmhexpmmphoenixm1.2.1hhba aha a9a0ebWLAhhwouter_checksummhexpmm db_connectionm 1.0.0-rc.3m )Xω+IOW10KT@%2B5bWLAhhwretiredmhexpmmpostgrexm0.13.1wnil>bWLAhhwretiredmhexpmm db_connectionm 2.0.0-rc.0wnil9bWLAhhwretiredmhexpmm db_connectionm1.0.0wnil^bWLAhhwouter_checksummhexpmm plug_cryptom1.1.1m &Mo A iabWLAhhwinner_checksummhexpmmphoenix_paramsm1.1.2m Tp}%Շz_BF!:ȷry0bWLAhhwretiredmhexpmmplugm1.1.9wnil1bWLAhhwretiredmhexpmmjasonm1.1.0wnilbWLAhhwdepsmhexpmm db_connectionm0.2.1lhmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 0.7wtruejBbWLAhhw timestampmhexpmmmimem1.3.0hhba aha a9a0^bWLAhhwouter_checksummhexpmm plug_cowboym2.2.0m NrrlgzyGMR;(Ѯ\}[bWLAhhwdepsmhexpmmphoenixm0.3.1lhmhexpmmex_confmex_confm0.1.3wfalsehmhexpmminflexminflexm0.2.4wfalsehmhexpmmjazzmjazzm0.1.2wfalsehmhexpmmplugmplugm0.5.1wfalsejFbWLAhhw timestampmhexpmmphoenixm0.10.0hhba aha a9a05bWLAhhwretiredmhexpmmpostgrexm0.15.5wnilbWLAhhwdepsmhexpmmphoenixm1.4.4lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruej\bWLAhhwinner_checksummhexpmmpostgrexm0.12.2m ׄHx3\8)c%&X8bWLAhhwretiredmhexpmmphoenixm 1.3.0-rc.2wnil:bWLAhhwretiredmhexpmmpostgrexm 0.14.0-rc.0wnilEbWLAhhw timestampmhexpmmphoenixm0.5.0hhba aha a9a0bbWLAhhwdepsmhexpmmplugm0.11.3lhmhexpmmcowboymcowboym~> 1.0wtruejEbWLAhhw timestampmhexpmmdecimalm1.2.0hhba aha a9a0ZbWLAhhwinner_checksummhexpmmphoenixm0.6.2m {*NP<`]Ksƫj4HbWLAhhw timestampmhexpmmjasonm 1.0.0-rc.1hhba aha a9a0FbWLAhhw timestampmhexpmmphoenixm0.16.0hhba aha a9a03bWLAhhwretiredmhexpmmphoenixm1.1.5wnil4bWLAhhwretiredmhexpmmphoenixm1.4.14wnil`bWLAhhwouter_checksummhexpmmpostgrexm 1.0.0-rc.0m Tm؆!8"s9ĒW݂ZIhlZbWLAhhwouter_checksummhexpmmphoenixm1.1.8m n^YF%{(l<: ]'viDbWLAhhwdepsmhexpmm db_connectionm0.2.5lhmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 0.7wtruej.bWLAhhwdepsmhexpmm telemetrym0.3.0jEbWLAhhw timestampmhexpmmdecimalm1.1.1hhba aha a9a0WbWLAhhwinner_checksummhexpmmplugm1.3.4m :8?YERI44GHw3bWLAhhwretiredmhexpmmdecimalm0.2.4wnilBbWLAhhw timestampmhexpmmplugm1.7.0hhba aha a9a0bWLAhhwdepsmhexpmmplugm1.6.3lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4wtruehmhexpmmmimemmimem~> 1.0wfalsejbWLAhhwdepsmhexpmmplugm1.4.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsej4bWLAhhwretiredmhexpmmphoenixm0.11.0wnilWbWLAhhwouter_checksummhexpmmplugm1.3.5m X̡(9D/q*tmRbWLAhhwdepsmhexpmmphoenixm1.1.7lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsej3bWLAhhwretiredmhexpmmdecimalm1.0.1wnilJbWLAhhw timestampmhexpmmphoenixm 1.3.0-rc.3hhba aha a9a0_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.1.0m `+&}C!kI˯E ,bWLAhhwdepsmhexpmmdecimalm1.9.0jcbWLAhhwdepsmhexpmmphoenixm1.4.8lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsejPbWLAhhw timestampmhexpmm db_connectionm 1.0.0-rc.5hhba aha a9a0EbWLAhhw timestampmhexpmmdecimalm1.4.1hhba aha a9a0=bWLAhhw timestampmhexpmm telemetryhhba aha a9a0WbWLAhhwouter_checksummhexpmmplugm1.3.2m jhZ84 cM^~KbWLAhhw timestampmhexpmm phoenix_htmlm2.14.0hhba aha a9a0`bWLAhhwinner_checksummhexpmm db_connectionm0.1.3m q&A>DgVjn" f>0bWLAhhwretiredmhexpmmplugm1.3.4wnilZbWLAhhwinner_checksummhexpmmphoenixm1.4.0m Vs_>;1IԴ6fb{_bWLAhhwouter_checksummhexpmmphoenixm 1.3.0-rc.2m ap RŸo ,nE4ns,ND8bWLAhhwretiredmhexpmmphoenixm 1.2.0-rc.1wnil0bWLAhhwretiredmhexpmmplugm1.1.6wnilBbWLAhhw timestampmhexpmmplugm1.2.6hhba aha a9a0ZbWLAhhwouter_checksummhexpmmdecimalm1.1.2m zmM8׸8P"H|h=3Q㦑_bWLAhhwouter_checksummhexpmm phoenix_htmlm1.1.0m tD/}"/Gv"{>_vP]1abWLAhhwinner_checksummhexpmmphoenix_paramsm1.0.1m S%*5`-a ,N6$kpabWLAhhwouter_checksummhexpmmphoenix_pubsubm1.0.2m o6L]kӨ4lVˮft8bWLAhhwretiredmhexpmm phoenix_htmlm2.0.0wnil,bWLAhhwdepsmhexpmmdecimalm1.3.0jqbWLAhhwdepsmhexpmm phoenix_htmlm2.0.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsej_bWLAhhwinner_checksummhexpmmphoenixm 1.2.0-rc.1m /a'c՚PQ b?')bWLAhhwdepsmhexpmmplugm0.5.1jWbWLAhhwouter_checksummhexpmmplugm1.3.3m C4t~ws/[4 };WbWLAhhwouter_checksummhexpmmplugm1.2.1m = ;BoƳa+]>FZbWLAhhwinner_checksummhexpmmphoenixm1.3.4m ^U#:{mဿ,TS#I<*%=EbWLAhhw timestampmhexpmmphoenixm1.3.3hhba aha a9a03bWLAhhwretiredmhexpmmphoenixm0.2.9wnilXbWLAhhwouter_checksummhexpmmplugm1.10.4m #?=.Vaeh`w{g1H-NwZbWLAhhwouter_checksummhexpmmdecimalm1.3.0m B 0 0.2.3wfalsejIbWLAhhw timestampmhexpmm plug_cowboym2.0.2hhba aha a9a00bWLAhhwretiredmhexpmmplugm0.6.0wnilbWLAhhwdepsmhexpmmphoenixm0.7.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmpoisonmpoisonm~> 1.2wfalsej9bWLAhhwretiredmhexpmm db_connectionm1.1.2wnilWbWLAhhwouter_checksummhexpmmplugm1.3.6m ?/ ߶? V.\od/uR-}zbWLAhhwretiredmhexpmmpostgrexm 1.0.0-rc.0twmessagemSuperseded by v0.13.0-rc.0wreasonwRETIRED_INVALID9bWLAhhwretiredmhexpmm db_connectionm1.1.3wnilXbWLAhhwinner_checksummhexpmmjasonm1.1.0m 4/thՆS\[ WRO[bWLAhhwouter_checksummhexpmmpostgrexm0.7.0m p/E-F.p?~) 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruejibWLAhhwversionsmhexpmm telemetrylm0.1.0m0.2.0m0.3.0m0.4.0m0.4.1m0.4.2jbbWLAhhwdepsmhexpmmplugm0.11.1lhmhexpmmcowboymcowboym~> 1.0wtruejWbWLAhhwouter_checksummhexpmmplugm1.9.0m ** d4h.I?]MjƼ_#PLbWLAhhw timestampmhexpmmphoenix_pubsubm1.1.1hhba aha a9a08bWLAhhwretiredmhexpmm phoenix_htmlm2.8.0wnilEbWLAhhw timestampmhexpmmphoenixm1.4.0hhba aha a9a00bWLAhhwretiredmhexpmmplugm0.7.0wnilbWLAhhwdepsmhexpmm db_connectionm 1.0.0-rc.3lhmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 1.0.0-beta.3wtruejEbWLAhhw timestampmhexpmmphoenixm0.1.0hhba aha a9a0KbWLAhhw timestampmhexpmm db_connectionm1.1.3hhba aha a9a0ZbWLAhhwouter_checksummhexpmmphoenixm1.0.5m иdm 0˨YOvo//7׬)e1 ZbWLAhhwinner_checksummhexpmmphoenixm1.4.1m c(e|e|bLB#GbWLAhhw timestampmhexpmmpostgrexm0.13.0hhba aha a9a0JbWLAhhw timestampmhexpmmphoenixm 1.3.0-rc.0hhba aha a9a0ebWLAhhwouter_checksummhexpmm db_connectionm 1.0.0-rc.4m b|M,7LHXpS%ҫШ!bWLAhhwdepsmhexpmmphoenixm0.2.1lhmhexpmmex_confmex_confm0.1.1wfalsehmhexpmminflexminflexm0.2.0wfalsehmhexpmmplugmplugm0.4.2wfalsej3bWLAhhwdepsmhexpmmphoenix_pubsubm0.1.0jBbWLAhhw timestampmhexpmmplugm1.4.5hhba aha a9a0GbWLAhhw timestampmhexpmmpostgrexm0.14.1hhba aha a9a0WbWLAhhwinner_checksummhexpmmplugm1.4.4m 'Tvb',5 ӾQG5NFtĴabWLAhhwouter_checksummhexpmmphoenix_paramsm1.0.1m o!&pPk7hܓ'F&O͠W6GbWLAhhw timestampmhexpmm telemetrym0.4.0hhba aha a9a0abWLAhhwinner_checksummhexpmmphoenix_pubsubm1.1.0m ^%/dcfa-PSYD.bWLAhhwdepsmhexpmm telemetrym0.4.0j:bWLAhhwretiredmhexpmmphoenix_paramsm0.3.0wnilRbWLAhhw registry_etagmhexpmmpostgrexm""35d9bc39d21b39e549bb40de95a08cda"9bWLAhhwretiredmhexpmm phoenix_htmlm2.10.0wnilWbWLAhhwinner_checksummhexpmmplugm1.1.1m UWlٹUh e<>h|,{\{GbWLAhhw timestampmhexpmm telemetrym0.3.0hhba aha a9a09bWLAhhw timestampmhexpmmjasonhhba aha a9a0JbWLAhhw timestampmhexpmm phoenix_htmlm2.0.1hhba aha a9a0`bWLAhhwouter_checksummhexpmm db_connectionm1.1.0m 4F7c@~ױ )KUa|LbWLAhhw timestampmhexpmmphoenix_paramsm1.1.1hhba aha a9a09bWLAhhwretiredmhexpmm db_connectionm0.2.2wnilZbWLAhhwouter_checksummhexpmmphoenixm0.9.0m ' =/ˍvVފ(tOP#jicbWLAhhwdepsmhexpmmplugm0.8.0lhmhexpmmcowboymcowboym~> 1.0.0wtruejKbWLAhhw timestampmhexpmm db_connectionm2.0.5hhba aha a9a03bWLAhhwretiredmhexpmmdecimalm1.3.0wnil0bWLAhhwretiredmhexpmmplugm1.3.0wnil4bWLAhhwretiredmhexpmmphoenixm0.13.1wnil3bWLAhhwretiredmhexpmmphoenixm1.3.0wnil,bWLAhhwdepsmhexpmmdecimalm1.5.0jWbWLAhhwouter_checksummhexpmmplugm1.2.4m @<*< 0!Ǝ +D(;P}[bWLAhhwouter_checksummhexpmmphoenixm0.15.0m ijLvȮB.At"u >V!LbWLAhhw timestampmhexpmmphoenix_paramsm0.4.2hhba aha a9a0abWLAhhwinner_checksummhexpmmphoenix_paramsm0.4.0m UtM;uX`zQ|Y Ԑ3bWLAhhwretiredmhexpmmphoenixm0.3.1wnilbWLAhhwdepsmhexpmm db_connectionm1.1.2lhmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 1.0wtruej3bWLAhhwretiredmhexpmmdecimalm1.1.1wnil5bWLAhhwretiredmhexpmmpostgrexm0.13.2wnilCbWLAhhw timestampmhexpmmplugm0.12.0hhba aha a9a0bWLAhhwdepsmhexpmmplugm1.10.3lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej3bWLAhhwretiredmhexpmmphoenixm1.4.5wnil^bWLAhhwinner_checksummhexpmm plug_cowboym2.1.1m (ۥUbuwK3FbWLAhhw timestampmhexpmmpostgrexm0.5.4hhba aha a9a0EbWLAhhw timestampmhexpmmphoenixm0.6.2hhba aha a9a0\bWLAhhwouter_checksummhexpmmpostgrexm0.13.5m a3y6=p. &~bWLAhhwdepsmhexpmm db_connectionm0.2.4lhmhexpmm connectionm connectionm~> 1.0.2wfalsehmhexpmmpoolboympoolboym~> 1.5wtruehmhexpmmsbrokermsbrokerm~> 0.7wtruej3bWLAhhwretiredmhexpmmphoenixm1.2.0wnil:bWLAhhwretiredmhexpmmpostgrexm 0.13.0-rc.0wnil_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.8.0m wu`֫{%ɯ)dNH"Θ EbWLAhhw timestampmhexpmmphoenixm0.4.0hhba aha a9a0JbWLAhhw timestampmhexpmm phoenix_htmlm2.6.0hhba aha a9a0qbWLAhhwdepsmhexpmm phoenix_htmlm2.0.1lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsej:bWLAhhwretiredmhexpmmphoenix_paramsm0.1.2wnilabWLAhhwdepsmhexpmmplugm1.1.9lhmhexpmmcowboymcowboym~> 1.0wtruejbWLAhhwdepsmhexpmmphoenixm0.2.11lhmhexpmmex_confmex_confm0.1.2wfalsehmhexpmminflexminflexm0.2.4wfalsehmhexpmmjazzmjazzm0.1.2wfalsehmhexpmmplugmplugm0.5.1wfalsej0bWLAhhwdepsmhexpmm plug_cryptom1.1.1j\bWLAhhwouter_checksummhexpmmpostgrexm0.11.0m mD}$ֈ j*mJ~\Cg_ZbWLAhhwouter_checksummhexpmmphoenixm1.3.4m I&`L^F/YYi6zĠS-WbWLAhhwouter_checksummhexpmmplugm0.5.2m }8gD'CviD@ӋVS)bWLAhhwdepsmhexpmmmimem1.2.0jLbWLAhhw timestampmhexpmmphoenix_paramsm0.1.0hhba aha a9a04bWLAhhwretiredmhexpmmphoenixm0.17.1wnil0bWLAhhwretiredmhexpmmplugm1.4.5wnilabWLAhhwinner_checksummhexpmmphoenix_pubsubm2.0.0m vq{h읒H֍G9_`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.11.1m wRnsd7RX͡2f&~9bWLAhhwretiredmhexpmm db_connectionm0.1.1wnilubWLAhhwdepsmhexpmm phoenix_htmlm1.2.0lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0wfalsejBbWLAhhw timestampmhexpmmplugm1.2.0hhba aha a9a09bWLAhhwretiredmhexpmm db_connectionm0.1.8wnilKbWLAhhw timestampmhexpmm db_connectionm2.2.1hhba aha a9a0EbWLAhhw timestampmhexpmmphoenixm1.1.7hhba aha a9a0EbWLAhhw timestampmhexpmmdecimalm1.3.1hhba aha a9a0GbWLAhhw timestampmhexpmmpostgrexm0.14.0hhba aha a9a05bWLAhhwretiredmhexpmmpostgrexm0.12.2wnilEbWLAhhw timestampmhexpmmphoenixm1.1.1hhba aha a9a03bWLAhhwretiredmhexpmmphoenixm1.4.6wnilWbWLAhhwinner_checksummhexpmmplugm1.0.1m N=XB.@9l t HbWLAhhw timestampmhexpmm connectionm1.0.2hhba aha a9a08bWLAhhwretiredmhexpmmdecimalm 1.9.0-rc.0wnilZbWLAhhwinner_checksummhexpmmphoenixm1.4.4m ],4C 6Wq9wrh2N1bWLAhhwretiredmhexpmmjasonm1.2.1wnil9bWLAhhwretiredmhexpmm phoenix_htmlm2.14.1wnilgbWLAhhwdepsmhexpmm phoenix_htmlm2.10.1lhmhexpmmplugmplugm~> 1.0wfalsej0bWLAhhwretiredmhexpmmplugm1.4.1wnilbbWLAhhwouter_checksummhexpmm connectionm 1.0.0-rc.1m Z{ax:`Np!rjHbbWLAhhwdepsmhexpmmplugm0.10.0lhmhexpmmcowboymcowboym~> 1.0wtruej4bWLAhhwretiredmhexpmmphoenixm0.12.0wnilZbWLAhhwinner_checksummhexpmmphoenixm0.2.6m '1C &Sno1lmbu Q^IvbWLAhhwretiredmhexpmm plug_cowboym2.2.1twmessagemBroken telemetry supportwreasonwRETIRED_INVALIDZbWLAhhwouter_checksummhexpmmphoenixm0.7.0m $y|6; w'Px`j1TEXbWLAhhwinner_checksummhexpmmjasonm1.2.0m 4-$!-?]HCv 8yY8HbWLAhhwdepsmhexpmmplugm1.10.4lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejJbWLAhhw timestampmhexpmm phoenix_htmlm2.3.0hhba aha a9a0EbWLAhhw timestampmhexpmmphoenixm1.5.0hhba aha a9a0.bWLAhhwdepsmhexpmm telemetrym0.2.0j[bWLAhhwouter_checksummhexpmmpostgrexm0.5.5m =(s֚5J_һHx?nXԬ4! cbWLAhhwdepsmhexpmmplugm0.8.1lhmhexpmmcowboymcowboym~> 1.0.0wtruej?bWLAhhwretiredmhexpmmphoenix_pubsubm 1.0.0-rc.0wnil[bWLAhhwinner_checksummhexpmmphoenixm0.2.10m !/I7תck[%NS 5bWLAhhwretiredmhexpmmpostgrexm0.14.3wnilabWLAhhwinner_checksummhexpmmphoenix_pubsubm1.0.0m "FY'x@ʇa A ,WJbWLAhhw timestampmhexpmm phoenix_htmlm2.9.0hhba aha a9a09bWLAhhwretiredmhexpmm phoenix_htmlm2.11.1wnilfbWLAhhwdepsmhexpmm phoenix_htmlm2.9.0lhmhexpmmplugmplugm~> 1.0wfalsej7bWLAhhwretiredmhexpmm plug_cowboym2.1.0wnilXbWLAhhwouter_checksummhexpmmjasonm1.2.2m (򟞮aðvhя!yZbWLAhhwouter_checksummhexpmmphoenixm1.1.9m 1gxف 1.0wtruehmhexpmmpoisonmpoisonm~> 1.2wfalsej)bWLAhhwdepsmhexpmmplugm0.4.3jGbWLAhhw timestampmhexpmmpostgrexm0.12.0hhba aha a9a0ebWLAhhwinner_checksummhexpmm db_connectionm 1.0.0-rc.4m rQkނA+r1hr!ߎ\BbWLAhhw timestampmhexpmmplugm0.5.2hhba aha a9a0gbWLAhhwdepsmhexpmm phoenix_htmlm2.13.1lhmhexpmmplugmplugm~> 1.5wfalsejZbWLAhhwouter_checksummhexpmmdecimalm2.0.0m 4fnUި}7b2j# DwbWLAhhwdepsmhexpmmplugm1.4.1lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsej0bWLAhhwretiredmhexpmmplugm1.3.3wnilWbWLAhhwouter_checksummhexpmmplugm1.4.5m (k4oA뽰ĸ^bWLAhhwdepsmhexpmmplugm 1.5.0-rc.2lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.1wtruehmhexpmmmimemmimem~> 1.0wfalsej3bWLAhhwretiredmhexpmmphoenixm0.7.0wnil`bWLAhhwinner_checksummhexpmm db_connectionm2.2.2m ;$[qbHI&0;]F Q%Θh_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.0.0m ϲ aq|Ǿoqd$6ubWLAhhwdepsmhexpmm db_connectionm2.2.0lhmhexpmm connectionm connectionm~> 1.0.2wfalsejbWLAhhwdepsmhexpmmphoenixm1.2.2lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.1wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsejabWLAhhwdepsmhexpmmplugm0.8.3lhmhexpmmcowboymcowboym~> 1.0wtruej\bWLAhhwouter_checksummhexpmmpostgrexm0.13.0m H*zE\̤8@BbWLAhhwdepsmhexpmmphoenixm0.16.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 0.14 or ~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejZbWLAhhwinner_checksummhexpmmdecimalm1.0.0m 4") h_S)3rm"K^4bWLAhhwretiredmhexpmmpostgrexm0.8.3wnilKbWLAhhw timestampmhexpmm db_connectionm0.2.0hhba aha a9a0ZbWLAhhwinner_checksummhexpmmphoenixm1.1.4m e7srZVeK@nw<|18bWLAhhwretiredmhexpmmdecimalm 2.0.0-rc.0wnil\bWLAhhwouter_checksummhexpmmpostgrexm0.15.2m u.ȃe. b 3#yi>q]bWLAhhwouter_checksummhexpmm connectionm1.0.1m B KK{41ヺsBbWLAhhw timestampmhexpmmplugm0.5.0hhba aha a9a0,bWLAhhwdepsmhexpmmdecimalm1.8.0jZbWLAhhwinner_checksummhexpmmdecimalm0.2.4m qt6Dh2A?k't*PgLICVZbWLAhhwouter_checksummhexpmmphoenixm1.4.3m ܨwg*Khb+l0Bňi1XJbWLAhhw timestampmhexpmmdecimalm 1.9.0-rc.0hhba aha a9a0EbWLAhhw timestampmhexpmmphoenixm0.2.3hhba aha a9a0[bWLAhhwinner_checksummhexpmmpostgrexm0.6.0m l&$hus(c|},LbWLAhhw timestampmhexpmmpostgrexm 0.13.0-rc.0hhba aha a9a0VbWLAhhw registry_etagmhexpmm phoenix_htmlm""1afb92af7c2927f48535ea600645d7b3"ZbWLAhhwouter_checksummhexpmmphoenixm0.2.5m ~sؒv `1 l,h˕PybѶ19bWLAhhwretiredmhexpmm phoenix_htmlm2.14.2wnilgbWLAhhwdepsmhexpmm phoenix_htmlm2.11.0lhmhexpmmplugmplugm~> 1.5wfalsejZbWLAhhwinner_checksummhexpmmphoenixm1.1.5m >xx*K9YΫK8u[bWLAhhwinner_checksummhexpmmphoenixm1.4.16m ,fV|D8 3B7*Bn=:HCEbWLAhhw timestampmhexpmmdecimalm0.2.1hhba aha a9a0_bWLAhhwinner_checksummhexpmm phoenix_htmlm1.2.0m Aگ^˸L^9sgSX9HX5bWLAhhwretiredmhexpmmpostgrexm0.12.0wnilbWLAhhwdepsmhexpmm plug_cowboym2.2.0lhmhexpmmcowboymcowboym~> 2.7wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej4bWLAhhwretiredmhexpmmpostgrexm0.6.0wnil[bWLAhhwouter_checksummhexpmmphoenixm1.4.11m 7#os3s^oB~XᤲZbWLAhhwouter_checksummhexpmmphoenixm1.5.1m '+8(y̮og鷐=hx^=#1R8bWLAhhwretiredmhexpmmphoenixm 1.3.0-rc.0wnilIbWLAhhw timestampmhexpmm plug_cowboym2.1.3hhba aha a9a0bWLAhhwdepsmhexpmmphoenixm1.0.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejZbWLAhhwinner_checksummhexpmmphoenixm0.2.2m "r4|2_IijJPtT fB5bWLAhhwretiredmhexpmmpostgrexm0.13.0wnil9bWLAhhwretiredmhexpmm phoenix_htmlm2.10.2wnilFbWLAhhw timestampmhexpmmpostgrexm0.5.1hhba aha a9a05bWLAhhwretiredmhexpmmpostgrexm0.15.1wnil0bWLAhhwretiredmhexpmmplugm1.1.3wnil`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.14.1m SmR7[2A z:, _wc$ZbWLAhhwinner_checksummhexpmmphoenixm0.4.1m Fwbp1YGױg 8ZZbWLAhhwinner_checksummhexpmmphoenixm0.2.7m A_y~rtw(P3T[i(VZbWLAhhwinner_checksummhexpmmphoenixm0.2.4m Nr[+:Igʨ(4Y\KbWLAhhw timestampmhexpmm db_connectionm0.2.1hhba aha a9a0bWLAhhwdepsmhexpmmpostgrexm0.13.4lhmhexpmm connectionm connectionm~> 1.0wfalsehmhexpmm db_connectionm db_connectionm~> 1.1wfalsehmhexpmmdecimalmdecimalm~> 1.0wfalsejKbWLAhhw timestampmhexpmm db_connectionm2.1.0hhba aha a9a0bWLAhhwdepsmhexpmmphoenixm1.1.4lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsejIbWLAhhw timestampmhexpmm plug_cowboym2.3.0hhba aha a9a0jbWLAhhwdepsmhexpmmpostgrexm0.6.0lhmhexpmmdecimalmdecimalm~> 0.2.3wfalsejBbWLAhhw timestampmhexpmmplugm1.2.1hhba aha a9a00bWLAhhwretiredmhexpmmplugm0.5.0wnilEbWLAhhw timestampmhexpmmphoenixm1.5.1hhba aha a9a0\bWLAhhwouter_checksummhexpmmpostgrexm0.13.1m pEXN 䂺1h{z;SECbWLAhhw timestampmhexpmmjasonm1.1.1hhba aha a9a0XbWLAhhwinner_checksummhexpmmplugm1.10.0m e)\TE&7䨃4vHuM0bWLAhhwretiredmhexpmmplugm0.5.1wnil^bWLAhhwinner_checksummhexpmm plug_cowboym2.0.0m r+ 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejhex-2.5.1/test/fixtures/registries/20210915.ets000066400000000000000000006661741522402544500207620ustar00rootroot00000000000000cXM bWLAhhwidZw nonode@nohostˮ\xFhwdecentralized_counterswfalsehwread_concurrencywfalsehwwrite_concurrencywfalsehw compressedwfalsehwmemoryb5hwownerXw nonode@nohostehwheirwnonehwnamewElixir.Hex.Devhwsizebhwnodew nonode@nohosthw named_tablewfalsehwtypewsethwkeyposahw protectionw protectedhw major_versionahw minor_versionahw extended_infoj2bWLAhhwretiredmhexpmmcowlibm2.1.0wnilDbWLAhhw timestampmhexpmmcowboym1.0.3hhbaahaaa._bWLAhhwouter_checksummhexpmmphoenixm 1.4.0-rc.2m &h (0"B}pe:8B0bWLAhhwretiredmhexpmmmimem2.0.0wnilWbWLAhhwinner_checksummhexpmmplugm1.1.2m ~$ZVɭ\1L>m ٛHE 1bWLAhhwretiredmhexpmmplugm1.10.4wnilZbWLAhhwinner_checksummhexpmmcowlibm2.10.1m TBkN48^Y/F\GިZbWLAhhwouter_checksummhexpmmphoenixm1.4.0m "ڏe=;;vfÉ=B%̄KZbWLAhhwinner_checksummhexpmmphoenixm1.5.7m )#:$EK _Fi=&&XbWLAhhwouter_checksummhexpmmplugm0.10.0m a{9 aH5;7KC]ϖ5"Qy!qJ^bWLAhhwouter_checksummhexpmm plug_cowboym2.2.2m #dۭ7SՈ~^Pbf17'ZbWLAhhwouter_checksummhexpmmphoenixm1.0.2m VBSkeѠOU~9V#wk`RgZbWLAhhwouter_checksummhexpmmphoenixm0.4.0m 碆B~@9YUOb*b1bWLAhhwretiredmhexpmmjasonm1.2.2wnil1bWLAhhwretiredmhexpmmranchm1.7.0wnilWbWLAhhwinner_checksummhexpmmmimem1.0.0m 0's;;]ȶbˁw1TJVWbWLAhhwinner_checksummhexpmmplugm0.8.1m $0 ;pD/L_awFҐG0bWLAhhwretiredmhexpmmplugm1.2.3wnilZbWLAhhwouter_checksummhexpmmphoenixm1.3.0m 5̑l7O2O/]eeSl1bWLAhhwretiredmhexpmmplugm0.12.2wnilBbWLAhhw timestampmhexpmmplugm1.2.5hhbaahaa4a[bWLAhhwouter_checksummhexpmmphoenixm0.2.10m eXNil ?-_0qA $Q"*bWLAhhwdepsmhexpmmranchm1.3.2j1bWLAhhwretiredmhexpmmplugm0.11.0wnilYbWLAhhwouter_checksummhexpmmpoisonm1.5.0m wIO-l,2575.幜yaWbWLAhhwinner_checksummhexpmmplugm1.3.3m پ$7NG sЕIί#DbWLAhhw timestampmhexpmmpoisonm3.0.0hhba a ha a.aYbWLAhhwouter_checksummhexpmmpoisonm1.3.1m ׍嫺ؙ/m 0FUZ`ҊXZbWLAhhwinner_checksummhexpmmphoenixm1.4.2m :P e; T4iMEbWLAhhw timestampmhexpmmphoenixm1.1.6hhba a ha aa KbWLAhhw timestampmhexpmm phoenix_htmlm2.10.2hhba a ha aa ObWLAhhw registry_etagmhexpmmranchm""db832848a90690400d75bacdc5ccd01e""bWLAhhwdepsmhexpmmphoenixm 1.4.0-rc.2lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruejYbWLAhhwouter_checksummhexpmmpoisonm2.2.0m Q C9a(AtėRL([yz`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.13.2m 4gddے*yz#$YbWLAhhwinner_checksummhexpmmpoisonm1.0.2m M;su@Sޱ@oVa. 7@S8ZbWLAhhwinner_checksummhexpmmphoenixm0.2.9m (N76eVҫbnK;L}d,bWLAhhwdepsmhexpmmdecimalm1.7.0j0bWLAhhwretiredmhexpmmplugm1.0.0wnil_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.0.1m ߲8MςÝzffSpo&ZbWLAhhwinner_checksummhexpmmdecimalm1.3.0m Afx`E|Rӣ՗M'}EbWLAhhw timestampmhexpmmphoenixm1.0.2hhba a ha aa 8bWLAhhwretiredmhexpmm phoenix_htmlm3.0.2wnilNbWLAhhw timestampmhexpmmcowboy_telemetrym0.3.0hhbaahaaa7bWLAhhwretiredmhexpmm plug_cryptom1.2.0wnilBbWLAhhw timestampmhexpmmplugm1.3.0hhbaahaa4aIbWLAhhw timestampmhexpmm plug_cowboym2.2.0hhba a ha aa bWLAhhwdepsmhexpmmplugm1.5.1lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.3wtruehmhexpmmmimemmimem~> 1.0wfalsej`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.12.0m y /DGP)%kmn^[bWLAhhwouter_checksummhexpmmphoenixm1.4.13m vZہbl{_QYb`tRv}bWLAhhwdepsmhexpmmphoenixm0.2.8lhmhexpmmex_confmex_confm0.1.2wfalsehmhexpmminflexminflexm0.2.0wfalsehmhexpmmjazzmjazzm0.1.1wfalsehmhexpmmplugmplugm0.4.4wfalsejWbWLAhhwouter_checksummhexpmmplugm1.2.0m '8êBJm0ʭOh::+bWLAhhwdepsmhexpmmpoisonm2.0.1j,bWLAhhwdepsmhexpmmdecimalm1.3.1jWbWLAhhwouter_checksummhexpmmplugm1.1.0m iijK'%.? -ޙ/قK2bWLAhhwretiredmhexpmmcowboym2.6.0wnil+bWLAhhwdepsmhexpmmcowlibm2.7.3jDbWLAhhw timestampmhexpmmpoisonm1.2.1hhba a ha a.aZbWLAhhwouter_checksummhexpmmdecimalm1.6.0m $@@s`I*sGHM:bJibWLAhhwretiredmhexpmmcowlibm2.7.1twmessagem+Published with invalid version in .app filewreasonwRETIRED_INVALID^bWLAhhwinner_checksummhexpmm plug_cryptom1.2.1m \D'RU%"_0mZM-n:bWLAhhwretiredmhexpmmphoenix_pubsubm0.0.1wnil_bWLAhhwinner_checksummhexpmmphoenixm 1.3.0-rc.2m SJ%h܂o8Bs E"(تZbWLAhhwouter_checksummhexpmmdecimalm1.5.0m  &XUMu_]r?3ɻ9W⡭.^bWLAhhwouter_checksummhexpmm plug_cryptom1.2.1m iaп2ӑT].7h9ax,`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.10.4m 2I1c| p?-Mm8bWLAhhwretiredmhexpmmphoenixm 1.4.0-rc.1wnilbWLAhhwdepsmhexpmm plug_cowboym2.1.3lhmhexpmmcowboymcowboym~> 2.5wfalsehmhexpmmplugmplugm~> 1.7wfalsejbWLAhhwdepsmhexpmmcowboym2.9.0lhmhexpmmcowlibmcowlibm2.11.0wfalsehmhexpmmranchmranchm1.8.0wfalsejWbWLAhhwouter_checksummhexpmmplugm1.7.2m ޘ%o֭ ͈k=e~`kZbWLAhhwinner_checksummhexpmmphoenixm1.5.2m {]lNvA I/jIgXbWLAhhwouter_checksummhexpmmplugm1.10.0m B*'g; {w[-퐏2dA^JabWLAhhwdepsmhexpmmplugm1.0.6lhmhexpmmcowboymcowboym~> 1.0wtruejWbWLAhhwouter_checksummhexpmmplugm1.2.2m ^Dl]R&~,23~DbWLAhhw timestampmhexpmmcowboym2.0.0hhbaahaaa.3bWLAhhwretiredmhexpmmphoenixm1.4.0wnilbWLAhhwdepsmhexpmmphoenixm1.5.1lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej[bWLAhhwinner_checksummhexpmmphoenixm0.12.0m Bp5 »O+hȠ& F!@ F2,_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.9.0m ne7WuG5FD))%cA:k .35bWLAhhwretiredmhexpmmplugm 1.2.0-rc.0wnilXbWLAhhwinner_checksummhexpmmplugm1.10.4m AѢqSgd[գ}*?9YbWLAhhwinner_checksummhexpmmcowboym2.9.0m ]ض`~(.4:ؾok!m R5bWLAhhwretiredmhexpmm telemetrym0.4.2wnilWbWLAhhwouter_checksummhexpmmplugm1.2.3m e”ynw^:݃mrH/-PYbWLAhhwinner_checksummhexpmmpoisonm1.0.0m IY+9s2x#Fc|v#_abWLAhhwdepsmhexpmmplugm1.1.7lhmhexpmmcowboymcowboym~> 1.0wtruejXbWLAhhwinner_checksummhexpmmranchm1.3.1m =OYALRz z)HE-. ryvbWLAhwversionabbWLAhhwdepsmhexpmmplugm0.13.0lhmhexpmmcowboymcowboym~> 1.0wtruejYbWLAhhwinner_checksummhexpmmpoisonm1.5.2m V ߷D=#!" XեꄉZbWLAhhwinner_checksummhexpmmphoenixm1.4.7m 弚Lu R}Zl*<awm,bWLAhhwdepsmhexpmmdecimalm1.4.0jYbWLAhhwinner_checksummhexpmmcowlibm2.7.0m >nwV/U`YF-v/î>C^bWLAhhwinner_checksummhexpmm plug_cowboym2.1.0m Wh<:9Բ[NŎJnj' 0 "bWLAhhwdepsmhexpmmphoenixm1.3.2lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4wfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0wfalsejFbWLAhhw timestampmhexpmmphoenixm1.5.12hhba a ha aa BbWLAhhw timestampmhexpmmplugm1.4.3hhbaahaa4abWLAhhwversionsmhexpmmmimel m0.0.1m1.0.0m1.0.1m1.1.0m1.2.0m1.3.0m1.3.1m1.4.0m1.5.0m1.6.0m2.0.0m2.0.1jWbWLAhhwouter_checksummhexpmmplugm1.8.2m TȻ,(#GU(1 VgbWLAhhwdepsmhexpmm phoenix_htmlm2.14.3lhmhexpmmplugmplugm~> 1.5wfalsejdbWLAhhwdepsmhexpmmphoenixm1.4.12lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsejYbWLAhhwinner_checksummhexpmmcowlibm2.7.2m 2ϑ15ͥ#B`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.11.1m LEwOJZrBx4iŢgϮEbWLAhhw timestampmhexpmmphoenixm0.3.1hhba a ha aa `bWLAhhwouter_checksummhexpmm phoenix_htmlm2.13.1m BϩNޖ3@ٜ4]Om鲳BdǡdYbWLAhhwinner_checksummhexpmmcowlibm2.0.1m M^pt ZSڂ8%bWLAhw last_updatehhba ahaaaEbWLAhhw timestampmhexpmmphoenixm1.4.2hhba a ha aa XbWLAhhwinner_checksummhexpmmplugm0.14.0m KN&ïF9VgD|Az[[bWLAhhwinner_checksummhexpmmphoenixm0.10.0m ,~33gaTyV0A*IXbWLAhhwdepsmhexpmm plug_cowboym2.4.0lhmhexpmmcowboymcowboym~> 2.7wfalsehmhexpmmcowboy_telemetrymcowboy_telemetrym~> 0.3wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejbWLAhhwdepsmhexpmmphoenixm0.2.3lhmhexpmmex_confmex_confm0.1.1wfalsehmhexpmminflexminflexm0.2.0wfalsehmhexpmmjazzmjazzm0.1.0wfalsehmhexpmmplugmplugm0.4.3wfalsejYbWLAhhwouter_checksummhexpmmpoisonm1.2.1m {hhb4>B`4.#3bWLAhhwretiredmhexpmmphoenixm0.7.1wnilFbWLAhhw timestampmhexpmmphoenixm0.17.1hhba a ha aa WbWLAhhwinner_checksummhexpmmmimem1.1.0m =ǸFQ9b4;( [bWLAhhwouter_checksummhexpmmphoenixm1.4.14m IX%G{eeq [v+XTUbWLAhhw registry_etagmhexpmm plug_cryptom""29f1eae1dd8d5dc4c084385d2b97a633"cbWLAhhwinner_checksummhexpmmcowboy_telemetrym0.2.0m ,CP!ԕu'VRa-<) ZbWLAhhwinner_checksummhexpmmphoenixm0.2.1m e/4QDU(R=7qPDdJ zkZbWLAhhwinner_checksummhexpmmphoenixm0.2.0m lE$ KMb™ua@Eh4\bWLAhhwinner_checksummhexpmm telemetrym0.4.1m 'HHD$GjA|2w(ttR^bWLAhhwouter_checksummhexpmm plug_cryptom1.2.0m S8DRU| $ ih?vbWLAhhwretiredmhexpmm plug_cowboym2.2.0twmessagemBroken telemetry supportwreasonwRETIRED_INVALIDDbWLAhhw timestampmhexpmmcowboym1.0.2hhbaahaaa.WbWLAhhwouter_checksummhexpmmmimem2.0.1m z Үbas8ߑcBZbWLAhhwinner_checksummhexpmmphoenixm1.0.1m .ٝrl#H[;'r9p؂3_bWLAhhwinner_checksummhexpmmphoenixm 1.4.0-rc.1m rZsVAE fKjtbWLAhhwdepsmhexpmm plug_cowboym2.3.0lhmhexpmmcowboymcowboym~> 2.7wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejEbWLAhhw timestampmhexpmmphoenixm1.4.1hhba a ha aa abWLAhhwouter_checksummhexpmmphoenix_pubsubm1.0.1m J/a?bsBqv˗ Ә3bWLAhhwretiredmhexpmmphoenixm1.5.5wnilabWLAhhwdepsmhexpmmplugm1.1.6lhmhexpmmcowboymcowboym~> 1.0wtruejZbWLAhhwinner_checksummhexpmmphoenixm1.2.2m 4[RY8ΖlL‰'JDZbWLAhhwinner_checksummhexpmmphoenixm1.2.3m ֧>8YwDXIHbWLAhhwdepsmhexpmmplugm1.2.3lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmmimemmimem~> 1.0wfalsej3bWLAhhwretiredmhexpmmphoenixm0.9.0wnilFbWLAhhw timestampmhexpmmphoenixm0.17.0hhba a ha aa 2bWLAhhwretiredmhexpmmcowlibm2.8.0wnilWbWLAhhwinner_checksummhexpmmmimem1.2.0m x+6 ӴQ@уֈ[ 4:bWLAhhwretiredmhexpmmphoenix_pubsubm1.0.0wnilEbWLAhhw timestampmhexpmmphoenixm0.9.0hhba a ha aa ZbWLAhhwouter_checksummhexpmmphoenixm1.0.4m Y_:oTIj- {O5&foiWbWLAhhwouter_checksummhexpmmmimem1.3.0m ^(`2j` Ɠݫ%y{ffS(abWLAhhwinner_checksummhexpmmphoenix_pubsubm1.1.1m fhׇ$z_i̘Q6 <BbWLAhhw timestampmhexpmmplugm1.8.1hhbaahaa4a7bWLAhhwretiredmhexpmm plug_cryptom1.1.0wnilgbWLAhhwdepsmhexpmm phoenix_htmlm2.11.1lhmhexpmmplugmplugm~> 1.5wfalsejWbWLAhhwouter_checksummhexpmmplugm1.0.4m ,翈QEfss0Lӛ[QYbWLAhhwinner_checksummhexpmmcowlibm2.1.0m 6X=C@@ >Oٗ aLNbWLAhhw timestampmhexpmm phoenix_htmlm 2.7.0-devhhba a ha aa 0bWLAhhwretiredmhexpmmplugm1.7.0wnil_bWLAhhwouter_checksummhexpmmphoenixm 1.5.0-rc.0m 7}ʋNEBt_y e%gfXbWLAhhwouter_checksummhexpmmplugm1.10.1m R%1EII*,bWLAhhwdepsmhexpmmphoenixm1.5.4lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejGbWLAhhw timestampmhexpmm telemetrym0.2.0hhbaahaa4aZbWLAhhwouter_checksummhexpmmphoenixm1.2.5m S2&īaipHdRα8ZbWLAhhwinner_checksummhexpmmcowlibm2.10.0m âɹ5.[ZAib}!_bWLAhhwinner_checksummhexpmmphoenixm 1.4.0-rc.0m k]MvF/x[bcjfj8{.ޤWbWLAhhwinner_checksummhexpmmplugm1.6.4m 5aiE/k7vЙs?'G?\bWLAhhwinner_checksummhexpmm telemetrym0.2.0m [@ʣ޳-|OUomk\7L#fw!a1wqbWLAhhwdepsmhexpmm phoenix_htmlm2.5.1lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsej2bWLAhhwretiredmhexpmmpoisonm4.0.1wnilBbWLAhhw timestampmhexpmmplugm1.0.2hhbaahaa4aCbWLAhhw timestampmhexpmmplugm0.13.1hhbaahaa4a0bWLAhhwretiredmhexpmmplugm1.3.1wnil,bWLAhhwdepsmhexpmmphoenixm0.1.0jZbWLAhhwinner_checksummhexpmmphoenixm1.2.0m ޹%OLSLߘ )|b]Wb|;fZbWLAhhwinner_checksummhexpmmphoenixm0.2.3m ^v& G prY 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejFbWLAhhwdepsmhexpmmphoenixm1.2.4lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm6~> 1.4 or ~> 1.3.3 or ~> 1.2.4 or ~> 1.1.8 or ~> 1.0.5wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsejbWLAhhwdepsmhexpmm plug_cowboym2.2.2lhmhexpmmcowboymcowboym~> 2.7wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej^bWLAhhwouter_checksummhexpmm plug_cowboym2.5.0m [,%XtF8k<4VR쀛vz[bWLAhhwinner_checksummhexpmmphoenixm0.13.1m i^`KxiPSளz2R.nۅXbWLAhhwouter_checksummhexpmmplugm0.12.1m 4+`%`bȩ@L)^DF4bWLAhhwretiredmhexpmmphoenixm1.5.10wnil4bWLAhhwretiredmhexpmmphoenixm1.4.10wnil0bWLAhhwretiredmhexpmmplugm1.0.6wnilabWLAhhwinner_checksummhexpmmphoenix_pubsubm0.1.0m zڏAI+Y@W<5y5YbWLAhhwinner_checksummhexpmmcowboym2.4.0m /ȥdZ_TtG3Ro]K EbWLAhhw timestampmhexpmmphoenixm0.6.1hhba a ha aa 2bWLAhhwretiredmhexpmmcowboym2.6.3wnil_bWLAhhwouter_checksummhexpmmphoenixm 1.4.0-rc.1m t4<Q7lWHAw>9bWLAhhwretiredmhexpmm phoenix_htmlm2.13.0wnilcbWLAhhwinner_checksummhexpmm phoenix_htmlm 2.0.0-devm 3{ۼ/"\4s9sHee[}(eZbWLAhhwouter_checksummhexpmmdecimalm1.1.1m ?6"/+ִZPA$<y5PτWDzi ZbWLAhhwinner_checksummhexpmmphoenixm1.1.7m 4ظ ?TL_?85ȭaU]bWLAhhwouter_checksummhexpmmjasonm 1.0.0-rc.2m wv3ԉ ĘDo$bWLAhhwdepsmhexpmmphoenixm0.2.7lhmhexpmmex_confmex_confm0.1.2wfalsehmhexpmminflexminflexm0.2.0wfalsehmhexpmmjazzmjazzm0.1.1wfalsehmhexpmmplugmplugm0.4.4wfalsej,bWLAhhwdepsmhexpmmdecimalm1.2.0jKbWLAhhw timestampmhexpmm phoenix_htmlm2.14.2hhba a ha aa [bWLAhhwouter_checksummhexpmmphoenixm1.5.11m 1ê"t 6y_1ւvᴙzֺ$DbWLAhhw timestampmhexpmmcowlibm2.4.0hhbaahaaa.,bWLAhhwdepsmhexpmmdecimalm1.1.1jcbWLAhhwinner_checksummhexpmm phoenix_htmlm 2.4.0-devm 4 }hd2+6VDbWLAhhw timestampmhexpmmpoisonm1.0.3hhba a ha a.a1bWLAhhwretiredmhexpmmplugm1.10.2wnil[bWLAhhwinner_checksummhexpmmphoenixm0.16.0m I~TY$G$nc; aZ%w~(8S_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.3.1m ?in`"yԡ_n}fo0-BEbWLAhhw timestampmhexpmmphoenixm1.3.4hhba a ha aa 8bWLAhhwretiredmhexpmmphoenixm 1.4.0-rc.3wnilYbWLAhhwinner_checksummhexpmmcowlibm1.1.0m , ؁^l^GZYbWLAhhwouter_checksummhexpmmcowlibm2.2.1m 1NdEf8K2kRh:bWLAhhwretiredmhexpmmphoenix_pubsubm1.1.1wnil`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.10.1m H%[6id~^d hh3bWLAhhwretiredmhexpmmphoenixm1.1.2wnil.bWLAhhwdepsmhexpmm telemetrym0.4.2j3bWLAhhwretiredmhexpmmphoenixm1.2.4wnil^bWLAhhwinner_checksummhexpmm plug_cryptom1.1.2m чW,mq6)%򵀡VB0'^bWLAhhwouter_checksummhexpmm plug_cryptom1.1.2m k`[oԜ7Lj>j(;["ֵBbWLAhhw timestampmhexpmmplugm1.5.1hhbaahaa4aKbWLAhhw timestampmhexpmm phoenix_htmlm2.13.1hhba a ha aa [bWLAhhwouter_checksummhexpmmphoenixm0.17.0m ,}ce1::lyw)1yZbWLAhhwinner_checksummhexpmmdecimalm1.8.1m ?_4( 57@)Nޅ3SoEh٬<JbWLAhhw timestampmhexpmmphoenixm 1.3.0-rc.2hhba a ha aa 2bWLAhhwretiredmhexpmmcowboym2.2.2wnilJbWLAhhw timestampmhexpmm phoenix_htmlm2.4.0hhba a ha aa 2bWLAhhwretiredmhexpmmcowlibm2.7.0wnil"bWLAhhwdepsmhexpmmphoenixm1.3.4lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4wfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0wfalsejZbWLAhhwouter_checksummhexpmmdecimalm1.4.1m #{RNUV]JrV&hL{ʀ_;XbWLAhhwinner_checksummhexpmmjasonm1.0.0m |#!Aw&ZqoXbWLAhhwouter_checksummhexpmmjasonm1.2.0m gGWyL:>N;|cNNJYu.WbWLAhhwouter_checksummhexpmmplugm1.6.1m s?2IC/_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.4.0m "s3f?r&Yg~?f׹KJbWLAhhw timestampmhexpmm phoenix_htmlm2.7.0hhba a ha aa BbWLAhhw timestampmhexpmmplugm0.8.3hhbaahaa4abWLAhhwdepsmhexpmmphoenixm1.5.7lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej0bWLAhhwretiredmhexpmmplugm1.2.5wnilbWLAhhwdepsmhexpmmphoenixm1.5.2lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej2bWLAhhwretiredmhexpmmcowboym1.0.0wnil2bWLAhhwretiredmhexpmmcowlibm2.3.0wnil_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.0.0m JnPp~ղi>TCHYF>!\57bWLAhhwretiredmhexpmm plug_cowboym2.0.1wnilGbWLAhhw timestampmhexpmmplugm 1.5.0-rc.0hhbaahaa4a:bWLAhhw timestampmhexpmmcowlibhhbaahaaa.[bWLAhhwinner_checksummhexpmmphoenixm0.13.0m FN6"K]m/6Mݛ%KbWLAhhw timestampmhexpmm phoenix_htmlm2.10.3hhba a ha aa *bWLAhhwdepsmhexpmmranchm2.0.0jEbWLAhhw timestampmhexpmmphoenixm1.1.4hhba a ha aa 4bWLAhhwretiredmhexpmmphoenixm0.16.1wnilDbWLAhhw timestampmhexpmmcowboym2.2.1hhbaahaaa.ZbWLAhhwouter_checksummhexpmmphoenixm1.2.1m ;f`5 nz 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13 or ~> 3.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej_bWLAhhwinner_checksummhexpmm phoenix_htmlm3.0.3m 2-p~^++iy}[ 0IbWLAhhw timestampmhexpmm plug_cryptom1.1.0hhbaahaaa.bWLAhhwdepsmhexpmmplugm1.4.3lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsejYbWLAhhwinner_checksummhexpmmcowboym2.6.3m PNhUW.pDW3jE=Jx9"1)bWLAhhwdepsmhexpmmmimem1.3.0jXbWLAhhwinner_checksummhexpmmranchm2.1.0m "atDkmU@++3g;rebWLAhhwdepsmhexpmmpoisonm5.0.0lhmhexpmmdecimalmdecimalm~> 2.0wtruej8bWLAhhwretiredmhexpmm phoenix_htmlm3.0.1wnilYbWLAhhwinner_checksummhexpmmcowboym1.1.1m  ԃ]]=m mMJHV#nLbWLAhhw timestampmhexpmmphoenix_pubsubm1.0.2hhba a ha aa XbWLAhhwinner_checksummhexpmmplugm1.12.0m 9kmRlE,Sh؅Ym0ZbWLAhhwouter_checksummhexpmmdecimalm1.8.1m z^bWLAhhwinner_checksummhexpmm plug_cowboym2.0.1m טZ̆-DPf\#nQ2AzbWLAhhwdepsmhexpmmplugm1.2.4lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmmimemmimem~> 1.0wfalsej\bWLAhhwouter_checksummhexpmmplugm 1.5.0-rc.1m A^3\1=ݴGp 0.13 or ~> 1.0wfalsejZbWLAhhwouter_checksummhexpmmphoenixm1.5.6m Ӛ0kj')\y,po竻=3mSyVWbWLAhhwouter_checksummhexpmmmimem1.0.0m ~gG틓\Tzy,OLm}?\9bWLAhhwretiredmhexpmm phoenix_htmlm2.11.2wnil)bWLAhhwdepsmhexpmmmimem2.0.0jqbWLAhhwdepsmhexpmm phoenix_htmlm2.1.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsejcbWLAhhwinner_checksummhexpmmcowboy_telemetrym0.3.1m ѡׯ'fTzF'CؠAgTYbWLAhhwinner_checksummhexpmmcowlibm1.3.0m l(cעcsC+VF z;9bWLAhhwdepsmhexpmmphoenixm1.4.1lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruejWbWLAhhwinner_checksummhexpmmmimem2.0.1m n$2MMׇ7VDbWLAhhw timestampmhexpmmcowlibm1.2.0hhbaahaaa.2bWLAhhwretiredmhexpmmpoisonm2.2.0wnilZbWLAhhwinner_checksummhexpmmcowlibm2.11.0m FbV.vwn  c0bWLAhhwretiredmhexpmmplugm1.3.5wnilabWLAhhwdepsmhexpmmplugm0.8.4lhmhexpmmcowboymcowboym~> 1.0wtruej0bWLAhhwretiredmhexpmmplugm0.4.3wnilDbWLAhhw timestampmhexpmmcowlibm2.7.0hhbaahaaa.\bWLAhhwinner_checksummhexpmm telemetrym1.0.0m E:,$7T 0R[bWLAhhwinner_checksummhexpmmphoenixm1.5.10m >bmrt 2?UI㫻ӋRCbWLAhhw timestampmhexpmmplugm1.10.2hhbaahaa4aEbWLAhhw timestampmhexpmmdecimalm1.7.0hhbaaha aa:bWLAhhwdepsmhexpmmplugm1.3.5lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsejWbWLAhhwouter_checksummhexpmmmimem0.0.1m $ ߽#C8Fd7S;:L5C' BbWLAhhw timestampmhexpmmplugm1.1.9hhbaahaa4a;bWLAhhw timestampmhexpmmdecimalhhbaaha aa:3bWLAhhwretiredmhexpmmdecimalm1.1.2wnilXbWLAhhwinner_checksummhexpmmranchm1.2.1m ,{F|Oiva~(babWLAhhwinner_checksummhexpmmphoenix_pubsubm0.0.1m |ļ[kY`dF>NSKtCbWLAhhw timestampmhexpmmranchm1.2.1hhba a ha aa dbWLAhhwdepsmhexpmmjasonm1.0.1lhmhexpmmdecimalmdecimalm~> 1.0wtruejWbWLAhhwouter_checksummhexpmmplugm1.0.2m A› Nm~Q@LÅDyS8bWLAhhwretiredmhexpmm phoenix_htmlm2.9.1wnilZbWLAhhwouter_checksummhexpmmphoenixm1.5.9m ~K |/ ] 4gyWJuB} 1.0.0wtruejXbWLAhhwouter_checksummhexpmmjasonm1.0.1m QQjL Y= hv%W4 1bWLAhhwretiredmhexpmmplugm1.12.0wnil2bWLAhhwretiredmhexpmmcowboym2.1.0wnil8bWLAhhwretiredmhexpmmphoenixm 1.3.0-rc.3wnil_bWLAhhwinner_checksummhexpmm phoenix_htmlm1.1.0m UL-\^'u2˖ut(1bWLAhhwretiredmhexpmmranchm1.3.1wnilKbWLAhhw timestampmhexpmm phoenix_htmlm2.11.1hhba a ha aa ZbWLAhhwinner_checksummhexpmmphoenixm1.0.3m wafnQ+èzh $\2bWLAhhwretiredmhexpmmcowboym1.1.0wnilFbWLAhhw timestampmhexpmmphoenixm1.4.17hhba a ha aa YbWLAhhwouter_checksummhexpmmcowboym1.0.0m D:FܧY^11 _+hWbWLAhhwinner_checksummhexpmmplugm0.5.2m |"Wž/1޹\T5 J0bWLAhhwretiredmhexpmmplugm1.4.0wnilbWLAhhwdepsmhexpmmplugm1.6.4lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4wtruehmhexpmmmimemmimem~> 1.0wfalsej0bWLAhhwretiredmhexpmmplugm1.1.7wnilbWLAhhwdepsmhexpmmphoenixm1.2.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.1wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsej`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.13.2m |Έ`}+R'1OkA4WbWLAhhwdepsmhexpmmphoenixm1.5.5lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejYbWLAhhwouter_checksummhexpmmcowboym2.2.0m ]`>_J40BN./d5|bWLAhhwversionsmhexpmmphoenixllm0.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.0jXbWLAhhwinner_checksummhexpmmjasonm1.0.1m d6K<p3T]s.g\bWLAhhwouter_checksummhexpmmplugm 1.4.0-rc.0m \u6q ]v8' ln*AM}~WbWLAhhwinner_checksummhexpmmplugm1.1.3m ,Cp~imL`ZYbWLAhhwinner_checksummhexpmmpoisonm1.4.0m ZєW/d#WFNwy%YbWLAhhwinner_checksummhexpmmcowlibm2.4.0m R_(zJ{kf'DpHeڎ4bWLAhhwretiredmhexpmmphoenixm1.5.11wnil1bWLAhhwretiredmhexpmmplugm0.13.1wnil3bWLAhhwretiredmhexpmmphoenixm1.5.8wnilXbWLAhhw registry_etagmhexpmmphoenix_pubsubm""9c40253b1ea746f7a666b077918a909f"DbWLAhhw timestampmhexpmmcowboym2.6.0hhbaahaaa.EbWLAhhw timestampmhexpmmdecimalm1.8.1hhbaaha aa:WbWLAhhwouter_checksummhexpmmplugm0.4.3m &[ }yv6[A.q4bWLAhhwretiredmhexpmmphoenixm0.13.0wnil3bWLAhhwdepsmhexpmmphoenix_pubsubm1.1.0j[bWLAhhwinner_checksummhexpmmphoenixm0.11.0m `3dLڜ,\O,pO@XbWLAhhwouter_checksummhexpmmplugm1.11.0m -c?\/iuhܻ,t0v2bWLAhhwretiredmhexpmmcowlibm1.0.2wnilZbWLAhhwouter_checksummhexpmmdecimalm1.0.1m dk+5~ kƿF|xwtabWLAhhwdepsmhexpmmplugm1.0.2lhmhexpmmcowboymcowboym~> 1.0wtruejLbWLAhhw timestampmhexpmmphoenix_pubsubm0.0.1hhba a ha aa YbWLAhhwinner_checksummhexpmmcowlibm2.8.0m x}J!s >qr+#ȤZbWLAhhwouter_checksummhexpmmphoenixm1.1.4m We#ΒB;C?DpQ Yz+bWLAhhwdepsmhexpmmpoisonm4.0.0jZbWLAhhwinner_checksummhexpmmphoenixm1.0.5m E)PQ!jflW1)dfbWLAhhwdepsmhexpmm phoenix_htmlm2.9.1lhmhexpmmplugmplugm~> 1.0wfalsej^bWLAhhwouter_checksummhexpmm plug_cowboym2.2.1m ;C$F (nz b䎷#YTh ]޶EbWLAhhw timestampmhexpmmdecimalm0.2.2hhbaaha aa:YbWLAhhwinner_checksummhexpmmcowlibm2.2.0m ^7L%L021得bUbWLAhhwdepsmhexpmmcowboy_telemetrym0.3.0lhmhexpmmcowboymcowboym~> 2.7wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejDbWLAhhw timestampmhexpmmpoisonm1.3.1hhba a ha a.aWbWLAhhwouter_checksummhexpmmmimem1.0.1m ^m r,)G DbWLAhhw timestampmhexpmmcowboym2.6.2hhbaahaaa.DbWLAhhw timestampmhexpmmcowboym2.1.0hhbaahaaa.bWLAhhwversionsmhexpmmpoisonlm1.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.0jbWLAhhwdepsmhexpmmcowboym2.2.2lhmhexpmmcowlibmcowlibm~> 2.1.0wfalsehmhexpmmranchmranchm~> 1.4.0wfalsejZbWLAhhwinner_checksummhexpmmdecimalm0.2.3m r}$޿>4m\lZ$7bWLAhhwretiredmhexpmm plug_cowboym2.1.3wnilBbWLAhhw timestampmhexpmmplugm0.9.0hhbaahaa4a3bWLAhhwretiredmhexpmmcowlibm2.11.0wnildbWLAhhwdepsmhexpmmphoenixm1.4.16lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsej]bWLAhhwinner_checksummhexpmmranchm 2.0.0-rc.2m Hm4$2|;*vxQAs0bWLAhhwdepsmhexpmm plug_cryptom1.2.2jZbWLAhhwinner_checksummhexpmmdecimalm1.2.0m F)`q(.W{Gkkh'}M dcRiK 9bWLAhhwretiredmhexpmm phoenix_htmlm2.13.1wnilDbWLAhhw timestampmhexpmmcowlibm2.9.0hhbaahaaa.bWLAhhwdepsmhexpmmcowboym2.8.0lhmhexpmmcowlibmcowlibm~> 2.9.1wfalsehmhexpmmranchmranchm~> 1.7.1wfalsej`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.13.0m ;^lYz ?GALV,A bWLAhhwdepsmhexpmmphoenixm0.10.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm ~> 0.11.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsehmhexpmmpoolboympoolboym~> 1.4.2wtruejcbWLAhhwinner_checksummhexpmmcowboy_telemetrym0.4.0m 9XwM *:PWjZ `bWLAhhwouter_checksummhexpmm phoenix_htmlm2.11.0m 6h+2INp#9:@7}~Ne h_)bWLAhhwdepsmhexpmmphoenixm0.9.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm ~> 0.10.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsehmhexpmmpoolboympoolboym~> 1.4.2wtruejGbWLAhhw timestampmhexpmmplugm 1.5.0-rc.2hhbaahaa4aWbWLAhhwouter_checksummhexpmmplugm1.3.0m RD5~mVQ5)_4E3-BbWLAhhw timestampmhexpmmplugm0.8.4hhbaahaa4aEbWLAhhw timestampmhexpmmphoenixm1.1.0hhba a ha aa 8bWLAhhwretiredmhexpmm phoenix_htmlm1.1.0wnilubWLAhhwdepsmhexpmm phoenix_htmlm1.0.1lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0wfalsej9bWLAhhw timestampmhexpmmranchhhba a ha aa JbWLAhhw timestampmhexpmm phoenix_htmlm1.2.1hhba a ha aa XbWLAhhwouter_checksummhexpmmranchm2.1.0m $N*au' Ő$v)J2Wޏ;yiEbWLAhhw timestampmhexpmmdecimalm1.0.1hhbaaha aa:WbWLAhhwouter_checksummhexpmmplugm1.1.2m %XB=!UG^5EE*dTZbWLAhhwouter_checksummhexpmmdecimalm0.1.2m Jޗ֊YJ AJ'm\bWLAhhwouter_checksummhexpmm telemetrym0.4.0m |1p| 0-Ow?AWbWLAhhwinner_checksummhexpmmplugm1.3.1m FuB93pL^?B`dūUYbWLAhhwouter_checksummhexpmmcowlibm2.1.0m }!:,KK#M)YV-fm23bWLAhhwretiredmhexpmmcowlibm2.10.0wnilWbWLAhhwinner_checksummhexpmmplugm1.1.4m .B n[1vMoaB+Ű.ΙYbWLAhhwinner_checksummhexpmmpoisonm1.0.1m {Uۯn)S"FWbWLAhhwouter_checksummhexpmmplugm1.1.1m  E!/&uW><fJ]pq8bWLAhhwretiredmhexpmmphoenixm 1.2.0-rc.0wnilZbWLAhhwinner_checksummhexpmmdecimalm1.1.1m [g1ͬoyo(]nVnvYbWLAhhwinner_checksummhexpmmcowboym1.0.3m _lStsn~I._&TL++bWLAhhwdepsmhexpmmcowlibm2.1.0jXbWLAhhwinner_checksummhexpmmplugm0.11.3m v;X5!6חtwI`+?WJYbWLAhhwouter_checksummhexpmmcowlibm1.0.2m b-:96jŚ)7'DCbWLAhhw timestampmhexpmmranchm1.0.0hhba a ha aa BbWLAhhw timestampmhexpmmplugm1.0.4hhbaahaa4a0bWLAhhwretiredmhexpmmmimem1.3.0wnilWbWLAhhwouter_checksummhexpmmplugm1.6.4m iE;2 0 tH`7bWLAhhwretiredmhexpmm plug_cowboym1.0.0wnilbWLAhhwdepsmhexpmm plug_cowboym2.0.1lhmhexpmmcowboymcowboym~> 2.5wfalsehmhexpmmplugmplugm~> 1.7wfalsej`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.10.0m iFM|S“4'QA8 JWbWLAhhwinner_checksummhexpmmplugm0.5.1m X)],{/xL5_RxP)WbWLAhhwinner_checksummhexpmmplugm1.1.5m VEpAZrS!'Z˒7Bn'CbWLAhhw timestampmhexpmmranchm1.1.0hhba a ha aa 0bWLAhhwretiredmhexpmmplugm1.2.6wnilbWLAhhwdepsmhexpmmphoenixm0.17.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejDbWLAhhw timestampmhexpmmcowboym1.1.2hhbaahaaa.,bWLAhhwdepsmhexpmmdecimalm2.0.0jgbWLAhhwdepsmhexpmm phoenix_htmlm2.10.5lhmhexpmmplugmplugm~> 1.0wfalsej_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.7.0m h׫~~څ1m%s{q ORNwZbWLAhhwouter_checksummhexpmmphoenixm1.5.0m ƕr9K}bc%P;K Ծ609ubWLAhhwretiredmhexpmmcowboym2.6.2twmessagemcowlib version 2.7.2 retiredwreasonwRETIRED_INVALIDbWLAhhwdepsmhexpmmphoenixm0.8.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 0.9.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejZbWLAhhwouter_checksummhexpmmphoenixm0.7.2m leuevoLGgұzoZbWLAhhwouter_checksummhexpmmphoenixm1.0.1m s$DeOă&x Uk4AWbWLAhhwinner_checksummhexpmmplugm1.4.1m .g} 1.5wfalsejXbWLAhhwinner_checksummhexpmmranchm1.0.0m NBewim֦{u%`3CbWLAhhw timestampmhexpmmplugm1.12.1hhbaahaa4a0bWLAhhwretiredmhexpmmplugm1.0.5wnilWbWLAhhwouter_checksummhexpmmplugm0.6.0m ' ]R\*bWLAhhwdepsmhexpmmranchm1.3.1jBbWLAhhw timestampmhexpmmmimem1.4.0hhbaahaaa.)bWLAhhwdepsmhexpmmmimem1.0.0jbWLAhhwdepsmhexpmmphoenixm0.14.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejZbWLAhhwouter_checksummhexpmmphoenixm1.5.5m n.3oZ7xU$N' F;YbWLAhhwinner_checksummhexpmmcowboym2.2.1m Cj"*uPQB`j2<bWLAhhwdepsmhexpmmphoenixm1.5.9lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13 or ~> 3.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej+bWLAhhwdepsmhexpmmpoisonm1.4.0jfbWLAhhwdepsmhexpmm phoenix_htmlm2.6.1lhmhexpmmplugmplugm~> 1.0wfalsejBbWLAhhw timestampmhexpmmplugm1.4.4hhbaahaa4aCbWLAhhw timestampmhexpmmranchm1.3.1hhba a ha aa JbWLAhhw timestampmhexpmm phoenix_htmlm1.3.0hhba a ha aa IbWLAhhw timestampmhexpmm plug_cryptom1.2.0hhbaahaaa.^bWLAhhwinner_checksummhexpmm plug_cowboym2.4.1m wP'.TWIP_Zc )XbWLAhhwinner_checksummhexpmmplugm0.11.0m ΰ`P&o@dS5IbWLAhhw timestampmhexpmm plug_cowboym2.1.2hhba a ha aa ZbWLAhhwinner_checksummhexpmmphoenixm1.1.2m kl`ٛ D3x e&Qm4|}DbWLAhhw timestampmhexpmmcowboym2.9.0hhbaahaaa.YbWLAhhwouter_checksummhexpmmpoisonm4.0.1m 6K9KaY mkߗo%WbWLAhhwouter_checksummhexpmmplugm1.7.0m ~ƫjxXoDm* \bWLAhhwinner_checksummhexpmm telemetrym0.4.0m 9˄L)5sm U84[bWLAhhwinner_checksummhexpmmphoenixm0.2.11m -bboW8v[xhFbWLAhhw timestampmhexpmmphoenixm0.14.0hhba a ha aa fbWLAhhwdepsmhexpmm phoenix_htmlm2.7.0lhmhexpmmplugmplugm~> 1.0wfalsejDbWLAhhw timestampmhexpmmcowlibm2.3.0hhbaahaaa.WbWLAhhwinner_checksummhexpmmmimem1.5.0m >^8m6?/z ;Z>`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.14.1m }R* G DI fcbWLAhhwinner_checksummhexpmmcowboy_telemetrym0.3.0m iϒcsVu@b]5HAc}ey{XbWLAhhwinner_checksummhexpmmjasonm1.2.1m (%/F>Kˍ spDZbWLAhhwouter_checksummhexpmmphoenixm1.1.7m MRVA1,OO'kT)1ZbWLAhhwinner_checksummhexpmmdecimalm2.0.0m LlWCG/By+VB֗qbWLAhhwdepsmhexpmm phoenix_htmlm2.6.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsej9bWLAhhwretiredmhexpmm phoenix_htmlm2.10.3wnil+bWLAhhwdepsmhexpmmcowlibm2.5.0jJbWLAhhw timestampmhexpmmphoenixm 1.4.0-rc.2hhba a ha aa 5bWLAhhwretiredmhexpmmplugm 1.5.0-rc.1wnil_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.4.0m ;ݙ\_^$HENf ^wQ5jbWLAhhwdepsmhexpmm phoenix_htmlm 2.7.0-devlhmhexpmmplugmplugm~> 1.0wfalsej,bWLAhhwdepsmhexpmmdecimalm1.1.0jNbWLAhhw timestampmhexpmmcowboy_telemetrym0.4.0hhbaahaaabWLAhhwdepsmhexpmmphoenixm1.5.6lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejabWLAhhwdepsmhexpmmplugm1.1.0lhmhexpmmcowboymcowboym~> 1.0wtruej*bWLAhhwdepsmhexpmmranchm1.5.0jDbWLAhhw timestampmhexpmmcowlibm1.3.0hhbaahaaa.WbWLAhhwinner_checksummhexpmmplugm1.0.0m ߶ 3]gN{a!<0bWLAhhwretiredmhexpmmcowlibm2.7.2twmessagem+Published with invalid version in .app filewreasonwRETIRED_INVALIDYbWLAhhwinner_checksummhexpmmcowboym2.2.0m R3xk;{^K| a杈vcbWLAhhwouter_checksummhexpmmcowboy_telemetrym0.3.1m :n3f7,J}<:I#;]pcEbWLAhhw timestampmhexpmmphoenixm0.2.7hhba a ha aa 7bWLAhhwretiredmhexpmm plug_cowboym2.5.2wnil`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.13.3m ӗ1TWm%u_U7%gFbWLAhhw timestampmhexpmmphoenixm1.4.11hhba a ha aa bWLAhhwdepsmhexpmmcowboym1.0.1lhmhexpmmcowlibmcowlibm~> 1.0.0wfalsehmhexpmmranchmranchm~> 1.0wfalsejbWLAhhwdepsmhexpmmphoenixm1.0.6lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0.5wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsej1bWLAhhwretiredmhexpmmranchm1.4.0wnilZbWLAhhwouter_checksummhexpmmphoenixm1.4.9m 3&oUu xOՔ_tz읰.EbWLAhhw timestampmhexpmmphoenixm1.0.4hhba a ha aa CbWLAhhw timestampmhexpmmranchm1.5.0hhba a ha aa 8bWLAhhwretiredmhexpmm phoenix_htmlm2.3.0wnilWbWLAhhwinner_checksummhexpmmplugm1.6.0m 8Lb->$rE[QTKwSo_bWLAhhwouter_checksummhexpmmphoenixm 1.3.0-rc.0m 5PN 1.0 or ~> 2.3wtruehmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.6.2 or ~> 1.7wfalsej1bWLAhhwretiredmhexpmmplugm1.10.0wnil3bWLAhhwretiredmhexpmmcowlibm2.10.1wnilEbWLAhhw timestampmhexpmmphoenixm1.0.3hhba a ha aa FbWLAhhw timestampmhexpmmphoenixm0.2.10hhba a ha aa 8bWLAhhwretiredmhexpmm phoenix_htmlm2.1.2wnilCbWLAhhw timestampmhexpmmranchm1.6.2hhba a ha aa bWLAhhwdepsmhexpmm plug_cowboym2.1.1lhmhexpmmcowboymcowboym~> 2.5wfalsehmhexpmmplugmplugm~> 1.7wfalsej4bWLAhhwretiredmhexpmmphoenixm0.10.0wnilDbWLAhhw timestampmhexpmmcowlibm2.9.1hhbaahaaa.ZbWLAhhwouter_checksummhexpmmphoenixm1.1.0m Q>Ob S.- WbWLAhhwouter_checksummhexpmmmimem2.0.0m x%`&dƩJS+)_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.6.1m nIDb$ݲ޺`Ao>!5Ù~ XbWLAhhwinner_checksummhexpmmranchm1.8.0m z 2{dzYLDsYzXbWLAhhwouter_checksummhexpmmranchm1.8.0m Ih/]QWgm)Ym^R-_bWLAhhwouter_checksummhexpmm phoenix_htmlm3.0.2m ]RoRQy̿PCoO0bWLAhhwretiredmhexpmmplugm1.9.0wnilEbWLAhhw timestampmhexpmmphoenixm0.2.9hhba a ha aa +bWLAhhwdepsmhexpmmcowlibm2.9.0jKbWLAhhw timestampmhexpmm phoenix_htmlm2.11.2hhba a ha aa WbWLAhhwouter_checksummhexpmmplugm1.3.4m bˠ*^MujXR:_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.1.2m gKHuE?zDFx9 7to6[bWLAhhwinner_checksummhexpmmphoenixm1.4.13m g'֛QqT`OJ?a46V`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.10.2m IclL^>5,uaB1?*0bWLAhhwretiredmhexpmmplugm1.2.4wnilibWLAhhwdepsmhexpmmjasonm 1.0.0-rc.2lhmhexpmmdecimalmdecimalm~> 1.0wtruejWbWLAhhwinner_checksummhexpmmplugm1.5.0m "K%IREgpgIF [QbWLAhhw registry_etagmhexpmmphoenixm""b1c4092fd2fdcc64525a93181cd45bcd"[bWLAhhwinner_checksummhexpmmphoenixm1.5.12m u 88ji3?6CUfBbWLAhhw timestampmhexpmmplugm0.4.1hhbaahaa4a,bWLAhhwdepsmhexpmmdecimalm0.1.1j2bWLAhhwretiredmhexpmmcowboym2.3.0wnilYbWLAhhwinner_checksummhexpmmcowlibm2.7.3m  M_ γ}1FvL0bWLAhhwretiredmhexpmmplugm1.4.4wnil]bWLAhhwouter_checksummhexpmmjasonm 1.0.0-rc.1m Iл;_,n4%G)2bWLAhhwretiredmhexpmmcowboym2.9.0wnilWbWLAhhwouter_checksummhexpmmmimem1.6.0m 1a?!=`}(+ZEbWLAhhw timestampmhexpmmphoenixm0.2.0hhba a ha aa \bWLAhhwinner_checksummhexpmm telemetrym0.4.3m d(c)<٦&:f;șWbWLAhhwdepsmhexpmmphoenixm1.0.3lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsej,bWLAhhwdepsmhexpmmdecimalm1.4.1jXbWLAhhwouter_checksummhexpmmplugm0.11.0m "89fأXI?J\sT68EhGYbWLAhhwinner_checksummhexpmmcowboym2.6.0m 5Lj>>39mܻB#dmbWLAhhwdepsmhexpmmphoenixm0.7.2lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 0.9.0wfalsehmhexpmmpoisonmpoisonm~> 1.2wfalsejWbWLAhhwouter_checksummhexpmmplugm0.4.1m Hd,dÎY7qrwܩ:bWLAhhwdepsmhexpmmphoenixm1.2.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.1wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsej_bWLAhhwouter_checksummhexpmm phoenix_htmlm1.0.0m Eg^-"t"ޟR ʻu;ɒXw\(FbWLAhhw timestampmhexpmmphoenixm1.4.14hhba a ha aa ZbWLAhhwouter_checksummhexpmmphoenixm1.1.6m %Gd1D p4~ M_$#5* )4*bWLAhhwdepsmhexpmmranchm1.6.0jWbWLAhhwouter_checksummhexpmmplugm1.4.1m ʕ'PpL]MLH,kOZbWLAhhwouter_checksummhexpmmcowlibm2.10.1m Bq(q֘ꣳ.ihD<\tDFbWLAhhwdepsmhexpmmphoenixm1.2.3lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm6~> 1.4 or ~> 1.3.3 or ~> 1.2.4 or ~> 1.1.8 or ~> 1.0.5wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsej+bWLAhhwdepsmhexpmmpoisonm2.2.0jYbWLAhhwouter_checksummhexpmmpoisonm1.4.0m qZI99:N֝P`bfCbWLAhhw timestampmhexpmmjasonm1.0.1hhbaaha aa:EbWLAhhw timestampmhexpmmphoenixm1.0.1hhba a ha aa `bWLAhhwouter_checksummhexpmm phoenix_htmlm2.14.3m ֗ZkCۈGˣS}_+bWLAhhwdepsmhexpmmpoisonm1.2.0j7bWLAhhwretiredmhexpmm plug_cowboym2.0.2wnilZbWLAhhwinner_checksummhexpmmdecimalm1.5.0m C:6C=P)e<7of[CיchHeh_bWLAhhwouter_checksummhexpmmphoenixm 1.6.0-rc.0m * 4M*/eJ8!v.dooI_bWLAhhwinner_checksummhexpmm phoenix_htmlm3.0.2m q}_!"^d*05bWLAhhwretiredmhexpmmplugm 1.4.0-rc.0wnilXbWLAhhwouter_checksummhexpmmplugm1.12.0m RnCKhʙ8F;_bWLAhhwouter_checksummhexpmmdecimalm 1.9.0-rc.0m fuEsW\^KE0|dX< GYbWLAhhwouter_checksummhexpmmcowboym2.1.0m š6h2ߊ 6r&IbWLAhhw timestampmhexpmm plug_cowboym2.1.1hhba a ha aa 5bWLAhhwretiredmhexpmm telemetrym1.0.0wnilWbWLAhhwinner_checksummhexpmmplugm1.0.6m dpBcOfXeyu6LCelIYbWLAhhwinner_checksummhexpmmcowlibm2.6.0m )aܘ"C&%<~lHUI,bWLAhhwdepsmhexpmmcowlibm2.10.0j`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.11.0m m[\VB3x2ba~Weڡl83bWLAhhwretiredmhexpmmphoenixm0.6.1wnilZbWLAhhwouter_checksummhexpmmdecimalm1.8.0m RiNn`PsLfXO[}1xfc2bWLAhhwretiredmhexpmmcowboym2.6.1wnilEbWLAhhw timestampmhexpmmdecimalm0.2.3hhbaaha aa:YbWLAhhwinner_checksummhexpmmcowlibm2.0.0m —qG=RX#-S4 )P4bWLAhhwretiredmhexpmmphoenixm0.15.0wnilWbWLAhhwinner_checksummhexpmmplugm0.8.0m aC-N`|~<>&ҸEX.ęBbWLAhhw timestampmhexpmmplugm1.2.2hhbaahaa4abWLAhhwdepsmhexpmmphoenixm1.4.3lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruej7bWLAhhwretiredmhexpmm plug_cryptom1.1.2wnil.bWLAhhwdepsmhexpmm telemetrym0.1.0jEbWLAhhw timestampmhexpmmphoenixm1.4.3hhba a ha aa *bWLAhhwdepsmhexpmmranchm1.7.1jYbWLAhhwouter_checksummhexpmmcowboym1.0.1m  injY?Ka8'ũJbWLAhhw timestampmhexpmmphoenixm 1.4.0-rc.3hhba a ha aa `bWLAhhwinner_checksummhexpmm phoenix_htmlm2.13.4m =nFd$JI' P*}mء[OEbWLAhhw timestampmhexpmmphoenixm0.7.0hhba a ha aa WbWLAhhwinner_checksummhexpmmplugm1.1.6m '3Y8|(71'3PfZbWLAhhwouter_checksummhexpmmphoenixm0.2.8m YyT%#E*i| nfq;ЍނXbWLAhhwinner_checksummhexpmmplugm0.13.0m /:.Gűw υIl4KbWLAhhwdepsmhexpmmplugm1.4.5lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsej^bWLAhhwinner_checksummhexpmm plug_cryptom1.1.0m HCՐbOH%oGU]ɇT.XbWLAhhwinner_checksummhexpmmplugm1.10.1m jm-XY;T ҇@ZbWLAhhwinner_checksummhexpmmphoenixm1.4.3m Jd7,4rK֑Rv7]g}BbWLAhhw timestampmhexpmmplugm1.0.1hhbaahaa4a_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.6.2m J^X OLiP>0[(D9%>1bWLAhhwretiredmhexpmmplugm1.11.1wnil1bWLAhhwretiredmhexpmmplugm0.11.3wnilXbWLAhhwinner_checksummhexpmmjasonm1.2.2m CpѪΐi\Vx?REbWLAhhw timestampmhexpmmphoenixm1.3.1hhba a ha aa YbWLAhhwouter_checksummhexpmmpoisonm3.0.0m = /MBi'zC$氏|'!ZbWLAhhwouter_checksummhexpmmphoenixm1.3.1m e+a!= ETsyFML2hX2(CbWLAhhw timestampmhexpmmjasonm1.2.0hhbaaha aa:YbWLAhhwouter_checksummhexpmmcowlibm2.5.0m Ivs`ZjNP" NXbWLAhhwouter_checksummhexpmmplugm0.14.0m qh5Y#oAf]h0=_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.9.2m 7` Co񚮘dbw:[.``bWLAhhwinner_checksummhexpmm phoenix_htmlm2.10.5m Ot"D s*VlN3|(O abWLAhhwdepsmhexpmmplugm1.1.3lhmhexpmmcowboymcowboym~> 1.0wtruejEbWLAhhw timestampmhexpmmphoenixm0.2.5hhba a ha aa 0bWLAhhwretiredmhexpmmmimem1.3.1wnilYbWLAhhwinner_checksummhexpmmcowboym1.0.1m  Q˻u 4R eh3]@DbWLAhhw timestampmhexpmmpoisonm1.0.1hhba a ha a.a^bWLAhhwinner_checksummhexpmm plug_cryptom1.2.2m eEq2$G}`C@jM"tҀyMEbWLAhhw timestampmhexpmmdecimalm0.2.5hhbaaha aa:_bWLAhhwouter_checksummhexpmmphoenixm 1.2.0-rc.0m ߒҏ&=b Oֵ> #iZn`ZSakPZbWLAhhwouter_checksummhexpmmdecimalm1.7.0m wvPZՊOW\_`e],bWLAhhwdepsmhexpmmdecimalm1.0.1jZbWLAhhwinner_checksummhexpmmphoenixm0.4.0m *S]PoH39B;(X5uhj2bWLAhhwretiredmhexpmmpoisonm2.0.0wnilCbWLAhhw timestampmhexpmmranchm1.7.1hhba a ha aa [bWLAhhwouter_checksummhexpmmphoenixm0.16.0m !,@X _F(YbWLAhhwinner_checksummhexpmmpoisonm3.0.0m b^d:RMl|ȶ- x(ƒ 1YbWLAhhwinner_checksummhexpmmcowboym2.1.0m i;#Ji5q0k9͡ł>RX7bWLAhhwretiredmhexpmm plug_cowboym2.3.0wnilWbWLAhhwinner_checksummhexpmmplugm1.6.2m j{һm]ݕp ΈSQ"KَIbWLAhhw timestampmhexpmm plug_cowboym2.1.0hhba a ha aa WbWLAhhwinner_checksummhexpmmplugm1.0.2m 4)ȏưMq/gV@Lꗦ1:Iʨ#nXbWLAhhwouter_checksummhexpmmplugm1.10.2m xGgﳹ%p^f;ō*ؚZ0bWLAhhwretiredmhexpmmplugm1.3.6wnilbWLAhhwdepsmhexpmm plug_cowboym2.5.0lhmhexpmmcowboymcowboym~> 2.7wfalsehmhexpmmcowboy_telemetrymcowboy_telemetrym~> 0.3wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejbWLAhhwdepsmhexpmmcowboym2.1.0lhmhexpmmcowlibmcowlibm~> 2.0.1wfalsehmhexpmmranchmranchm~> 1.4.0wfalsejubWLAhhwdepsmhexpmm phoenix_htmlm1.2.1lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0wfalsejWbWLAhhwinner_checksummhexpmmplugm1.8.0m &٬'(&+x )oCwLbWLAhhw timestampmhexpmmphoenix_pubsubm0.1.0hhba a ha aa BbWLAhhw timestampmhexpmmplugm1.3.4hhbaahaa4aJbWLAhhw timestampmhexpmm phoenix_htmlm3.0.2hhba a ha aa dbWLAhhwdepsmhexpmmjasonm1.0.0lhmhexpmmdecimalmdecimalm~> 1.0wtruej2bWLAhhwretiredmhexpmmcowlibm2.6.0wnilNbWLAhhw registry_etagmhexpmmplugm""4d85e7c2ce3c20c0441d3cb07bf25c4c"gbWLAhhwdepsmhexpmm phoenix_htmlm2.11.2lhmhexpmmplugmplugm~> 1.5wfalsejBbWLAhhw timestampmhexpmmplugm1.7.1hhbaahaa4a9bWLAhhwretiredmhexpmm phoenix_htmlm2.13.3wnilIbWLAhhw timestampmhexpmm plug_cryptom1.2.2hhbaahaaa._bWLAhhwinner_checksummhexpmm phoenix_htmlm2.6.1m }S= zn$Ÿf0I["7 R}2bWLAhhwretiredmhexpmmpoisonm2.1.0wnilYbWLAhhwouter_checksummhexpmmcowlibm2.0.1m t\"gu"!qdr ]bWLAhhwinner_checksummhexpmmranchm 2.0.0-rc.1m J^.CD[jo[>cbWLAhhwdepsmhexpmmplugm0.7.0lhmhexpmmcowboymcowboym~> 1.0.0wtruejDbWLAhhw timestampmhexpmmcowlibm2.0.1hhbaahaaa.ZbWLAhhwouter_checksummhexpmmcowlibm2.11.0m +>EeumI L\ ҫm҇9bWLAhhwretiredmhexpmm phoenix_htmlm2.12.0wnil&t'bWLAhhwdepsmhexpmmplugm1.10.2lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej_bWLAhhwinner_checksummhexpmmphoenixm 1.6.0-rc.0m Xx,-"}1 t 4f?p4bWLAhhwretiredmhexpmmphoenixm0.14.0wnilZbWLAhhwouter_checksummhexpmmdecimalm1.4.0m vqN/q5 :=8K-zEFq#bWLAhhwdepsmhexpmmcowboym2.3.0lhmhexpmmcowlibmcowlibm~> 2.2.1wfalsehmhexpmmranchmranchm~> 1.4.0wfalsejbWLAhhwdepsmhexpmmplugm1.3.1lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsejWbWLAhhwouter_checksummhexpmmplugm1.6.0m vΛ6ѝ\U^/À.HZbWLAhhwouter_checksummhexpmmphoenixm1.4.7m S>ke5qN4'ՠ$]my'bWLAhhwdepsmhexpmmphoenixm 1.3.0-rc.2lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.3.2 or ~> 1.4wfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0wfalsej0bWLAhhwretiredmhexpmmplugm1.1.0wnilabWLAhhwversionsmhexpmm 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.3jBbWLAhhw timestampmhexpmmplugm1.9.0hhbaahaa4abWLAhhwdepsmhexpmmphoenixm0.2.4lhmhexpmmex_confmex_confm0.1.1wfalsehmhexpmminflexminflexm0.2.0wfalsehmhexpmmplugmplugm0.4.3wfalsejDbWLAhhw timestampmhexpmmpoisonm1.1.0hhba a ha a.abWLAhhwdepsmhexpmmphoenixm1.1.8lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0.5wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsej_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.6.0m =cEGw=v2ztUiYbWLAhhwouter_checksummhexpmmpoisonm1.0.1m a4&)•C}  3bWLAhhwretiredmhexpmmdecimalm1.7.0wnilBbWLAhhw timestampmhexpmmmimem1.0.1hhbaahaaa.FbWLAhhw timestampmhexpmmphoenixm0.15.0hhba a ha aa bWLAhhwdepsmhexpmmcowboym2.4.0lhmhexpmmcowlibmcowlibm~> 2.3.0wfalsehmhexpmmranchmranchm~> 1.5.0wfalsej3bWLAhhwretiredmhexpmmphoenixm1.4.2wnil,bWLAhhwdepsmhexpmmcowlibm2.10.1jYbWLAhhwouter_checksummhexpmmpoisonm3.1.0m fs>{W83vmωTGZbWLAhhwouter_checksummhexpmmdecimalm1.9.0m 45h֒>u[] o}BbWLAhhw timestampmhexpmmmimem1.0.0hhbaahaaa.CbWLAhhw timestampmhexpmmplugm0.11.2hhbaahaa4aHbWLAhhw timestampmhexpmmjasonm 1.0.0-rc.2hhbaaha aa:0bWLAhhwretiredmhexpmmplugm0.8.0wnilYbWLAhhwouter_checksummhexpmmpoisonm1.1.0m 8ws@] ૢfS cbWLAhhwouter_checksummhexpmmcowboy_telemetrym0.3.0m B")P]Un$b"O!gSFbWLAhhw timestampmhexpmmphoenixm1.4.10hhba a ha aa 4bWLAhhwretiredmhexpmmphoenixm0.2.11wnil3bWLAhhwretiredmhexpmmphoenixm0.2.1wnilWbWLAhhwinner_checksummhexpmmplugm0.7.0m ;\W/ppe5ȖL{dhLbWLAhhw timestampmhexpmmphoenix_pubsubm1.1.0hhba a ha aa 0bWLAhhwretiredmhexpmmplugm1.2.1wnil3bWLAhhwretiredmhexpmmphoenixm0.2.0wnilYbWLAhhwouter_checksummhexpmmcowboym2.2.1m J[S7XJ6ѐ⺄^ק]=EbWLAhhw timestampmhexpmmphoenixm1.5.7hhba a ha aa WbWLAhhwinner_checksummhexpmmplugm0.4.4m ÃnOWs˧'S/M)WW)bWLAhhwdepsmhexpmmplugm0.5.2j)bWLAhhwdepsmhexpmmmimem1.0.1jYbWLAhhwouter_checksummhexpmmcowboym2.9.0m ,rKNI|crS T\g0bWLAhhwretiredmhexpmmplugm0.4.4wnilXbWLAhhwinner_checksummhexpmmplugm0.11.1m +.W)yyڴ8w1q.o4&8bWLAhhwretiredmhexpmm phoenix_htmlm1.2.0wnil2bWLAhhwretiredmhexpmmcowboym1.0.2wnilbbWLAhhwdepsmhexpmmplugm0.11.2lhmhexpmmcowboymcowboym~> 1.0wtruejbWLAhhwdepsmhexpmmcowboym2.5.0lhmhexpmmcowlibmcowlibm~> 2.6.0wfalsehmhexpmmranchmranchm~> 1.6.2wfalsejWbWLAhhwouter_checksummhexpmmplugm0.4.2m Anۅm 4 u4:bWLAhhwretiredmhexpmmphoenix_pubsubm1.0.2wnilbWLAhhwversionsmhexpmmranchlm1.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.0jWbWLAhhwouter_checksummhexpmmplugm1.5.0m ͜\c~:ɿPtovͲױΓԠ.RZbWLAhhwouter_checksummhexpmmphoenixm1.0.3m e^c hwK>r6)BdڙabWLAhhwouter_checksummhexpmmphoenix_pubsubm0.0.1m SieZc6@1oW: ^bWLAhhwouter_checksummhexpmm plug_cowboym2.0.0m ?IbЙ$m|/.j) ns:tWbWLAhhwouter_checksummhexpmmplugm1.0.6m mK-7e ͦS'ZbWLAhhwouter_checksummhexpmmphoenixm1.3.3m (& 1A E-;4sN0]J+JbWLAhhw timestampmhexpmm phoenix_htmlm1.0.1hhba a ha aa ZbWLAhhwinner_checksummhexpmmphoenixm1.1.3m ZK`c`o^݆ p*<,azl3bWLAhhwretiredmhexpmmphoenixm1.0.4wnilEbWLAhhw timestampmhexpmmphoenixm0.2.8hhba a ha aa ZbWLAhhwinner_checksummhexpmmdecimalm1.7.0m 0ֵ,TfcsYPߞf S_~ŪcbWLAhhwouter_checksummhexpmm phoenix_htmlm 2.0.0-devm 0WyʀZPlϠG!ȪYbWLAhhwinner_checksummhexpmmcowboym2.8.0m bW٬Pta˜@SɥW{׼f}YbWLAhhwouter_checksummhexpmmcowboym2.4.0m ]wYG09uf++]AJ\bWLAhhwinner_checksummhexpmm telemetrym0.1.0m s 1 3|m*c8y+A<ZbWLAhhwouter_checksummhexpmmphoenixm0.5.0m /ߌͥB( P fk.OEbWLAhhw timestampmhexpmmphoenixm0.8.0hhba a ha aa cbWLAhhwdepsmhexpmmplugm0.6.0lhmhexpmmcowboymcowboym~> 1.0.0wtruejZbWLAhhwinner_checksummhexpmmphoenixm0.3.0m |&QP?Xߛ*, b\^G>&݌ZbWLAhhwinner_checksummhexpmmphoenixm1.3.0m LJzo PgA]$>ʼn+bWLAhhwdepsmhexpmmcowlibm2.5.1jXbWLAhhwouter_checksummhexpmmjasonm1.2.1m YW`Z`A@DqoiPbWLAhhwdepsmhexpmmphoenixm 1.2.0-rc.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmmimemmimem~> 1.0wfalsehmhexpmmphoenix_pubsubmphoenix_pubsubm ~> 1.0.0-rcwfalsehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsej3bWLAhhwdepsmhexpmmphoenix_pubsubm1.0.1jDbWLAhhw timestampmhexpmmcowlibm2.7.1hhbaahaaa.ZbWLAhhwinner_checksummhexpmmdecimalm1.4.0m eqjSӦEf(Ze.J35bWLAhhwretiredmhexpmm telemetrym0.4.0wnilXbWLAhhwouter_checksummhexpmmplugm0.11.3m O0% Ԣq\%V#4^Ie]bWLAhhwinner_checksummhexpmmjasonm 1.0.0-rc.2m *]f?v7@.FV)^bWLAhhwouter_checksummhexpmm plug_cowboym1.0.0m BzD$eE"c2u@Ja_bWLAhhwouter_checksummhexpmm phoenix_htmlm3.0.1m PUi]O[vkT z.:+bWLAhhwdepsmhexpmmpoisonm3.1.0jJbWLAhhw timestampmhexpmmphoenixm 1.2.0-rc.1hhba a ha aa 3bWLAhhwretiredmhexpmmdecimalm1.8.0wnil 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0wfalsej:bWLAhhwretiredmhexpmmphoenix_pubsubm0.1.0wnilEbWLAhhw timestampmhexpmmphoenixm0.3.0hhba a ha aa BbWLAhhw timestampmhexpmmplugm1.5.0hhbaahaa4aJbWLAhhw timestampmhexpmmdecimalm 2.0.0-rc.0hhbaaha aa:\bWLAhhwinner_checksummhexpmmplugm 1.2.0-rc.0m Yqr}Ђc.4wp[bWLAhhwinner_checksummhexpmmphoenixm0.16.1m 6F''-apJBo1tdq:3bWLAhhwretiredmhexpmmphoenixm0.8.0wnil3bWLAhhwretiredmhexpmmdecimalm1.2.0wnilWbWLAhhwouter_checksummhexpmmplugm1.6.3m o/J-s09+ %UpwJ`QiZbWLAhhwinner_checksummhexpmmdecimalm0.1.1m }VRqtgNpjY$YbWLAhhwinner_checksummhexpmmcowlibm2.9.0m esj*td ˢIKBbWLAhhw timestampmhexpmmplugm1.1.0hhbaahaa4abWLAhhwdepsmhexpmmplugm1.3.6lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsejZbWLAhhwinner_checksummhexpmmphoenixm1.2.4m ArG^!j^AuH 9 .c[YbWLAhhwinner_checksummhexpmmpoisonm2.0.1m $6Ѷ~UkRI QsYbWLAhhwinner_checksummhexpmmcowboym1.0.0m дj^_UCFokV}RbR|dU]^bWLAhhwinner_checksummhexpmm plug_cowboym2.1.2m ݵR8ÎD.'2F]|Gŀ^'bWLAhhwdepsmhexpmmphoenixm 1.3.0-rc.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.3.2 or ~> 1.4wfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0wfalsej8bWLAhhwretiredmhexpmmphoenixm 1.5.0-rc.0wnil3bWLAhhwretiredmhexpmmdecimalm0.2.0wnilKbWLAhhw timestampmhexpmm phoenix_htmlm2.13.3hhba a ha aa JbWLAhhw timestampmhexpmmphoenixm 1.6.0-rc.0hhba a ha aa ,bWLAhhwdepsmhexpmmdecimalm0.2.1j[bWLAhhwouter_checksummhexpmmphoenixm0.10.0m @` L)-%T2bWLAhhwretiredmhexpmmcowboym1.1.2wnilYbWLAhhwinner_checksummhexpmmcowboym2.3.0m &)HoAUdC(KVѐxOVbG$+bWLAhhwdepsmhexpmmpoisonm4.0.1j)bWLAhhwdepsmhexpmmplugm0.5.0j_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.8.0m V -*J= z%߻!BbWLAhhw timestampmhexpmmmimem2.0.1hhbaahaaa.bWLAhhwdepsmhexpmmphoenixm0.13.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm>= 0.12.2 and < 2.0.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsehmhexpmmpoolboympoolboym~> 1.5.1 or ~> 1.6wtruejCbWLAhhw timestampmhexpmmjasonm1.2.1hhbaaha aa:2bWLAhhwretiredmhexpmmpoisonm1.5.0wnilZbWLAhhwinner_checksummhexpmmphoenixm1.5.4m ʜ`Ic _%[ g~p?ZbWLAhhwouter_checksummhexpmmphoenixm1.1.5m ebHm O^<<9Ubj}še]HWbWLAhhwinner_checksummhexpmmmimem1.6.0m ڽv`ȒPV߰ YbWLAhhwouter_checksummhexpmmcowboym1.0.3m :SY]NR [=ȖJfuIdZbWLAhhwouter_checksummhexpmmphoenixm0.2.1m +glXOptDt hT9ky[9{-WbWLAhhwouter_checksummhexpmmplugm1.7.1m ڥ ċl2 b|uUM^[bWLAhhwinner_checksummhexpmmphoenixm0.14.0m q;{;  )=oR b/fbWLAhhwdepsmhexpmm phoenix_htmlm2.8.0lhmhexpmmplugmplugm~> 1.0wfalsej3bWLAhhwretiredmhexpmmdecimalm1.9.0wnilEbWLAhhw timestampmhexpmmphoenixm1.1.8hhba a ha aa bWLAhhwdepsmhexpmm plug_cowboym1.0.0lhmhexpmmcowboymcowboym~> 1.0wfalsehmhexpmmplugmplugm~> 1.7wfalsej]bWLAhhwouter_checksummhexpmmranchm 2.0.0-rc.1m P77_5#(!ݡRPDbWLAhhw timestampmhexpmmpoisonm1.1.1hhba a ha a.aEbWLAhhw timestampmhexpmmdecimalm1.6.0hhbaaha aa:WbWLAhhwinner_checksummhexpmmplugm1.3.0m n+?JBE(W Dk̒DzYbWLAhhwouter_checksummhexpmmcowboym2.3.0m Pq&'0CQ[W#h0Cg cEbWLAhhw timestampmhexpmmphoenixm1.5.5hhba a ha aa ZbWLAhhwinner_checksummhexpmmphoenixm0.6.1m 0Y 9cQ#hư(* q3bWLAhhwretiredmhexpmmphoenixm0.2.3wnil3bWLAhhwretiredmhexpmmphoenixm0.6.0wnilbWLAhhwdepsmhexpmmphoenixm0.4.1lhmhexpmmcowboymcowboym~> 1.0.0wtruehmhexpmmlinguistmlinguistm~> 0.1.2wfalsehmhexpmmplugmplugm0.7.0wfalsehmhexpmmpoisonmpoisonm~> 1.1.0wfalsejgbWLAhhwdepsmhexpmm phoenix_htmlm2.10.4lhmhexpmmplugmplugm~> 1.0wfalsejDbWLAhhw timestampmhexpmmcowboym2.5.0hhbaahaaa.1bWLAhhwretiredmhexpmmranchm1.3.0wnil3bWLAhhwretiredmhexpmmphoenixm1.3.1wnilWbWLAhhwouter_checksummhexpmmplugm1.8.0m L [8Ɵ*x}kTot$"bWLAhhwdepsmhexpmmphoenixm1.3.3lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4wfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0wfalsejEbWLAhhw timestampmhexpmmphoenixm0.2.4hhba a ha aa WbWLAhhwinner_checksummhexpmmplugm1.4.5m {sS&,^ ]$ '=ZbWLAhhwinner_checksummhexpmmdecimalm0.1.2m iڠV;j\åiTYjFbWLAhhw timestampmhexpmmphoenixm1.4.12hhba a ha aa bWLAhhwversionsmhexpmmjasonl 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.2jabWLAhhwdepsmhexpmmplugm0.8.2lhmhexpmmcowboymcowboym~> 1.0wtruejabWLAhhwdepsmhexpmmplugm1.0.4lhmhexpmmcowboymcowboym~> 1.0wtruejabWLAhhwouter_checksummhexpmmphoenix_pubsubm1.1.2m ig)tѕR@i8CbWLAhhw timestampmhexpmmjasonm1.1.2hhbaaha aa:WbWLAhhwouter_checksummhexpmmplugm0.8.0m s1eL>:cv6-+YbWLAhhwinner_checksummhexpmmpoisonm1.2.0m Am^‹vF-MHK@vgܩEbWLAhhw timestampmhexpmmphoenixm1.2.3hhba a ha aa WbWLAhhwouter_checksummhexpmmplugm0.5.1m '#ryNP9Xjk_C.j/bWLAhhwdepsmhexpmmphoenixm1.0.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsej+bWLAhhwdepsmhexpmmpoisonm1.5.0jbWLAhhwdepsmhexpmmcowboym2.2.0lhmhexpmmcowlibmcowlibm~> 2.1.0wfalsehmhexpmmranchmranchm~> 1.4.0wfalsejDbWLAhhw timestampmhexpmmcowboym2.2.0hhbaahaaa.YbWLAhhwouter_checksummhexpmmpoisonm1.0.3m c+\L.V;5+5l-i-f`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.11.2m /T06!lEZ n'BbWLAhhw timestampmhexpmmplugm1.1.3hhbaahaa4a[bWLAhhwouter_checksummhexpmmphoenixm1.4.10m %jס@)p6=|pnd"t_bWLAhhwouter_checksummhexpmm phoenix_htmlm1.4.0m jJZ:bxhk ~RsXI֟kL3bWLAhhwretiredmhexpmmphoenixm0.5.0wnil0bWLAhhwretiredmhexpmmplugm1.4.3wnil2bWLAhhwretiredmhexpmmcowboym2.8.0wnilHbWLAhhw timestampmhexpmmranchm 2.0.0-rc.1hhba a ha aa ZbWLAhhwinner_checksummhexpmmdecimalm1.4.1m Ps""|* ږ[ p*bWLAhhwdepsmhexpmmranchm2.1.0j8bWLAhhwretiredmhexpmm phoenix_htmlm2.6.2wnilbWLAhhwdepsmhexpmmcowboym2.6.0lhmhexpmmcowlibmcowlibm~> 2.7.0wfalsehmhexpmmranchmranchm~> 1.7.0wfalsejCbWLAhhw timestampmhexpmmplugm0.14.0hhbaahaa4abWLAhhwdepsmhexpmmcowboy_telemetrym0.1.0lhmhexpmmcowboymcowboym~> 2.7wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej:bWLAhhwretiredmhexpmmphoenix_pubsubm1.1.0wnil:bWLAhhw timestampmhexpmmpoisonhhba a ha a.aBbWLAhhw timestampmhexpmmmimem2.0.0hhbaahaaa.YbWLAhhwinner_checksummhexpmmcowboym2.5.0m Nn'.܏CGV7bWLAhhwdepsmhexpmmphoenixm1.1.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejDbWLAhhw timestampmhexpmmcowboy_telemetryhhbaahaaabWLAhhwdepsmhexpmmphoenixm1.4.2lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruejWbWLAhhwinner_checksummhexpmmmimem1.3.0m ^EP O`J4Ǣ QQ3bWLAhhwretiredmhexpmmphoenixm1.4.8wnilWbWLAhhwinner_checksummhexpmmplugm0.5.3m ͢Q:ID+Y1g%8RMDbWLAhhw timestampmhexpmmpoisonm1.5.2hhba a ha a.aJbWLAhhw timestampmhexpmm phoenix_htmlm2.9.2hhba a ha aa 2bWLAhhwretiredmhexpmmcowlibm1.2.0wnil)bWLAhhwdepsmhexpmmplugm0.4.4j;bWLAhhw timestampmhexpmmphoenixhhba a ha aa JbWLAhhw timestampmhexpmmphoenixm 1.5.0-rc.0hhba a ha aa gbWLAhhwdepsmhexpmm phoenix_htmlm2.10.2lhmhexpmmplugmplugm~> 1.0wfalsej^bWLAhhwouter_checksummhexpmm plug_cowboym2.4.1m !z}*[h(㩧R~P2@SPbWLAhhw registry_etagmhexpmmcowlibm""02967331d4715db91bfad19c64a3a0fa"WbWLAhhwinner_checksummhexpmmplugm1.0.4m ׼fFOshM$L3mYbWLAhhwouter_checksummhexpmmpoisonm2.0.0m \um'A ߫' إCq_bWLAhhwinner_checksummhexpmmphoenixm 1.3.0-rc.1m KH#L^Xeu7bWLAhhwdepsmhexpmmplugm 1.2.0-rc.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmmimemmimem~> 1.0wfalsejZbWLAhhwinner_checksummhexpmmphoenixm0.5.0m tlAe-8æ%Z6@*0bWLAhhwretiredmhexpmmplugm0.5.2wnilZbWLAhhwinner_checksummhexpmmphoenixm1.3.1m ܟ(``Kۓ L𝳁s3b7bWLAhhwretiredmhexpmm plug_cowboym2.4.0wnilbWLAhhwversionsmhexpmm 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.2jYbWLAhhwinner_checksummhexpmmcowlibm2.5.1m 2M5GgϘL/;šZC `GW3bWLAhhwretiredmhexpmmphoenixm1.4.1wnilXbWLAhhwouter_checksummhexpmmplugm1.12.1m ~yw{mڑJ qeE*cL 4bWLAhhwretiredmhexpmmphoenixm1.4.16wnilKbWLAhhw timestampmhexpmm phoenix_htmlm2.12.0hhba a ha aa 4bWLAhhwretiredmhexpmmphoenixm0.16.0wnil\bWLAhhwouter_checksummhexpmm telemetrym0.4.2m -j m{XRr.+#b {*5bWLAhhwretiredmhexpmm telemetrym0.4.3wnil0bWLAhhwretiredmhexpmmplugm1.5.0wnilObWLAhhw registry_etagmhexpmmjasonm""c7ccb0b0d3701498a0a717862ef103b1"WbWLAhhwinner_checksummhexpmmplugm1.5.1m [϶5^LG0?l)ҧoASeZbWLAhhwinner_checksummhexpmmdecimalm0.2.1m U%qs]A27MDbWLAhhw timestampmhexpmmcowlibm1.0.0hhbaahaaa.WbWLAhhwinner_checksummhexpmmmimem2.0.0m ާpxxFب?fG V*bWLAhhwdepsmhexpmmranchm1.1.0j_bWLAhhwouter_checksummhexpmmphoenixm 1.2.0-rc.1m _ʨul! ifʂA,+bWLAhhwdepsmhexpmmcowlibm2.9.1jWbWLAhhwouter_checksummhexpmmplugm1.0.1m Lq ۖ}|e7;d0bWLAhhwretiredmhexpmmplugm0.4.1wnil)bWLAhhwdepsmhexpmmmimem1.3.1j_bWLAhhwinner_checksummhexpmm phoenix_htmlm3.0.0m HkR!p;i(InE*+bWLAhhwdepsmhexpmmphoenixm0.4.0lhmhexpmmcowboymcowboym~> 1.0.0wtruehmhexpmmlinguistmlinguistm~> 0.1.2wfalsehmhexpmmplugmplugm0.7.0wfalsehmhexpmmpoisonmpoisonm~> 1.0.1wfalsej:bWLAhhwretiredmhexpmmphoenix_pubsubm1.1.2wnilZbWLAhhwinner_checksummhexpmmphoenixm0.6.0m wކM` 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4wfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0wfalsejBbWLAhhw timestampmhexpmmplugm0.8.1hhbaahaa4a2bWLAhhwretiredmhexpmmcowboym2.5.0wnilCbWLAhhw timestampmhexpmmranchm1.6.0hhba a ha aa XbWLAhhwouter_checksummhexpmmplugm0.13.1m P|u>p;y%N}퐉EwI @bWLAhhw timestampmhexpmm phoenix_htmlhhba a ha aa BbWLAhhw timestampmhexpmmplugm1.3.2hhbaahaa4aYbWLAhhwinner_checksummhexpmmcowboym2.0.0m loVQwp(ml$5a8bWLAhhw timestampmhexpmmmimehhbaahaaa.EbWLAhhw timestampmhexpmmphoenixm1.3.2hhba a ha aa ZbWLAhhwouter_checksummhexpmmphoenixm0.8.0m dgz1R,GdSv*>_bWLAhhwinner_checksummhexpmm phoenix_htmlm1.4.0m 9܍u7| ?׮:jzbWLAhhwdepsmhexpmmplugm1.12.1lhmhexpmmmimemmimem~> 1.0 or ~> 2.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4.3 or ~> 1.0wfalsej+bWLAhhwdepsmhexpmmpoisonm1.0.3jJbWLAhhw timestampmhexpmm phoenix_htmlm1.2.0hhba a ha aa DbWLAhhw timestampmhexpmmpoisonm1.5.1hhba a ha a.aZbWLAhhwouter_checksummhexpmmdecimalm0.2.1m v2 T80W'u̶J2UjWz cc "EbWLAhhw timestampmhexpmmdecimalm1.8.0hhbaaha aa:ZbWLAhhwouter_checksummhexpmmphoenixm1.4.8m 9e" MrJgjt--0bWLAhhwdepsmhexpmm plug_cryptom1.0.0jabWLAhhwdepsmhexpmmplugm1.1.5lhmhexpmmcowboymcowboym~> 1.0wtruej`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.13.3m )/%_LToS`·к38gZbWLAhhwinner_checksummhexpmmphoenixm1.1.6m {fi/Z0ڷ=b(ս*BkGIbWLAhhw timestampmhexpmm plug_cowboym2.4.1hhba a ha aa KbWLAhhw timestampmhexpmm phoenix_htmlm2.11.0hhba a ha aa ZbWLAhhwouter_checksummhexpmmphoenixm1.5.7m wLDţx+ j {| x9bWLAhhwretiredmhexpmm phoenix_htmlm2.10.5wnil3bWLAhhwretiredmhexpmmphoenixm1.4.4wnilWbWLAhhwinner_checksummhexpmmplugm0.8.3m ˯eF[q|C8S>!VTEbWLAhhw timestampmhexpmmphoenixm0.7.2hhba a ha aa ZbWLAhhwinner_checksummhexpmmdecimalm1.1.0m 33s/}z_ 0-g\ʁ*c>rGbWLAhhw timestampmhexpmmplugm 1.2.0-rc.0hhbaahaa4aXbWLAhhwinner_checksummhexpmmranchm1.5.0m AfVy*'Eux<+&ӟjɣӥbWLAhhwdepsmhexpmmphoenixm0.11.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm ~> 0.11.3wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsehmhexpmmpoolboympoolboym~> 1.4.2wtruejYbWLAhhwouter_checksummhexpmmcowboym2.5.0m v=%3vqW>,,δ )908bWLAhhwretiredmhexpmm phoenix_htmlm2.7.0wnil5bWLAhhwretiredmhexpmm telemetrym0.2.0wnilDbWLAhhw timestampmhexpmmpoisonm1.5.0hhba a ha a.aBbWLAhhw timestampmhexpmmplugm0.4.3hhbaahaa4aJbWLAhhw timestampmhexpmm phoenix_htmlm2.1.2hhba a ha aa BbWLAhhw timestampmhexpmmplugm1.7.2hhbaahaa4aYbWLAhhwinner_checksummhexpmmcowlibm1.2.0m / VE;>-b' N\,``bWLAhhwinner_checksummhexpmm phoenix_htmlm2.13.1m KS(ߠUi7K[ /ZbWLAhhwouter_checksummhexpmmphoenixm0.2.2m hm1%Ũ1oLR2/(8MXcoWbWLAhhwinner_checksummhexpmmplugm1.3.6m ߔKø~Y+QJ _DbWLAhhw timestampmhexpmmcowboym1.0.0hhbaahaaa.bWLAhhwdepsmhexpmmphoenixm0.12.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm ~> 0.12.1wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsehmhexpmmpoolboympoolboym~> 1.5.1wtruejWbWLAhhwinner_checksummhexpmmplugm0.9.0m c"&Ù]!UWrƒD3bWLAhhwretiredmhexpmmdecimalm1.6.0wnilbWLAhhwdepsmhexpmmplugm1.2.6lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmmimemmimem~> 1.0wfalsej5bWLAhhwretiredmhexpmmplugm 1.5.0-rc.2wnilbWLAhhwdepsmhexpmmphoenixm0.13.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm>= 0.12.2 and < 2.0.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsehmhexpmmpoolboympoolboym~> 1.5.1 or ~> 1.6wtruej)bWLAhhwdepsmhexpmmmimem1.5.0jbWLAhhwdepsmhexpmmphoenixm1.4.5lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruejZbWLAhhwouter_checksummhexpmmphoenixm0.2.9m x*;D4Dc~| d]5bWLAhhwretiredmhexpmm telemetrym0.3.0wnilCbWLAhhw timestampmhexpmmplugm1.10.0hhbaahaa4aWbWLAhhwinner_checksummhexpmmplugm1.8.2m ڤ I9@wa\;Y t@BbWLAhhw timestampmhexpmmmimem0.0.1hhbaahaaa.NbWLAhhw timestampmhexpmmcowboy_telemetrym0.1.0hhbaahaaaYbWLAhhwouter_checksummhexpmmpoisonm1.3.0m v QN F%\B6Üq>N_bWLAhhwdepsmhexpmmphoenixm0.6.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmlinguistmlinguistm~> 0.1.4wfalsehmhexpmmplugmplugm~> 0.8.4wfalsehmhexpmmpoisonmpoisonm~> 1.2wfalsej0bWLAhhwretiredmhexpmmmimem1.0.1wnil0bWLAhhwretiredmhexpmmplugm1.0.4wnil_bWLAhhwouter_checksummhexpmm phoenix_htmlm1.2.0m QR«`ǭ}10R~2*NЇlv3bWLAhhwdepsmhexpmmphoenix_pubsubm2.0.0jGbWLAhhw timestampmhexpmm telemetrym0.1.0hhbaahaa4a[bWLAhhwouter_checksummhexpmmphoenixm1.4.17m :]z=vR_n{7cԮ3bWLAhhwretiredmhexpmmdecimalm1.4.0wnil\bWLAhhwinner_checksummhexpmmplugm 1.5.0-rc.2m ; _A2heu=fog%WbWLAhhwinner_checksummhexpmmplugm1.1.8m ۋmyRDjp2CabWLAhhwdepsmhexpmmplugm1.1.1lhmhexpmmcowboymcowboym~> 1.0wtruej2bWLAhhwretiredmhexpmmpoisonm4.0.0wnilDbWLAhhw timestampmhexpmmpoisonm4.0.1hhba a ha a.aXbWLAhhwinner_checksummhexpmmjasonm1.1.1m ̸@߰o/56MH#X^8bWLAhhwretiredmhexpmm phoenix_htmlm2.2.0wnilJbWLAhhw timestampmhexpmm phoenix_htmlm2.6.2hhba a ha aa BbWLAhhw timestampmhexpmmplugm1.1.1hhbaahaa4a^bWLAhhwinner_checksummhexpmm plug_cryptom1.2.0m c3ף_ӚWgm-WbWLAhhwinner_checksummhexpmmplugm1.4.3m #mw{f*o6jIIJWbWLAhhwinner_checksummhexpmmplugm1.2.2m Ͻ!L?퍏K͛bWLAhhwdepsmhexpmmphoenixm1.1.6lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsej3bWLAhhwretiredmhexpmmphoenixm1.5.7wnilfbWLAhhwouter_checksummhexpmmphoenix_pubsubm 1.0.0-rc.0m v\f_vM^Z"q9ۏ ;W_YbWLAhhwinner_checksummhexpmmcowlibm2.9.1m a $[P ;fW\xvbWLAhhwretiredmhexpmm plug_cowboym2.2.2twmessagemBroken telemetry supportwreasonwRETIRED_INVALID3bWLAhhwdepsmhexpmmphoenix_pubsubm1.1.1jbWLAhhwdepsmhexpmmplugm1.2.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmmimemmimem~> 1.0wfalsejabWLAhhwouter_checksummhexpmmphoenix_pubsubm1.1.1m ؐmQayܪ2DV'ŇVxulYbWLAhhwouter_checksummhexpmmcowlibm1.3.0m + 祜2'y -:|N;MɁksYbWLAhhwouter_checksummhexpmmcowboym1.1.2m v;#>o$O̍q|CWsI|EbWLAhhw timestampmhexpmmphoenixm0.4.1hhba a ha aa 0bWLAhhwretiredmhexpmmplugm1.6.3wnil_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.9.3m Z!"C$/TOx0OKLC% XX0bWLAhhwretiredmhexpmmplugm1.1.8wnil)bWLAhhwdepsmhexpmmmimem2.0.1jEbWLAhhw timestampmhexpmmdecimalm1.0.0hhbaaha aa:ZbWLAhhwinner_checksummhexpmmphoenixm0.1.0m Hyr M{=w-XbWLAhhwouter_checksummhexpmmranchm1.6.2m ex֘wkf; mE'CZbWLAhhwouter_checksummhexpmmphoenixm1.5.8m 5У/H6z;.I!%C2UCbWLAhhw timestampmhexpmmranchm1.3.2hhba a ha aa ^bWLAhhwouter_checksummhexpmm plug_cowboym2.0.2m DSݢiu| Mq 1YbWLAhhwinner_checksummhexpmmpoisonm1.1.0m ge xgkn]9+9t Q8bWLAhhwretiredmhexpmmphoenixm 1.4.0-rc.2wnilWbWLAhhwinner_checksummhexpmmplugm0.4.2m ¤X {p&ʜGbWLAhhw timestampmhexpmm telemetrym1.0.0hhbaahaa4aYbWLAhhwouter_checksummhexpmmcowlibm2.2.0m ס ;X/-fy7Y@^wZ4XbWLAhhwouter_checksummhexpmmjasonm1.0.0m l@eLc# H;g͠y8bWLAhhwretiredmhexpmm phoenix_htmlm1.0.1wnil/bWLAhhwdepsmhexpmmranchm 2.0.0-rc.1jCbWLAhhw timestampmhexpmmplugm0.13.0hhbaahaa4aZbWLAhhwinner_checksummhexpmmphoenixm1.3.3m _9災Dj,y|XUf[bWLAhhwinner_checksummhexpmmphoenixm1.4.11m b÷lz>XPڧǦ K 3bWLAhhwretiredmhexpmmphoenixm0.1.0wnil3bWLAhhwretiredmhexpmmphoenixm1.5.3wnil3bWLAhhwretiredmhexpmmdecimalm0.1.2wnilIbWLAhhw timestampmhexpmm plug_cowboym2.5.2hhba a ha aa 3bWLAhhwretiredmhexpmmphoenixm0.2.7wnil4bWLAhhwretiredmhexpmmphoenixm0.2.10wnil_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.5.0m rJPwO`.; Sk ۩ ]-A?WbWLAhhwinner_checksummhexpmmplugm1.7.0m ͌U17 HuJ%#nUbWLAhhw registry_etagmhexpmm plug_cowboym""bb2b114bf8e3996487c6fc8358247b5e"ZbWLAhhwouter_checksummhexpmmphoenixm0.6.2m ATg98TA^eVAbWLAhhwdepsmhexpmmplugm1.7.1lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0wfalsej+bWLAhhwdepsmhexpmmcowlibm2.2.0jbWLAhhwdepsmhexpmmplugm1.12.0lhmhexpmmmimemmimem~> 1.0 or ~> 2.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4 or ~> 1.0wfalsej)bWLAhhwdepsmhexpmmplugm0.4.1jibWLAhhwdepsmhexpmmjasonm 1.0.0-rc.3lhmhexpmmdecimalmdecimalm~> 1.0wtruejWbWLAhhwouter_checksummhexpmmmimem1.2.0m MpB `UQ`\([+DbWLAhhw timestampmhexpmmcowlibm2.7.2hhbaahaaa.0bWLAhhwretiredmhexpmmplugm1.8.0wnilYbWLAhhwinner_checksummhexpmmcowboym2.6.2m -n3=-ҿ] 莲ASe>4Mɍ_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.5.0m D7zp6 . .cKkXbWLAhhwdepsmhexpmmphoenixm0.2.10lhmhexpmmex_confmex_confm0.1.2wfalsehmhexpmminflexminflexm0.2.3wfalsehmhexpmmjazzmjazzm0.1.1wfalsehmhexpmmplugmplugm0.5.0wfalsejbWLAhhwdepsmhexpmmphoenixm1.1.9lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0.5wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsejYbWLAhhwinner_checksummhexpmmpoisonm4.0.1m UoכX[{1 4 K-sW XbWLAhhwinner_checksummhexpmmranchm1.6.0m gFE] 1.0wtruej0bWLAhhwretiredmhexpmmplugm1.8.3wniltm~Ļsb`&x ewKbWLAhhw timestampmhexpmm phoenix_htmlm2.14.3hhba a ha aa ^bWLAhhwouter_checksummhexpmm plug_cowboym2.1.0m lѽM?G}gaSՍ@CzF2NbWLAhhw timestampmhexpmmcowboy_telemetrym0.2.0hhbaahaaabWLAhhwdepsmhexpmm plug_cowboym2.5.2lhmhexpmmcowboymcowboym~> 2.7wfalsehmhexpmmcowboy_telemetrymcowboy_telemetrym~> 0.3wfalsehmhexpmmplugmplugm~> 1.7wfalsej0bWLAhhwretiredmhexpmmplugm0.4.2wnil6bWLAhhwretiredmhexpmmranchm 2.0.0-rc.1wnil1bWLAhhwretiredmhexpmmplugm0.10.0wnilEbWLAhhw timestampmhexpmmphoenixm1.5.4hhba a ha aa 0bWLAhhwretiredmhexpmmplugm1.0.1wnilbWLAhhwdepsmhexpmmphoenixm1.5.11lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13 or ~> 3.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejEbWLAhhw timestampmhexpmmdecimalm1.1.0hhbaaha aa:JbWLAhhw timestampmhexpmm phoenix_htmlm2.6.1hhba a ha aa _bWLAhhwouter_checksummhexpmm phoenix_htmlm2.3.0m 'կ z9X>b+Qb: ڨ`( y+bWLAhhwdepsmhexpmmpoisonm1.1.1jEbWLAhhw timestampmhexpmmphoenixm0.2.6hhba a ha aa EbWLAhhw timestampmhexpmmphoenixm1.1.2hhba a ha aa bWLAhhwdepsmhexpmmcowboym2.6.3lhmhexpmmcowlibmcowlibm~> 2.7.3wfalsehmhexpmmranchmranchm~> 1.7.1wfalsejgbWLAhhwdepsmhexpmm phoenix_htmlm2.14.0lhmhexpmmplugmplugm~> 1.5wfalsejEbWLAhhw timestampmhexpmmphoenixm0.7.1hhba a ha aa 3bWLAhhwretiredmhexpmmdecimalm0.2.1wnilWbWLAhhwinner_checksummhexpmmplugm1.3.5m up*afm,T˺及Y ;p1ZbWLAhhwouter_checksummhexpmmphoenixm1.0.0m s/L!p_1iRV䒥WWbWLAhhwouter_checksummhexpmmmimem1.4.0m uB"?p#KjGO?jzObWLAhhwdepsmhexpmmphoenixm1.0.5lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejbWLAhhwdepsmhexpmmphoenixm0.2.6lhmhexpmmex_confmex_confm0.1.2wfalsehmhexpmminflexminflexm0.2.0wfalsehmhexpmmjazzmjazzm0.1.1wfalsehmhexpmmplugmplugm0.4.4wfalsejKbWLAhhw timestampmhexpmm phoenix_htmlm2.10.1hhba a ha aa BbWLAhhw timestampmhexpmmplugm0.7.0hhbaahaa4a*bWLAhhwdepsmhexpmmranchm1.6.1j^bWLAhhwouter_checksummhexpmm plug_cowboym2.5.1m } Xecr Q:-d+bWLAhhwdepsmhexpmmpoisonm1.3.1jZbWLAhhwinner_checksummhexpmmphoenixm0.7.0m *~t@ `fNV][:;MY1bWLAhhwretiredmhexpmmplugm1.11.0wnil0bWLAhhwretiredmhexpmmplugm1.2.2wnil3bWLAhhwretiredmhexpmmphoenixm0.7.2wnilbbWLAhhwdepsmhexpmmplugm0.13.1lhmhexpmmcowboymcowboym~> 1.0wtruej3bWLAhhwdepsmhexpmmphoenix_pubsubm1.0.2jBbWLAhhw timestampmhexpmmplugm1.4.0hhbaahaa4a2bWLAhhwretiredmhexpmmpoisonm1.5.2wnilqbWLAhhwdepsmhexpmm phoenix_htmlm2.5.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsej3bWLAhhwretiredmhexpmmphoenixm1.2.3wnil3bWLAhhwretiredmhexpmmdecimalm1.5.0wnilFbWLAhhw timestampmhexpmmphoenixm0.12.0hhba a ha aa WbWLAhhwinner_checksummhexpmmplugm1.9.0m |N&"3Gs$Ë{F}Y$:ZJ[bWLAhhwinner_checksummhexpmmphoenixm1.4.17m |ު}`݌"ChI@;8dbWLAhhwdepsmhexpmmphoenixm1.4.13lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsej)bWLAhhwdepsmhexpmmmimem1.1.0jEbWLAhhw timestampmhexpmmdecimalm1.4.0hhbaaha aa:XbWLAhhwinner_checksummhexpmmranchm1.2.0m Hpjp W~\-`yݲ S`iYbWLAhhwouter_checksummhexpmmcowlibm2.0.0m (!gEFD3v5eLѰ6bWLAhhwretiredmhexpmmjasonm 1.0.0-rc.1wnil0bWLAhhwretiredmhexpmmplugm1.8.1wnil2bWLAhhwretiredmhexpmmcowlibm2.7.3wnilWbWLAhhwouter_checksummhexpmmmimem1.1.0m 3 ګVh\ã8)'( _MZbWLAhhwouter_checksummhexpmmdecimalm1.0.0m T ! oC d0'${ՓBtbWLAhhwdepsmhexpmmcowboym1.1.2lhmhexpmmcowlibmcowlibm~> 1.0.2wfalsehmhexpmmranchmranchm~> 1.3.2wfalsejXbWLAhhwinner_checksummhexpmmplugm0.12.1m X"!崫*x/ӡ~(#_bWLAhhwouter_checksummhexpmm phoenix_htmlm1.0.1m /+P3)׾Jabʶ$§O5oIbWLAhhw timestampmhexpmm plug_cryptom1.1.1hhbaahaaa.0bWLAhhwretiredmhexpmmplugm0.8.4wnil3bWLAhhwretiredmhexpmmphoenixm1.0.5wnil_bWLAhhwinner_checksummhexpmmphoenixm 1.5.0-rc.0m 8;njxPZh>"Ξǣ,\bWLAhhwinner_checksummhexpmmplugm 1.5.0-rc.0m f!T`B sU-m(LebWLAhhwdepsmhexpmm phoenix_htmlm3.0.3lhmhexpmmplugmplugm~> 1.5wtruejKbWLAhhw timestampmhexpmm phoenix_htmlm2.13.4hhba a ha aa 3bWLAhhwretiredmhexpmmphoenixm1.4.9wnil_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.5.1m |] ^7d"> Q)`WbWLAhhwouter_checksummhexpmmplugm1.1.3m 0c倉w<\zQY/OZ~N3o_bWLAhhwouter_checksummhexpmmphoenixm 1.3.0-rc.1m v,TOƿ<~RS7=b3bWLAhhwretiredmhexpmmphoenixm1.1.4wnil1bWLAhhwretiredmhexpmmplugm0.12.0wnilPbWLAhhw registry_etagmhexpmmpoisonm""644f196a2afe2fb023680a25490fecdd"BbWLAhhw timestampmhexpmmplugm1.2.3hhbaahaa4abbWLAhhwdepsmhexpmmplugm0.12.1lhmhexpmmcowboymcowboym~> 1.0wtruejBbWLAhhw timestampmhexpmmplugm1.1.8hhbaahaa4aQbWLAhhw registry_etagmhexpmmdecimalm""4657f6f92e3c01945c9c9b06b6fd850e"`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.14.0m Ƽ(_Z=,XiB{eZ0bWLAhhwretiredmhexpmmplugm1.1.4wnilubWLAhhwversionsmhexpmm plug_cryptolm1.0.0m1.1.0m1.1.1m1.1.2m1.2.0m1.2.1m1.2.2j_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.9.0m ŔEtYLjU yL{D+XbWLAhhwouter_checksummhexpmmplugm0.12.2m ncoŸ> iv,i7n|-JbWLAhhw timestampmhexpmm phoenix_htmlm2.1.0hhba a ha aa JbWLAhhw timestampmhexpmm phoenix_htmlm2.8.0hhba a ha aa bWLAhhwdepsmhexpmmplugm1.5.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.1wtruehmhexpmmmimemmimem~> 1.0wfalsej8bWLAhhwretiredmhexpmm phoenix_htmlm2.4.0wnilbWLAhhwdepsmhexpmmplugm1.3.4lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsejZbWLAhhwinner_checksummhexpmmdecimalm0.2.2m b^BJ;@>ҜLz:K WbWLAhhwinner_checksummhexpmmplugm1.1.9m r!gкgfk8邗#3(7$s%bWLAhhwdepsmhexpmm plug_cowboym2.5.1lhmhexpmmcowboymcowboym~> 2.7wfalsehmhexpmmcowboy_telemetrymcowboy_telemetrym~> 0.3wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm telemetrym telemetrym~> 0.4 or ~> 1.0wfalsej1bWLAhhwretiredmhexpmmplugm0.13.0wnilfbWLAhhwversionsmhexpmmcowboy_telemetrylm0.1.0m0.2.0m0.3.0m0.3.1m0.4.0j+bWLAhhwdepsmhexpmmcowlibm1.0.2jWbWLAhhwinner_checksummhexpmmplugm1.8.3m ym.aNL Be ZhDx4bWLAhhwretiredmhexpmmphoenixm1.4.15wnil3bWLAhhwretiredmhexpmmdecimalm2.0.0wnilBbWLAhhw timestampmhexpmmplugm1.3.5hhbaahaa4a+bWLAhhwdepsmhexpmmpoisonm1.2.1jbWLAhhwdepsmhexpmmphoenixm1.5.8lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejbWLAhhwdepsmhexpmmplugm1.11.1lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejabWLAhhwouter_checksummhexpmmphoenix_pubsubm1.1.0m B.*ҞKna߄*cz;e,JbWLAhhw timestampmhexpmmphoenixm 1.2.0-rc.0hhba a ha aa dbWLAhhwdepsmhexpmmphoenixm1.4.10lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsejYbWLAhhwouter_checksummhexpmmcowlibm2.3.0m ֕sTht]MwN؛XbWLAhhwouter_checksummhexpmmjasonm1.1.1m cEϬ2^4gry:42•%#LIbWLAhhw timestampmhexpmm plug_cowboym1.0.0hhba a ha aa _bWLAhhwinner_checksummhexpmm phoenix_htmlm2.1.1m /q?e*;155~`N*m2bWLAhhwretiredmhexpmmcowboym2.2.0wnilBbWLAhhw timestampmhexpmmplugm1.6.2hhbaahaa4a_bWLAhhwouter_checksummhexpmm phoenix_htmlm3.0.0m 7=O),[v,2 ^HK@Pf,,_bWLAhhwinner_checksummhexpmmphoenixm 1.4.0-rc.3m >Y$z^-c㬭}GknډA)g^bWLAhhwouter_checksummhexpmm plug_cryptom1.0.0m sh/AL]oi;tNXwYZbWLAhhwouter_checksummhexpmmphoenixm0.3.0m syeݞ 5aREx#7vK"bWLAhhwdepsmhexpmmcowboym2.2.1lhmhexpmmcowlibmcowlibm~> 2.1.0wfalsehmhexpmmranchmranchm~> 1.4.0wfalsejbWLAhhwdepsmhexpmmplugm 1.5.0-rc.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.1wtruehmhexpmmmimemmimem~> 1.0wfalsejNbWLAhhw timestampmhexpmm phoenix_htmlm 2.0.0-devhhba a ha aa EbWLAhhw timestampmhexpmmdecimalm0.2.0hhbaaha aa:_bWLAhhwinner_checksummhexpmmphoenixm 1.3.0-rc.0m Ni!{19n}o⾊шùGJbWLAhhw timestampmhexpmm phoenix_htmlm3.0.0hhba a ha aa 'bWLAhhwdepsmhexpmmphoenixm 1.3.0-rc.3lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.3.2 or ~> 1.4wfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0wfalsejqbWLAhhwdepsmhexpmm phoenix_htmlm2.1.2lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsej3bWLAhhwretiredmhexpmmphoenixm1.0.6wnil1bWLAhhwretiredmhexpmmjasonm1.1.1wnil^bWLAhhwouter_checksummhexpmm plug_cowboym2.4.0m ]nUyx<˙N^ ^JYbWLAhhwouter_checksummhexpmmcowlibm1.0.1m OkG L& #؏As\#SIbWLAhhw timestampmhexpmm plug_cryptom1.1.2hhbaahaaa.3bWLAhhwretiredmhexpmmphoenixm1.3.3wnil)bWLAhhwdepsmhexpmmmimem0.0.1jJbWLAhhw timestampmhexpmm phoenix_htmlm2.5.0hhba a ha aa ZbWLAhhwinner_checksummhexpmmphoenixm1.4.9m tm t3M=<ʱud5C4Ay.ftXbWLAhhwouter_checksummhexpmmranchm1.2.1m Wa\7\#U0ű%ZЎ[GbWLAhhw timestampmhexpmmplugm 1.4.0-rc.0hhbaahaa4aZbWLAhhwouter_checksummhexpmmphoenixm1.2.4m ]ͤr GM(\rn\qV[9bWLAhhwretiredmhexpmm phoenix_htmlm2.11.0wnilKbWLAhhw timestampmhexpmm phoenix_htmlm2.10.0hhba a ha aa WbWLAhhwouter_checksummhexpmmplugm1.4.3m zLe5"e۹zFk6LaHbWLAhhw timestampmhexpmmjasonm 1.0.0-rc.3hhbaaha aa:bWLAhhwdepsmhexpmmphoenixm0.6.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmlinguistmlinguistm~> 0.1.3wfalsehmhexpmmplugmplugm~> 0.8.2wfalsehmhexpmmpoisonmpoisonm~> 1.2wfalsej9bWLAhhwretiredmhexpmm phoenix_htmlm2.13.4wnilXbWLAhhwinner_checksummhexpmmranchm1.7.0m q`b]T2nߘMOFm3bWLAhhwretiredmhexpmmphoenixm1.5.4wnilubWLAhhwdepsmhexpmm phoenix_htmlm1.4.0lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0wfalsejDbWLAhhw timestampmhexpmmpoisonm5.0.0hhba a ha a.a8bWLAhhwretiredmhexpmmphoenixm 1.6.0-rc.0wnilWbWLAhhwinner_checksummhexpmmplugm1.2.4m e)Do=."'vu=HϾXbWLAhhwouter_checksummhexpmmplugm1.11.1m #RNOX|;9.%4( N:bWLAhhwdepsmhexpmmplugm1.9.0lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0wfalsehmhexpmm telemetrym telemetrym~> 0.4wtruejXbWLAhhwouter_checksummhexpmmranchm1.6.0m apeFh=1E^sa`]J7XbWLAhhwinner_checksummhexpmmranchm1.7.1m kQ s:IvFZG љȁWbWLAhhwouter_checksummhexpmmplugm0.8.1m Pߙ%5s}_ݡD/TD]dbWLAhhwdepsmhexpmmphoenixm1.4.14lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsejWbWLAhhwinner_checksummhexpmmplugm1.2.5m |؋f4Bf w{їSbWLAhhw registry_etagmhexpmm telemetrym""4a441cbd068ab1fc1e6f8fabddac30ff"cbWLAhhwdepsmhexpmmphoenixm1.4.7lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsejbWLAhhwdepsmhexpmmphoenixm0.6.2lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmlinguistmlinguistm~> 0.1.4wfalsehmhexpmmplugmplugm~> 0.8.4wfalsehmhexpmmpoisonmpoisonm~> 1.2wfalsejZbWLAhhwinner_checksummhexpmmphoenixm1.0.4m /(L#:ߋkK2}?LJnZbWLAhhwouter_checksummhexpmmphoenixm0.6.0m \&9&)!cigN{Ezx91bWLAhhwretiredmhexpmmranchm1.6.2wnil4bWLAhhwretiredmhexpmmphoenixm1.4.12wnil*bWLAhhwdepsmhexpmmranchm1.3.0j3bWLAhhwretiredmhexpmmdecimalm1.0.0wnil,bWLAhhwdepsmhexpmmdecimalm1.0.0jYbWLAhhwouter_checksummhexpmmcowlibm2.8.0m yT0!P(iR=L02WbWLAhhwinner_checksummhexpmmplugm1.2.3m P W8r^ @&sS3,Us1bWLAhhwretiredmhexpmmranchm2.0.0wnil0bWLAhhwretiredmhexpmmplugm1.4.2wnil,bWLAhhwdepsmhexpmmcowlibm2.11.0jZbWLAhhwouter_checksummhexpmmdecimalm0.2.5m .-h̆ lok$+FXnYGZbWLAhhwouter_checksummhexpmmphoenixm1.1.2m dƼmd/(VYZ#DbbWLAhhwdepsmhexpmmplugm0.11.0lhmhexpmmcowboymcowboym~> 1.0wtruejXbWLAhhwinner_checksummhexpmmplugm0.11.2m Җ\7İԩ _e_O`*Z,bWLAhhwdepsmhexpmmdecimalm0.2.2jcbWLAhhwdepsmhexpmmphoenixm1.4.9lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsejfbWLAhhwdepsmhexpmm phoenix_htmlm2.9.2lhmhexpmmplugmplugm~> 1.0wfalsej6bWLAhhwretiredmhexpmmranchm 2.0.0-rc.2wnil+bWLAhhwdepsmhexpmmpoisonm1.1.0jEbWLAhhw timestampmhexpmmphoenixm1.0.6hhba a ha aa 4bWLAhhwretiredmhexpmmphoenixm1.4.17wnilCbWLAhhw timestampmhexpmmplugm1.10.4hhbaahaa4aYbWLAhhwouter_checksummhexpmmcowboym2.2.2m KpTïtKse_ƞ;Y /k V\bWLAhhwouter_checksummhexpmm telemetrym0.1.0m <1bhxAO\"BMPCD$[bWLAhhwouter_checksummhexpmmphoenixm0.13.0m 2W@ ụ) [8YbWLAhhwouter_checksummhexpmmpoisonm1.5.2m JYܭȹ4UNdz+Gg8:f?YbWLAhhwouter_checksummhexpmmcowlibm2.4.0m }Aϣ= }еd9w#fbWLAhhwinner_checksummhexpmmphoenix_pubsubm 1.0.0-rc.0m \S+@L?1+ xH+bWLAhhwdepsmhexpmmpoisonm1.5.1j/bWLAhhwdepsmhexpmmranchm 2.0.0-rc.2j[bWLAhhwinner_checksummhexpmmphoenixm0.17.0m fFznM[|q]TM8^_r,wBbWLAhhw timestampmhexpmmplugm0.4.4hhbaahaa4aZbWLAhhwouter_checksummhexpmmphoenixm1.0.6m 7.zUH}c[u뷃ܞ^6mü LbWLAhhw timestampmhexpmmphoenix_pubsubm1.0.0hhba a ha aa ^bWLAhhwinner_checksummhexpmm plug_cowboym2.2.0m 1{Qp"YrA z $bWLAhhwdepsmhexpmmplugm1.2.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmmimemmimem~> 1.0wfalsej bWLAhhwdepsmhexpmmphoenixm 1.2.0-rc.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm ~> 1.0.0-rcwfalsehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsejWbWLAhhwinner_checksummhexpmmplugm1.8.1m py9pߠ~L֫X qL:YbWLAhhwinner_checksummhexpmmpoisonm2.1.0m !&udl=bslv]{Ը.'ZbWLAhhwinner_checksummhexpmmphoenixm0.8.0m ҡ:l $,I 2aEӰ|KgbWLAhhwdepsmhexpmm phoenix_htmlm2.13.3lhmhexpmmplugmplugm~> 1.5wfalsej7bWLAhhwretiredmhexpmm plug_cryptom1.0.0wnil3bWLAhhwretiredmhexpmmphoenixm0.4.0wnilZbWLAhhwinner_checksummhexpmmdecimalm0.2.0m [.ٙ)b9*O^GLzb}F+bWLAhhwdepsmhexpmmcowlibm2.0.0j`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.10.4m vٙ84*F"\h402i gZbWLAhhwinner_checksummhexpmmphoenixm1.5.6m ʹC$+ArAhuf^bWLAhhwinner_checksummhexpmm plug_cowboym1.0.0m .*}4 tm3_EX0=h' nzdbWLAhhwdepsmhexpmmjasonm1.2.0lhmhexpmmdecimalmdecimalm~> 1.0wtruejDbWLAhhw timestampmhexpmmpoisonm1.3.0hhba a ha a.a\bWLAhhwinner_checksummhexpmm telemetrym0.4.2m (ɒE^s"M;kb_#:sPXps/E1bWLAhhwretiredmhexpmmplugm0.11.2wnilZbWLAhhwouter_checksummhexpmmphoenixm1.1.3m Wv pwd6Bf&Ͱp`R JCbWLAhhw timestampmhexpmmranchm2.1.0hhba a ha aa ZbWLAhhwouter_checksummhexpmmphoenixm0.3.1m YHy7^6aM+VSN%bWLAhhwdepsmhexpmmplugm1.3.2lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsej1bWLAhhwretiredmhexpmmjasonm1.0.0wnil3bWLAhhwretiredmhexpmmdecimalm1.4.1wnil1bWLAhhwretiredmhexpmmplugm0.14.0wnilCbWLAhhwversionsmhexpmmdecimallm0.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.0j3bWLAhhwretiredmhexpmmphoenixm1.2.1wnilbWLAhhwdepsmhexpmmplugm1.10.1lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wtruejJbWLAhhw timestampmhexpmm phoenix_htmlm1.4.0hhba a ha aa 2bWLAhhwretiredmhexpmmcowlibm2.2.0wnilJbWLAhhw timestampmhexpmm phoenix_htmlm3.0.1hhba a ha aa 0bWLAhhwretiredmhexpmmmimem1.5.0wnil2bWLAhhwretiredmhexpmmpoisonm5.0.0wnil2bWLAhhwretiredmhexpmmcowlibm2.9.0wnil0bWLAhhwretiredmhexpmmmimem1.4.0wnil3bWLAhhwretiredmhexpmmphoenixm0.4.1wnilgbWLAhhwdepsmhexpmm phoenix_htmlm2.10.0lhmhexpmmplugmplugm~> 1.0wfalsejYbWLAhhwouter_checksummhexpmmpoisonm5.0.0m a bYO9BnYbWLAhhwinner_checksummhexpmmcowboym2.6.1m ou|3{;17rxE[ڡTxY?NbWLAhhw timestampmhexpmm phoenix_htmlm 2.4.0-devhhba a ha aa 1bWLAhhwretiredmhexpmmranchm2.1.0wnil\bWLAhhwouter_checksummhexpmm telemetrym0.4.3m r6_֊b %&˂^T$DvA`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.13.0m kyi,(vS؜'miwu}oaKZ3bWLAhhwretiredmhexpmmdecimalm0.2.5wnil^bWLAhhwouter_checksummhexpmm plug_cowboym2.1.2m }r%ΆZ#~mopAt %k>c"pbWLAhhwdepsmhexpmm plug_cowboym2.2.1lhmhexpmmcowboymcowboym~> 2.7wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejDbWLAhhw timestampmhexpmmcowlibm2.6.0hhbaahaaa.bWLAhhwdepsmhexpmmphoenixm1.5.3lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej_bWLAhhwinner_checksummhexpmm phoenix_htmlm1.0.1m wcC$f-fZ$ mK]d!8Z1xXbWLAhhwouter_checksummhexpmmranchm2.0.0m H@b<-:|/SLTQؤ*bWLAhhwversionsmhexpmmphoenix_pubsubl m0.0.1m0.1.0m 1.0.0-rc.0m1.0.0m1.0.1m1.0.2m1.1.0m1.1.1m1.1.2m2.0.0jZbWLAhhwinner_checksummhexpmmphoenixm1.0.2m ] 2EmUah⽶GzC2f0bWLAhhwretiredmhexpmmmimem0.0.1wnilgbWLAhhwdepsmhexpmm phoenix_htmlm2.13.0lhmhexpmmplugmplugm~> 1.5wfalsej?bWLAhhw timestampmhexpmm plug_cowboyhhba a ha aa 6bWLAhhwretiredmhexpmmjasonm 1.0.0-rc.2wnilgbWLAhhwdepsmhexpmm phoenix_htmlm2.10.3lhmhexpmmplugmplugm~> 1.0wfalsejJbWLAhhw timestampmhexpmm phoenix_htmlm2.1.1hhba a ha aa WbWLAhhwinner_checksummhexpmmplugm1.4.0m '^f^Bl% $m_E;l }dbWLAhhwdepsmhexpmmjasonm1.1.1lhmhexpmmdecimalmdecimalm~> 1.0wtruejFbWLAhhw timestampmhexpmmphoenixm1.5.10hhba a ha aa ZbWLAhhwouter_checksummhexpmmphoenixm1.2.0m l@CF/l,86B"D^xQ_UnXbWLAhhwouter_checksummhexpmmranchm1.0.0m ) J\x$.=<)J#U`cbWLAhhwouter_checksummhexpmmcowboy_telemetrym0.2.0m  DحU gGdK9h.ZbWLAhhwinner_checksummhexpmmdecimalm1.3.1m { 1.0wtruej2bWLAhhwretiredmhexpmmpoisonm1.1.0wnilZbWLAhhwouter_checksummhexpmmphoenixm0.2.3m o, B)| X0pi+bWLAhhwdepsmhexpmmpoisonm1.0.2j)bWLAhhwdepsmhexpmmplugm0.4.2jZbWLAhhwouter_checksummhexpmmphoenixm1.2.2m vLJUԾ=er* 1.0wtruej9bWLAhhwretiredmhexpmm phoenix_htmlm2.14.0wnil8bWLAhhwretiredmhexpmm phoenix_htmlm2.0.1wnil]bWLAhhwouter_checksummhexpmmjasonm 1.0.0-rc.3m tE;#:˛V83;jWD+lE,ZbWLAhhwinner_checksummhexpmmphoenixm1.3.2m *QpkNn~ˊ,q痍\sx3bWLAhhwretiredmhexpmmphoenixm1.1.6wnildbWLAhhwdepsmhexpmmphoenixm1.4.11lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsejcbWLAhhwouter_checksummhexpmmcowboy_telemetrym0.1.0m ٖqczPJ~|L4be3_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.1.0m ud*7kH|K~9dƀ^bWLAhhwouter_checksummhexpmm plug_cowboym2.0.1m s nh4jQQ@! 1.0wtruej2bWLAhhwretiredmhexpmmcowlibm1.0.1wnilWbWLAhhwinner_checksummhexpmmplugm0.8.2m 9[-!2bA&~YezAXbWLAhhwouter_checksummhexpmmranchm1.2.0m Qzע- ɱOÙdbWLAhhwdepsmhexpmmjasonm1.1.2lhmhexpmmdecimalmdecimalm~> 1.0wtruej`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.10.1m ,ƭwLx@1D>Qy!¦ZbWLAhhwinner_checksummhexpmmphoenixm1.0.0m QyՉy ecoN*abWLAhhwdepsmhexpmmplugm1.0.5lhmhexpmmcowboymcowboym~> 1.0wtruejBbWLAhhw timestampmhexpmmplugm1.6.0hhbaahaa4aEbWLAhhw timestampmhexpmmphoenixm1.5.3hhba a ha aa ZbWLAhhwouter_checksummhexpmmphoenixm0.4.1m 'Y Kj]MunLT܋YgebWLAhhwdepsmhexpmm phoenix_htmlm3.0.1lhmhexpmmplugmplugm~> 1.5wtruej1bWLAhhwretiredmhexpmmjasonm1.0.1wnilDbWLAhhw timestampmhexpmmpoisonm1.0.0hhba a ha a.aWbWLAhhwinner_checksummhexpmmplugm1.0.5m $W .:ѵȫ2RU)wJZbWLAhhwouter_checksummhexpmmphoenixm0.2.0m D7܆qO6ǁ79*0]P쿺sEiZbWLAhhwouter_checksummhexpmmdecimalm0.2.0m &EQ{-D; Mʝ4$YbWLAhhwinner_checksummhexpmmcowboym1.1.0m cr~p:,oz"[DA4bWLAhhwdepsmhexpmm plug_cowboym2.4.1lhmhexpmmcowboymcowboym~> 2.7wfalsehmhexpmmcowboy_telemetrymcowboy_telemetrym~> 0.3wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej$bWLAhhwdepsmhexpmmphoenixm 1.4.0-rc.1lhmhexpmmcowboymcowboym~> 1.0 or ~> 2.5wtruehmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.6.4 or ~> 1.7wfalsej?bWLAhhw timestampmhexpmm plug_cryptohhbaahaaa.WbWLAhhwouter_checksummhexpmmplugm1.1.8m -nOWz?~iW6F1؀,J[bWLAhhwouter_checksummhexpmmphoenixm0.11.0m @7#&[vnf"ď?O#y*.bWLAhhwdepsmhexpmm telemetrym0.4.1j5bWLAhhwretiredmhexpmmplugm 1.5.0-rc.0wnilbWLAhhwdepsmhexpmmplugm1.4.2lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsej0bWLAhhwdepsmhexpmm plug_cryptom1.2.1jFbWLAhhw timestampmhexpmmphoenixm1.5.11hhba a ha aa bWLAhhwversionsmhexpmmcowboylm1.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.0j_bWLAhhwinner_checksummhexpmmphoenixm 1.4.0-rc.2m _ 4a KCGE(/gͮoY3bWLAhhwretiredmhexpmmdecimalm1.8.1wnilYbWLAhhwinner_checksummhexpmmcowboym1.0.4m $ߟ#3p:'"l穿ixbWLAhhwdepsmhexpmmphoenixm1.1.2lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejXbWLAhhwinner_checksummhexpmmranchm1.1.0m mی*'̨\T5XţU:{eT[YbWLAhhwouter_checksummhexpmmcowlibm2.7.2m .6ЖH U;h~_>k LU@J/0bWLAhhwretiredmhexpmmplugm1.7.2wnil^bWLAhhwinner_checksummhexpmm plug_cowboym2.3.0m P\<Pj7WP2f#ݩcbWLAhhwouter_checksummhexpmm phoenix_htmlm 2.7.0-devm OhnV.m%VH;Dyd ZBbWLAhhw timestampmhexpmmplugm1.8.3hhbaahaa4aIbWLAhhw timestampmhexpmm plug_cryptom1.2.1hhbaahaaa.WbWLAhhwinner_checksummhexpmmplugm1.6.1m /b=P Ϙ Fi~)4XnlFbWLAhhw timestampmhexpmmphoenixm0.13.1hhba a ha aa "bWLAhhwdepsmhexpmmphoenixm 1.4.0-rc.3lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruejIbWLAhhw timestampmhexpmm plug_cowboym2.2.2hhba a ha aa _bWLAhhwouter_checksummhexpmm phoenix_htmlm2.9.1m pqm e=EFxenX".Hi6DX?WbWLAhhwinner_checksummhexpmmmimem1.0.1m Ó$v}6'qbCM$zQZbWLAhhwinner_checksummhexpmmphoenixm1.5.8m qϧ7M9yB_ik\ZF!XbWLAhhwinner_checksummhexpmmplugm1.12.1m dVx`'ۜb=ۖMl5}WbWLAhhwinner_checksummhexpmmplugm1.1.0m iHI Pjy=W%ZbWLAhhwouter_checksummhexpmmdecimalm1.1.0m  }9 ,SHmga8-k!bWLAhhwdepsmhexpmmplugm1.2.2lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmmimemmimem~> 1.0wfalsejEbWLAhhw timestampmhexpmmdecimalm0.1.2hhbaaha aa:bWLAhhwdepsmhexpmmphoenixm1.1.5lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsejabWLAhhwouter_checksummhexpmmphoenix_pubsubm0.1.0m ktp\5Ikz@}9N\ҡVb9^bWLAhhwinner_checksummhexpmm plug_cowboym2.0.2m `UhhHKncҺڔIxA3w*l_bWLAhhwouter_checksummhexpmm phoenix_htmlm1.3.0m E~<&[_^ N9bWLAhhwretiredmhexpmm phoenix_htmlm2.10.1wnil3bWLAhhwretiredmhexpmmphoenixm0.2.6wnil3bWLAhhwdepsmhexpmmphoenix_pubsubm1.1.2jbWLAhhwdepsmhexpmmcowboym2.6.1lhmhexpmmcowlibmcowlibm~> 2.7.0wfalsehmhexpmmranchmranchm~> 1.7.1wfalsejBbWLAhhw timestampmhexpmmplugm1.3.6hhbaahaa4a1bWLAhhwretiredmhexpmmplugm1.10.1wnilbWLAhhwdepsmhexpmmplugm1.8.3lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0wfalsehmhexpmm telemetrym telemetrym~> 0.4wtruejEbWLAhhw timestampmhexpmmphoenixm1.1.5hhba a ha aa CbWLAhhw timestampmhexpmmplugm1.11.1hhbaahaa4aYbWLAhhwouter_checksummhexpmmcowlibm2.9.1m ]@ aV"8rEt 8MGapYbWLAhhwinner_checksummhexpmmpoisonm1.1.1m  5?# z·8`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.14.0m 0xmra838aQoI?XbWLAhhwinner_checksummhexpmmplugm0.12.0m =o.?Eg b,?(y3WbWLAhhwinner_checksummhexpmmplugm0.5.0m r"cuM>͖ x7ɮ4|)uR#vt3bWLAhhwretiredmhexpmmphoenixm0.2.8wnil'bWLAhhwdepsmhexpmmphoenixm 1.3.0-rc.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.3.2 or ~> 1.4wfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0wfalsej0bWLAhhwretiredmhexpmmmimem1.1.0wnil:bWLAhhwretiredmhexpmmphoenix_pubsubm2.0.0wnilYbWLAhhwinner_checksummhexpmmcowboym2.7.0m 8d5_C1k#k ag|ZbWLAhhwouter_checksummhexpmmphoenixm0.2.6m xJlӐ,ϔMwYe`?bWLAhhwretiredmhexpmm plug_cryptom1.1.1twmessagem/Wrong default value is used for salt on encryptwreasonwRETIRED_INVALIDqbWLAhhwdepsmhexpmm phoenix_htmlm2.1.1lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsej+bWLAhhwdepsmhexpmmpoisonm1.0.0jbWLAhhwdepsmhexpmm plug_cowboym2.0.2lhmhexpmmcowboymcowboym~> 2.5wfalsehmhexpmmplugmplugm~> 1.7wfalsejJbWLAhhw timestampmhexpmm phoenix_htmlm1.0.0hhba a ha aa WbWLAhhwouter_checksummhexpmmplugm1.4.4m _~AVƐ:݈x53,/Cm:T8bWLAhhwretiredmhexpmm phoenix_htmlm1.3.0wnilDbWLAhhw timestampmhexpmmpoisonm2.2.0hhba a ha a.a]bWLAhhwinner_checksummhexpmmjasonm 1.0.0-rc.3m RHr `D>PŕQOˣfƔ~abWLAhhwinner_checksummhexpmmphoenix_pubsubm1.0.2m Rx^ QQXR?4EbWLAhhw timestampmhexpmmphoenixm1.0.0hhba a ha aa [bWLAhhwouter_checksummhexpmmphoenixm0.17.1m L+bP"n tߔҋۑqbWLAhhwdepsmhexpmm phoenix_htmlm2.2.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsejabWLAhhwouter_checksummhexpmmphoenix_pubsubm1.0.0m ,{?\MfVI$iqrEbWLAhhw timestampmhexpmmphoenixm1.2.4hhba a ha aa DbWLAhhw timestampmhexpmmpoisonm2.1.0hhba a ha a.aWbWLAhhwouter_checksummhexpmmplugm0.8.4m "5 3T[ۼ~`2p2(5_BbWLAhhw timestampmhexpmmplugm1.6.4hhbaahaa4aFbWLAhhw timestampmhexpmmphoenixm0.11.0hhba a ha aa bWLAhhwdepsmhexpmmphoenixm0.3.0lhmhexpmmex_confmex_confm0.1.2wfalsehmhexpmminflexminflexm0.2.4wfalsehmhexpmmjazzmjazzm0.1.2wfalsehmhexpmmplugmplugm0.5.1wfalsej0bWLAhhwretiredmhexpmmplugm0.9.0wnilZbWLAhhwinner_checksummhexpmmphoenixm0.7.1m }x3V%1,TY9 L~* 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wtruejbWLAhhwdepsmhexpmmphoenixm0.2.0lhmhexpmmex_confmex_confm0.1.1wfalsehmhexpmmplugmplugm0.4.2wfalsejXbWLAhhwouter_checksummhexpmmplugm1.10.3m z*3jDKʻ8l s9^bWLAhhwouter_checksummhexpmm plug_cryptom1.2.2m czE𣀟y:KsHݞ(8bWLAhhwretiredmhexpmm phoenix_htmlm2.3.1wnil0bWLAhhwretiredmhexpmmplugm1.3.2wnilZbWLAhhwouter_checksummhexpmmdecimalm0.2.4m eBB:QN.cGJ9@5"YbWLAhhwinner_checksummhexpmmpoisonm2.2.0m GcwwoG}d(A&vW'SJcZbWLAhhwinner_checksummhexpmmdecimalm0.2.5m @[x:`&'?ԀC{Xk4NêabWLAhhwdepsmhexpmmplugm1.1.8lhmhexpmmcowboymcowboym~> 1.0wtruej`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.12.0m KfUd=6UFֵSL+Fv"0bWLAhhwretiredmhexpmmplugm0.8.2wnilKbWLAhhw timestampmhexpmm phoenix_htmlm2.10.5hhba a ha aa XbWLAhhwinner_checksummhexpmmranchm1.6.1m & rL~<l7Bb_]a"3XbWLAhhwinner_checksummhexpmmplugm1.11.1m +fgSa4 oCѲ-SY_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.3.1m 7i8x }6JyHhk0bWLAhhwretiredmhexpmmmimem1.2.0wnilYbWLAhhwinner_checksummhexpmmcowlibm1.0.2m v,:S o!s 7}=~߄*bWLAhhwdepsmhexpmmranchm1.7.0jNbWLAhhw timestampmhexpmmcowboy_telemetrym0.3.1hhbaahaaa3bWLAhhwdepsmhexpmmphoenix_pubsubm1.0.0jZbWLAhhwouter_checksummhexpmmphoenixm0.2.7m :=ќxלFaG5=Z;@vB9ϕ2bWLAhhwretiredmhexpmmcowlibm2.2.1wnilbWLAhhwdepsmhexpmmphoenixm0.2.2lhmhexpmmex_confmex_confm0.1.1wfalsehmhexpmminflexminflexm0.2.0wfalsehmhexpmmjazzmjazzm0.1.0wfalsehmhexpmmplugmplugm0.4.2wfalsej 1.0 or ~> 2.0wtruej[bWLAhhwinner_checksummhexpmmphoenixm0.15.0m JRlá=z^J@"!KEbWLAhhw timestampmhexpmmphoenixm1.4.7hhba a ha aa 2bWLAhhwretiredmhexpmmpoisonm2.0.1wnilYbWLAhhwouter_checksummhexpmmcowboym2.0.0m l| CSߟ?,=XѰM+e>VUx3bWLAhhwretiredmhexpmmdecimalm1.1.0wnilJbWLAhhw timestampmhexpmmphoenixm 1.4.0-rc.0hhba a ha aa BbWLAhhw timestampmhexpmmplugm1.8.2hhbaahaa4aBbWLAhhw timestampmhexpmmplugm1.1.6hhbaahaa4aDbWLAhhw timestampmhexpmmcowboym1.0.1hhbaahaaa.WbWLAhhwinner_checksummhexpmmplugm1.6.3m C3{*/c5i~LSH,^WbWLAhhwouter_checksummhexpmmplugm1.1.9m SPi]I\+B*'3$BbWLAhhw timestampmhexpmmplugm1.2.4hhbaahaa4a7bWLAhhwretiredmhexpmm plug_cowboym2.0.0wnilIbWLAhhw timestampmhexpmm plug_cowboym2.5.1hhba a ha aa _bWLAhhwinner_checksummhexpmm phoenix_htmlm2.2.0m ҩ7|˷D/!:P0ؠ ^bWLAhhwouter_checksummhexpmm plug_cryptom1.1.0m ͡ 3%eu|%ʮȧ-l=ZbWLAhhwinner_checksummhexpmmdecimalm1.9.0m 12q$,QK =MYbWLAhhwouter_checksummhexpmmcowlibm2.9.0m ;? B-ⵂ'[_헏zLdbWLAhhwdepsmhexpmmphoenixm1.4.15lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsejabWLAhhwinner_checksummhexpmmphoenix_pubsubm1.0.1m b7|Kչ .: Nj[bWLAhhwouter_checksummhexpmmphoenixm0.12.0m -V>dOva1MSdUC~68 FbWLAhhw timestampmhexpmmphoenixm0.2.11hhba a ha aa YbWLAhhwinner_checksummhexpmmcowlibm1.0.0m 9} fVԆ2s$ 47i֔A[bWLAhhwouter_checksummhexpmmphoenixm0.2.11m I0BDaT>@y dƀ3bWLAhhwretiredmhexpmmdecimalm1.3.1wnil1bWLAhhwretiredmhexpmmranchm1.2.1wnil+bWLAhhwdepsmhexpmmpoisonm3.0.0jBbWLAhhw timestampmhexpmmplugm1.8.0hhbaahaa4aCbWLAhhw timestampmhexpmmjasonm1.1.0hhbaaha aa:1bWLAhhwdepsmhexpmmdecimalm 1.9.0-rc.0jEbWLAhhw timestampmhexpmmphoenixm1.1.9hhba a ha aa EbWLAhhw timestampmhexpmmcowlibm2.11.0hhbaahaaa.^bWLAhhwouter_checksummhexpmm plug_cowboym2.1.1m O W[9DXm}rLs},bWLAhhwdepsmhexpmmdecimalm1.8.1jWbWLAhhwouter_checksummhexpmmplugm1.0.0m YC1S [(R-7Vc \bWLAhhwinner_checksummhexpmm telemetrym0.3.0m <Gqc <"f" :*:jguJsGbWLAhhw timestampmhexpmm telemetrym0.4.2hhbaahaa4a2bWLAhhwretiredmhexpmmpoisonm3.1.0wnilYbWLAhhwinner_checksummhexpmmpoisonm1.2.1m P$:er ts I^bWLAhhwinner_checksummhexpmm plug_cowboym2.5.2m bL`Y~,#'#~9k䘈[bWLAhhwinner_checksummhexpmmphoenixm1.4.15m \90j3RβV)bX+Y(WbWLAhhwouter_checksummhexpmmplugm1.4.2m P@`%ܧDd[͑Z1"? [BbWLAhhw timestampmhexpmmplugm1.3.1hhbaahaa4aZbWLAhhwinner_checksummhexpmmphoenixm1.5.0m YόsJ0W6eIaij ĮBXbWLAhhwouter_checksummhexpmmranchm1.7.0m YP:V[/hL0HCM9A,'ZbWLAhhwinner_checksummhexpmmphoenixm0.3.1m wg!m m~!SPbJXig0a+bWLAhhwdepsmhexpmmphoenixm0.15.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm>= 0.13.1 and < 2.0.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsej,bWLAhhwdepsmhexpmmdecimalm0.2.0jZbWLAhhwouter_checksummhexpmmphoenixm1.4.4m DEL@3aho9O3i^t7mZ;y ~EbWLAhhw timestampmhexpmmdecimalm1.5.0hhbaaha aa:3bWLAhhwretiredmhexpmmphoenixm0.2.5wnil8bWLAhhwretiredmhexpmm phoenix_htmlm2.5.0wnilZbWLAhhwinner_checksummhexpmmdecimalm1.1.2m yie{-S{Q<Қ+HkQlBS \bWLAhhwouter_checksummhexpmm telemetrym0.4.1m G88.6]g`,V m)ތXH^bWLAhhwinner_checksummhexpmm plug_cowboym2.2.2m z ]盒2ĔvLȐbWLAhhwdepsmhexpmmplugm1.4.4lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsej8bWLAhhwretiredmhexpmm phoenix_htmlm2.6.1wnilbWLAhhwdepsmhexpmmcowboym1.0.0lhmhexpmmcowlibmcowlibm~> 1.0.0wfalsehmhexpmmranchmranchm~> 1.0wfalsej 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsej1bWLAhhwretiredmhexpmmranchm1.3.2wnil`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.10.3m C-7.zmYxCDQA"75bbWLAhhwdepsmhexpmmplugm0.12.2lhmhexpmmcowboymcowboym~> 1.0wtruejJbWLAhhw timestampmhexpmm phoenix_htmlm2.9.3hhba a ha aa BbWLAhhw timestampmhexpmmplugm1.4.2hhbaahaa4aXbWLAhhwinner_checksummhexpmmranchm1.4.0m '/y4wK r]lٔk_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.5.1m cSE\&m'BipgB_YbWLAhhwouter_checksummhexpmmcowboym2.8.0m FCJmM,u=г]T_WbWLAhhwouter_checksummhexpmmplugm1.1.4m !&=zq(H#10rVƨTmݔ%MEbWLAhhw timestampmhexpmmphoenixm1.4.8hhba a ha aa JbWLAhhw timestampmhexpmm phoenix_htmlm2.0.0hhba a ha aa ZbWLAhhwinner_checksummhexpmmdecimalm1.0.1m Jćs1K܃ǒ+l 3bWLAhhwretiredmhexpmmphoenixm1.5.9wnil3bWLAhhwretiredmhexpmmphoenixm1.0.1wnil0bWLAhhwretiredmhexpmmplugm0.8.3wnilDbWLAhhw timestampmhexpmmcowboym1.0.4hhbaahaaa.DbWLAhhw timestampmhexpmmcowboym2.7.0hhbaahaaa.\bWLAhhwouter_checksummhexpmmplugm 1.2.0-rc.0m ~Ie -JizŅkwiC iIbWLAhhw timestampmhexpmm plug_cowboym2.0.1hhba a ha aa BbWLAhhw timestampmhexpmmmimem1.2.0hhbaahaaa.WbWLAhhwinner_checksummhexpmmmimem0.0.1m s3ާj2Ӭeěs!>&4bWLAhhwretiredmhexpmmphoenixm1.4.11wnilbWLAhhwdepsmhexpmmphoenixm0.2.9lhmhexpmmex_confmex_confm0.1.2wfalsehmhexpmminflexminflexm0.2.0wfalsehmhexpmmjazzmjazzm0.1.1wfalsehmhexpmmplugmplugm0.5.0wfalsejYbWLAhhwouter_checksummhexpmmcowlibm2.7.1m /`sx\Sk]Eh)6^0bWLAhhwretiredmhexpmmplugm0.8.1wnil3bWLAhhwretiredmhexpmmphoenixm1.1.9wnilWbWLAhhwinner_checksummhexpmmplugm1.7.2m ׷u^ }D"7.ZbWLAhhwouter_checksummhexpmmphoenixm1.4.2m Q8|YΥS*rUŨa,AB|F|kMKX_bWLAhhwinner_checksummhexpmmdecimalm 2.0.0-rc.0m ĸK%5 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.3.3 or ~> 1.4wfalsehmhexpmmpoisonmpoisonm~> 2.2 or ~> 3.0wfalsejBbWLAhhw timestampmhexpmmplugm1.0.5hhbaahaa4aXbWLAhhwouter_checksummhexpmmplugm0.12.0m LeEo448W }L<NJfbWLAhhwdepsmhexpmm phoenix_htmlm2.9.3lhmhexpmmplugmplugm~> 1.0wfalsejZbWLAhhwouter_checksummhexpmmphoenixm1.4.6m oI uqqYnb>LbWLAhhw timestampmhexpmmphoenix_pubsubm1.0.1hhba a ha aa EbWLAhhw timestampmhexpmmcowlibm2.10.0hhbaahaaa.+bWLAhhwdepsmhexpmmcowlibm2.7.2jDbWLAhhw timestampmhexpmmcowlibm2.2.0hhbaahaaa.FbWLAhhw timestampmhexpmmphoenixm1.4.16hhba a ha aa EbWLAhhw timestampmhexpmmphoenixm1.2.5hhba a ha aa WbWLAhhwouter_checksummhexpmmplugm1.1.5m phq=fLKΪP4@P1cM=1bWLAhhwretiredmhexpmmplugm1.10.3wnil1bWLAhhwretiredmhexpmmplugm0.12.1wnilBbWLAhhw timestampmhexpmmplugm1.1.4hhbaahaa4a2bWLAhhwretiredmhexpmmpoisonm1.0.0wnilWbWLAhhwouter_checksummhexpmmmimem1.5.0m ULU"I=-:ޝ3 Z}9bWLAhhwretiredmhexpmm phoenix_htmlm2.13.2wnil0bWLAhhwretiredmhexpmmplugm1.1.5wnil_bWLAhhwinner_checksummhexpmmphoenixm 1.2.0-rc.0m ?uF&ːIbJZ} |3ҋbWLAhhwdepsmhexpmmcowboy_telemetrym0.3.1lhmhexpmmcowboymcowboym~> 2.7wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejZbWLAhhwinner_checksummhexpmmphoenixm1.1.1m XH.!ޜnT-cI!_aG,0 C$bWLAhhwdepsmhexpmmplugm1.8.1lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0wfalsehmhexpmm telemetrym telemetrym~> 0.4wtruejEbWLAhhw timestampmhexpmmdecimalm1.9.0hhbaaha aa:[bWLAhhwouter_checksummhexpmmphoenixm0.16.1m 8NP ޱ7|zɚYbWLAhhwinner_checksummhexpmmcowboym1.0.2m j/c[ai@,?H37DbWLAhhw timestampmhexpmmcowboym2.6.3hhbaahaaa.JbWLAhhw timestampmhexpmmphoenixm 1.3.0-rc.1hhba a ha aa EbWLAhhw timestampmhexpmmphoenixm0.2.1hhba a ha aa 3bWLAhhwretiredmhexpmmphoenixm1.0.2wnilBbWLAhhw timestampmhexpmmplugm1.1.7hhbaahaa4a_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.3.0m (&M"$& $bn 2q>/YbWLAhhwinner_checksummhexpmmpoisonm1.3.1m ]B߂^.>)jJ[/2}L0*bWLAhhwdepsmhexpmmranchm1.0.0j2bWLAhhwretiredmhexpmmcowlibm2.5.0wnilbbWLAhhwdepsmhexpmmplugm0.14.0lhmhexpmmcowboymcowboym~> 1.0wtruej`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.10.0m .8CN҄W~`s}") .U<=mJbWLAhhw timestampmhexpmm phoenix_htmlm2.3.1hhba a ha aa QbWLAhhw timestampmhexpmmphoenix_pubsubm 1.0.0-rc.0hhba a ha aa 3bWLAhhwretiredmhexpmmdecimalm0.2.3wnilYbWLAhhwinner_checksummhexpmmpoisonm3.1.0m cfo%٤o5V Y;2nHFe`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.10.5m O82cηF|IUTu yQ`^EZbWLAhhwinner_checksummhexpmmphoenixm1.0.6m ד kK[=rEMWhEbWLAhhw timestampmhexpmmphoenixm1.4.5hhba a ha aa XbWLAhhwinner_checksummhexpmmranchm1.3.0m ,U]X@]'.-v/XbWLAhhwouter_checksummhexpmmjasonm1.1.0m #aJc5o( ǫh_QЄR68l0bWLAhhwretiredmhexpmmplugm1.6.2wnil_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.2.0m tO$w^WGwLUcՌA2bWLAhhwretiredmhexpmmcowlibm2.4.0wnil1bWLAhhwretiredmhexpmmranchm1.6.1wnilZbWLAhhwinner_checksummhexpmmphoenixm1.1.9m Gւ52(t,[֛A}9ytY_XbWLAhhwinner_checksummhexpmmjasonm1.1.2m =g#&LqTVM=!dYbWLAhhwinner_checksummhexpmmpoisonm1.0.3m Ljxq߉Õԓ7O`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.14.2m r?Hd0P}ٜ lwRFO=V>ZbWLAhhwouter_checksummhexpmmphoenixm1.3.2m Cn [N24z9Or|Ex۶ZbWLAhhwinner_checksummhexpmmdecimalm1.6.0m Mn]CpӔ42_Ӻp0Y bWLAhhwdepsmhexpmmphoenixm1.1.3lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsejWbWLAhhwouter_checksummhexpmmplugm1.2.5m >m?ªc`p\ѥLEubWLAhhwdepsmhexpmmplugm 1.4.0-rc.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsejbWLAhhwdepsmhexpmmplugm1.7.2lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0wfalsejBbWLAhhw timestampmhexpmmplugm1.1.5hhbaahaa4aJbWLAhhw timestampmhexpmmphoenixm 1.4.0-rc.1hhba a ha aa 3bWLAhhwretiredmhexpmmphoenixm1.5.0wnilWbWLAhhwouter_checksummhexpmmplugm1.1.7m ֵePvEb}DزVAubWLAhhwdepsmhexpmm phoenix_htmlm1.1.0lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0wfalsej8bWLAhhwretiredmhexpmm phoenix_htmlm3.0.3wnilZbWLAhhwouter_checksummhexpmmdecimalm1.2.0m t"HD2HDM_dшZbWLAhhwouter_checksummhexpmmphoenixm0.7.1m 7R8CIY<+h Aa&G:bWLAhhwretiredmhexpmmphoenix_pubsubm1.0.1wnilabWLAhhwinner_checksummhexpmmphoenix_pubsubm1.1.2m Il0;.n[::75KM*Z>cbWLAhhwinner_checksummhexpmmcowboy_telemetrym0.1.0m i{8:JX:iCB"S'wC[bWLAhhwouter_checksummhexpmmphoenixm0.13.1m Z. MeC9fԮZB_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.9.2m MI?V`1= f[0g7iqKNoWbWLAhhwouter_checksummhexpmmplugm0.4.4m 1-W3SXd#_Y:EbWLAhhw timestampmhexpmmphoenixm1.5.9hhba a ha aa IbWLAhhw timestampmhexpmm plug_cowboym2.2.1hhba a ha aa ubWLAhhwdepsmhexpmm phoenix_htmlm1.0.0lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0wfalsejXbWLAhhwinner_checksummhexpmmplugm0.10.0m D?K/Fr"E*ScˋZbWLAhhwinner_checksummhexpmmphoenixm0.7.2m HN!3rWDvQ b{wf;bWLAhhwdepsmhexpmmplugm 1.5.0-rc.1lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.1wtruehmhexpmmmimemmimem~> 1.0wfalsejNbWLAhhw registry_etagmhexpmmmimem""13b840300088bfad4831e95842f0833d"*bWLAhhwdepsmhexpmmranchm1.4.0j1bWLAhhwretiredmhexpmmjasonm1.2.0wnil7bWLAhhwretiredmhexpmm plug_cowboym2.1.2wnil,bWLAhhwdepsmhexpmmdecimalm1.6.0jbWLAhhwdepsmhexpmmcowboy_telemetrym0.2.0lhmhexpmmcowboymcowboym~> 2.7wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej3bWLAhhwretiredmhexpmmphoenixm1.2.2wnilGbWLAhhw timestampmhexpmm telemetrym0.4.3hhbaahaa4abbWLAhhwdepsmhexpmmplugm0.12.0lhmhexpmmcowboymcowboym~> 1.0wtruej8bWLAhhwretiredmhexpmm phoenix_htmlm2.9.2wnil^bWLAhhwouter_checksummhexpmm plug_cowboym2.3.0m YZp?Vhp!u̬ۏ\~WbWLAhhwinner_checksummhexpmmplugm1.4.2m ğ :?a`j!UBbWLAhhw timestampmhexpmmplugm0.5.1hhbaahaa4a,bWLAhhwdepsmhexpmmdecimalm0.1.2jbWLAhhwdepsmhexpmmphoenixm1.1.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejDbWLAhhw timestampmhexpmmcowlibm1.0.1hhbaahaaa.0bWLAhhwretiredmhexpmmplugm1.8.2wnil0bWLAhhwdepsmhexpmm plug_cryptom1.2.0j1bWLAhhwversionsmhexpmmcowliblm1.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.0jcbWLAhhwinner_checksummhexpmm phoenix_htmlm 2.7.0-devm <|nKx;&y-0GY+bWLAhhwdepsmhexpmmcowlibm1.3.0jYbWLAhhwouter_checksummhexpmmcowlibm2.7.3m =mR˼|'r`vVy©9Tڝ"WaEbWLAhhw timestampmhexpmmphoenixm1.4.9hhba a ha aa bWLAhhwdepsmhexpmmplugm1.3.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsej`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.14.2m X%] |/l\ғpE/DbWLAhhw timestampmhexpmmcowlibm2.2.1hhbaahaaa.3bWLAhhwretiredmhexpmmphoenixm0.6.2wnilbWLAhhwdepsmhexpmmplugm1.8.0lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0wfalsehmhexpmm telemetrym telemetrym~> 0.4wtruejXbWLAhhwouter_checksummhexpmmranchm1.3.1m ?2ߑ< J _\q,Cud,bWLAhhwdepsmhexpmmplugm1.6.1lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4wtruehmhexpmmmimemmimem~> 1.0wfalsejYbWLAhhwouter_checksummhexpmmcowboym2.7.0m j9ƪa#a*:R{pg2bWLAhhwretiredmhexpmmpoisonm1.1.1wnil0bWLAhhwdepsmhexpmm plug_cryptom1.1.0jWbWLAhhwouter_checksummhexpmmplugm0.7.0m \ꬽVSOy#M96s-vyXg2bWLAhhwretiredmhexpmmpoisonm1.0.3wnil\bWLAhhwouter_checksummhexpmm telemetrym0.3.0m c}11b!1 Z6h!/rYbWLAhhwinner_checksummhexpmmpoisonm2.0.0m e'0kIt U&Ca3bWLAhhwretiredmhexpmmphoenixm1.0.3wnilBbWLAhhw timestampmhexpmmplugm1.3.3hhbaahaa4a3bWLAhhwretiredmhexpmmdecimalm0.1.1wnilBbWLAhhw timestampmhexpmmmimem1.1.0hhbaahaaa.XbWLAhhwouter_checksummhexpmmplugm0.11.2m 0Hи#ƢJ-Pr4WbWLAhhwinner_checksummhexpmmplugm1.2.6m a6ՀEʫz:( :$"JWbWLAhhwouter_checksummhexpmmplugm0.5.3m E}A[; 1.0wtruehmhexpmmplugmplugm~> 0.14 or ~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsej_bWLAhhwouter_checksummhexpmmphoenixm 1.4.0-rc.3m SJjJDv.8[˭FOn>35KbWLAhhw timestampmhexpmm phoenix_htmlm2.14.1hhba a ha aa XbWLAhhwinner_checksummhexpmmplugm1.10.3m ξv7u9jܠiN4n?Bƒ[bWLAhhwinner_checksummhexpmmphoenixm0.17.1m xG.rgk.W%Y49+kY+bWLAhhwdepsmhexpmmcowlibm2.8.0jYbWLAhhwouter_checksummhexpmmpoisonm1.1.1m W|4vLl$GcH0kb! gZbWLAhhwinner_checksummhexpmmphoenixm1.5.1m eƒǐ an)4ubWLAhhwdepsmhexpmmcowboym1.0.3lhmhexpmmcowlibmcowlibm~> 1.0.0wfalsehmhexpmmranchmranchm~> 1.0wfalsej*bWLAhhwdepsmhexpmmranchm1.6.2j,bWLAhhwdepsmhexpmmdecimalm0.2.3j+bWLAhhwdepsmhexpmmcowlibm2.6.0jZbWLAhhwouter_checksummhexpmmphoenixm1.1.1m Iu꿣 CMMbK[bWLAhhwinner_checksummhexpmmphoenixm1.4.10m aJTUb)M"7-(#4ez$(UZbWLAhhwinner_checksummhexpmmphoenixm0.2.8m _3uDj*EЏ%e I}=cWbWLAhhwouter_checksummhexpmmplugm1.1.6m #d):4p]mϤ\*bWLAhhwdepsmhexpmmranchm1.8.0jEbWLAhhw timestampmhexpmmphoenixm1.5.2hhba a ha aa +bWLAhhwdepsmhexpmmcowlibm2.7.0jbWLAhhwdepsmhexpmmphoenixm 1.6.0-rc.0lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmm phoenix_viewm phoenix_viewm~> 1.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4 or ~> 1.0wfalsej+bWLAhhwdepsmhexpmmpoisonm1.0.1j8bWLAhhwretiredmhexpmm phoenix_htmlm2.1.1wnil+bWLAhhwdepsmhexpmmcowlibm2.7.1jCbWLAhhw timestampmhexpmmplugm0.11.0hhbaahaa4aDbWLAhhw timestampmhexpmmpoisonm2.0.0hhba a ha a.a2bWLAhhwretiredmhexpmmcowboym1.1.1wnilEbWLAhhw timestampmhexpmmdecimalm1.3.0hhbaaha aa:+bWLAhhwdepsmhexpmmcowlibm1.0.1jZbWLAhhwinner_checksummhexpmmphoenixm1.4.6m 5 xj.HQxvlYCbWLAhhw timestampmhexpmmjasonm1.0.0hhbaaha aa:`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.11.2m h% 'W [ۓH]doL1fWbWLAhhwinner_checksummhexpmmmimem1.4.0m PfIDp(aF}/sQ̨/H5і|GEbWLAhhw timestampmhexpmmphoenixm1.1.3hhba a ha aa bWLAhhwdepsmhexpmmphoenixm1.5.0lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej3bWLAhhwretiredmhexpmmphoenixm0.2.2wnil1bWLAhhwretiredmhexpmmranchm1.6.0wnilYbWLAhhwinner_checksummhexpmmcowboym2.2.2m lcMڋ+ mYc$ph-6`g^T[bWLAhhwouter_checksummhexpmmphoenixm1.4.15m dv#f mW7JܻiIBbWLAhhw timestampmhexpmmmimem1.6.0hhbaahaaa.YbWLAhhwouter_checksummhexpmmcowboym1.0.2m 89·,Gq[OLX?JbWLAhhw timestampmhexpmm phoenix_htmlm3.0.3hhba a ha aa LbWLAhhw timestampmhexpmmphoenix_pubsubm2.0.0hhba a ha aa 3bWLAhhwretiredmhexpmmphoenixm1.1.7wnilabWLAhhwdepsmhexpmmplugm1.0.0lhmhexpmmcowboymcowboym~> 1.0wtruej3bWLAhhwretiredmhexpmmphoenixm1.5.1wnilWbWLAhhwinner_checksummhexpmmplugm1.3.2m غ.,}i!YdU Uac-ZbWLAhhwouter_checksummhexpmmdecimalm0.1.1m ZA57/P,#n^}[WBxWbWLAhhwouter_checksummhexpmmplugm1.2.6m P;hQ>y4Ԇ-.rQL$"4EbWLAhhw timestampmhexpmmphoenixm1.0.5hhba a ha aa FbWLAhhw timestampmhexpmmphoenixm0.16.1hhba a ha aa WbWLAhhwouter_checksummhexpmmmimem1.3.1m lvj  1Lc KζE8duEbWLAhhw timestampmhexpmmdecimalm2.0.0hhbaaha aa:WbWLAhhwouter_checksummhexpmmplugm0.5.0m CQ rU!w*q LkSצo2bWLAhhwretiredmhexpmmpoisonm1.0.2wnil+bWLAhhwdepsmhexpmmpoisonm2.1.0jWbWLAhhwinner_checksummhexpmmplugm0.4.3m }.sDc+bWLAhhwdepsmhexpmmcowlibm2.0.1j1bWLAhhwretiredmhexpmmranchm1.2.0wnilabWLAhhwouter_checksummhexpmmphoenix_pubsubm2.0.0m -O&wKPYuCSPqBbWLAhhw timestampmhexpmmplugm0.8.2hhbaahaa4abWLAhhwdepsmhexpmm plug_cowboym2.1.2lhmhexpmmcowboymcowboym~> 2.5wfalsehmhexpmmplugmplugm~> 1.7wfalsej_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.7.0m . D4 .C jYg|Yh5T8ՒB XbWLAhhwinner_checksummhexpmmplugm0.13.1m GA2wY!^asoK/}bWLAhhwdepsmhexpmmphoenixm 1.5.0-rc.0lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.1wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejbWLAhhwdepsmhexpmmphoenixm1.0.4lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejbWLAhhwdepsmhexpmmplugm1.6.2lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4wtruehmhexpmmmimemmimem~> 1.0wfalsej8bWLAhhwdepsmhexpmmphoenix_pubsubm 1.0.0-rc.0j,bWLAhhwdepsmhexpmmdecimalm0.2.4jJbWLAhhw timestampmhexpmm phoenix_htmlm2.2.0hhba a ha aa XbWLAhhwouter_checksummhexpmmplugm0.11.1m M}O໿Fk΍]/]/9ݸ3bWLAhhwretiredmhexpmmphoenixm1.4.3wnilZbWLAhhwinner_checksummhexpmmphoenixm0.9.0m cmHZ^j:x:_h[EDbWLAhhw timestampmhexpmmpoisonm2.0.1hhba a ha a.aDbWLAhhw timestampmhexpmmcowboym1.1.1hhbaahaaa.DbWLAhhw timestampmhexpmmcowboym1.1.0hhbaahaaa.XbWLAhhwouter_checksummhexpmmranchm1.4.0m S "]CG<(L _8bWLAhhwretiredmhexpmm phoenix_htmlm2.9.0wnilbWLAhhwdepsmhexpmmplugm1.8.2lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.0wfalsehmhexpmm telemetrym telemetrym~> 0.4wtruejbWLAhhwdepsmhexpmmcowboym1.1.0lhmhexpmmcowlibmcowlibm~> 1.0.2wfalsehmhexpmmranchmranchm~> 1.2.0wfalsejfbWLAhhwdepsmhexpmm phoenix_htmlm2.6.2lhmhexpmmplugmplugm~> 1.0wfalsej1bWLAhhwretiredmhexpmmranchm1.1.0wnilJbWLAhhw timestampmhexpmm phoenix_htmlm2.9.1hhba a ha aa EbWLAhhw timestampmhexpmmphoenixm0.6.0hhba a ha aa gbWLAhhwdepsmhexpmm phoenix_htmlm2.12.0lhmhexpmmplugmplugm~> 1.5wfalsejbWLAhhwdepsmhexpmmcowboym2.6.2lhmhexpmmcowlibmcowlibm~> 2.7.2wfalsehmhexpmmranchmranchm~> 1.7.1wfalsejWbWLAhhwinner_checksummhexpmmplugm1.2.0m IkcJI׀:gΜ෹% Y 0bWLAhhwretiredmhexpmmplugm1.0.2wnildbWLAhhwdepsmhexpmmjasonm1.2.1lhmhexpmmdecimalmdecimalm~> 1.0wtruejYbWLAhhwouter_checksummhexpmmpoisonm1.0.2m 0AX55cfWYbWLAhhwouter_checksummhexpmmpoisonm1.2.0m :b#%?)A@ʽĭXZbʦ[BbWLAhhw timestampmhexpmmplugm0.6.0hhbaahaa4aYbWLAhhwouter_checksummhexpmmcowlibm1.0.0m MV~ϓ9t51aM=ZtWbWLAhhwouter_checksummhexpmmplugm0.8.2m ɗg)&'(|VVԃ7 ZbWLAhhwinner_checksummhexpmmphoenixm1.5.9m 66՝{7C`Z"|~%WbWLAhhwinner_checksummhexpmmplugm1.1.7m @MMerFL/YH"[\bWLAhhwinner_checksummhexpmmplugm 1.5.0-rc.1m r>%f-k,\a$B/9&YbWLAhhwinner_checksummhexpmmpoisonm4.0.0m 7sA /ҏ.*®!B}0{GbWLAhhw timestampmhexpmm telemetrym0.4.1hhbaahaa4aBbWLAhhw timestampmhexpmmplugm1.1.2hhbaahaa4a3bWLAhhwretiredmhexpmmphoenixm0.3.0wnil,bWLAhhwdepsmhexpmmdecimalm1.1.2j+bWLAhhwdepsmhexpmmcowlibm2.4.0j8bWLAhhwretiredmhexpmm phoenix_htmlm3.0.0wnil0bWLAhhwretiredmhexpmmplugm1.0.3wnilJbWLAhhw timestampmhexpmm phoenix_htmlm1.1.0hhba a ha aa qbWLAhhwdepsmhexpmm phoenix_htmlm2.3.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsej\bWLAhhwinner_checksummhexpmmplugm 1.4.0-rc.0m Solm$a63;3r~[ EbWLAhhw timestampmhexpmmphoenixm1.2.1hhba a ha aa DbWLAhhw timestampmhexpmmcowboym2.4.0hhbaahaaa.^bWLAhhwouter_checksummhexpmm plug_cryptom1.1.1m &Mo A i0bWLAhhwretiredmhexpmmplugm1.1.9wnil1bWLAhhwretiredmhexpmmjasonm1.1.0wnil^bWLAhhwouter_checksummhexpmm plug_cowboym2.5.2m nt``@"svIb5D0bWLAhhwretiredmhexpmmmimem2.0.1wnilBbWLAhhw timestampmhexpmmmimem1.3.0hhbaahaaa.^bWLAhhwouter_checksummhexpmm plug_cowboym2.2.0m NrrlgzyGMR;(Ѯ\}[bWLAhhwdepsmhexpmmphoenixm0.3.1lhmhexpmmex_confmex_confm0.1.3wfalsehmhexpmminflexminflexm0.2.4wfalsehmhexpmmjazzmjazzm0.1.2wfalsehmhexpmmplugmplugm0.5.1wfalsejFbWLAhhw timestampmhexpmmphoenixm0.10.0hhba a ha aa YbWLAhhwouter_checksummhexpmmcowboym1.0.4m jh 3SBcSLڦDbWLAhhw timestampmhexpmmcowboym2.8.0hhbaahaaa.bWLAhhwdepsmhexpmmphoenixm1.4.4lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruej8bWLAhhwretiredmhexpmmphoenixm 1.3.0-rc.2wnilbWLAhhwdepsmhexpmmcowboym1.1.1lhmhexpmmcowlibmcowlibm~> 1.0.2wfalsehmhexpmmranchmranchm~> 1.3.1wfalsejEbWLAhhw timestampmhexpmmphoenixm0.5.0hhba a ha aa bbWLAhhwdepsmhexpmmplugm0.11.3lhmhexpmmcowboymcowboym~> 1.0wtruejEbWLAhhw timestampmhexpmmdecimalm1.2.0hhbaaha aa:XbWLAhhwinner_checksummhexpmmranchm1.6.2m m 1.0.1 or ~> 1.1 or ~> 2.4wtruehmhexpmmmimemmimem~> 1.0wfalsejYbWLAhhwouter_checksummhexpmmcowlibm2.5.1m 4¶:k@UD%,S]bWLAhhwdepsmhexpmmplugm1.4.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsej4bWLAhhwretiredmhexpmmphoenixm0.11.0wnilWbWLAhhwouter_checksummhexpmmplugm1.3.5m X̡(9D/q*tmRYbWLAhhwouter_checksummhexpmmcowlibm2.6.0m Eo*e)YU7,-3$SIbWLAhhwdepsmhexpmmphoenixm1.1.7lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsej3bWLAhhwretiredmhexpmmdecimalm1.0.1wnilDbWLAhhw timestampmhexpmmcowlibm2.0.0hhbaahaaa.2bWLAhhwretiredmhexpmmcowlibm1.1.0wnilXbWLAhhwouter_checksummhexpmmranchm1.6.1m &O]4b uj[X}b+']X8#n7bWLAhhwretiredmhexpmm plug_cowboym2.5.1wnilJbWLAhhw timestampmhexpmmphoenixm 1.3.0-rc.3hhba a ha aa _bWLAhhwouter_checksummhexpmm phoenix_htmlm2.1.0m `+&}C!kI˯E ,bWLAhhwdepsmhexpmmdecimalm1.9.0jcbWLAhhwdepsmhexpmmphoenixm1.4.8lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsejbWLAhhwdepsmhexpmmcowboym1.0.4lhmhexpmmcowlibmcowlibm~> 1.0.0wfalsehmhexpmmranchmranchm~> 1.0wfalsejEbWLAhhw timestampmhexpmmdecimalm1.4.1hhbaaha aa:bWLAhhwdepsmhexpmmphoenixm0.2.5lhmhexpmmex_confmex_confm0.1.1wfalsehmhexpmminflexminflexm0.2.0wfalsehmhexpmmplugmplugm0.4.4wfalsej_bWLAhhwinner_checksummhexpmmphoenixm 1.3.0-rc.3m 7 9 5r}F!Yj 0DYbWLAhhwinner_checksummhexpmmcowlibm2.7.1m |[bfĘy_d{I5`0bWLAhhwdepsmhexpmm plug_cryptom1.1.2j8bWLAhhw timestampmhexpmmplughhbaahaa4a[bWLAhhwouter_checksummhexpmmphoenixm1.4.12m X3mw1*=o4%F0bWLAhhwdepsmhexpmmcowboy_telemetrym0.4.0lhmhexpmmcowboymcowboym~> 2.7wfalsehmhexpmm telemetrym telemetrym~> 1.0wfalsejWbWLAhhwinner_checksummhexpmmplugm1.7.1m er.t%O6N >[bWLAhhwinner_checksummhexpmmphoenixm1.4.14m ڭh\@WF>ˇj슶ޢBL$AN8XbWLAhhwouter_checksummhexpmmplugm0.13.0m +oĦ%W{US1o":gL__bWLAhhwouter_checksummhexpmm phoenix_htmlm1.2.1m N ĪI,"sin ]}DbWLAhhw timestampmhexpmmcowlibm2.7.3hhbaahaaa.ZbWLAhhwinner_checksummhexpmmphoenixm1.5.5m Z~(8RMCj5Ex鴽^bWLAhhwinner_checksummhexpmm plug_cowboym2.1.3m 8>k߂aa2D\ܶ3dbWLAhhwdepsmhexpmmphoenixm1.4.17lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.8.1 or ~> 1.9wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruehmhexpmm telemetrym telemetrym~> 0.4wfalsejBbWLAhhw timestampmhexpmmplugm0.8.0hhbaahaa4aWbWLAhhwouter_checksummhexpmmplugm1.8.3m K8-ĄI**w 3N'4DbWLAhhw timestampmhexpmmcowlibm1.1.0hhbaahaaa.3bWLAhhwretiredmhexpmmphoenixm1.1.8wnil8bWLAhhwretiredmhexpmm phoenix_htmlm1.4.0wnil.bWLAhhwdepsmhexpmm telemetrym1.0.0jgbWLAhhwdepsmhexpmm phoenix_htmlm2.13.2lhmhexpmmplugmplugm~> 1.5wfalsejZbWLAhhwinner_checksummhexpmmphoenixm1.1.8m %J&RG ʩB).A-6T\_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.1.1m *Zbu6n4"0A(uTW70BbWLAhhw timestampmhexpmmphoenix_pubsubhhba a ha aa ubWLAhhwdepsmhexpmm phoenix_htmlm 2.4.0-devlhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsej8bWLAhhwretiredmhexpmm phoenix_htmlm2.9.3wnil_bWLAhhwouter_checksummhexpmmdecimalm 2.0.0-rc.0m %^,>8W$d. 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruej`bWLAhhwinner_checksummhexpmm phoenix_htmlm2.14.3m Q CW]0=WxieBqCbWLAhhw timestampmhexpmmjasonm1.2.2hhbaaha aa:[bWLAhhwinner_checksummhexpmmphoenixm1.4.12m oZ+6I\VA2dc~w39]lIbWLAhhw timestampmhexpmm plug_cryptom1.0.0hhbaahaaa.WbWLAhhwouter_checksummhexpmmplugm1.3.6m ?/ ߶? V.\od/uR-}+bWLAhhwdepsmhexpmmcowlibm1.2.0jXbWLAhhwouter_checksummhexpmmranchm1.3.2m nVI:$3:0%Fr R5XbWLAhhwinner_checksummhexpmmjasonm1.1.0m 4/thՆS\[ WRO+bWLAhhwdepsmhexpmmcowlibm1.0.0j^bWLAhhwouter_checksummhexpmm plug_cowboym2.1.3m oA۳FƯdn%(7fYbWLAhhwinner_checksummhexpmmpoisonm1.5.0m `b:oF45%%8gͽWP2H7bWLAhhwretiredmhexpmm plug_cowboym2.1.1wnil3bWLAhhwretiredmhexpmmphoenixm1.0.0wnilXbWLAhhwouter_checksummhexpmmranchm1.7.1m E'x}ٝabY4].ǯ8bWLAhhwretiredmhexpmm phoenix_htmlm2.1.0wnilZbWLAhhwouter_checksummhexpmmphoenixm0.1.0m uW X  Tb~ n?p*bWLAhhwdepsmhexpmmphoenixm1.4.0lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.1wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.0wtruej}bWLAhhwversionsmhexpmm telemetrylm0.1.0m0.2.0m0.3.0m0.4.0m0.4.1m0.4.2m0.4.3m1.0.0jbbWLAhhwdepsmhexpmmplugm0.11.1lhmhexpmmcowboymcowboym~> 1.0wtruejWbWLAhhwouter_checksummhexpmmplugm1.9.0m ** d4h.I?]MjƼ_#P2bWLAhhwretiredmhexpmmcowboym2.4.0wnilLbWLAhhw timestampmhexpmmphoenix_pubsubm1.1.1hhba a ha aa 2bWLAhhwretiredmhexpmmpoisonm3.0.0wnil8bWLAhhwretiredmhexpmm phoenix_htmlm2.8.0wnil7bWLAhhwretiredmhexpmm plug_cowboym2.4.1wnilEbWLAhhw timestampmhexpmmphoenixm1.4.0hhba a ha aa *bWLAhhwdepsmhexpmmranchm1.2.1j2bWLAhhwretiredmhexpmmcowboym2.0.0wnilPbWLAhhw registry_etagmhexpmmcowboym""f50970e94cfffebacd79a64ebac870b1"0bWLAhhwretiredmhexpmmplugm0.7.0wnilEbWLAhhw timestampmhexpmmphoenixm0.1.0hhba a ha aa ZbWLAhhwouter_checksummhexpmmphoenixm1.0.5m иdm 0˨YOvo//7׬)e1 ZbWLAhhwinner_checksummhexpmmphoenixm1.4.1m c(e|e|bLB#+bWLAhhwdepsmhexpmmcowlibm2.3.0jJbWLAhhw timestampmhexpmmphoenixm 1.3.0-rc.0hhba a ha aa CbWLAhhw timestampmhexpmmranchm1.8.0hhba a ha aa bWLAhhwdepsmhexpmmphoenixm0.2.1lhmhexpmmex_confmex_confm0.1.1wfalsehmhexpmminflexminflexm0.2.0wfalsehmhexpmmplugmplugm0.4.2wfalsej3bWLAhhwdepsmhexpmmphoenix_pubsubm0.1.0jBbWLAhhw timestampmhexpmmplugm1.4.5hhbaahaa4aWbWLAhhwinner_checksummhexpmmplugm1.4.4m 'Tvb',5 ӾQG5NFtĴGbWLAhhw timestampmhexpmm telemetrym0.4.0hhbaahaa4aabWLAhhwinner_checksummhexpmmphoenix_pubsubm1.1.0m ^%/dcfa-PSYD.bWLAhhwdepsmhexpmm telemetrym0.4.0j9bWLAhhwretiredmhexpmm phoenix_htmlm2.10.0wnilWbWLAhhwinner_checksummhexpmmplugm1.1.1m UWlٹUh e<>h|,{\{GbWLAhhw timestampmhexpmm telemetrym0.3.0hhbaahaa4aDbWLAhhw timestampmhexpmmcowlibm2.1.0hhbaahaaa.YbWLAhhwouter_checksummhexpmmpoisonm1.0.0m ~)E#b :Q G{9bWLAhhw timestampmhexpmmjasonhhbaaha aa:YbWLAhhwinner_checksummhexpmmcowlibm2.5.0m bXKK%֋ IFrJbWLAhhw timestampmhexpmm phoenix_htmlm2.0.1hhba a ha aa 2bWLAhhwretiredmhexpmmcowboym1.0.3wnilCbWLAhhw timestampmhexpmmplugm1.11.0hhbaahaa4aZbWLAhhwouter_checksummhexpmmphoenixm0.9.0m ' =/ˍvVފ(tOP#jicbWLAhhwdepsmhexpmmplugm0.8.0lhmhexpmmcowboymcowboym~> 1.0.0wtruej3bWLAhhwretiredmhexpmmdecimalm1.3.0wnil0bWLAhhwretiredmhexpmmplugm1.3.0wnil2bWLAhhwretiredmhexpmmcowboym1.0.4wnil4bWLAhhwretiredmhexpmmphoenixm0.13.1wnil3bWLAhhwretiredmhexpmmphoenixm1.3.0wnil,bWLAhhwdepsmhexpmmdecimalm1.5.0j[bWLAhhwouter_checksummhexpmmphoenixm0.15.0m ijLvȮB.At"u >V!WbWLAhhwouter_checksummhexpmmplugm1.2.4m @<*< 0!Ǝ +D(;P}3bWLAhhwretiredmhexpmmphoenixm0.3.1wnil3bWLAhhwretiredmhexpmmdecimalm1.1.1wnilabWLAhhwdepsmhexpmmplugm1.1.4lhmhexpmmcowboymcowboym~> 1.0wtruejZbWLAhhwinner_checksummhexpmmphoenixm1.2.1m mŒ$VV5E6M^^AE@uUEh0bWLAhhwretiredmhexpmmplugm1.5.1wnilWbWLAhhwouter_checksummhexpmmplugm1.4.0m p>PPSm mɆE[bWLAhhwouter_checksummhexpmmphoenixm1.5.12m sOdl/o?gS 2A2bWLAhhwretiredmhexpmmpoisonm1.0.1wnilubWLAhhwdepsmhexpmm phoenix_htmlm1.3.0lhmhexpmmplugmplugm>= 0.12.2 and < 2.0.0wfalsejCbWLAhhw timestampmhexpmmplugm0.10.0hhbaahaa4aDbWLAhhw timestampmhexpmmpoisonm1.2.0hhba a ha a.a3bWLAhhwretiredmhexpmmphoenixm1.1.3wnilWbWLAhhwinner_checksummhexpmmmimem1.3.1m 01u \w=R=sxM<abWLAhhwdepsmhexpmmplugm0.9.0lhmhexpmmcowboymcowboym~> 1.0wtruej3bWLAhhwretiredmhexpmmphoenixm1.2.5wnil[bWLAhhwouter_checksummhexpmmphoenixm1.4.16m l2S'7A<u%Wm\$ZbWLAhhwinner_checksummhexpmmphoenixm0.2.5m ",Ҍ5 L<ؼ߬3YbWLAhhwinner_checksummhexpmmcowlibm2.3.0m Վ7NO|.jNdL8bWLAhhwretiredmhexpmmphoenixm 1.4.0-rc.0wnil9bWLAhhwretiredmhexpmm phoenix_htmlm2.10.4wnilBbWLAhhw timestampmhexpmmmimem1.3.1hhbaahaaa.ZbWLAhhwinner_checksummhexpmmphoenixm1.5.3m @NH4X? [b0;IgbWLAhhwdepsmhexpmm phoenix_htmlm2.13.4lhmhexpmmplugmplugm~> 1.5wfalsej1bWLAhhwretiredmhexpmmplugm0.11.1wnilBbWLAhhw timestampmhexpmmplugm1.6.1hhbaahaa4aKbWLAhhw timestampmhexpmm phoenix_htmlm2.10.4hhba a ha aa DbWLAhhw timestampmhexpmmcowlibm2.5.1hhbaahaaa.2bWLAhhwretiredmhexpmmcowlibm2.0.1wnilqbWLAhhwdepsmhexpmm phoenix_htmlm2.3.1lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsejabWLAhhwdepsmhexpmmplugm1.0.3lhmhexpmmcowboymcowboym~> 1.0wtruejFbWLAhhw timestampmhexpmmphoenixm1.4.15hhba a ha aa ZbWLAhhwinner_checksummhexpmmphoenixm1.2.5m Z_"Y^#*evH#^bWLAhhwinner_checksummhexpmm plug_cryptom1.0.0m 4?$b')ԥ- }M3bWLAhhwretiredmhexpmmphoenixm1.5.2wnilcbWLAhhwouter_checksummhexpmm phoenix_htmlm 2.4.0-devm 0a/@/։ꠦ=;Aj8bWLAhhwretiredmhexpmm phoenix_htmlm1.0.0wnilZbWLAhhwinner_checksummhexpmmdecimalm1.8.0m F. _ ţB~5HZes4.*bWLAhhwdepsmhexpmmranchm1.2.0jebWLAhhwdepsmhexpmm phoenix_htmlm3.0.0lhmhexpmmplugmplugm~> 1.5wtruej0bWLAhhwretiredmhexpmmplugm1.7.1wnil4bWLAhhwretiredmhexpmmphoenixm0.12.0wnilZbWLAhhwinner_checksummhexpmmphoenixm0.2.6m '1C &Sno1lmbu Q^IvbWLAhhwretiredmhexpmm plug_cowboym2.2.1twmessagemBroken telemetry supportwreasonwRETIRED_INVALIDXbWLAhhwouter_checksummhexpmmranchm1.1.0m 9>eg][ŽS?`@WHCbWLAhhw timestampmhexpmmranchm1.7.0hhba a ha aa DbWLAhhw timestampmhexpmmcowlibm1.0.2hhbaahaaa.^bWLAhhwinner_checksummhexpmm plug_cowboym2.4.0m 6QLQshͭreYÓ l)HZbWLAhhwouter_checksummhexpmmphoenixm0.7.0m $y|6; w'Px`j1TEZbWLAhhwouter_checksummhexpmmcowlibm2.10.0m /- vt)u!H{*MNXbWLAhhwinner_checksummhexpmmjasonm1.2.0m 4-$!-?]HCv 8yY8HbWLAhhwdepsmhexpmmplugm1.10.4lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejJbWLAhhw timestampmhexpmm phoenix_htmlm2.3.0hhba a ha aa YbWLAhhwouter_checksummhexpmmpoisonm1.5.1m =Z'V-6{ =`X@G ?EbWLAhhw timestampmhexpmmphoenixm1.5.0hhba a ha aa .bWLAhhwdepsmhexpmm telemetrym0.2.0j+bWLAhhwdepsmhexpmmcowlibm2.2.1jcbWLAhhwdepsmhexpmmplugm0.8.1lhmhexpmmcowboymcowboym~> 1.0.0wtruej?bWLAhhwretiredmhexpmmphoenix_pubsubm 1.0.0-rc.0wnil[bWLAhhwinner_checksummhexpmmphoenixm0.2.10m !/I7תck[%NS abWLAhhwinner_checksummhexpmmphoenix_pubsubm1.0.0m "FY'x@ʇa A ,WJbWLAhhw timestampmhexpmm phoenix_htmlm2.9.0hhba a ha aa 9bWLAhhwretiredmhexpmm phoenix_htmlm2.11.1wnilfbWLAhhwdepsmhexpmm phoenix_htmlm2.9.0lhmhexpmmplugmplugm~> 1.0wfalsej7bWLAhhwretiredmhexpmm plug_cowboym2.1.0wnilXbWLAhhwouter_checksummhexpmmjasonm1.2.2m (򟞮aðvhя!yIbWLAhhw timestampmhexpmm plug_cowboym2.5.0hhba a ha aa 2bWLAhhwretiredmhexpmmpoisonm1.5.1wnilZbWLAhhwouter_checksummhexpmmphoenixm1.1.9m 1gxف 1.0wtruehmhexpmmpoisonmpoisonm~> 1.2wfalsej)bWLAhhwdepsmhexpmmplugm0.4.3j]bWLAhhwouter_checksummhexpmmranchm 2.0.0-rc.2m gY`sC0o=Ho[;BbWLAhhw timestampmhexpmmplugm0.5.2hhbaahaa4agbWLAhhwdepsmhexpmm phoenix_htmlm2.13.1lhmhexpmmplugmplugm~> 1.5wfalsejbWLAhhwdepsmhexpmmphoenixm1.5.12lhmhexpmmjasonmjasonm~> 1.0wtruehmhexpmm phoenix_htmlm phoenix_htmlm~> 2.13 or ~> 3.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 2.0wfalsehmhexpmmplugmplugm~> 1.10wfalsehmhexpmm plug_cowboym plug_cowboym~> 1.0 or ~> 2.2wtruehmhexpmm plug_cryptom plug_cryptom~> 1.1.2 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejZbWLAhhwouter_checksummhexpmmdecimalm2.0.0m 4fnUި}7b2j# DwbWLAhhwdepsmhexpmmplugm1.4.1lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1wtruehmhexpmmmimemmimem~> 1.0wfalsej.bWLAhhwdepsmhexpmm telemetrym0.4.3j0bWLAhhwretiredmhexpmmplugm1.3.3wnilWbWLAhhwouter_checksummhexpmmplugm1.4.5m (k4oA뽰ĸ^YbWLAhhwinner_checksummhexpmmcowlibm1.0.1m Zc 1.0.1 or ~> 1.1 or ~> 2.1wtruehmhexpmmmimemmimem~> 1.0wfalsej3bWLAhhwretiredmhexpmmphoenixm0.7.0wnil_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.0.0m ϲ aq|Ǿoqd$6bWLAhhwdepsmhexpmmphoenixm1.2.2lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm~> 1.1wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsejabWLAhhwdepsmhexpmmplugm0.8.3lhmhexpmmcowboymcowboym~> 1.0wtruejbWLAhhwdepsmhexpmmphoenixm0.16.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 0.14 or ~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejDbWLAhhw timestampmhexpmmpoisonm1.0.2hhba a ha a.a1bWLAhhwretiredmhexpmmranchm1.8.0wnilZbWLAhhwinner_checksummhexpmmdecimalm1.0.0m 4") h_S)3rm"K^YbWLAhhwouter_checksummhexpmmcowboym2.6.2m CC^`1lhr`A{1bWLAhhwretiredmhexpmmranchm1.7.1wnil8bWLAhhwretiredmhexpmmdecimalm 2.0.0-rc.0wnilDbWLAhhw timestampmhexpmmcowboym2.3.0hhbaahaaa.ZbWLAhhwinner_checksummhexpmmphoenixm1.1.4m e7srZVeK@nw<|1bWLAhhwdepsmhexpmmcowboym1.0.2lhmhexpmmcowlibmcowlibm~> 1.0.0wfalsehmhexpmmranchmranchm~> 1.0wfalsejBbWLAhhw timestampmhexpmmplugm0.5.0hhbaahaa4a,bWLAhhwdepsmhexpmmdecimalm1.8.0jDbWLAhhw timestampmhexpmmpoisonm3.1.0hhba a ha a.a=bWLAhhw timestampmhexpmm telemetryhhbaahaa4aWbWLAhhwouter_checksummhexpmmplugm1.3.2m jhZ84 cM^~KbWLAhhw timestampmhexpmm phoenix_htmlm2.14.0hhba a ha aa 0bWLAhhwretiredmhexpmmplugm1.3.4wnilZbWLAhhwinner_checksummhexpmmphoenixm1.4.0m Vs_>;1IԴ6fb{CbWLAhhw timestampmhexpmmranchm1.2.0hhba a ha aa 7bWLAhhwretiredmhexpmm plug_cryptom1.2.2wnil_bWLAhhwouter_checksummhexpmmphoenixm 1.3.0-rc.2m ap RŸo ,nE4ns,ND8bWLAhhwretiredmhexpmmphoenixm 1.2.0-rc.1wnil0bWLAhhwretiredmhexpmmplugm1.1.6wnilBbWLAhhw timestampmhexpmmplugm1.2.6hhbaahaa4aYbWLAhhwinner_checksummhexpmmcowboym1.1.2m a)Ze``-7 po)ܩ;_bWLAhhwouter_checksummhexpmm phoenix_htmlm1.1.0m tD/}"/Gv"{>_vP]1ZbWLAhhwouter_checksummhexpmmdecimalm1.1.2m zmM8׸8P"H|h=3Q㦑abWLAhhwouter_checksummhexpmmphoenix_pubsubm1.0.2m o6L]kӨ4lVˮft8bWLAhhwretiredmhexpmm phoenix_htmlm2.0.0wnil,bWLAhhwdepsmhexpmmdecimalm1.3.0jqbWLAhhwdepsmhexpmm phoenix_htmlm2.0.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsej1bWLAhhwretiredmhexpmmranchm1.0.0wnil_bWLAhhwinner_checksummhexpmmphoenixm 1.2.0-rc.1m /a'c՚PQ b?'0bWLAhhwretiredmhexpmmmimem1.6.0wnil)bWLAhhwdepsmhexpmmplugm0.5.1jWbWLAhhwouter_checksummhexpmmplugm1.3.3m C4t~ws/[4 };WbWLAhhwouter_checksummhexpmmplugm1.2.1m = ;BoƳa+]>FebWLAhhwdepsmhexpmm phoenix_htmlm3.0.2lhmhexpmmplugmplugm~> 1.5wtruejZbWLAhhwinner_checksummhexpmmphoenixm1.3.4m ^U#:{mဿ,TS#I<*%=EbWLAhhw timestampmhexpmmphoenixm1.3.3hhba a ha aa 3bWLAhhwretiredmhexpmmphoenixm0.2.9wnilXbWLAhhwouter_checksummhexpmmplugm1.10.4m #?=.Vaeh`w{g1H-NwZbWLAhhwouter_checksummhexpmmdecimalm1.3.0m B 0 1.0wtruehmhexpmmpoisonmpoisonm~> 1.2wfalsej2bWLAhhwretiredmhexpmmcowlibm1.3.0wnilEbWLAhhw timestampmhexpmmphoenixm0.2.3hhba a ha aa VbWLAhhw registry_etagmhexpmm phoenix_htmlm""0ecf0546b4ca52ab2dbe5ee3a933fc6a"ZbWLAhhwouter_checksummhexpmmphoenixm0.2.5m ~sؒv `1 l,h˕PybѶ19bWLAhhwretiredmhexpmm phoenix_htmlm2.14.2wnilgbWLAhhwdepsmhexpmm phoenix_htmlm2.11.0lhmhexpmmplugmplugm~> 1.5wfalsejZbWLAhhwinner_checksummhexpmmphoenixm1.1.5m >xx*K9YΫK8u[bWLAhhwinner_checksummhexpmmphoenixm1.4.16m ,fV|D8 3B7*Bn=:HCEbWLAhhw timestampmhexpmmdecimalm0.2.1hhbaaha aa:YbWLAhhwouter_checksummhexpmmcowboym2.6.0m |z'b#EF2N\~ x[(294_bWLAhhwinner_checksummhexpmm phoenix_htmlm1.2.0m Aگ^˸L^9sgSX9HXbWLAhhwdepsmhexpmm plug_cowboym2.2.0lhmhexpmmcowboymcowboym~> 2.7wfalsehmhexpmmplugmplugm~> 1.7wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej[bWLAhhwouter_checksummhexpmmphoenixm1.4.11m 7#os3s^oB~XᤲYbWLAhhwouter_checksummhexpmmcowboym2.6.3m X)?t6.ZkZ. ZbWLAhhwouter_checksummhexpmmphoenixm1.5.1m '+8(y̮og鷐=hx^=#1R8bWLAhhwretiredmhexpmmphoenixm 1.3.0-rc.0wnilIbWLAhhw timestampmhexpmm plug_cowboym2.1.3hhba a ha aa bWLAhhwdepsmhexpmmphoenixm1.0.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejZbWLAhhwinner_checksummhexpmmphoenixm0.2.2m "r4|2_IijJPtT fB9bWLAhhwretiredmhexpmm phoenix_htmlm2.10.2wnil4bWLAhhwretiredmhexpmmphoenixm1.5.12wnil_bWLAhhwouter_checksummhexpmm phoenix_htmlm3.0.3m *eaĻ~) T~-0bWLAhhwretiredmhexpmmplugm1.1.3wnil1bWLAhhwretiredmhexpmmplugm1.12.1wnilYbWLAhhwouter_checksummhexpmmcowlibm2.7.0m YRoS"oۋJg'||ުmC؎`bWLAhhwouter_checksummhexpmm phoenix_htmlm2.14.1m SmR7[2A z:, _wc$ZbWLAhhwinner_checksummhexpmmphoenixm0.4.1m Fwbp1YGױg 8ZZbWLAhhwinner_checksummhexpmmphoenixm0.2.7m A_y~rtw(P3T[i(VZbWLAhhwinner_checksummhexpmmphoenixm0.2.4m Nr[+:Igʨ(4Y\bWLAhhwdepsmhexpmmphoenixm1.1.4lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsejDbWLAhhw timestampmhexpmmcowboym2.6.1hhbaahaaa.IbWLAhhw timestampmhexpmm plug_cowboym2.3.0hhba a ha aa YbWLAhhwouter_checksummhexpmmcowlibm1.1.0m LQucIWϕ;{xׂIbWLAhhw timestampmhexpmm plug_cowboym2.4.0hhba a ha aa BbWLAhhw timestampmhexpmmplugm1.2.1hhbaahaa4a0bWLAhhwretiredmhexpmmplugm0.5.0wnilEbWLAhhw timestampmhexpmmphoenixm1.5.1hhba a ha aa bWLAhhwdepsmhexpmmcowboym2.7.0lhmhexpmmcowlibmcowlibm~> 2.8.0wfalsehmhexpmmranchmranchm~> 1.7.1wfalsejCbWLAhhw timestampmhexpmmjasonm1.1.1hhbaaha aa:3bWLAhhwretiredmhexpmmphoenixm1.5.6wnilXbWLAhhwinner_checksummhexpmmplugm1.10.0m e)\TE&7䨃4vHuMDbWLAhhw timestampmhexpmmpoisonm4.0.0hhba a ha a.a0bWLAhhwretiredmhexpmmplugm0.5.1wnil^bWLAhhwinner_checksummhexpmm plug_cowboym2.0.0m r+ 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsejCbWLAhhw timestampmhexpmmplugm0.12.0hhbaahaa4abWLAhhwdepsmhexpmmplugm1.10.3lhmhexpmmmimemmimem~> 1.0wfalsehmhexpmm plug_cryptom plug_cryptom~> 1.1.1 or ~> 1.2wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej3bWLAhhwretiredmhexpmmphoenixm1.4.5wnil^bWLAhhwinner_checksummhexpmm plug_cowboym2.1.1m (ۥUbuwK3cbWLAhhwouter_checksummhexpmmcowboy_telemetrym0.4.0m }Eeb՟#5j˸819t7bWLAhhwretiredmhexpmm plug_cowboym2.5.0wnilEbWLAhhw timestampmhexpmmphoenixm0.6.2hhba a ha aa 3bWLAhhwretiredmhexpmmphoenixm1.2.0wnil7bWLAhhwretiredmhexpmm plug_cryptom1.2.1wnilYbWLAhhwinner_checksummhexpmmpoisonm1.3.0m 1|S7Yx.k^mF_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.8.0m wu`֫{%ɯ)dNH"Θ EbWLAhhw timestampmhexpmmphoenixm0.4.0hhba a ha aa JbWLAhhw timestampmhexpmm phoenix_htmlm2.6.0hhba a ha aa \bWLAhhwouter_checksummhexpmm telemetrym1.0.0m s Y(NF$3U(~ɮvng8PzqbWLAhhwdepsmhexpmm phoenix_htmlm2.0.1lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsejabWLAhhwdepsmhexpmmplugm1.1.9lhmhexpmmcowboymcowboym~> 1.0wtruejbWLAhhwdepsmhexpmmphoenixm0.2.11lhmhexpmmex_confmex_confm0.1.2wfalsehmhexpmminflexminflexm0.2.4wfalsehmhexpmmjazzmjazzm0.1.2wfalsehmhexpmmplugmplugm0.5.1wfalsej0bWLAhhwdepsmhexpmm plug_cryptom1.1.1jZbWLAhhwouter_checksummhexpmmphoenixm1.3.4m I&`L^F/YYi6zĠS-WbWLAhhwouter_checksummhexpmmplugm0.5.2m }8gD'CviD@ӋVS[bWLAhhwinner_checksummhexpmmphoenixm1.5.11m `՗J1O:ɰ6FΚG`y= 0.12.2 and < 2.0.0wfalsejZbWLAhhw registry_etagmhexpmmcowboy_telemetrym""fa0b78215ec75029c429f147ec08d767"YbWLAhhwinner_checksummhexpmmpoisonm1.5.1m ¯_7t`FkJdB ;c4sjI+bWLAhhwdepsmhexpmmcowlibm1.1.0jBbWLAhhw timestampmhexpmmplugm1.2.0hhbaahaa4a2bWLAhhwretiredmhexpmmpoisonm1.3.0wnilEbWLAhhw timestampmhexpmmphoenixm1.1.7hhba a ha aa EbWLAhhw timestampmhexpmmdecimalm1.3.1hhbaaha aa:EbWLAhhw timestampmhexpmmphoenixm1.1.1hhba a ha aa YbWLAhhwinner_checksummhexpmmcowlibm2.2.1m [#H ٤>!CՖ63bWLAhhwretiredmhexpmmphoenixm1.4.6wnilWbWLAhhwinner_checksummhexpmmplugm1.0.1m N=XB.@9l t 8bWLAhhwretiredmhexpmmdecimalm 1.9.0-rc.0wnilZbWLAhhwinner_checksummhexpmmphoenixm1.4.4m ],4C 6Wq9wrh2NDbWLAhhw timestampmhexpmmcowlibm2.5.0hhbaahaaa.^bWLAhhwinner_checksummhexpmm plug_cowboym2.5.1m |oE>tDdAO(-hGേa1bWLAhhwretiredmhexpmmjasonm1.2.1wnil9bWLAhhwretiredmhexpmm phoenix_htmlm2.14.1wnil2bWLAhhwretiredmhexpmmcowlibm2.9.1wnilYbWLAhhwouter_checksummhexpmmpoisonm2.0.1m 4j9󴛛vGFQDxvC!Bv+gbWLAhhwdepsmhexpmm phoenix_htmlm2.10.1lhmhexpmmplugmplugm~> 1.0wfalsej2bWLAhhwretiredmhexpmmpoisonm1.2.0wnil0bWLAhhwretiredmhexpmmplugm1.4.1wnilYbWLAhhwouter_checksummhexpmmcowboym1.1.0m t]"n){z>Wx=$ތ"w^YbWLAhhwouter_checksummhexpmmcowboym2.6.1m AC6 xӻe^v})"J[8~ 8͠bbWLAhhwdepsmhexpmmplugm0.10.0lhmhexpmmcowboymcowboym~> 1.0wtruejFbWLAhhw timestampmhexpmmphoenixm0.13.0hhba a ha aa 3bWLAhhwretiredmhexpmmphoenixm1.1.0wnil8bWLAhhwretiredmhexpmm phoenix_htmlm2.5.1wnil6bWLAhhwretiredmhexpmmjasonm 1.0.0-rc.3wnilWbWLAhhwouter_checksummhexpmmplugm1.5.1m 1-]yuCSN!nj6bWLAhhwdepsmhexpmm plug_cowboym2.0.0lhmhexpmmcowboymcowboym~> 2.5wfalsehmhexpmmplugmplugm~> 1.7wfalsejCbWLAhhw timestampmhexpmmplugm0.12.2hhbaahaa4a 1.0wtruehmhexpmmphoenix_pubsubmphoenix_pubsubm~> 1.0wfalsehmhexpmmplugmplugm,~> 1.3.3 or ~> 1.2.4 or ~> 1.1.8 or ~> 1.0.5wfalsehmhexpmmpoisonmpoisonm~> 1.5 or ~> 2.0wfalsej_bWLAhhwinner_checksummhexpmm phoenix_htmlm2.9.1m ȽP#4|oA iOT_bWLAhhwouter_checksummhexpmm phoenix_htmlm2.6.2m i{1HT]hBrX5p- _2, 2.5wfalsehmhexpmmplugmplugm~> 1.7wfalsejZbWLAhhwinner_checksummhexpmmphoenixm1.4.5m n ? e?E 7]t_bWLAhhwinner_checksummhexpmm phoenix_htmlm3.0.1m 5 __T81y@&'[SMYbWLAhhwouter_checksummhexpmmpoisonm2.1.0m ,9|S>!AN< C,c5bWLAhhwretiredmhexpmm telemetrym0.4.1wnilZbWLAhhwinner_checksummhexpmmphoenixm1.4.8m -íIc$dQNzSCbWLAhhw timestampmhexpmmranchm1.4.0hhba a ha aa XbWLAhhwinner_checksummhexpmmplugm1.10.2m y4\}FTTŇd8\wZBbWLAhhw timestampmhexpmmplugm1.4.1hhbaahaa4aWbWLAhhwouter_checksummhexpmmplugm1.6.2m q<W&a +UlALu{DbWLAhhw timestampmhexpmmcowlibm2.8.0hhbaahaaa.0bWLAhhwretiredmhexpmmplugm1.6.0wnilEbWLAhhw timestampmhexpmmphoenixm1.2.2hhba a ha aa ,bWLAhhwdepsmhexpmmdecimalm0.2.5j0bWLAhhwretiredmhexpmmplugm1.2.0wnil_bWLAhhwinner_checksummhexpmmdecimalm 1.9.0-rc.0m cfja8{@<Lp}bWLAhhwdepsmhexpmmplugm1.6.0lhmhexpmmcowboymcowboym~> 1.0.1 or ~> 1.1 or ~> 2.4wtruehmhexpmmmimemmimem~> 1.0wfalsej3bWLAhhwdepsmhexpmmphoenix_pubsubm0.0.1jbWLAhhwdepsmhexpmmplugm1.2.5lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmmimemmimem~> 1.0wfalsejEbWLAhhw timestampmhexpmmphoenixm1.5.8hhba a ha aa \bWLAhhwouter_checksummhexpmm telemetrym0.2.0m Nqy]YL?C i .x@#2EbWLAhhw timestampmhexpmmphoenixm1.4.4hhba a ha aa YbWLAhhwouter_checksummhexpmmcowboym1.1.1m VrnE|5@ ECLyBSXZ860bWLAhhwretiredmhexpmmplugm1.6.4wnilFbWLAhhw timestampmhexpmmphoenixm1.4.13hhba a ha aa bWLAhhwdepsmhexpmmphoenixm0.17.1lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmplugmplugm~> 1.0wfalsehmhexpmmpoisonmpoisonm~> 1.3wfalsej3bWLAhhwretiredmhexpmmphoenixm1.1.1wnilbWLAhhwdepsmhexpmmphoenixm0.5.0lhmhexpmmcowboymcowboym~> 1.0wtruehmhexpmmlinguistmlinguistm~> 0.1.2wfalsehmhexpmmplugmplugm~> 0.8.1wfalsehmhexpmmpoisonmpoisonm~> 1.1wfalsejqbWLAhhwdepsmhexpmm phoenix_htmlm2.4.0lhmhexpmmplugmplugm~> 0.13 or ~> 1.0wfalsej2bWLAhhwretiredmhexpmmcowlibm2.0.0wnil0bWLAhhwretiredmhexpmmplugm0.5.3wnilCbWLAhhw timestampmhexpmmranchm1.6.1hhba a ha aa DbWLAhhw timestampmhexpmmcowboym2.2.2hhbaahaaa.bWLAhhwdepsmhexpmmcowboym2.0.0lhmhexpmmcowlibmcowlibm~> 2.0.0wfalsehmhexpmmranchmranchm~> 1.4.0wfalsej^bWLAhhwinner_checksummhexpmm plug_cowboym2.5.0m QɘG:RvLpe8bWLAhhwretiredmhexpmm phoenix_htmlm2.6.0wnilBbWLAhhw timestampmhexpmmmimem1.5.0hhbaahaaa.gbWLAhhwdepsmhexpmm phoenix_htmlm2.14.1lhmhexpmmplugmplugm~> 1.5wfalsej:bWLAhhw timestampmhexpmmcowboyhhbaahaaa.^bWLAhhwinner_checksummhexpmm plug_cryptom1.1.1m K`*BЄq'I5 FZbWLAhhwinner_checksummhexpmmdecimalm0.2.4m qt6Dh2A?k't*PgLICVZbWLAhhwouter_checksummhexpmmphoenixm1.4.3m ܨwg*Khb+l0Bňi1XJbWLAhhw timestampmhexpmmdecimalm 1.9.0-rc.0hhbaaha aa:hex-2.5.1/test/fixtures/registries/20210926.ets000066400000000000000000001170051522402544500207440ustar00rootroot00000000000000cXM bWLAhhwidZw nonode@nohostˮ\xFhwdecentralized_counterswfalsehwread_concurrencywfalsehwwrite_concurrencywfalsehw compressedwfalsehwmemoryb>hwownerXw nonode@nohostehwheirwnonehwnamewElixir.Hex.Devhwsizebhwnodew nonode@nohosthw named_tablewfalsehwtypewsethwkeyposahw protectionw protectedhw major_versionahw minor_versionahw extended_infojbWLAhhwdepsmhexpmm chromic_pdfm0.7.0lhmhexpmmjasonmjasonm~> 1.1wfalsehmhexpmm nimble_poolm nimble_poolm~> 0.2.3wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejZbWLAhhwouter_checksummhexpmmdecimalm1.9.0m 45h֒>u[] o}^bWLAhhwinner_checksummhexpmm chromic_pdfm0.6.1m CMN-W2Qo,Ĥ78n~(ZbWLAhhwinner_checksummhexpmmpoolboym1.4.0m BcP gHM&H`n)w|D_HbWLAhhw timestampmhexpmmjasonm 1.0.0-rc.2hhba ahaaa4,bWLAhhwdepsmhexpmmdecimalm0.2.4jIbWLAhhw timestampmhexpmm chromic_pdfm0.5.0hhba aha a8a EbWLAhhw timestampmhexpmmpoolboym1.4.2hhbaaha a9aZbWLAhhwouter_checksummhexpmmpoolboym1.3.0m 25c*rutR<ϰP+aQbWLAhhw registry_etagmhexpmmdecimalm""4657f6f92e3c01945c9c9b06b6fd850e"ZbWLAhhwouter_checksummhexpmmpoolboym1.4.0m uzުc 1.0wtruej^bWLAhhwouter_checksummhexpmm nimble_poolm0.2.3m g}49+krI_p mXQ=PZbWLAhhwinner_checksummhexpmmdecimalm0.2.4m qt6Dh2A?k't*PgLICV,bWLAhhwdepsmhexpmmdecimalm1.7.0jbWLAhhwdepsmhexpmm chromic_pdfm0.1.0lhmhexpmmjasonmjasonm~> 1.1wfalsehmhexpmmpoolboympoolboym~> 1.5wfalsejZbWLAhhwinner_checksummhexpmmdecimalm0.2.2m b^BJ;@>ҜLz:K JbWLAhhw timestampmhexpmmdecimalm 1.9.0-rc.0hhba ahaaa4XbWLAhhwinner_checksummhexpmmjasonm1.1.2m =g#&LqTVM=!dZbWLAhhwinner_checksummhexpmmdecimalm1.6.0m Mn]CpӔ42_Ӻp0Y ZbWLAhhwinner_checksummhexpmmdecimalm1.3.0m Afx`E|Rӣ՗M'}{bWLAhhwretiredmhexpmm nimble_poolm0.2.2twmessagemstrategy: :lifo is not usefulwreasonwRETIRED_INVALID^bWLAhhwinner_checksummhexpmm nimble_poolm0.1.0m վ' cNI'{9oxN3bWLAhhwretiredmhexpmmdecimalm2.0.0wnilibWLAhhwdepsmhexpmmjasonm 1.0.0-rc.2lhmhexpmmdecimalmdecimalm~> 1.0wtruejZbWLAhhwouter_checksummhexpmmdecimalm1.2.0m t"HD2HDM_dшXbWLAhhwouter_checksummhexpmmjasonm1.1.1m cEϬ2^4gry:42•%#L,bWLAhhwdepsmhexpmmdecimalm0.1.1jZbWLAhhwinner_checksummhexpmmpoolboym1.5.1m kF9С=i&W~YSZwϛl]bWLAhhwouter_checksummhexpmmjasonm 1.0.0-rc.1m Iл;_,n4%G)EbWLAhhw timestampmhexpmmdecimalm0.2.1hhba ahaaa41bWLAhhwretiredmhexpmmjasonm1.2.0wnil,bWLAhhwdepsmhexpmmdecimalm1.6.0jEbWLAhhw timestampmhexpmmdecimalm0.2.0hhba ahaaa4\bWLAhhwinner_checksummhexpmm telemetrym0.4.3m d(c)<٦&:f;șWGbWLAhhw timestampmhexpmm telemetrym0.4.3hhbaaha aa,bWLAhhwdepsmhexpmmdecimalm1.4.1jEbWLAhhw timestampmhexpmmpoolboym1.4.1hhbaaha a9a,bWLAhhwdepsmhexpmmdecimalm1.3.1j1bWLAhhwretiredmhexpmmjasonm1.1.1wnil,bWLAhhwdepsmhexpmmdecimalm0.1.2jZbWLAhhwouter_checksummhexpmmdecimalm1.6.0m $@@s`I*sGHM:bJi^bWLAhhwouter_checksummhexpmm chromic_pdfm0.6.1m 4z6,nAh'%TߠDh*cbWLAhhwinner_checksummhexpmmtelemetry_pollerm0.3.0m | ˝EyqZ~US+ɡ#g 7bWLAhhwretiredmhexpmm nimble_poolm0.1.0wnilNbWLAhhw timestampmhexpmmtelemetry_pollerm0.1.0hhbaaha a*a CbWLAhhw timestampmhexpmmjasonm1.0.1hhba ahaaa4,bWLAhhwdepsmhexpmmpoolboym1.5.2jkbWLAhhwversionsmhexpmm nimble_poollm0.1.0m0.2.0m0.2.1m0.2.2m0.2.3m0.2.4jHbWLAhhw timestampmhexpmmjasonm 1.0.0-rc.3hhba ahaaa4EbWLAhhw timestampmhexpmmpoolboym1.3.0hhbaaha a9aZbWLAhhwouter_checksummhexpmmdecimalm1.5.0m  &XUMu_]r?3ɻ9W⡭.cbWLAhhwouter_checksummhexpmmtelemetry_pollerm0.2.0m m>\IIid!}^gZbWLAhhwinner_checksummhexpmmdecimalm1.5.0m C:6C=P)e<7of[CיchHehZbWLAhhwouter_checksummhexpmmpoolboym1.5.2m חT@գh%ѥaɕ( _bWLAhhwouter_checksummhexpmmdecimalm 1.9.0-rc.0m fuEsW\^KE0|dX< GZbWLAhhwinner_checksummhexpmmpoolboym1.2.0m RjᙎJ^DX}#^T#N6\bWLAhhwouter_checksummhexpmm telemetrym0.3.0m c}11b!1 Z6h!/r5bWLAhhwretiredmhexpmm telemetrym1.0.0wnilcbWLAhhwouter_checksummhexpmmtelemetry_pollerm0.4.0m 7MRg_3h䱤9ڌ}B3gSbWLAhhw registry_etagmhexpmm telemetrym""4a441cbd068ab1fc1e6f8fabddac30ff"3bWLAhhwretiredmhexpmmdecimalm0.1.1wniltbWLAhhwdepsmhexpmmtelemetry_pollerm1.0.0lhmhexpmm telemetrym telemetrym~> 1.0wfalsej^bWLAhhwinner_checksummhexpmm chromic_pdfm0.7.1m [Lʘ^Qd&rB-F0BYZbWLAhhwouter_checksummhexpmmdecimalm1.8.0m RiNn`PsLfXO[}1xfcZbWLAhhwinner_checksummhexpmmpoolboym1.4.1m ~pDG|j;Ҋ*GYZGz^bWLAhhwouter_checksummhexpmm chromic_pdfm1.0.0m P H @4gENciZ1À2] bVEbWLAhhw timestampmhexpmmdecimalm0.2.3hhba ahaaa4ZbWLAhhwouter_checksummhexpmmdecimalm0.2.2m 䑳ƑfZz hӨuR?/ ,.7}.XbWLAhhwouter_checksummhexpmmjasonm1.0.0m l@eLc# H;g͠y^bWLAhhwouter_checksummhexpmm chromic_pdfm0.3.0m Cڜ"A@-rO"AϮG3bWLAhhwretiredmhexpmmdecimalm0.1.2wnil5bWLAhhwretiredmhexpmm telemetrym0.4.2wnil.bWLAhhwdepsmhexpmm telemetrym0.1.0jCbWLAhhw timestampmhexpmmjasonm1.1.1hhba ahaaa4^bWLAhhwinner_checksummhexpmm chromic_pdfm1.1.1m j QFV07:Rz-yiZbWLAhwversionacbWLAhhwinner_checksummhexpmmtelemetry_pollerm0.5.1m !6 V(5RpX>*ibWLAhhwdepsmhexpmmjasonm 1.0.0-rc.3lhmhexpmmdecimalmdecimalm~> 1.0wtruej,bWLAhhwdepsmhexpmmdecimalm1.4.0j,bWLAhhwdepsmhexpmmdecimalm0.2.3jIbWLAhhw timestampmhexpmm nimble_poolm0.2.4hhbaahaaa!IbWLAhhw timestampmhexpmm chromic_pdfm0.6.0hhba aha a8a bWLAhhwdepsmhexpmm chromic_pdfm0.5.0lhmhexpmmjasonmjasonm~> 1.1wfalsehmhexpmmpoolboympoolboym~> 1.5wfalsejXbWLAhhwinner_checksummhexpmmjasonm1.2.2m CpѪΐi\Vx?R,bWLAhhwdepsmhexpmmpoolboym1.4.1jCbWLAhhw timestampmhexpmmjasonm1.2.0hhba ahaaa4dbWLAhhwdepsmhexpmmjasonm1.1.0lhmhexpmmdecimalmdecimalm~> 1.0wtruej,bWLAhhwdepsmhexpmmdecimalm0.2.0j^bWLAhhwouter_checksummhexpmm chromic_pdfm0.7.0m VT5;TvGL˻;oVcbWLAhhwinner_checksummhexpmmtelemetry_pollerm1.0.0m ۑBNnso> %剠~SEbWLAhhw timestampmhexpmmdecimalm1.5.0hhba ahaaa4 1.1wfalsehmhexpmmpoolboympoolboym~> 1.5wfalsej\bWLAhhwouter_checksummhexpmm telemetrym0.4.1m G88.6]g`,V m)ތXHZbWLAhhwinner_checksummhexpmmdecimalm1.1.2m yie{-S{Q<Қ+HkQlBS ^bWLAhhwouter_checksummhexpmm chromic_pdfm0.4.0m \(Xɧ$C+t{؍y7bWLAhhwretiredmhexpmm chromic_pdfm1.1.0wnilNbWLAhhw timestampmhexpmmtelemetry_pollerm0.5.1hhbaaha a*a EbWLAhhw timestampmhexpmmpoolboym1.4.0hhbaaha a9a\bWLAhhwinner_checksummhexpmm telemetrym0.2.0m [@ʣ޳-|OUomk\7L#fw!a1wXbWLAhhwinner_checksummhexpmmjasonm1.2.1m (%/F>Kˍ spD1bWLAhhwretiredmhexpmmjasonm1.2.1wnil_bWLAhhwinner_checksummhexpmmdecimalm 2.0.0-rc.0m ĸK%5b~Ƭ>D@,~- yl.bWLAhhwdepsmhexpmm telemetrym1.0.0jXbWLAhhwouter_checksummhexpmmjasonm1.1.2m CX :.4]C$=:3bWLAhhwretiredmhexpmmpoolboym1.4.1wnilXbWLAhhwinner_checksummhexpmmjasonm1.2.0m 4-$!-?]HCv 8yY8H3bWLAhhwretiredmhexpmmdecimalm1.9.0wnilIbWLAhhw timestampmhexpmm chromic_pdfm0.5.1hhba aha a8a 6bWLAhhwretiredmhexpmmjasonm 1.0.0-rc.3wnil8W$d.r,bWLAhhwdepsmhexpmmdecimalm1.1.0jEbWLAhhw timestampmhexpmmdecimalm1.6.0hhba ahaaa4 1.1wfalsehmhexpmmpoolboympoolboym~> 1.5wfalsejZbWLAhhwouter_checksummhexpmmdecimalm0.2.3m tVX7aCVh`ZV1ւuEbWLAhhw timestampmhexpmmdecimalm1.3.0hhba ahaaa4ZbWLAhhwouter_checksummhexpmmdecimalm1.1.1m ?6"/+ 1.0wtruej,bWLAhhwdepsmhexpmmdecimalm1.2.0j]bWLAhhwouter_checksummhexpmmjasonm 1.0.0-rc.2m wv3ԉ ĘDo$\bWLAhhwinner_checksummhexpmm telemetrym0.4.2m (ɒE^s"M;kb_#:sPXps/EZbWLAhhwouter_checksummhexpmmdecimalm0.1.1m ZA57/P,#n^}[WBx,bWLAhhwdepsmhexpmmdecimalm1.1.1jcbWLAhhwouter_checksummhexpmmtelemetry_pollerm0.3.0m .uvӺ .dK4?,闂sdbWLAhhwdepsmhexpmmjasonm1.0.0lhmhexpmmdecimalmdecimalm~> 1.0wtruej^bWLAhhwinner_checksummhexpmm chromic_pdfm0.1.0m ?S8(Z,}?Zū5)y 1.0wtruejIbWLAhhw timestampmhexpmm nimble_poolm0.2.3hhbaahaaa!ZbWLAhhwinner_checksummhexpmmdecimalm1.3.1m { 1.1wfalsehmhexpmm nimble_poolm nimble_poolm~> 0.2.3wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejNbWLAhhw timestampmhexpmmtelemetry_pollerm0.5.0hhbaaha a*a .bWLAhhwdepsmhexpmm telemetrym0.4.1jUbWLAhhw registry_etagmhexpmm nimble_poolm""06c85751bbe7bda60efea8ca3d0ebf94"3bWLAhhwretiredmhexpmmdecimalm1.1.2wnil7bWLAhhwretiredmhexpmm nimble_poolm0.2.3wnilbWLAhhwdepsmhexpmm chromic_pdfm1.1.1lhmhexpmmjasonmjasonm~> 1.1wfalsehmhexpmm nimble_poolm nimble_poolm~> 0.2.3wfalsehmhexpmm telemetrym telemetrym~> 0.4 or ~> 1.0wfalsejdbWLAhhwdepsmhexpmmjasonm1.0.1lhmhexpmmdecimalmdecimalm~> 1.0wtruej^bWLAhhwouter_checksummhexpmm chromic_pdfm0.7.2m +O|*N:숽s{ϸZbWLAhhwouter_checksummhexpmmpoolboym1.4.2m a3rQ2Oq<ҙ/vgz'b98ZbWLAhhwinner_checksummhexpmmdecimalm1.7.0m 0ֵ,TfcsYPߞf S_~Ū7bWLAhhwretiredmhexpmm chromic_pdfm0.1.0wnil^bWLAhhwinner_checksummhexpmm chromic_pdfm0.7.2m ꑍ {ibn 9 upO\bWLAhhwinner_checksummhexpmm telemetrym0.1.0m s 1 3|m*c8y+A<3bWLAhhwretiredmhexpmmdecimalm1.8.1wnilEbWLAhhw timestampmhexpmmdecimalm1.1.2hhba ahaaa4^bWLAhhwinner_checksummhexpmm nimble_poolm0.2.0m %kaD="ivxgm>oXbWLAhhwouter_checksummhexpmmjasonm1.2.1m YW`Z`A@Dqoi 0.4wfalsej%bWLAhw last_updatehhba ahaaa1bWLAhhwretiredmhexpmmjasonm1.1.0wnilZbWLAhhwinner_checksummhexpmmdecimalm1.4.0m eqjSӦEf(Ze.J35bWLAhhwretiredmhexpmm telemetrym0.4.0wnil]bWLAhhwinner_checksummhexpmmjasonm 1.0.0-rc.2m *]f?v7@.FV),bWLAhhwdepsmhexpmmpoolboym1.3.0j^bWLAhhwouter_checksummhexpmm nimble_poolm0.1.0m 4:b C *#p9 Ox\uZ3bWLAhhwretiredmhexpmmdecimalm1.8.0wnil^bWLAhhwinner_checksummhexpmm chromic_pdfm0.5.1m S9RSQϒhg=߀԰8?HQ(tWcbWLAhhwouter_checksummhexpmmtelemetry_pollerm0.1.0m Vz,bWLAhhwdepsmhexpmmdecimalm2.0.0jbWLAhhwversionsmhexpmm 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.1jcbWLAhhwinner_checksummhexpmmtelemetry_pollerm0.5.0m GpUӜQִ#lib]Tޣ٥ KEbWLAhhw timestampmhexpmmdecimalm1.1.0hhba ahaaa4bWLAhhwdepsmhexpmm chromic_pdfm0.3.0lhmhexpmmjasonmjasonm~> 1.1wfalsehmhexpmmpoolboympoolboym~> 1.5wfalsej?bWLAhhw timestampmhexpmm chromic_pdfhhba aha a8a \bWLAhhwinner_checksummhexpmm telemetrym0.4.1m 'HHD$GjA|2w(ttR 1.1wfalsehmhexpmm nimble_poolm nimble_poolm~> 0.2.3wfalsehmhexpmm telemetrym telemetrym~> 0.4.2wfalsej3bWLAhhwretiredmhexpmmdecimalm1.0.0wnil7bWLAhhwretiredmhexpmm chromic_pdfm0.7.1wnilNbWLAhhw timestampmhexpmmtelemetry_pollerm0.3.0hhbaaha a*a ,bWLAhhwdepsmhexpmmdecimalm1.0.0j0bWLAhhwdepsmhexpmm nimble_poolm0.2.2jEbWLAhhw timestampmhexpmmdecimalm1.4.1hhba ahaaa4\bWLAhhwinner_checksummhexpmm telemetrym0.4.0m 9˄L)5sm U843bWLAhhwretiredmhexpmmdecimalm0.2.0wnil,bWLAhhwdepsmhexpmmdecimalm0.2.1jZbWLAhhwouter_checksummhexpmmdecimalm0.2.1m v2 T80W'u̶J2PŕQOˣfƔ~ZbWLAhhwinner_checksummhexpmmpoolboym1.2.1m }X͉05&lإ孈^bWLAhhwouter_checksummhexpmm chromic_pdfm0.5.2m X}+?^.f*Ρ6.8_XH 1IbWLAhhw timestampmhexpmm nimble_poolm0.2.2hhbaahaaa!ZbWLAhhwinner_checksummhexpmmpoolboym1.3.0m Aw rl,%VV)ٽ*|b~JcbWLAhhwouter_checksummhexpmmtelemetry_pollerm0.5.0m i[ wL,|Ćn4 &,ZbWLAhhwouter_checksummhexpmmdecimalm1.3.0m B 0U+l~!CEbWLAhhw timestampmhexpmmdecimalm0.2.2hhba ahaaa40bWLAhhwdepsmhexpmm nimble_poolm0.2.0jbWLAhhwdepsmhexpmm chromic_pdfm0.6.1lhmhexpmmjasonmjasonm~> 1.1wfalsehmhexpmm nimble_poolm nimble_poolm~> 0.2.3wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsejtbWLAhhwdepsmhexpmmtelemetry_pollerm0.5.0lhmhexpmm telemetrym telemetrym~> 0.4wfalsej]bWLAhhwinner_checksummhexpmmjasonm 1.0.0-rc.1m BNnn|+dcXah=S`F%ZbWLAhhwinner_checksummhexpmmdecimalm0.1.2m iڠV;j\åiTYj1bWLAhhwretiredmhexpmmjasonm1.1.2wnilbWLAhhwversionsmhexpmmjasonl 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.2jZbWLAhhwouter_checksummhexpmmdecimalm0.2.4m eBB:QN.cGJ9@5"IbWLAhhw timestampmhexpmm chromic_pdfm0.7.2hhba aha a8a ZbWLAhhwinner_checksummhexpmmdecimalm0.2.3m r}$޿>4m\lZ$ZbWLAhhwinner_checksummhexpmmdecimalm0.2.5m @[x:`&'?ԀC{Xk4Nê^bWLAhhwinner_checksummhexpmm chromic_pdfm0.4.0m ~mpTwmj{4&i{ 4hXbWLAhhwinner_checksummhexpmmjasonm1.1.0m 4/thՆS\[ WROcbWLAhhwouter_checksummhexpmmtelemetry_pollerm0.5.1m Lrnz ѳ3py|_ f?ZbWLAhhwouter_checksummhexpmmpoolboym1.2.1m ^M |P磃b+pSYy/%]b#i?bWLAhhw timestampmhexpmm nimble_poolhhbaahaaa!CbWLAhhw timestampmhexpmmjasonm1.1.2hhba ahaaa4ZbWLAhhwinner_checksummhexpmmdecimalm1.2.0m F)`q(.W{Gkkh'}M dcRiK cbWLAhhwinner_checksummhexpmmtelemetry_pollerm0.4.0m dޥKw`@#]]L>V+T2}bWLAhhwversionsmhexpmm telemetrylm0.1.0m0.2.0m0.3.0m0.4.0m0.4.1m0.4.2m0.4.3m1.0.0j1bWLAhhwdepsmhexpmmdecimalm 2.0.0-rc.0jZbWLAhhwinner_checksummhexpmmdecimalm1.4.1m Ps""|* ږ[ p7bWLAhhwretiredmhexpmm chromic_pdfm0.6.0wnilEbWLAhhw timestampmhexpmmdecimalm1.0.1hhba ahaaa4^bWLAhhwinner_checksummhexpmm chromic_pdfm1.1.0m oGFs-+8AvLE@ xX(VsbWLAhhwdepsmhexpmm chromic_pdfm0.5.2lhmhexpmmjasonmjasonm~> 1.1wfalsehmhexpmmpoolboympoolboym~> 1.5wfalsejnbWLAhhwdepsmhexpmmjasonm1.2.2lhmhexpmmdecimalmdecimalm~> 1.0 or ~> 2.0wtruejZbWLAhhwouter_checksummhexpmmdecimalm0.1.2m Jޗ֊YJ AJ'm\bWLAhhwouter_checksummhexpmm telemetrym0.4.0m |1p| 0-Ow?A3bWLAhhwretiredmhexpmmdecimalm1.1.0wnilZbWLAhhwinner_checksummhexpmmdecimalm1.1.1m [g1ͬoyo(]nVnv,bWLAhhwdepsmhexpmmpoolboym1.4.2jGbWLAhhw timestampmhexpmm telemetrym0.4.0hhbaaha aa.bWLAhhwdepsmhexpmm telemetrym0.4.0j^bWLAhhwinner_checksummhexpmm chromic_pdfm0.3.0m ?Gx1[v?%%}/grp07bWLAhhwretiredmhexpmm chromic_pdfm0.3.1wnil0bWLAhhwdepsmhexpmm nimble_poolm0.1.0jDbWLAhhw timestampmhexpmmtelemetry_pollerhhbaaha a*a GbWLAhhw timestampmhexpmm telemetrym0.3.0hhbaaha aaIbWLAhhw timestampmhexpmm chromic_pdfm0.1.0hhba aha a8a 9bWLAhhw timestampmhexpmmjasonhhba ahaaa4IbWLAhhw timestampmhexpmm chromic_pdfm1.1.1hhba aha a8a .bWLAhhwdepsmhexpmm telemetrym0.4.2j7bWLAhhwretiredmhexpmm chromic_pdfm0.5.0wnilZbWLAhhwinner_checksummhexpmmdecimalm1.9.0m 12q$,QK =McbWLAhhwinner_checksummhexpmmtelemetry_pollerm0.1.0m iphb!%"yfYw~U3bWLAhhwretiredmhexpmmdecimalm1.3.1wnilZbWLAhhwinner_checksummhexpmmdecimalm1.8.1m ?_4( 57@)Nޅ3SoEh٬<1bWLAhhwdepsmhexpmmdecimalm 1.9.0-rc.0jCbWLAhhw timestampmhexpmmjasonm1.1.0hhba ahaaa43bWLAhhwretiredmhexpmmdecimalm1.3.0wnil,bWLAhhwdepsmhexpmmdecimalm1.8.1j\bWLAhhwinner_checksummhexpmm telemetrym0.3.0m <Gqc <"f" :*:jguJsEbWLAhhw timestampmhexpmmpoolboym1.2.0hhbaaha a9aGbWLAhhw timestampmhexpmm telemetrym0.4.2hhbaaha aa,bWLAhhwdepsmhexpmmdecimalm1.5.0j3bWLAhhwretiredmhexpmmdecimalm1.1.1wnilXbWLAhhwinner_checksummhexpmmjasonm1.0.0m |#!Aw&ZqoZbWLAhhwouter_checksummhexpmmdecimalm1.4.1m #{RNUV]JrV&hL{ʀ_;XbWLAhhwouter_checksummhexpmmjasonm1.2.0m gGWyL:>N;|cNNJYu.,bWLAhhwdepsmhexpmmpoolboym1.5.1j\bWLAhhwouter_checksummhexpmm telemetrym0.4.2m -j m{XRr.+#b {*z]bWLAhhwouter_checksummhexpmmjasonm 1.0.0-rc.3m tE;#:˛V83;jWD+lE,,bWLAhhwdepsmhexpmmpoolboym1.2.1jIbWLAhhw timestampmhexpmm chromic_pdfm0.5.2hhba aha a8a ,bWLAhhwdepsmhexpmmpoolboym1.4.0jibWLAhhwdepsmhexpmmjasonm 1.0.0-rc.1lhmhexpmmdecimalmdecimalm~> 1.0wtruejdbWLAhhwdepsmhexpmmjasonm1.1.2lhmhexpmmdecimalmdecimalm~> 1.0wtruej7bWLAhhwretiredmhexpmm chromic_pdfm0.4.0wnilEbWLAhhw timestampmhexpmmpoolboym1.5.2hhbaaha a9aZbWLAhhw registry_etagmhexpmmtelemetry_pollerm""5c9b38c868f4640a594548ebba5bc657"7bWLAhhwretiredmhexpmm chromic_pdfm0.5.1wnil^bWLAhhwouter_checksummhexpmm chromic_pdfm0.6.0m EWC?O c )5X-!/JVB1bWLAhhwretiredmhexpmmjasonm1.0.1wnilZbWLAhhwinner_checksummhexpmmdecimalm1.8.0m F. _ ţB~5HZes4.IbWLAhhw timestampmhexpmm chromic_pdfm0.6.2hhba aha a8a bWLAhhwdepsmhexpmm chromic_pdfm0.6.0lhmhexpmmjasonmjasonm~> 1.1wfalsehmhexpmm nimble_poolm nimble_poolm~> 0.2.3wfalsehmhexpmm telemetrym telemetrym~> 0.4wfalsej7bWLAhhwretiredmhexpmm chromic_pdfm0.6.2wnil\bWLAhhwinner_checksummhexpmm telemetrym1.0.0m E:,$7T 0RbWLAhhwversionsmhexpmmtelemetry_pollerlm0.1.0m0.2.0m0.3.0m0.4.0m0.4.1m0.5.0m0.5.1m1.0.0j5bWLAhhwretiredmhexpmm telemetrym0.2.0wnilXbWLAhhwouter_checksummhexpmmjasonm1.2.2m (򟞮aðvhя!y^bWLAhhwinner_checksummhexpmm chromic_pdfm0.3.1m wBQBH&71r̽^bWLAhhwouter_checksummhexpmm chromic_pdfm0.1.0m h'Nv!5R f!KJK^bWLAhhwouter_checksummhexpmm chromic_pdfm0.3.1m 8RCO-`O`~H[|bWLAhhwdepsmhexpmm chromic_pdfm1.0.0lhmhexpmmjasonmjasonm~> 1.1wfalsehmhexpmm nimble_poolm nimble_poolm~> 0.2.3wfalsehmhexpmm telemetrym telemetrym~> 0.4.2wfalsej3bWLAhhwretiredmhexpmmdecimalm1.6.0wniltbWLAhhwdepsmhexpmmtelemetry_pollerm0.2.0lhmhexpmm telemetrym telemetrym~> 0.3wfalsej^bWLAhhwinner_checksummhexpmm chromic_pdfm0.6.0m <[ -œ.4N`!֥8ZbWLAhhwouter_checksummhexpmmdecimalm2.0.0m 4fnUި}7b2j# DwbWLAhhwdepsmhexpmm chromic_pdfm1.1.0lhmhexpmmjasonmjasonm~> 1.1wfalsehmhexpmm nimble_poolm nimble_poolm~> 0.2.3wfalsehmhexpmm telemetrym telemetrym~> 0.4.2wfalsej5bWLAhhwretiredmhexpmm telemetrym0.3.0wnil.bWLAhhwdepsmhexpmm telemetrym0.4.3jcbWLAhhwinner_checksummhexpmmtelemetry_pollerm0.2.0m ߏmRΚ] &*mu9|j R#5bWLAhhwdepsmhexpmmtelemetry_pollerm0.4.0jbWLAhhwdepsmhexpmm chromic_pdfm0.2.0lhmhexpmmjasonmjasonm~> 1.1wfalsehmhexpmmpoolboympoolboym~> 1.5wfalsejGbWLAhhw timestampmhexpmm telemetrym0.1.0hhbaaha aaIbWLAhhw timestampmhexpmm chromic_pdfm1.1.0hhba aha a8a 3bWLAhhwretiredmhexpmmdecimalm1.4.0wnil7bWLAhhwretiredmhexpmm nimble_poolm0.2.1wnilbWLAhhwdepsmhexpmm chromic_pdfm0.4.0lhmhexpmmjasonmjasonm~> 1.1wfalsehmhexpmmpoolboympoolboym~> 1.5wfalsejZbWLAhhwinner_checksummhexpmmdecimalm1.0.0m 4") h_S)3rm"K^XbWLAhhwinner_checksummhexpmmjasonm1.1.1m ̸@߰o/56MH#X^7bWLAhhwretiredmhexpmm chromic_pdfm1.1.1wnil8bWLAhhwretiredmhexpmmdecimalm 2.0.0-rc.0wnilEbWLAhhw timestampmhexpmmdecimalm1.9.0hhba ahaaa4XbWLAhhwouter_checksummhexpmmjasonm1.0.1m QQjL Y= hv%W4 QbWLAhhw registry_etagmhexpmmpoolboym""624f36e367e5b64af565501d4ca9ce2c",bWLAhhwdepsmhexpmmdecimalm1.8.0j=bWLAhhw timestampmhexpmm telemetryhhbaaha aa7bWLAhhwretiredmhexpmm chromic_pdfm1.0.0wnilUbWLAhhw registry_etagmhexpmm chromic_pdfm""0d9ce686ad99a9b52eff34be0e96ada6"XbWLAhhwinner_checksummhexpmmjasonm1.0.1m d6K<p3T]s.g0bWLAhhwdepsmhexpmm nimble_poolm0.2.3j^bWLAhhwouter_checksummhexpmm chromic_pdfm1.1.1m +02jA&Lk@k|K`=JIy{EbWLAhhw timestampmhexpmmpoolboym1.5.1hhbaaha a9a3bWLAhhwretiredmhexpmmdecimalm0.2.3wnilIbWLAhhw timestampmhexpmm chromic_pdfm0.7.1hhba aha a8a IbWLAhhw timestampmhexpmm chromic_pdfm0.3.1hhba aha a8a EbWLAhhw timestampmhexpmmdecimalm1.8.1hhba ahaaa4XbWLAhhwouter_checksummhexpmmjasonm1.1.0m #aJc5o( ǫh_QЄR68l7bWLAhhwretiredmhexpmm chromic_pdfm0.2.0wnil7bWLAhhwretiredmhexpmm chromic_pdfm0.7.0wnilEbWLAhhw timestampmhexpmmdecimalm1.0.0hhba ahaaa4ZbWLAhhwinner_checksummhexpmmpoolboym1.5.0m ]q:k{L= W@QQGZ#=mZbWLAhhwouter_checksummhexpmmdecimalm1.1.2m zmM8׸8P"H|h=3Q㦑NbWLAhhw timestampmhexpmmtelemetry_pollerm0.2.0hhbaaha a*a ZbWLAhhwouter_checksummhexpmmdecimalm1.0.1m dk+5~ kƿF|xwt^bWLAhhwouter_checksummhexpmm chromic_pdfm0.5.0m `>Ņ.yCzMU x2GbWLAhhw timestampmhexpmm telemetrym1.0.0hhbaaha aaIbWLAhhw timestampmhexpmm chromic_pdfm0.3.0hhba aha a8a ,bWLAhhwdepsmhexpmmdecimalm1.3.0jhex-2.5.1/test/fixtures/sample/000077500000000000000000000000001522402544500164175ustar00rootroot00000000000000hex-2.5.1/test/fixtures/sample/mix.exs000066400000000000000000000002041522402544500177310ustar00rootroot00000000000000defmodule Sample.Fixture.MixProject do use Mix.Project def project do [app: :sample, version: "0.0.1", deps: []] end end hex-2.5.1/test/fixtures/test_priv.pem000066400000000000000000000032171522402544500176630ustar00rootroot00000000000000-----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.5.1/test/fixtures/test_pub.pem000066400000000000000000000007031522402544500174660ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA70iFalPXSDJX0ZqQNYw2 yPyMWmpV4ssLwuGm4l3TjS50UgKYnyL2j7m0mmO7DhLNqRKn8IsIJoHeeuFhf5cv W162mp2Kn2e9LXRobafYIM3hxzB8LZqeQjxPR6xsDkY5HgQsxtTkbNRq/8ODAjx6 XsZgFRSkjgD+nWO0D4i67+8lWSyGBaXAvyYQVRimkvm400PYD4RT2dk9lSrBjhAv sZ+buCX/F8XuK2sOdhoFC7PXz4kO2q41IF1LjVfKz6WdXH91jUCUG80TAzK35lRT GRes/NOIV/aYwt6fc3BjgzOg/X8ucFwuWi5Tn5lU+eFYHcv1Qyxxx9yi03pZ7hH4 iwIDAQAB -----END PUBLIC KEY----- hex-2.5.1/test/fixtures/umbrella/000077500000000000000000000000001522402544500167415ustar00rootroot00000000000000hex-2.5.1/test/fixtures/umbrella/apps/000077500000000000000000000000001522402544500177045ustar00rootroot00000000000000hex-2.5.1/test/fixtures/umbrella/apps/my_app1/000077500000000000000000000000001522402544500212525ustar00rootroot00000000000000hex-2.5.1/test/fixtures/umbrella/apps/my_app1/mix.exs000066400000000000000000000005571522402544500225770ustar00rootroot00000000000000defmodule 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.5.1/test/fixtures/umbrella/apps/my_app2/000077500000000000000000000000001522402544500212535ustar00rootroot00000000000000hex-2.5.1/test/fixtures/umbrella/apps/my_app2/mix.exs000066400000000000000000000005651522402544500225770ustar00rootroot00000000000000defmodule 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.5.1/test/fixtures/umbrella/apps/my_app3/000077500000000000000000000000001522402544500212545ustar00rootroot00000000000000hex-2.5.1/test/fixtures/umbrella/apps/my_app3/mix.exs000066400000000000000000000005301522402544500225700ustar00rootroot00000000000000defmodule 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.5.1/test/fixtures/umbrella/mix.exs000066400000000000000000000006041522402544500202570ustar00rootroot00000000000000defmodule 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.5.1/test/fixtures/umbrella_override/000077500000000000000000000000001522402544500206405ustar00rootroot00000000000000hex-2.5.1/test/fixtures/umbrella_override/apps/000077500000000000000000000000001522402544500216035ustar00rootroot00000000000000hex-2.5.1/test/fixtures/umbrella_override/apps/my_app1/000077500000000000000000000000001522402544500231515ustar00rootroot00000000000000hex-2.5.1/test/fixtures/umbrella_override/apps/my_app1/mix.exs000066400000000000000000000006421522402544500244710ustar00rootroot00000000000000defmodule 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.5.1/test/fixtures/umbrella_override/mix.exs000066400000000000000000000006141522402544500221570ustar00rootroot00000000000000defmodule 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.5.1/test/hex/000077500000000000000000000000001522402544500140515ustar00rootroot00000000000000hex-2.5.1/test/hex/api/000077500000000000000000000000001522402544500146225ustar00rootroot00000000000000hex-2.5.1/test/hex/api/client_test.exs000066400000000000000000000013551522402544500176640ustar00rootroot00000000000000defmodule Hex.API.ClientTest do use HexTest.Case alias Hex.API.Client describe "config/1 OTP handling" do test "uses the OTP passed in opts" do assert Client.config(otp: "123456").api_otp == "123456" end test "falls back to HEX_OTP (api_otp state) for non-interactive use" do Hex.State.put(:api_otp, "654321") assert Client.config().api_otp == "654321" end test "prefers an explicit opts OTP over the api_otp state" do Hex.State.put(:api_otp, "654321") assert Client.config(otp: "123456").api_otp == "123456" end test "leaves api_otp undefined when no OTP is available" do Hex.State.put(:api_otp, nil) assert Client.config().api_otp == :undefined end end end hex-2.5.1/test/hex/api_test.exs000066400000000000000000000127521522402544500164110ustar00rootroot00000000000000defmodule 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 is_binary(key_a) and byte_size(key_a) > 0 assert is_binary(key_b) and byte_size(key_b) > 0 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 is_binary(key_c) and byte_size(key_c) > 0 assert is_binary(key_d) and byte_size(key_d) > 0 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.5.1/test/hex/auth_test.exs000066400000000000000000000053171522402544500166000ustar00rootroot00000000000000defmodule Hex.AuthTest do use HexTest.Case defp get_auth_config(repo) do Hex.Auth.callbacks().get_auth_config.(repo) end defp put_repo(name, config) do Hex.State.update!(:repos, &Map.put(&1, name, config)) end describe "get_auth_config/1 callback" do test "returns the repo config when no HEX_API_KEY is set" do put_repo("acme", %{url: "https://acme.example", auth_key: "repo_key"}) Hex.State.put(:api_key, nil) config = get_auth_config("acme") assert config.auth_key == "repo_key" refute Map.has_key?(config, :api_key) end test "surfaces HEX_API_KEY alongside the repo's own credentials" do # API and repo credentials authenticate different endpoints, so the env # api_key must not displace the repo's auth_key/oauth_token. put_repo("acme", %{url: "https://acme.example", auth_key: "repo_key"}) Hex.State.put(:api_key, "env_api_key") config = get_auth_config("acme") assert config.api_key == "env_api_key" assert config.auth_key == "repo_key" end test "does not override a per-repo api_key with HEX_API_KEY" do put_repo("acme", %{url: "https://acme.example", api_key: "repo_api_key"}) Hex.State.put(:api_key, "env_api_key") assert get_auth_config("acme").api_key == "repo_api_key" end test "returns just the api_key when the repo is unknown" do Hex.State.put(:api_key, "env_api_key") assert get_auth_config("does-not-exist") == %{api_key: "env_api_key"} end test "returns :undefined when the repo is unknown and no HEX_API_KEY is set" do Hex.State.put(:api_key, nil) assert get_auth_config("does-not-exist") == :undefined end end describe "clear_oauth_tokens/0 callback" do test "drops the in-memory token and warns to re-authenticate" do Hex.State.put(:oauth_token, %{ access_token: "expired", refresh_token: "refresh", expires_at: System.system_time(:second) - 100 }) assert Hex.Auth.callbacks().clear_oauth_tokens.() == :ok assert Hex.State.get(:oauth_token) == nil output = Case.shell_output() assert output =~ "could not be refreshed" assert output =~ "mix hex.user auth" end test "keeps the on-disk token so a later run can retry the refresh" do in_tmp("clear_oauth_tokens", fn -> set_home_cwd() Hex.OAuth.store_token(%{ access_token: "expired", refresh_token: "refresh", expires_at: System.system_time(:second) - 100 }) assert Hex.Auth.callbacks().clear_oauth_tokens.() == :ok assert Hex.State.get(:oauth_token) == nil assert Hex.Config.read()[:"$oauth_token"] != nil end) end end end hex-2.5.1/test/hex/config_test.exs000066400000000000000000000043171522402544500171030ustar00rootroot00000000000000defmodule Hex.ConfigTest do use HexTest.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 test "read/0 migrates string-keyed OAuth tokens to atom keys" do in_tmp(fn -> set_home_cwd() Config.write( "$oauth_token": %{ "access_token" => "a_token", "refresh_token" => "r_token", "expires_at" => 123 }, "$repos": %{ "hexpm:org" => %{ url: "https://example.com", oauth_token: %{ "access_token" => "repo_token", "expires_at" => 456 } } } ) config = Config.read() assert config[:"$oauth_token"] == %{ access_token: "a_token", refresh_token: "r_token", expires_at: 123 } assert config[:"$repos"]["hexpm:org"].oauth_token == %{ access_token: "repo_token", expires_at: 456 } end) end end hex-2.5.1/test/hex/cooldown_test.exs000066400000000000000000000310551522402544500174610ustar00rootroot00000000000000defmodule Hex.CooldownTest do use HexTest.Case alias Hex.Cooldown describe "parse_config/1" do test "accepts integer + unit forms" do assert {:ok, "0d"} == Cooldown.parse_config("0d") assert {:ok, "7d"} == Cooldown.parse_config("7d") assert {:ok, "14d"} == Cooldown.parse_config("14d") assert {:ok, "2w"} == Cooldown.parse_config("2w") assert {:ok, "1mo"} == Cooldown.parse_config("1mo") assert {:ok, "0"} == Cooldown.parse_config("0") end test "treats nil and empty as default" do assert {:ok, "0d"} == Cooldown.parse_config(nil) assert {:ok, "0d"} == Cooldown.parse_config("") end test "rejects malformed durations" do assert :error == Cooldown.parse_config("7day") assert :error == Cooldown.parse_config("7 days") assert :error == Cooldown.parse_config("1m") assert :error == Cooldown.parse_config("1 month") assert :error == Cooldown.parse_config("d7") assert :error == Cooldown.parse_config("seven") assert :error == Cooldown.parse_config("-1d") end end describe "duration_to_seconds/1" do test "converts canonical units to seconds" do assert {:ok, 0} == Cooldown.duration_to_seconds("0") assert {:ok, 0} == Cooldown.duration_to_seconds("0d") assert {:ok, 86_400} == Cooldown.duration_to_seconds("1d") assert {:ok, 7 * 86_400} == Cooldown.duration_to_seconds("7d") assert {:ok, 14 * 86_400} == Cooldown.duration_to_seconds("2w") assert {:ok, 30 * 86_400} == Cooldown.duration_to_seconds("1mo") end test "rejects malformed durations" do assert :error == Cooldown.duration_to_seconds("") assert :error == Cooldown.duration_to_seconds("5") assert :error == Cooldown.duration_to_seconds("00") assert :error == Cooldown.duration_to_seconds("d") assert :error == Cooldown.duration_to_seconds("5m") assert :error == Cooldown.duration_to_seconds("5x") assert :error == Cooldown.duration_to_seconds("5dd") assert :error == Cooldown.duration_to_seconds("+5d") assert :error == Cooldown.duration_to_seconds("-5d") assert :error == Cooldown.duration_to_seconds("1_0d") assert :error == Cooldown.duration_to_seconds(" 5d") assert :error == Cooldown.duration_to_seconds("5d ") end end describe "build_cutoff/0" do test "returns :disabled when cooldown is zero" do Hex.State.put(:cooldown, "0d") assert :disabled == Cooldown.build_cutoff() end test "returns a cutoff for non-zero durations" do Hex.State.put(:cooldown, "7d") assert {:cutoff, _, seconds} = Cooldown.build_cutoff() assert seconds == 7 * 86_400 end test "weeks and months are honored" do Hex.State.put(:cooldown, "2w") assert {:cutoff, _, seconds} = Cooldown.build_cutoff() assert seconds == 14 * 86_400 Hex.State.put(:cooldown, "1mo") assert {:cutoff, _, seconds} = Cooldown.build_cutoff() assert seconds == 30 * 86_400 end end describe "eligible?/2" do test ":disabled cutoff makes everything eligible" do assert Cooldown.eligible?(0, :disabled) assert Cooldown.eligible?(nil, :disabled) end test "nil published_at is treated as eligible" do now = System.system_time(:second) cutoff = {:cutoff, now - 7 * 86_400, 7 * 86_400} assert Cooldown.eligible?(nil, cutoff) end test "publish time older than cutoff is eligible" do now = System.system_time(:second) cutoff = {:cutoff, now - 7 * 86_400, 7 * 86_400} old_publish = now - 10 * 86_400 assert Cooldown.eligible?(old_publish, cutoff) end test "publish time newer than cutoff is not eligible" do now = System.system_time(:second) cutoff = {:cutoff, now - 7 * 86_400, 7 * 86_400} fresh_publish = now - 86_400 refute Cooldown.eligible?(fresh_publish, cutoff) end end describe "HEX_COOLDOWN= env handling" do test "empty HEX_COOLDOWN= falls through to next source, not env" do original = System.get_env("HEX_COOLDOWN") try do System.put_env("HEX_COOLDOWN", "") Hex.State.refresh() # Without a project or global config contribution, fallthrough lands at default. # The key assertion is that the source is NOT :env — if fallthrough were broken # the source would be {:env, "HEX_COOLDOWN"} with value "0d". assert :default == Hex.State.fetch_source!(:cooldown) assert "0d" == Hex.State.fetch!(:cooldown) after if original do System.put_env("HEX_COOLDOWN", original) else System.delete_env("HEX_COOLDOWN") end Hex.State.refresh() HexTest.Case.reset_state() end end test "HEX_COOLDOWN=0 explicitly disables (env wins)" do original = System.get_env("HEX_COOLDOWN") try do System.put_env("HEX_COOLDOWN", "0") Hex.State.refresh() assert {:env, "HEX_COOLDOWN"} == Hex.State.fetch_source!(:cooldown) assert "0" == Hex.State.fetch!(:cooldown) after if original do System.put_env("HEX_COOLDOWN", original) else System.delete_env("HEX_COOLDOWN") end Hex.State.refresh() HexTest.Case.reset_state() end end test "empty HEX_COOLDOWN_EXCLUDE_REPOS= falls through to next source, not env" do # Symmetric to the HEX_COOLDOWN= fall-through. Without # skip_env_if_empty an empty env would silently shadow a project's # cooldown_exclude_repos with []. original = System.get_env("HEX_COOLDOWN_EXCLUDE_REPOS") try do System.put_env("HEX_COOLDOWN_EXCLUDE_REPOS", "") Hex.State.refresh() assert :default == Hex.State.fetch_source!(:cooldown_exclude_repos) assert [] == Hex.State.fetch!(:cooldown_exclude_repos) after if original do System.put_env("HEX_COOLDOWN_EXCLUDE_REPOS", original) else System.delete_env("HEX_COOLDOWN_EXCLUDE_REPOS") end Hex.State.refresh() HexTest.Case.reset_state() end end end describe "parse_exclude_repos/1" do test "accepts a list of strings" do assert {:ok, ["a", "b"]} == Cooldown.parse_exclude_repos(["a", "b"]) assert {:ok, []} == Cooldown.parse_exclude_repos([]) end test "accepts a comma-separated string (env var form)" do assert {:ok, ["hexpm:myorg", "private"]} == Cooldown.parse_exclude_repos("hexpm:myorg,private") assert {:ok, ["hexpm:myorg"]} == Cooldown.parse_exclude_repos("hexpm:myorg") end test "trims whitespace and drops empty entries" do assert {:ok, ["a", "b"]} == Cooldown.parse_exclude_repos("a, b ,") assert {:ok, ["a"]} == Cooldown.parse_exclude_repos([" a ", "", " "]) end test "rejects non-string list elements" do assert :error == Cooldown.parse_exclude_repos([:atom]) end test "accepts nil and empty string as empty list" do assert {:ok, []} == Cooldown.parse_exclude_repos(nil) assert {:ok, []} == Cooldown.parse_exclude_repos("") end end describe "repo_excluded?/1" do test "true when the repo name appears in the exclude list" do Hex.State.put(:cooldown_exclude_repos, ["hexpm:myorg"]) assert Cooldown.repo_excluded?("hexpm:myorg") end test "false when the repo is not in the list" do Hex.State.put(:cooldown_exclude_repos, ["hexpm:other"]) refute Cooldown.repo_excluded?("hexpm:myorg") end test "nil repo is treated as hexpm" do Hex.State.put(:cooldown_exclude_repos, ["hexpm"]) assert Cooldown.repo_excluded?(nil) end test "false on empty list" do Hex.State.put(:cooldown_exclude_repos, []) refute Cooldown.repo_excluded?("hexpm:myorg") refute Cooldown.repo_excluded?(nil) end end describe "strictest/1" do test "picks the candidate with the longest duration" do assert {{"myorg", "p"}, "14d"} == Cooldown.strictest([ {:local, "7d"}, {{"myorg", "p"}, "14d"}, {{"myorg", "q"}, "3d"} ]) end test "treats nil and empty as zero" do assert {:local, "7d"} == Cooldown.strictest([ {:local, "7d"}, {{"myorg", "p"}, nil}, {{"myorg", "q"}, ""} ]) end test "uses policy when local is 0d" do assert {{"myorg", "p"}, "14d"} == Cooldown.strictest([ {:local, "0d"}, {{"myorg", "p"}, "14d"} ]) end test "returns 0d when nothing is set" do assert {:local, "0d"} == Cooldown.strictest([ {:local, "0d"}, {{"myorg", "p"}, nil} ]) end test "returns :local when nothing else contributes" do assert {:local, "7d"} == Cooldown.strictest([{:local, "7d"}]) end test "normalizes nil/empty local to 0d" do assert {{"myorg", "p"}, "14d"} == Cooldown.strictest([ {:local, nil}, {{"myorg", "p"}, "14d"} ]) assert {{"myorg", "p"}, "14d"} == Cooldown.strictest([ {:local, ""}, {{"myorg", "p"}, "14d"} ]) end test "normalizes unparseable and zero durations to 0d" do assert {:local, "0d"} == Cooldown.strictest([{:local, "garbage"}]) assert {:local, "0d"} == Cooldown.strictest([{:local, "0"}]) assert {{"myorg", "p"}, "14d"} == Cooldown.strictest([ {:local, "5x"}, {{"myorg", "p"}, "14d"} ]) end end describe "format_summary/2" do test "returns nil for an empty list" do Hex.State.put(:cooldown, "7d") cutoff = Cooldown.build_cutoff() assert Cooldown.format_summary([], cutoff) == nil end test "returns nil when cutoff is :disabled" do now = System.system_time(:second) assert Cooldown.format_summary([{"hexpm", "foo", "1.0.0", now}], :disabled) == nil end test "renders a single entry with days ago and eligible date" do Hex.State.put(:cooldown, "7d") cutoff = Cooldown.build_cutoff() now = System.system_time(:second) published_at = now - 3 * 86_400 summary = Cooldown.format_summary([{"hexpm", "castore", "1.0.19", published_at}], cutoff) assert summary =~ "Versions filtered by cooldown:" assert summary =~ "castore 1.0.19" assert summary =~ "eligible #{Cooldown.eligible_on(published_at, cutoff)}" end test "groups by package, lists versions newest-first, dedupes identical entries" do Hex.State.put(:cooldown, "7d") cutoff = Cooldown.build_cutoff() now = System.system_time(:second) young = now - 1 * 86_400 entries = [ {"hexpm", "plug", "1.16.5", young}, {"hexpm", "castore", "1.0.19", young}, {"hexpm", "castore", "1.0.19", young}, {"hexpm", "castore", "1.0.18", young} ] summary = Cooldown.format_summary(entries, cutoff) # Packages alphabetically, versions newest-first; dedup keeps one castore 1.0.19. [_header, line1, line2, line3 | _] = String.split(summary, "\n", trim: true) assert line1 =~ "castore 1.0.19" assert line2 =~ "castore 1.0.18" assert line3 =~ "plug 1.16.5" occurrences = summary |> String.split("castore 1.0.19") |> length() assert occurrences == 2 end test "caps the listing per package with an overflow line" do Hex.State.put(:cooldown, "7d") cutoff = Cooldown.build_cutoff() young = System.system_time(:second) - 1 * 86_400 entries = for minor <- 0..7, do: {"hexpm", "castore", "1.#{minor}.0", young} summary = Cooldown.format_summary(entries, cutoff) assert summary =~ "castore 1.7.0" assert summary =~ "castore 1.3.0" refute summary =~ "castore 1.2.0" assert summary =~ " ...and 3 more" end test "omits entries with nil published_at" do Hex.State.put(:cooldown, "7d") cutoff = Cooldown.build_cutoff() now = System.system_time(:second) entries = [ {"hexpm", "legacy", "1.0.0", nil}, {"hexpm", "castore", "1.0.19", now - 1 * 86_400} ] summary = Cooldown.format_summary(entries, cutoff) assert summary =~ "castore 1.0.19" refute summary =~ "legacy" end test "returns nil when every entry has nil published_at" do Hex.State.put(:cooldown, "7d") cutoff = Cooldown.build_cutoff() assert Cooldown.format_summary([{"hexpm", "legacy", "1.0.0", nil}], cutoff) == nil end end end hex-2.5.1/test/hex/http/000077500000000000000000000000001522402544500150305ustar00rootroot00000000000000hex-2.5.1/test/hex/http/verify_hostname_test.exs000066400000000000000000000032141522402544500220120ustar00rootroot00000000000000defmodule 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.5.1/test/hex/http_test.exs000066400000000000000000000154531522402544500166200ustar00rootroot00000000000000defmodule 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"]} # Headers decoded from a real response arrive as binaries, not charlists Hex.HTTP.handle_hex_message("\"oops, you done goofed\" ; level = warn") assert_received {:mix_shell, :info, ["API warning: oops, you done goofed"]} end test "x-hex-message escapes terminal control sequences from the server" do Hex.HTTP.handle_hex_message("\"danger\e[31m\u{202E}\";level=fatal") assert_received {:mix_shell, :error, ["API error: danger\\e[31m\\u{202E}"]} end test "request emits the x-hex-message response header", %{bypass: bypass} do # handle_response/3 runs inside Task.async, so route shell output to the test process Hex.State.put(:shell_process, self()) Bypass.expect(bypass, fn conn -> conn |> Plug.Conn.put_resp_header("x-hex-message", "\"heads up\";level=warn") |> Plug.Conn.resp(200, "") end) assert {:ok, {200, _headers, _body}} = Hex.HTTP.request(:get, "http://localhost:#{bypass.port}", %{}, nil) assert_received {:mix_shell, :info, ["API warning: heads up"]} 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.5.1/test/hex/ignores_test.exs000066400000000000000000000131111522402544500172740ustar00rootroot00000000000000defmodule Hex.IgnoresTest do use ExUnit.Case, async: true alias Hex.Ignores describe "parse_advisories/1" do test "nil, empty string and empty list parse to no entries" do assert Ignores.parse_advisories(nil) == {:ok, []} assert Ignores.parse_advisories("") == {:ok, []} assert Ignores.parse_advisories([]) == {:ok, []} end test "accepts a list of id strings" do assert Ignores.parse_advisories(["CVE-2026-32686", "GHSA-xxxx-yyyy-zzzz"]) == {:ok, ["CVE-2026-32686", "GHSA-xxxx-yyyy-zzzz"]} end test "rejects non-string and empty entries" do assert Ignores.parse_advisories([:cve]) == :error assert Ignores.parse_advisories(["CVE-2026-32686", 1]) == :error assert Ignores.parse_advisories([""]) == :error assert Ignores.parse_advisories(%{}) == :error end test "parses a comma-separated environment string" do assert Ignores.parse_advisories("CVE-2026-32686, GHSA-xxxx-yyyy-zzzz ,") == {:ok, ["CVE-2026-32686", "GHSA-xxxx-yyyy-zzzz"]} end end describe "parse_retirements/1" do test "nil, empty string and empty list parse to no entries" do assert Ignores.parse_retirements(nil) == {:ok, []} assert Ignores.parse_retirements("") == {:ok, []} assert Ignores.parse_retirements([]) == {:ok, []} end test "accepts atoms and atom-version pairs" do assert Ignores.parse_retirements([:decimal, phoenix: "1.0.0"]) == {:ok, [{"decimal", nil}, {"phoenix", "1.0.0"}]} end test "rejects string names" do assert Ignores.parse_retirements(["decimal"]) == :error assert Ignores.parse_retirements([{"phoenix", "1.0.0"}]) == :error end test "rejects invalid versions and terms" do assert Ignores.parse_retirements(phoenix: "1.0") == :error assert Ignores.parse_retirements(phoenix: :latest) == :error assert Ignores.parse_retirements([nil]) == :error assert Ignores.parse_retirements("phoenix@1.0") == :error assert Ignores.parse_retirements(%{}) == :error end test "parses a comma-separated environment string" do assert Ignores.parse_retirements("decimal, phoenix@1.0.0") == {:ok, [{"decimal", nil}, {"phoenix", "1.0.0"}]} end end describe "advisory matching" do @advisory %{ id: "GHSA-xxxx-yyyy-zzzz", aliases: ["CVE-2026-32686"], summary: "Remote code execution", severity: :SEVERITY_HIGH } test "matches the advisory id case-insensitively" do assert Ignores.advisory_matches?(@advisory, "GHSA-xxxx-yyyy-zzzz") assert Ignores.advisory_matches?(@advisory, "ghsa-XXXX-yyyy-zzzz") refute Ignores.advisory_matches?(@advisory, "GHSA-aaaa-bbbb-cccc") end test "matches aliases case-insensitively" do assert Ignores.advisory_matches?(@advisory, "CVE-2026-32686") assert Ignores.advisory_matches?(@advisory, "cve-2026-32686") end test "handles advisories without aliases" do refute Ignores.advisory_matches?(%{id: "GHSA-1111-2222-3333"}, "CVE-2026-1") assert Ignores.advisory_matches?(%{id: "GHSA-1111-2222-3333"}, "GHSA-1111-2222-3333") end test "advisory_ignored? checks all configured ids" do assert Ignores.advisory_ignored?(@advisory, ["CVE-2020-0000", "CVE-2026-32686"]) refute Ignores.advisory_ignored?(@advisory, ["CVE-2020-0000"]) refute Ignores.advisory_ignored?(@advisory, []) end end describe "retirement matching" do test "name-only entries match any version" do assert Ignores.retirement_matches?("decimal", "1.0.0", {"decimal", nil}) assert Ignores.retirement_matches?("decimal", "2.0.0", {"decimal", nil}) refute Ignores.retirement_matches?("phoenix", "1.0.0", {"decimal", nil}) end test "pinned entries match exactly one version" do assert Ignores.retirement_matches?("phoenix", "1.0.0", {"phoenix", "1.0.0"}) refute Ignores.retirement_matches?("phoenix", "1.0.1", {"phoenix", "1.0.0"}) refute Ignores.retirement_matches?("decimal", "1.0.0", {"phoenix", "1.0.0"}) end test "retirement_ignored? checks all configured entries" do entries = [{"decimal", nil}, {"phoenix", "1.0.0"}] assert Ignores.retirement_ignored?("decimal", "3.0.0", entries) assert Ignores.retirement_ignored?("phoenix", "1.0.0", entries) refute Ignores.retirement_ignored?("phoenix", "1.1.0", entries) refute Ignores.retirement_ignored?("ecto", "1.0.0", []) end end describe "split_advisories/2" do @eef %{ id: "EEF-0001", aliases: ["CVE-2026-33333"], summary: "Remote code execution", severity: :SEVERITY_HIGH } @ghsa %{ id: "GHSA-aaaa-bbbb-cccc", aliases: ["CVE-2026-33333"], summary: "Remote code execution", severity: :SEVERITY_HIGH } @other %{id: "GHSA-dddd-eeee-ffff", summary: "Denial of service", severity: :SEVERITY_LOW} test "no ignores keeps everything active" do assert Ignores.split_advisories([@eef, @ghsa, @other], []) == {[], [@eef, @ghsa, @other]} end test "ignoring one member id suppresses the whole aliased group" do assert Ignores.split_advisories([@eef, @ghsa, @other], ["EEF-0001"]) == {[@eef, @ghsa], [@other]} end test "ignoring the shared CVE suppresses the whole aliased group" do assert Ignores.split_advisories([@eef, @ghsa, @other], ["cve-2026-33333"]) == {[@eef, @ghsa], [@other]} end test "ungrouped advisories are matched individually" do assert Ignores.split_advisories([@other], ["GHSA-dddd-eeee-ffff"]) == {[@other], []} end end end hex-2.5.1/test/hex/mix_task_test.exs000066400000000000000000001014121522402544500174470ustar00rootroot00000000000000defmodule Hex.MixTaskTest do use HexTest.IntegrationCase defp drain_shell_info_matching(substring) do receive do {:mix_shell, :info, [text]} -> if text =~ substring, do: text, else: drain_shell_info_matching(substring) after 0 -> nil end end 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 describe "cooldown" do # All registry packages in the integration suite were just published by # setup_hexpm.exs, so any non-zero cooldown filters them out. The unit # tests cover env / project / global precedence and the empty-env # fallthrough; this layer covers the end-to-end resolution behavior. test "non-zero cooldown lets the solver fail and prints a filtered-versions summary" do Mix.Project.push(Simple) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Hex.State.put(:cooldown, "1d") assert_raise Mix.Error, "Hex dependency resolution failed", fn -> Mix.Task.run("deps.get") end # Solver prints its own "no matching versions" message; Hex appends a # post-solver summary naming the ecto versions filtered by cooldown. summary = drain_shell_info_matching("Versions filtered by cooldown:") assert summary assert summary =~ "ecto" end) after Hex.State.put(:cooldown, "0d") purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject ]) end test "cooldown 0d (default) resolves normally" do Mix.Project.push(Simple) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) Hex.State.put(:cooldown, "0d") Mix.Task.run("deps.get") assert_received {:mix_shell, :info, ["* Getting ecto (Hex package)"]} end) after purge([ Ecto.NoConflict.MixProject, Postgrex.NoConflict.MixProject, Ex_doc.NoConflict.MixProject ]) end defmodule TiredPin do def project do [ app: :tired_app, version: "0.1.0", consolidate_protocols: false, deps: [{:tired, "0.1.0"}] ] end end defmodule TiredRange do def project do [ app: :tired_app, version: "0.1.0", consolidate_protocols: false, deps: [{:tired, "~> 0.1"}] ] end end test "retired locked version escapes cooldown via bypass on deps.update" do # setup_hexpm.exs publishes the `tired` package with versions 0.1.0 # (retired) and 0.2.0. Both were published in the test session, so a # 1-day cooldown filters BOTH from the candidate set. Without the # unsafe-locked-version bypass, `mix deps.update tired` would fail # resolution because every matching version is in cooldown. The # bypass detects the locked 0.1.0 is retired and lets the resolver # see the full set, so it picks 0.2.0. Mix.Project.push(TiredPin) in_tmp(fn -> Hex.State.put(:cache_home, File.cwd!()) # Lock at the retired 0.1.0 — cooldown off so this resolves cleanly. Mix.Task.run("deps.get") assert %{tired: {:hex, :tired, "0.1.0", _, _, _, _, _}} = Mix.Dep.Lock.read() purge([Tired.NoConflict.MixProject]) Mix.State.clear_cache() Mix.Project.pop() Mix.Project.push(TiredRange) Mix.Task.clear() # Now turn on cooldown that would filter every version of tired — # only the retirement bypass can let the upgrade succeed. Hex.State.put(:cooldown, "1d") Mix.Task.run("deps.update", ["tired"]) assert %{tired: {:hex, :tired, "0.2.0", _, _, _, _, _}} = Mix.Dep.Lock.read() end) after Hex.State.put(:cooldown, "0d") purge([Tired.NoConflict.MixProject]) end end end hex-2.5.1/test/hex/mix_test.exs000066400000000000000000000023161522402544500164300ustar00rootroot00000000000000defmodule 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.5.1/test/hex/netrc/000077500000000000000000000000001522402544500151645ustar00rootroot00000000000000hex-2.5.1/test/hex/netrc/cache_test.exs000066400000000000000000000026371522402544500200170ustar00rootroot00000000000000defmodule 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.5.1/test/hex/netrc/parser_test.exs000066400000000000000000000057761522402544500202570ustar00rootroot00000000000000defmodule 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.5.1/test/hex/netrc_test.exs000066400000000000000000000014711522402544500167470ustar00rootroot00000000000000defmodule 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.5.1/test/hex/no_regex_test.exs000066400000000000000000000015511522402544500174410ustar00rootroot00000000000000defmodule Hex.NoRegexTest do use ExUnit.Case, async: true # Compiled regexes are embedded into beam files, and the compiled PCRE # pattern format is not portable across OTP versions (for example, # :re.import/1 does not exist before OTP 28.1). The Hex archive is # precompiled once and installed on many OTP versions, so code under # lib/ must not use regexes; parse with pattern matching instead. test "lib/ contains no regexes" do paths = Path.wildcard("lib/**/*.ex") assert paths != [] offenders = Enum.filter(paths, fn path -> content = File.read!(path) String.contains?(content, ["~r", "~R", "Regex.", ":re."]) end) assert offenders == [], "Regex usage found in shipped code: #{inspect(offenders)}. " <> "Regexes must not appear under lib/, see comment in #{__ENV__.file}" end end hex-2.5.1/test/hex/policy/000077500000000000000000000000001522402544500153505ustar00rootroot00000000000000hex-2.5.1/test/hex/policy/diagnostics_test.exs000066400000000000000000000123301522402544500214360ustar00rootroot00000000000000defmodule Hex.Policy.DiagnosticsTest do use HexTest.Case alias Hex.Policy.Diagnostics defp policy(opts) do opts = Map.new(opts) {cooldown, opts} = Map.pop(opts, :cooldown) repositories = if cooldown do [%{repository: "hexpm", restriction: %{cooldown: cooldown}, overrides: []}] else [] end Map.merge( %{repository: "myorg", visibility: :VISIBILITY_PUBLIC, repositories: repositories}, opts ) end describe "resolution_summary/3" do test "returns nil when no policy is loaded" do assert Diagnostics.resolution_summary(nil, [], "0d") == nil end test "returns a header + cooldown line + per-version listing" do p = policy(name: "strict-prod", cooldown: "14d") filtered = [ %{ repo: "hexpm", package: "phoenix", version: "1.7.18", reasons: [{:advisory, :SEVERITY_HIGH}] } ] out = Diagnostics.resolution_summary(p, filtered, "0d") assert out =~ "Active policy: myorg/strict-prod" assert out =~ "Effective cooldown: 14d (myorg/strict-prod)" assert out =~ "Policy hid 1 candidate version:\n" assert out =~ " phoenix 1.7.18 — advisory ≥ HIGH" end test "pluralizes the hidden count and lists versions newest-first" do p = policy(name: "strict-prod") filtered = [ %{repo: "hexpm", package: "phoenix", version: "1.7.18", reasons: [:override_deny]}, %{repo: "hexpm", package: "phoenix", version: "1.7.19", reasons: [:override_deny]} ] out = Diagnostics.resolution_summary(p, filtered, "0d") assert out =~ "Policy hid 2 candidate versions:" [_count_line, line1, line2] = out |> String.split("\n", trim: true) |> Enum.take(-3) assert line1 =~ "phoenix 1.7.19" assert line2 =~ "phoenix 1.7.18" end test "caps the listing per package and points at mix hex.policy why" do p = policy(name: "strict-prod") filtered = for minor <- 0..7 do %{repo: "hexpm", package: "phoenix", version: "1.#{minor}.0", reasons: [:override_deny]} end out = Diagnostics.resolution_summary(p, filtered, "0d") assert out =~ "Policy hid 8 candidate versions:" assert out =~ "phoenix 1.7.0" assert out =~ "phoenix 1.3.0" refute out =~ "phoenix 1.2.0" assert out =~ " ...and 3 more — run `mix hex.policy why phoenix`" end test "omits the cooldown line when the strictest cooldown is local" do p = policy(name: "strict-prod", cooldown: "7d") out = Diagnostics.resolution_summary(p, [], "1mo") assert out == "Active policy: myorg/strict-prod" end end describe "effective_cooldown/2" do test "is nil when nothing sets a cooldown" do assert Diagnostics.effective_cooldown(policy(name: "a"), "0d") == nil end test "picks the strictest across local and policy tabs" do assert {{"myorg", "b"}, "14d"} = Diagnostics.effective_cooldown(policy(name: "b", cooldown: "14d"), "2d") end test "local config can be the strictest" do assert {:local, "1mo"} = Diagnostics.effective_cooldown(policy(name: "a", cooldown: "7d"), "1mo") end test "handles a nil policy" do assert {:local, "7d"} = Diagnostics.effective_cooldown(nil, "7d") end end describe "failure_note/1" do test "returns nil when nothing filtered" do assert Diagnostics.failure_note([]) == nil end test "groups by package and lists reasons" do out = Diagnostics.failure_note([ %{ repo: "hexpm", package: "decimal", version: "2.0.0", reasons: [{:retirement, :RETIRED_SECURITY}] }, %{ repo: "hexpm", package: "decimal", version: "2.0.1", reasons: [{:advisory, :SEVERITY_HIGH}] } ]) assert out =~ "Note: active policy hides 2 versions of \"decimal\"" assert out =~ "decimal 2.0.0" assert out =~ "decimal 2.0.1" assert out =~ "advisory ≥ HIGH" assert out =~ "retirement: security" end test "formats cooldown and override reasons" do out = Diagnostics.failure_note([ %{ repo: "hexpm", package: "phoenix", version: "1.7.19", reasons: [{:cooldown, "14d", ~D[2026-05-20]}] }, %{ repo: "hexpm", package: "compromised", version: "1.0.0", reasons: [:override_deny] } ]) assert out =~ "hides 1 version of \"phoenix\"" assert out =~ "cooldown 14d; eligible 2026-05-20" assert out =~ "override deny" end test "caps the per-package listing like the resolution summary" do filtered = for minor <- 0..6 do %{repo: "hexpm", package: "phoenix", version: "1.#{minor}.0", reasons: [:override_deny]} end out = Diagnostics.failure_note(filtered) assert out =~ "hides 7 versions of \"phoenix\"" assert out =~ "phoenix 1.6.0" assert out =~ "phoenix 1.2.0" refute out =~ "phoenix 1.1.0" assert out =~ " ...and 2 more — run `mix hex.policy why phoenix`" end end end hex-2.5.1/test/hex/policy/filter_test.exs000066400000000000000000000137201522402544500204200ustar00rootroot00000000000000defmodule Hex.Policy.FilterTest do use HexTest.Case alias Hex.Policy.Filter # A policy with a single repository tab. defp policy(tab, opts \\ []) do %{ repository: Keyword.get(opts, :org, "myorg"), name: Keyword.get(opts, :name, "strict-prod"), visibility: :VISIBILITY_PUBLIC, repositories: [tab] } end defp tab(fields) do Map.merge(%{repository: "hexpm", overrides: []}, Map.new(fields)) end defp candidate(fields \\ []) do Map.merge( %{ repo: "hexpm", package: "phoenix", version: "1.0.0", advisories: [], retired: nil, published_at: nil }, Map.new(fields) ) end describe "classify/3 — advisory rule" do test "blocks when release advisory >= threshold" do p = policy(tab(restriction: %{advisory_min_severity: :SEVERITY_HIGH})) c = candidate(advisories: [%{severity: :SEVERITY_HIGH}]) assert {:blocked, reasons} = Filter.classify(p, c) assert {:advisory, :SEVERITY_HIGH} in reasons end test "allows when release advisory < threshold" do p = policy(tab(restriction: %{advisory_min_severity: :SEVERITY_HIGH})) c = candidate(advisories: [%{severity: :SEVERITY_LOW}]) assert :allowed == Filter.classify(p, c) end test "allows when no advisories" do p = policy(tab(restriction: %{advisory_min_severity: :SEVERITY_HIGH})) assert :allowed == Filter.classify(p, candidate()) end test "blocks unknown severities instead of letting them slip under the threshold" do p = policy(tab(restriction: %{advisory_min_severity: :SEVERITY_HIGH})) c = candidate(advisories: [%{severity: 99}]) assert {:blocked, reasons} = Filter.classify(p, c) assert {:advisory, :SEVERITY_HIGH} in reasons c = candidate(advisories: [%{}]) assert {:blocked, _reasons} = Filter.classify(p, c) end test "allows when the tab has no restriction" do p = policy(tab(%{})) c = candidate(advisories: [%{severity: :SEVERITY_CRITICAL}]) assert :allowed == Filter.classify(p, c) end end describe "classify/3 — retirement rule" do test "blocks when release retired with selected reason" do p = policy(tab(restriction: %{retirement_reasons: [:RETIRED_SECURITY, :RETIRED_DEPRECATED]})) c = candidate(retired: %{reason: :RETIRED_SECURITY}) assert {:blocked, reasons} = Filter.classify(p, c) assert {:retirement, :RETIRED_SECURITY} in reasons end test "allows when reason not in set" do p = policy(tab(restriction: %{retirement_reasons: [:RETIRED_SECURITY]})) c = candidate(retired: %{reason: :RETIRED_RENAMED}) assert :allowed == Filter.classify(p, c) end test "allows when not retired" do p = policy(tab(restriction: %{retirement_reasons: [:RETIRED_SECURITY]})) assert :allowed == Filter.classify(p, candidate()) end end describe "classify/3 — cooldown rule" do @now 1_700_000_000 test "blocks a release younger than the cooldown" do p = policy(tab(restriction: %{cooldown: "14d"})) c = candidate(published_at: @now - 1 * 86_400) assert {:blocked, [{:cooldown, "14d", %Date{}}]} = Filter.classify(p, c, now: @now) end test "allows a release older than the cooldown" do p = policy(tab(restriction: %{cooldown: "14d"})) c = candidate(published_at: @now - 20 * 86_400) assert :allowed == Filter.classify(p, c, now: @now) end test "allows when published_at is unknown" do p = policy(tab(restriction: %{cooldown: "14d"})) assert :allowed == Filter.classify(p, candidate(published_at: nil), now: @now) end test "a zero cooldown never blocks" do p = policy(tab(restriction: %{cooldown: "0"})) c = candidate(published_at: @now) assert :allowed == Filter.classify(p, c, now: @now) end end describe "classify/3 — overrides" do test "an allow override bypasses the restriction" do p = policy( tab( restriction: %{advisory_min_severity: :SEVERITY_LOW}, overrides: [%{action: :OVERRIDE_ACTION_ALLOW, ref: %{package: "phoenix"}}] ) ) c = candidate(advisories: [%{severity: :SEVERITY_CRITICAL}]) assert :allowed == Filter.classify(p, c) end test "a deny override blocks" do p = policy(tab(overrides: [%{action: :OVERRIDE_ACTION_DENY, ref: %{package: "phoenix"}}])) assert {:blocked, [:override_deny]} = Filter.classify(p, candidate()) end test "most specific override wins (versioned allow beats bare deny)" do p = policy( tab( overrides: [ %{action: :OVERRIDE_ACTION_DENY, ref: %{package: "phoenix"}}, %{ action: :OVERRIDE_ACTION_ALLOW, ref: %{package: "phoenix", requirement: "== 1.7.10"} } ] ) ) assert :allowed == Filter.classify(p, candidate(version: "1.7.10")) assert {:blocked, [:override_deny]} = Filter.classify(p, candidate(version: "1.7.11")) end test "an override for a different package does not match" do p = policy(tab(overrides: [%{action: :OVERRIDE_ACTION_DENY, ref: %{package: "ecto"}}])) assert :allowed == Filter.classify(p, candidate(package: "phoenix")) end end describe "classify/3 — repository matching" do test "a policy does not constrain a repository it has no tab for" do p = policy(tab(repository: "hexpm", restriction: %{advisory_min_severity: :SEVERITY_LOW})) c = candidate(repo: "hexpm:myorg", advisories: [%{severity: :SEVERITY_CRITICAL}]) assert :allowed == Filter.classify(p, c) end test "the org tab matches a hexpm: candidate repo" do p = policy(tab(repository: "myorg", restriction: %{advisory_min_severity: :SEVERITY_LOW})) c = candidate(repo: "hexpm:myorg", advisories: [%{severity: :SEVERITY_HIGH}]) assert {:blocked, _} = Filter.classify(p, c) end end end hex-2.5.1/test/hex/policy_test.exs000066400000000000000000000034631522402544500171360ustar00rootroot00000000000000defmodule Hex.PolicyTest do use HexTest.Case, async: true alias Hex.Policy describe "parse_config/1" do test "accepts a keyword list with a repo (mix.exs form)" do assert {:ok, "myrepo/strict-prod"} == Policy.parse_config(repo: "myrepo", name: "strict-prod") end test "a keyword list with an org resolves to the hexpm: repo" do assert {:ok, "hexpm:myorg/strict-prod"} == Policy.parse_config(org: "myorg", name: "strict-prod") end test "rejects a keyword list with both repo and org" do assert :error == Policy.parse_config(repo: "myrepo", org: "myorg", name: "strict-prod") end test "accepts a repo/name string (env-var form)" do assert {:ok, "myorg/strict-prod"} == Policy.parse_config("myorg/strict-prod") end test "trims whitespace in the string form" do assert {:ok, "myorg/strict-prod"} == Policy.parse_config(" myorg/strict-prod ") end test "treats nil and empty as no policy" do assert {:ok, nil} == Policy.parse_config(nil) assert {:ok, nil} == Policy.parse_config("") assert {:ok, nil} == Policy.parse_config([]) end test "rejects malformed entries" do assert :error == Policy.parse_config("missing-slash") assert :error == Policy.parse_config("a/b/c") assert :error == Policy.parse_config("myorg/strict,acme/baseline") assert :error == Policy.parse_config(repo: "x") assert :error == Policy.parse_config(org: "") assert :error == Policy.parse_config(org: "myorg", name: "") assert :error == Policy.parse_config(42) end test "rejects the bare hexpm repo (no organization scope)" do assert :error == Policy.parse_config("hexpm/strict") assert :error == Policy.parse_config(repo: "hexpm", name: "strict") end end end hex-2.5.1/test/hex/registry/000077500000000000000000000000001522402544500157215ustar00rootroot00000000000000hex-2.5.1/test/hex/registry/cooldown_test.exs000066400000000000000000000167541522402544500213420ustar00rootroot00000000000000defmodule Hex.Registry.CooldownTest do use HexTest.Case alias Hex.Registry.Cooldown alias Hex.Registry.Server setup do now = System.system_time(:second) registry = [ {:hexpm, :foo, "1.0.0", []}, {:hexpm, :foo, "1.1.0", []}, {:hexpm, :foo, "1.2.0", []} ] publish_times = %{ {:hexpm, :foo, "1.0.0"} => now - 30 * 86_400, {:hexpm, :foo, "1.1.0"} => now - 14 * 86_400, {:hexpm, :foo, "1.2.0"} => now - 2 * 86_400 } path = tmp_path("cache.ets") File.rm(path) create_test_registry(path, registry, [], publish_times) Hex.State.put(:offline, true) Server.open(registry_path: path) Server.prefetch([{"hexpm", "foo"}]) %{now: now} end test "passes through versions when cooldown disabled" do Hex.State.put(:cooldown_cutoff, :disabled) Hex.State.put(:cooldown_bypass_packages, MapSet.new()) Hex.State.put(:cooldown_locked_versions, %{}) Hex.State.put(:cooldown_filtered_versions, []) {:ok, versions} = Cooldown.versions("hexpm", "foo") assert Enum.map(versions, &to_string/1) == ["1.0.0", "1.1.0", "1.2.0"] end test "filters versions within cooldown window" do Hex.State.put(:cooldown, "7d") cutoff = Hex.Cooldown.build_cutoff() Hex.State.put(:cooldown_cutoff, cutoff) Hex.State.put(:cooldown_bypass_packages, MapSet.new()) Hex.State.put(:cooldown_locked_versions, %{}) Hex.State.put(:cooldown_filtered_versions, []) {:ok, versions} = Cooldown.versions("hexpm", "foo") # 1.2.0 published 2 days ago should be filtered assert Enum.map(versions, &to_string/1) == ["1.0.0", "1.1.0"] end test "bypass set short-circuits filtering for a package" do Hex.State.put(:cooldown, "60d") cutoff = Hex.Cooldown.build_cutoff() Hex.State.put(:cooldown_cutoff, cutoff) Hex.State.put(:cooldown_bypass_packages, MapSet.new(["foo"])) {:ok, versions} = Cooldown.versions("hexpm", "foo") assert Enum.map(versions, &to_string/1) == ["1.0.0", "1.1.0", "1.2.0"] end test "cooldown_exclude_repos short-circuits filtering for repos in the list" do Hex.State.put(:cooldown, "60d") cutoff = Hex.Cooldown.build_cutoff() Hex.State.put(:cooldown_cutoff, cutoff) Hex.State.put(:cooldown_bypass_packages, MapSet.new()) Hex.State.put(:cooldown_locked_versions, %{}) Hex.State.put(:cooldown_filtered_versions, []) Hex.State.put(:cooldown_exclude_repos, ["hexpm"]) {:ok, versions} = Cooldown.versions("hexpm", "foo") assert Enum.map(versions, &to_string/1) == ["1.0.0", "1.1.0", "1.2.0"] after Hex.State.put(:cooldown_exclude_repos, []) end test "cooldown_exclude_repos does not affect non-excluded repos" do Hex.State.put(:cooldown, "60d") cutoff = Hex.Cooldown.build_cutoff() Hex.State.put(:cooldown_cutoff, cutoff) Hex.State.put(:cooldown_bypass_packages, MapSet.new()) Hex.State.put(:cooldown_locked_versions, %{}) Hex.State.put(:cooldown_filtered_versions, []) Hex.State.put(:cooldown_exclude_repos, ["hexpm:other"]) # hexpm is not in the exclude list — cooldown still applies, filtering # everything in the fixture (all versions are <60d old). {:ok, versions} = Cooldown.versions("hexpm", "foo") assert versions == [] after Hex.State.put(:cooldown_exclude_repos, []) end test "nil published_at is treated as eligible" do now = System.system_time(:second) registry = [ {:hexpm, :legacy, "1.0.0", []}, {:hexpm, :legacy, "1.1.0", []} ] publish_times = %{ {:hexpm, :legacy, "1.1.0"} => now - 1 * 86_400 } path = tmp_path("cache_legacy.ets") File.rm(path) create_test_registry(path, registry, [], publish_times) Server.close() Hex.State.put(:offline, true) Server.open(registry_path: path) Server.prefetch([{"hexpm", "legacy"}]) Hex.State.put(:cooldown, "30d") cutoff = Hex.Cooldown.build_cutoff() Hex.State.put(:cooldown_cutoff, cutoff) Hex.State.put(:cooldown_bypass_packages, MapSet.new()) Hex.State.put(:cooldown_locked_versions, %{}) Hex.State.put(:cooldown_filtered_versions, []) {:ok, versions} = Cooldown.versions("hexpm", "legacy") # 1.0.0 has nil published_at -> eligible; 1.1.0 is too fresh -> filtered assert Enum.map(versions, &to_string/1) == ["1.0.0"] end test "delegates prefetch and dependencies to Server" do Code.ensure_loaded!(Cooldown) assert function_exported?(Cooldown, :prefetch, 1) assert function_exported?(Cooldown, :dependencies, 3) end test "version present in :cooldown_locked_versions survives the filter" do # An in-cooldown version that is currently in the lockfile must remain # a solver candidate so re-resolution can fall back to it when no newer # eligible version exists. Hex.State.put(:cooldown, "7d") cutoff = Hex.Cooldown.build_cutoff() Hex.State.put(:cooldown_cutoff, cutoff) Hex.State.put(:cooldown_bypass_packages, MapSet.new()) Hex.State.put(:cooldown_locked_versions, %{}) Hex.State.put(:cooldown_filtered_versions, []) Hex.State.put(:cooldown_locked_versions, %{{"hexpm", "foo"} => ["1.2.0"]}) {:ok, versions} = Cooldown.versions("hexpm", "foo") assert Enum.map(versions, &to_string/1) == ["1.0.0", "1.1.0", "1.2.0"] end test "locked-version exemption only applies to the matching repo+package+version" do Hex.State.put(:cooldown, "7d") cutoff = Hex.Cooldown.build_cutoff() Hex.State.put(:cooldown_cutoff, cutoff) Hex.State.put(:cooldown_bypass_packages, MapSet.new()) Hex.State.put(:cooldown_locked_versions, %{}) Hex.State.put(:cooldown_filtered_versions, []) # Stale entry naming a different version — does not save 1.2.0. Hex.State.put(:cooldown_locked_versions, %{{"hexpm", "foo"} => ["1.1.0"]}) {:ok, versions} = Cooldown.versions("hexpm", "foo") assert Enum.map(versions, &to_string/1) == ["1.0.0", "1.1.0"] end describe "filtered-versions recording" do test "records each filtered version with its published_at", %{now: now} do Hex.State.put(:cooldown, "7d") cutoff = Hex.Cooldown.build_cutoff() Hex.State.put(:cooldown_cutoff, cutoff) Hex.State.put(:cooldown_bypass_packages, MapSet.new()) Hex.State.put(:cooldown_locked_versions, %{}) Hex.State.put(:cooldown_filtered_versions, []) Hex.State.put(:cooldown_filtered_versions, []) {:ok, _} = Cooldown.versions("hexpm", "foo") filtered = Hex.State.fetch!(:cooldown_filtered_versions) assert [{"hexpm", "foo", "1.2.0", published_at}] = filtered assert_in_delta published_at, now - 2 * 86_400, 2 end test "does not record versions that survive via the locked exemption" do Hex.State.put(:cooldown, "7d") cutoff = Hex.Cooldown.build_cutoff() Hex.State.put(:cooldown_cutoff, cutoff) Hex.State.put(:cooldown_bypass_packages, MapSet.new()) Hex.State.put(:cooldown_locked_versions, %{{"hexpm", "foo"} => ["1.2.0"]}) Hex.State.put(:cooldown_filtered_versions, []) {:ok, _} = Cooldown.versions("hexpm", "foo") assert Hex.State.fetch!(:cooldown_filtered_versions) == [] end test "records nothing when cooldown is disabled" do Hex.State.put(:cooldown_cutoff, :disabled) Hex.State.put(:cooldown_bypass_packages, MapSet.new()) Hex.State.put(:cooldown_locked_versions, %{}) Hex.State.put(:cooldown_filtered_versions, []) Hex.State.put(:cooldown_filtered_versions, []) {:ok, _} = Cooldown.versions("hexpm", "foo") assert Hex.State.fetch!(:cooldown_filtered_versions) == [] end end end hex-2.5.1/test/hex/registry/policy_test.exs000066400000000000000000000130551522402544500210040ustar00rootroot00000000000000defmodule Hex.Registry.PolicyTest do use HexTest.Case alias Hex.Registry.Policy alias Hex.Registry.Server setup do registry = [ {:hexpm, :clean, "1.0.0", []}, {:hexpm, :clean, "1.1.0", []}, {:hexpm, :advised, "1.0.0", []}, {:hexpm, :advised, "1.1.0", []}, {:hexpm, :retired, "1.0.0", []}, {:hexpm, :retired, "1.1.0", []} ] advisories = [ {{"hexpm", "advised", "1.0.0"}, [%{id: "GHSA-test-aaaa-bbbb", summary: "test", severity: :SEVERITY_HIGH}]} ] retired = %{ {:hexpm, :retired, "1.0.0"} => %{reason: :RETIRED_SECURITY, message: "CVE"} } path = tmp_path("cache_policy.ets") File.rm(path) create_test_registry(path, registry, advisories, %{}, retired) Server.close() Hex.State.put(:offline, true) Server.open(registry_path: path) Server.prefetch([{"hexpm", "clean"}, {"hexpm", "advised"}, {"hexpm", "retired"}]) # Reset diagnostics for each test Hex.State.put(:policy_filtered_versions, []) Hex.State.put(:active_policy, nil) Hex.State.put(:policy_locked_versions, %{}) # Disable cooldown so Hex.Registry.Cooldown is a no-op pass-through Hex.State.put(:cooldown_cutoff, :disabled) Hex.State.put(:cooldown_bypass_packages, MapSet.new()) Hex.State.put(:cooldown_locked_versions, %{}) Hex.State.put(:cooldown_filtered_versions, []) :ok end defp policy(name, tab_fields) do %{ repository: "myorg", name: name, visibility: :VISIBILITY_PUBLIC, repositories: [ Map.merge(%{repository: "hexpm", overrides: []}, Map.new(tab_fields)) ] } end test "passes through when no policies are configured" do assert {:ok, versions} = Policy.versions("hexpm", "clean") {:ok, expected} = Server.versions("hexpm", "clean") assert versions == expected end test "passes through when no policies are configured for a package with advisories" do assert {:ok, versions} = Policy.versions("hexpm", "advised") assert Enum.map(versions, &to_string/1) == ["1.0.0", "1.1.0"] assert Hex.State.fetch!(:policy_filtered_versions) == [] end test "filters versions that an advisory rule blocks" do Hex.State.put( :active_policy, policy("strict", restriction: %{advisory_min_severity: :SEVERITY_HIGH}) ) assert {:ok, versions} = Policy.versions("hexpm", "advised") assert Enum.map(versions, &to_string/1) == ["1.1.0"] [entry] = Hex.State.fetch!(:policy_filtered_versions) assert entry.repo == "hexpm" assert entry.package == "advised" assert entry.version == "1.0.0" assert entry.reasons == [{:advisory, :SEVERITY_HIGH}] end test "filters versions that a retirement rule blocks" do Hex.State.put( :active_policy, policy("no-security-retired", restriction: %{retirement_reasons: [:RETIRED_SECURITY]}) ) assert {:ok, versions} = Policy.versions("hexpm", "retired") assert Enum.map(versions, &to_string/1) == ["1.1.0"] [entry] = Hex.State.fetch!(:policy_filtered_versions) assert entry.package == "retired" assert entry.version == "1.0.0" assert entry.reasons == [{:retirement, :RETIRED_SECURITY}] end test "a deny override blocks all versions of a package" do Hex.State.put( :active_policy, policy("blocklist", overrides: [%{action: :OVERRIDE_ACTION_DENY, ref: %{package: "clean"}}] ) ) assert {:ok, versions} = Policy.versions("hexpm", "clean") assert versions == [] assert [%{version: "1.1.0"}, %{version: "1.0.0"}] = Hex.State.fetch!(:policy_filtered_versions) end test "an allow override bypasses the restriction" do Hex.State.put( :active_policy, policy("vetted", restriction: %{advisory_min_severity: :SEVERITY_HIGH}, overrides: [%{action: :OVERRIDE_ACTION_ALLOW, ref: %{package: "advised"}}] ) ) assert {:ok, versions} = Policy.versions("hexpm", "advised") assert Enum.map(versions, &to_string/1) == ["1.0.0", "1.1.0"] assert Hex.State.fetch!(:policy_filtered_versions) == [] end test "a locked version is exempt from policy filtering" do Hex.State.put( :active_policy, policy("strict", restriction: %{advisory_min_severity: :SEVERITY_HIGH}) ) Hex.State.put(:policy_locked_versions, %{{"hexpm", "advised"} => ["1.0.0"]}) assert {:ok, versions} = Policy.versions("hexpm", "advised") assert Enum.map(versions, &to_string/1) == ["1.0.0", "1.1.0"] assert Hex.State.fetch!(:policy_filtered_versions) == [] end test "repeated lookups of the same package record a blocked version once" do Hex.State.put( :active_policy, policy("strict", restriction: %{advisory_min_severity: :SEVERITY_HIGH}) ) assert {:ok, _} = Policy.versions("hexpm", "advised") assert {:ok, _} = Policy.versions("hexpm", "advised") assert {:ok, _} = Policy.versions("hexpm", "advised") assert [%{package: "advised", version: "1.0.0"}] = Hex.State.fetch!(:policy_filtered_versions) end test "no diagnostics recorded when nothing is blocked" do Hex.State.put( :active_policy, policy("strict", restriction: %{advisory_min_severity: :SEVERITY_HIGH}) ) assert {:ok, versions} = Policy.versions("hexpm", "clean") assert Enum.map(versions, &to_string/1) == ["1.0.0", "1.1.0"] assert Hex.State.fetch!(:policy_filtered_versions) == [] end test "delegates prefetch and dependencies" do Code.ensure_loaded!(Policy) assert function_exported?(Policy, :prefetch, 1) assert function_exported?(Policy, :dependencies, 3) end end hex-2.5.1/test/hex/registry/server_policy_test.exs000066400000000000000000000105571522402544500223760ustar00rootroot00000000000000defmodule Hex.Registry.ServerPolicyTest do use HexTest.Case, async: false alias Hex.Registry.Server, as: Registry setup do bypass = Bypass.open() repos = Hex.State.fetch!(:repos) repos = Map.put(repos, "hexpm:myorg", %{ url: "http://localhost:#{bypass.port}", public_key: File.read!(fixture_path("test_pub.pem")), auth_key: "key", trusted: true, oauth_exchange: false, repo_organization: "myorg" }) Hex.State.put(:repos, repos) {:ok, bypass: bypass} end defp signed_policy(fields) do private_key = File.read!(fixture_path("test_priv.pem")) payload = :mix_hex_registry.encode_policy(Map.new(fields)) signed = :mix_hex_registry.sign_protobuf(payload, private_key) :zlib.gzip(signed) end defp fresh_policy(repositories) do base = %{ repository: "myorg", name: "strict-prod", visibility: :VISIBILITY_PUBLIC, repositories: repositories } signed_policy(base) end test "prefetch_policies/1 fetches and decodes; policy/2 returns the decoded map", %{bypass: bypass} do repositories = [ %{ repository: "hexpm", restriction: %{advisory_min_severity: :SEVERITY_HIGH}, overrides: [] } ] Bypass.expect_once(bypass, "GET", "/repos/myorg/policies/strict-prod", fn conn -> conn |> Plug.Conn.put_resp_header("etag", "\"v1\"") |> Plug.Conn.resp(200, fresh_policy(repositories)) end) in_tmp("registry_policy_fetch", fn -> Hex.State.put(:cache_home, File.cwd!()) Registry.open(check_version: false, registry_path: Path.join(File.cwd!(), "cache.ets")) assert :ok = Registry.prefetch_policies([{"hexpm:myorg", "strict-prod"}]) assert {:ok, policy} = Registry.policy("hexpm:myorg", "strict-prod") assert policy.name == "strict-prod" assert [%{repository: "hexpm", restriction: restriction}] = policy.repositories assert restriction.advisory_min_severity == :SEVERITY_HIGH end) end test "policy/2 falls back to the cached payload when the registry is unreachable", %{bypass: bypass} do Bypass.expect_once(bypass, "GET", "/repos/myorg/policies/strict-prod", fn conn -> Plug.Conn.resp(conn, 200, fresh_policy([])) end) in_tmp("registry_policy_cache_fallback", fn -> Hex.State.put(:cache_home, File.cwd!()) registry_path = Path.join(File.cwd!(), "cache.ets") Registry.open(check_version: false, registry_path: registry_path) assert :ok = Registry.prefetch_policies([{"hexpm:myorg", "strict-prod"}]) assert {:ok, _} = Registry.policy("hexpm:myorg", "strict-prod") Registry.persist() Registry.close() Bypass.down(bypass) Registry.open(check_version: false, registry_path: registry_path) assert :ok = Registry.prefetch_policies([{"hexpm:myorg", "strict-prod"}]) assert {:ok, policy} = Registry.policy("hexpm:myorg", "strict-prod") assert policy.name == "strict-prod" end) end test "close/0 waits for in-flight policy fetches", %{bypass: bypass} do Bypass.expect_once(bypass, "GET", "/repos/myorg/policies/strict-prod", fn conn -> Process.sleep(100) Plug.Conn.resp(conn, 200, fresh_policy([])) end) in_tmp("registry_policy_close_in_flight", fn -> Hex.State.put(:cache_home, File.cwd!()) registry_path = Path.join(File.cwd!(), "cache.ets") Registry.open(check_version: false, registry_path: registry_path) pid = Process.whereis(Registry) assert :ok = Registry.prefetch_policies([{"hexpm:myorg", "strict-prod"}]) assert :ok = Registry.close() # Without waiting, the fetch result would land in a deleted ETS table # and crash the server. Process.sleep(150) assert Process.whereis(Registry) == pid end) end test "prefetch_policies/1 raises a helpful error in offline mode when missing" do in_tmp("registry_policy_offline", fn -> Hex.State.put(:cache_home, File.cwd!()) Hex.State.put(:offline, true) Registry.open(check_version: false, registry_path: Path.join(File.cwd!(), "cache.ets")) assert_raise Mix.Error, ~r"Hex is running in offline mode and policy hexpm:myorg/strict-prod is not cached locally", fn -> Registry.prefetch_policies([{"hexpm:myorg", "strict-prod"}]) end end) end end hex-2.5.1/test/hex/registry/server_test.exs000066400000000000000000000011051522402544500210040ustar00rootroot00000000000000defmodule Hex.Registry.ServerTest do use HexTest.Case alias Hex.Registry.Server, as: Registry setup do Hex.State.put(:offline, true) Registry.open(registry_path: tmp_path("cache.ets")) :ok end test "prefetch raises a helpful error in offline mode when a package is not cached" do assert_raise Mix.Error, ~r"Hex is running in offline mode and the registry entry for package missing_package is not cached locally", fn -> Registry.prefetch([{"hexpm", "missing_package"}]) end end end hex-2.5.1/test/hex/remote_converger_cooldown_test.exs000066400000000000000000000222101522402544500230770ustar00rootroot00000000000000defmodule Hex.RemoteConvergerCooldownTest do # Non-integration tests for the cooldown helpers in Hex.RemoteConverger. # The full integration scenarios live under HexTest.IntegrationCase. use HexTest.Case alias Hex.RemoteConverger alias Hex.Registry.Server setup do registry = [ {:hexpm, :good, "1.0.0", []}, {:hexpm, :retired_dep, "1.0.0", []}, {:hexpm, :retired_dep, "2.0.0", []}, {:hexpm, :advised_dep, "1.0.0", []}, {:hexpm, :advised_dep, "2.0.0", []} ] retired = %{ # The currently-locked version of retired_dep is retired. {:hexpm, :retired_dep, "1.0.0"} => %{reason: :RETIRED_SECURITY, message: "CVE"} } # Per-release advisories. Stored under {:advisories, repo, pkg, vsn} as # a list; non-empty means the version is known-unsafe. advisories = [ {{"hexpm", "advised_dep", "1.0.0"}, [%{id: "GHSA-test-aaaa-bbbb", summary: "test", severity: :SEVERITY_HIGH}]} ] path = tmp_path("cache_bypass.ets") File.rm(path) create_test_registry(path, registry, advisories, %{}, retired) Server.close() Hex.State.put(:offline, true) Server.open(registry_path: path) Server.prefetch([{"hexpm", "good"}, {"hexpm", "retired_dep"}, {"hexpm", "advised_dep"}]) :ok end defp lock_tuple(name, version, repo \\ "hexpm") do {:hex, String.to_atom(name), version, "checksum", [:mix], [], repo, "outer_checksum"} end defp lock_tuple_with_deps(name, version, child_names, repo \\ "hexpm") do deps = Enum.map(child_names, fn child -> {String.to_atom(child), "~> 0.0.0", [hex: child, repo: "hexpm", optional: false]} end) {:hex, String.to_atom(name), version, "checksum", [:mix], deps, repo, "outer_checksum"} end defp locked_request(name, version, repo \\ "hexpm") do %{repo: repo, name: name, app: name, version: version} end @cutoff {:cutoff, System.system_time(:second) - 7 * 86_400, 7 * 86_400} describe "build_cooldown_bypass/3 — disabled" do test "returns empty set when cutoff is :disabled" do old_lock = %{retired_dep: lock_tuple("retired_dep", "1.0.0")} assert MapSet.new() == RemoteConverger.build_cooldown_bypass(old_lock, [], :disabled) end end describe "build_cooldown_bypass/3 — unsafe-locked-version bypass" do test "includes packages whose locked version is retired" do old_lock = %{ retired_dep: lock_tuple("retired_dep", "1.0.0"), good: lock_tuple("good", "1.0.0") } bypass = RemoteConverger.build_cooldown_bypass(old_lock, [], @cutoff) assert MapSet.member?(bypass, "retired_dep") refute MapSet.member?(bypass, "good") end test "regression: unsafe-bypass walks old_lock so updates can escape retirement" do # Guards against the bug where the bypass was built from the post- # prepare_locked value, which removes the package being `mix deps.update`d # — exactly the package that needs bypass. old_lock = %{retired_dep: lock_tuple("retired_dep", "1.0.0")} # locked is empty because deps.update unlocked the package bypass = RemoteConverger.build_cooldown_bypass(old_lock, [], @cutoff) assert MapSet.member?(bypass, "retired_dep") end test "includes packages whose locked version carries a security advisory" do old_lock = %{advised_dep: lock_tuple("advised_dep", "1.0.0")} bypass = RemoteConverger.build_cooldown_bypass(old_lock, [], @cutoff) assert MapSet.member?(bypass, "advised_dep") end test "locked versions with empty advisory list are not bypassed via unsafe-set" do old_lock = %{advised_dep: lock_tuple("advised_dep", "2.0.0")} assert MapSet.new() == RemoteConverger.build_cooldown_bypass(old_lock, [], @cutoff) end test "lock-children of unsafe parent are also bypassed" do # Escaping an unsafe parent typically requires re-resolving its # dependency subtree, and the new parent version usually wants newer # versions of those children. Mirror `mix deps.update`'s behavior of # unlocking the children alongside the explicit target. old_lock = %{ retired_dep: lock_tuple_with_deps("retired_dep", "1.0.0", ["good", "advised_dep"]), good: lock_tuple("good", "1.0.0") } bypass = RemoteConverger.build_cooldown_bypass(old_lock, [], @cutoff) assert MapSet.member?(bypass, "retired_dep") assert MapSet.member?(bypass, "good") assert MapSet.member?(bypass, "advised_dep") end test "children of safe parents are not bypassed" do old_lock = %{ good: lock_tuple_with_deps("good", "1.0.0", ["advised_dep"]) } assert MapSet.new() == RemoteConverger.build_cooldown_bypass(old_lock, [], @cutoff) end end describe "build_cooldown_bypass/3 — lock-satisfied bypass (spec C)" do test "packages in `locked` (lockfile survived prepare_locked) are bypassed" do # mix deps.get against an intact lockfile: the dep made it through # prepare_locked, so it is being installed from the lock. Cooldown # must not interfere — matches the design's promise that # \"mix deps.get against an existing lockfile: cooldown does not apply\". old_lock = %{good: lock_tuple("good", "1.0.0")} locked = [locked_request("good", "1.0.0")] bypass = RemoteConverger.build_cooldown_bypass(old_lock, locked, @cutoff) assert MapSet.member?(bypass, "good") end test "packages in old_lock but not in locked (being updated) are not bypassed by lock-satisfied" do # mix deps.update foo: foo is in old_lock but prepare_locked removed it. # If foo's locked version is not unsafe, foo should NOT bypass cooldown. old_lock = %{good: lock_tuple("good", "1.0.0")} locked = [] assert MapSet.new() == RemoteConverger.build_cooldown_bypass(old_lock, locked, @cutoff) end test "new top-level deps (not in old_lock) are not bypassed" do # User added a new dep to mix.exs: no lock entry, no bypass. # Cooldown filtering applies normally to fresh additions. old_lock = %{} locked = [] assert MapSet.new() == RemoteConverger.build_cooldown_bypass(old_lock, locked, @cutoff) end end describe "print_cooldown_summary/0" do test "prints the summary when filtered versions were recorded" do now = System.system_time(:second) Hex.State.put(:cooldown, "7d") Hex.State.put(:cooldown_cutoff, Hex.Cooldown.build_cutoff()) Hex.State.put(:cooldown_filtered_versions, [ {"hexpm", "castore", "1.0.19", now - 6 * 86_400} ]) RemoteConverger.print_cooldown_summary() assert_received {:mix_shell, :info, [output]} assert output =~ "Versions filtered by cooldown:" assert output =~ "castore 1.0.19" end test "prints nothing when nothing was filtered" do Hex.State.put(:cooldown, "7d") Hex.State.put(:cooldown_cutoff, Hex.Cooldown.build_cutoff()) Hex.State.put(:cooldown_filtered_versions, []) RemoteConverger.print_cooldown_summary() refute_received {:mix_shell, :info, _} end test "prints nothing when cooldown is disabled" do Hex.State.put(:cooldown_cutoff, :disabled) Hex.State.put(:cooldown_filtered_versions, []) RemoteConverger.print_cooldown_summary() refute_received {:mix_shell, :info, _} end end describe "end-to-end advisory bypass" do # Wires the bypass set into the wrapper to confirm that an # advisory-flagged locked version actually unblocks the upgrade path # — not only that build_cooldown_bypass produces the right MapSet, # but that the wrapper consumes it and stops filtering. test "advisory-only locked version unblocks the upgrade through the wrapper" do # advised_dep 1.0.0 has an advisory; 2.0.0 is clean but assumed young. old_lock = %{advised_dep: lock_tuple("advised_dep", "1.0.0")} locked = [] bypass = RemoteConverger.build_cooldown_bypass(old_lock, locked, @cutoff) Hex.State.put(:cooldown_cutoff, @cutoff) Hex.State.put(:cooldown_bypass_packages, bypass) Hex.State.put(:cooldown_locked_versions, %{}) Hex.State.put(:cooldown_filtered_versions, []) # Without bypass the wrapper would filter every version (no published_at # in the fixture means eligible, but if we'd populated young # published_at the bypass is what saves us). Either way, the wrapper # must return the full version list because the package is bypassed. {:ok, versions} = Hex.Registry.Cooldown.versions("hexpm", "advised_dep") assert Enum.map(versions, &to_string/1) == ["1.0.0", "2.0.0"] end test "retired locked version unblocks the upgrade through the wrapper" do old_lock = %{retired_dep: lock_tuple("retired_dep", "1.0.0")} locked = [] bypass = RemoteConverger.build_cooldown_bypass(old_lock, locked, @cutoff) Hex.State.put(:cooldown_cutoff, @cutoff) Hex.State.put(:cooldown_bypass_packages, bypass) Hex.State.put(:cooldown_locked_versions, %{}) Hex.State.put(:cooldown_filtered_versions, []) {:ok, versions} = Hex.Registry.Cooldown.versions("hexpm", "retired_dep") assert Enum.map(versions, &to_string/1) == ["1.0.0", "2.0.0"] end end end hex-2.5.1/test/hex/remote_converger_policy_test.exs000066400000000000000000000100421522402544500225520ustar00rootroot00000000000000defmodule Hex.RemoteConvergerPolicyTest do use HexTest.Case import ExUnit.CaptureIO test "with no policy configured, Hex.Policy.load returns nil" do in_tmp("remote_converger_no_policy", fn -> Hex.State.put(:config_home, File.cwd!()) original = System.get_env("HEX_POLICY") System.delete_env("HEX_POLICY") try do Hex.State.refresh() assert {:ok, nil} = Hex.Policy.load() after case original do nil -> System.delete_env("HEX_POLICY") value -> System.put_env("HEX_POLICY", value) end end end) end test "an empty HEX_POLICY disables a configured policy for the invocation" do in_tmp("remote_converger_empty_env_policy", fn -> # State.refresh/0 re-reads the config from inside the state agent, # which resolves the path from HEX_HOME rather than :config_home Hex.State.put(:config_home, File.cwd!()) original_home = System.get_env("HEX_HOME") System.put_env("HEX_HOME", File.cwd!()) original = System.get_env("HEX_POLICY") System.delete_env("HEX_POLICY") try do Hex.Config.update(policy: "hexpm:myorg/strict-prod") Hex.State.refresh() assert "hexpm:myorg/strict-prod" == Hex.State.fetch!(:policy) System.put_env("HEX_POLICY", "") Hex.State.refresh() assert Hex.State.fetch!(:policy) == nil assert {:ok, nil} = Hex.Policy.load() after case original do nil -> System.delete_env("HEX_POLICY") value -> System.put_env("HEX_POLICY", value) end case original_home do nil -> System.delete_env("HEX_HOME") value -> System.put_env("HEX_HOME", value) end end end) end test "a malformed HEX_POLICY fails resolution instead of degrading to unenforced" do in_tmp("remote_converger_malformed_policy", fn -> Hex.State.put(:config_home, File.cwd!()) original = System.get_env("HEX_POLICY") System.put_env("HEX_POLICY", "myorgstrict") try do Hex.State.refresh() assert {:invalid, "myorgstrict"} = Hex.State.fetch!(:policy) assert_raise Mix.Error, ~r/Invalid policy configuration: "myorgstrict"/, fn -> Hex.Policy.load() end after case original do nil -> System.delete_env("HEX_POLICY") value -> System.put_env("HEX_POLICY", value) end end end) end test "print_policy_summary/0 prints the active policy block" do Mix.shell(Mix.Shell.IO) on_exit(fn -> Mix.shell(Hex.Shell.Process) end) Hex.State.put(:active_policy, %{ repository: "myorg", name: "strict-prod", visibility: :VISIBILITY_PUBLIC, repositories: [] }) Hex.State.put(:policy_filtered_versions, [ %{repo: "hexpm", package: "phoenix", version: "1.7.18", reasons: [:override_deny]} ]) output = capture_io(fn -> Hex.RemoteConverger.print_policy_summary() end) assert output =~ "Active policy: myorg/strict-prod" assert output =~ "Policy hid 1 candidate version" assert output =~ " phoenix 1.7.18 — override deny" end test "print_policy_summary/1 with list_filtered: false omits the hidden versions" do Mix.shell(Mix.Shell.IO) on_exit(fn -> Mix.shell(Hex.Shell.Process) end) Hex.State.put(:active_policy, %{ repository: "myorg", name: "strict-prod", visibility: :VISIBILITY_PUBLIC, repositories: [] }) Hex.State.put(:policy_filtered_versions, [ %{repo: "hexpm", package: "phoenix", version: "1.7.18", reasons: [:override_deny]} ]) output = capture_io(fn -> Hex.RemoteConverger.print_policy_summary(list_filtered: false) end) assert output =~ "Active policy: myorg/strict-prod" refute output =~ "Policy hid" refute output =~ "phoenix 1.7.18" end test "print_policy_summary/0 prints nothing without an active policy" do Mix.shell(Mix.Shell.IO) on_exit(fn -> Mix.shell(Hex.Shell.Process) end) assert capture_io(fn -> Hex.RemoteConverger.print_policy_summary() end) == "" end end hex-2.5.1/test/hex/remote_converger_test.exs000066400000000000000000000255361522402544500212110ustar00rootroot00000000000000defmodule 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.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?, _} # The unusable session is dropped after the first failed refresh, so the # rest of the resolution falls back to unauthenticated fetches instead of # retrying the doomed refresh for every package. assert Hex.State.get(:oauth_token) == nil 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 @advisory %{ id: "GHSA-rc-0001", summary: "Remote code execution via crafted input", html_url: "https://github.com/advisories/GHSA-rc-0001", severity: :SEVERITY_HIGH, api_url: "https://hex.pm/api/advisories/GHSA-rc-0001" } defmodule AdvisoryDeps.MixProject do def project do [ app: :advisory_deps, version: "0.1.0", deps: [{:rc_advisory_package, "0.1.0"}] ] end end test "deps.get prints security advisory warning for resolved version" do auth = Hexpm.new_user( "rc_advisory_user", "rc_advisory@mail.com", "passpass", "rc_advisory_key" ) Hexpm.new_package("hexpm", "rc_advisory_package", "0.1.0", [], %{}, auth) with_project(AdvisoryDeps.MixProject, fn -> in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) Hex.State.put(:api_key, auth[:key]) Mix.Dep.Lock.write(%{rc_advisory_package: {:hex, :rc_advisory_package, "0.1.0"}}) :ok = Mix.Tasks.Deps.Get.run([]) flush() :sys.replace_state(Hex.Registry.Server, fn %{ets: tid} = state -> :ets.insert( tid, {{:advisories, "hexpm", "rc_advisory_package", "0.1.0"}, [@advisory]} ) state end) File.rm!("mix.lock") Mix.Task.clear() :ok = Mix.Tasks.Deps.Get.run([]) info_messages = collect_info_messages([]) assert Enum.any?(info_messages, fn msg -> msg =~ "rc_advisory_package 0.1.0" and msg =~ "VULNERABLE!" end) assert Enum.any?(info_messages, fn msg -> msg =~ "GHSA-rc-0001" and msg =~ "(HIGH)" and msg =~ "Remote code execution via crafted input" end) end) end) end defp collect_info_messages(acc) do receive do {:mix_shell, :info, [msg]} -> collect_info_messages([msg | acc]) after 0 -> Enum.reverse(acc) end end defmodule IgnoredAdvisoryDeps.MixProject do def project do [ app: :ignored_advisory_deps, version: "0.1.0", deps: [{:rc_ignored_package, "0.1.0"}] ] end end test "deps.get does not warn about ignored advisories" do auth = Hexpm.new_user( "rc_ignored_user", "rc_ignored@mail.com", "passpass", "rc_ignored_key" ) Hexpm.new_package("hexpm", "rc_ignored_package", "0.1.0", [], %{}, auth) advisory = %{ id: "GHSA-rc-0002", aliases: ["CVE-2026-22222"], summary: "Remote code execution via crafted input", html_url: "https://github.com/advisories/GHSA-rc-0002", severity: :SEVERITY_HIGH, api_url: "https://hex.pm/api/advisories/GHSA-rc-0002" } with_project(IgnoredAdvisoryDeps.MixProject, fn -> in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) Hex.State.put(:api_key, auth[:key]) Mix.Dep.Lock.write(%{rc_ignored_package: {:hex, :rc_ignored_package, "0.1.0"}}) :ok = Mix.Tasks.Deps.Get.run([]) flush() :sys.replace_state(Hex.Registry.Server, fn %{ets: tid} = state -> :ets.insert( tid, {{:advisories, "hexpm", "rc_ignored_package", "0.1.0"}, [advisory]} ) state end) File.rm!("mix.lock") Mix.Task.clear() :ok = Mix.Tasks.Deps.Get.run([]) output = shell_output() assert output =~ "VULNERABLE!" assert output =~ "GHSA-rc-0002" Hex.State.put(:ignore_advisories, ["CVE-2026-22222"]) File.rm!("mix.lock") Mix.Task.clear() :ok = Mix.Tasks.Deps.Get.run([]) output = shell_output() refute output =~ "VULNERABLE!" refute output =~ "GHSA-rc-0002" refute output =~ "Found packages with security advisories" end) end) end defmodule IgnoredRetiredDeps.MixProject do def project do [ app: :ignored_retired_deps, version: "0.1.0", deps: [{:rc_ignored_retired, "0.1.0"}] ] end end test "deps.get does not warn about ignored retirements" do auth = Hexpm.new_user( "rc_retired_user", "rc_retired@mail.com", "passpass", "rc_retired_key" ) Hexpm.new_package("hexpm", "rc_ignored_retired", "0.1.0", [], %{}, auth) with_project(IgnoredRetiredDeps.MixProject, fn -> in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) Hex.State.put(:api_key, auth[:key]) Mix.Dep.Lock.write(%{rc_ignored_retired: {:hex, :rc_ignored_retired, "0.1.0"}}) :ok = Mix.Tasks.Deps.Get.run([]) flush() :sys.replace_state(Hex.Registry.Server, fn %{ets: tid} = state -> :ets.insert( tid, {{:retired, "hexpm", "rc_ignored_retired", "0.1.0"}, %{reason: :RETIRED_SECURITY, message: "Retired for testing"}} ) state end) File.rm!("mix.lock") Mix.Task.clear() :ok = Mix.Tasks.Deps.Get.run([]) output = shell_output() assert output =~ "RETIRED!" Hex.State.put(:ignore_retirements, [{"rc_ignored_retired", nil}]) File.rm!("mix.lock") Mix.Task.clear() :ok = Mix.Tasks.Deps.Get.run([]) output = shell_output() refute output =~ "RETIRED!" refute output =~ "Found retired packages" end) 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.5.1/test/hex/repo_test.exs000066400000000000000000000507241522402544500166060ustar00rootroot00000000000000defmodule 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 "warns about deprecation when a stored key authenticates to an organization repository" do Hex.Server.reset() repos = Hex.State.fetch!(:repos) hexpm = repos["hexpm"] org_repo = %{ url: hexpm.url <> "/repos/storedkeyorg", public_key: hexpm.public_key, auth_key: "stored_key_value", oauth_exchange: true, trusted: true } Hex.State.put(:repos, Map.put(repos, "hexpm:storedkeyorg", org_repo)) # The exchange outcome is irrelevant; the warning is emitted before the # stored key is exchanged. try do Hex.Repo.get_package("hexpm:storedkeyorg", "pkg", "") rescue _ -> :ok end output = Case.shell_output() assert output =~ "stored key is deprecated" assert output =~ "mix hex.user auth" end test "warns about deprecation when HEX_REPOS_KEY authenticates to an organization repository" do Hex.Server.reset() repos = Hex.State.fetch!(:repos) hexpm = repos["hexpm"] Hex.State.put(:repos_key, "repos_key_value") org_repo = %{ url: hexpm.url <> "/repos/reposkeyorg", public_key: hexpm.public_key, auth_key: "repos_key_value", oauth_exchange: true, trusted: true } Hex.State.put(:repos, Map.put(repos, "hexpm:reposkeyorg", org_repo)) try do Hex.Repo.get_package("hexpm:reposkeyorg", "pkg", "") rescue _ -> :ok end output = Case.shell_output() assert output =~ "the reposkeyorg repository with HEX_REPOS_KEY is deprecated" end test "does not warn for the base hexpm repository authenticated with HEX_REPOS_KEY" do Hex.Server.reset() auth = HexTest.Hexpm.new_user( "repos_key_user", "repos_key@example.com", "password", "repos_key_key" ) # HEX_REPOS_KEY still authenticates the base hexpm repo (and trusted mirrors) Hex.State.put(:repos_key, auth[:key]) try do Hex.Repo.get_package("hexpm", "postgrex", "") rescue _ -> :ok end output = Case.shell_output() refute output =~ "HEX_REPOS_KEY" refute output =~ "deprecated" 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" }} = 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" }} = 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" }} = 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" } } = Hex.Repo.update_organizations(repos) after {:ok, _} = Supervisor.start_child(Hex.Supervisor, Hex.State) end test "update_organizations/1 normalizes a legacy baked-in org URL" do repos = Hex.State.fetch!(:repos) source_url = repos["hexpm"].url repos = Map.put(repos, "hexpm:acme", %{url: source_url <> "/repos/acme"}) updated = Hex.Repo.update_organizations(repos) # build_url appends /repos/acme, so the stored URL must be the bare source URL assert updated["hexpm:acme"].url == source_url assert updated["hexpm:acme"].repo_organization == "acme" end test "update_organizations/1 keeps a custom org URL without repo_organization" do repos = Hex.State.fetch!(:repos) repos = Map.put(repos, "hexpm:acme", %{url: "http://custom.example/mirror"}) updated = Hex.Repo.update_organizations(repos) assert updated["hexpm:acme"].url == "http://custom.example/mirror" refute Map.has_key?(updated["hexpm:acme"], :repo_organization) end test "clean_organizations/1 round-trips derived and custom org URLs" do repos = Hex.State.fetch!(:repos) source_url = repos["hexpm"].url repos = repos |> Map.put("hexpm:derived", %{url: source_url <> "/repos/derived"}) |> Map.put("hexpm:custom", %{url: "http://custom.example/mirror"}) cleaned = repos |> Hex.Repo.update_organizations() |> Hex.Repo.clean_organizations() # Derived URL is rebuilt on read, so it is not persisted refute Map.has_key?(cleaned["hexpm:derived"], :url) refute Map.has_key?(cleaned["hexpm:derived"], :repo_organization) # Custom URL is persisted verbatim assert cleaned["hexpm:custom"].url == "http://custom.example/mirror" 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.5.1/test/hex/scm_test.exs000066400000000000000000000007741522402544500164230ustar00rootroot00000000000000defmodule 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.5.1/test/hex/server_test.exs000066400000000000000000000020461522402544500171410ustar00rootroot00000000000000defmodule 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 test "should_warn?/1 returns true only once per key" do {:ok, pid} = Hex.Server.start_link(name: nil) assert Hex.Server.should_warn?(:one, pid) refute Hex.Server.should_warn?(:one, pid) assert Hex.Server.should_warn?(:two, pid) refute Hex.Server.should_warn?(:two, pid) end test "should_warn?/1 tracks keys independently" do {:ok, pid} = Hex.Server.start_link(name: nil) assert Hex.Server.should_warn?({:repo, "acme"}, pid) assert Hex.Server.should_warn?({:repo, "other"}, pid) refute Hex.Server.should_warn?({:repo, "acme"}, pid) end end hex-2.5.1/test/hex/solver/000077500000000000000000000000001522402544500153635ustar00rootroot00000000000000hex-2.5.1/test/hex/solver/regression_test.exs000066400000000000000000000066441522402544500213350ustar00rootroot00000000000000defmodule 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.5.1/test/hex/solver_test.exs000066400000000000000000000221731522402544500171500ustar00rootroot00000000000000defmodule 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.5.1/test/hex/tarball_test.exs000066400000000000000000000073021522402544500172540ustar00rootroot00000000000000defmodule Hex.TarballTest do use HexTest.Case test "create rejects unsafe archive paths" do metadata = %{name: "foo", version: "1.0.0"} assert {:error, {:tarball, {:unsafe_path, ~c"../README.md"}}} = :mix_hex_tarball.create(metadata, [{~c"../README.md", "README"}]) assert {:error, {:tarball, {:unsafe_path, ~c"/README.md"}}} = :mix_hex_tarball.create(metadata, [{~c"/README.md", "README"}]) assert {:error, {:tarball, {:unsafe_path, ~c"../README.md"}}} = :mix_hex_tarball.create_docs([{~c"../README.md", "README"}]) assert {:error, {:tarball, {:unsafe_path, ~c"/README.md"}}} = :mix_hex_tarball.create_docs([{~c"/README.md", "README"}]) end test "create rejects symlinks whose disk target escapes the files root" do metadata = %{name: "foo", version: "1.0.0"} in_tmp(fn -> File.mkdir!("../outside") File.write!("../outside/secret.txt", "outside") File.ln_s!("../outside/secret.txt", "mismatch_link") config = :mix_hex_core.default_config() |> Map.put(:tarball_files_root, File.cwd!() |> String.to_charlist()) assert {:error, {:tarball, {:unsafe_path, ~c"nested/mismatch_link"}}} = :mix_hex_tarball.create( metadata, [{~c"nested/mismatch_link", ~c"mismatch_link"}], config ) assert {:error, {:tarball, {:unsafe_path, ~c"nested/mismatch_link"}}} = :mix_hex_tarball.create_docs( [{~c"nested/mismatch_link", ~c"mismatch_link"}], config ) end) end test "create rejects escaping symlinks and accepts internal symlinks" do metadata = %{name: "foo", version: "1.0.0"} in_tmp(fn -> File.write!("README.md", "README") File.mkdir!("dir") File.ln_s!("../README.md", "dir/internal") File.ln_s!("../../README.md", "dir/escaping") config = :mix_hex_core.default_config() |> Map.put(:tarball_files_root, File.cwd!() |> String.to_charlist()) assert {:ok, _} = :mix_hex_tarball.create( metadata, [ {~c"README.md", "README"}, {~c"dir/internal", ~c"dir/internal"} ], config ) assert {:error, {:tarball, {:unsafe_symlink, ~c"dir/escaping", ~c"../../README.md"}}} = :mix_hex_tarball.create( metadata, [{~c"dir/escaping", ~c"dir/escaping"}], config ) assert {:ok, _} = :mix_hex_tarball.create_docs( [ {~c"README.md", "README"}, {~c"dir/internal", ~c"dir/internal"} ], config ) assert {:error, {:tarball, {:unsafe_symlink, ~c"dir/escaping", ~c"../../README.md"}}} = :mix_hex_tarball.create_docs([{~c"dir/escaping", ~c"dir/escaping"}], config) end) end test "create rejects unsupported file types" do if mkfifo = System.find_executable("mkfifo") do metadata = %{name: "foo", version: "1.0.0"} in_tmp(fn -> System.cmd(mkfifo, ["fifo"]) config = :mix_hex_core.default_config() |> Map.put(:tarball_files_root, File.cwd!() |> String.to_charlist()) assert {:error, {:tarball, {:unsupported_file_type, ~c"fifo", :other}}} = :mix_hex_tarball.create(metadata, [{~c"fifo", ~c"fifo"}], config) assert {:error, {:tarball, {:unsupported_file_type, ~c"fifo", :other}}} = :mix_hex_tarball.create_docs([{~c"fifo", ~c"fifo"}], config) end) end end end hex-2.5.1/test/hex/update_checker_test.exs000066400000000000000000000040061522402544500205770ustar00rootroot00000000000000defmodule 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.5.1/test/hex/utils_test.exs000066400000000000000000000103361522402544500167740ustar00rootroot00000000000000defmodule Hex.UtilsTest do use HexTest.Case @advisory %{ id: "GHSA-test-0001", summary: "Remote code execution via crafted input", html_url: "https://github.com/advisories/GHSA-test-0001", severity: :SEVERITY_HIGH, api_url: "https://hex.pm/api/advisories/GHSA-test-0001" } describe "advisory_severity/1" do test "maps all severity atoms to strings" do assert Hex.Utils.advisory_severity(:SEVERITY_NONE) == "NONE" assert Hex.Utils.advisory_severity(:SEVERITY_LOW) == "LOW" assert Hex.Utils.advisory_severity(:SEVERITY_MEDIUM) == "MEDIUM" assert Hex.Utils.advisory_severity(:SEVERITY_HIGH) == "HIGH" assert Hex.Utils.advisory_severity(:SEVERITY_CRITICAL) == "CRITICAL" end test "passes unknown values through" do assert Hex.Utils.advisory_severity("unknown") == "unknown" end end describe "format_advisory_ansi/2" do test "includes id, severity and url" do assert render(@advisory) == "GHSA-test-0001 (HIGH)\nRemote code execution via crafted input\nhttps://github.com/advisories/GHSA-test-0001" end test "without severity omits severity" do advisory = Map.delete(@advisory, :severity) assert render(advisory) == "GHSA-test-0001\nRemote code execution via crafted input\nhttps://github.com/advisories/GHSA-test-0001" end test "without url omits url" do advisory = Map.delete(@advisory, :html_url) assert render(advisory) == "GHSA-test-0001 (HIGH)\nRemote code execution via crafted input" end test "applies line prefix to wrapped lines" do assert render(@advisory, " ") == "GHSA-test-0001 (HIGH)\n Remote code execution via crafted input\n https://github.com/advisories/GHSA-test-0001" end test "emits ANSI color for severity when enabled" do iodata = Hex.Utils.format_advisory_ansi(@advisory) rendered = iodata |> IO.ANSI.format(true) |> IO.iodata_to_binary() assert rendered =~ "\e[31m" assert rendered =~ "(HIGH)" end test "renders aliases on an aka: line when present" do advisory = Map.put(@advisory, :aliases, [ %{id: "CVE-2026-0001", url: :undefined}, %{id: "GHSA-other-0002", url: "https://osv.dev/vulnerability/GHSA-other-0002"} ]) assert render(advisory) == "GHSA-test-0001 (HIGH)\naka: CVE-2026-0001, GHSA-other-0002\nRemote code execution via crafted input\nhttps://github.com/advisories/GHSA-test-0001" end test "omits aka: line when aliases are empty" do advisory = Map.put(@advisory, :aliases, []) assert render(advisory) == "GHSA-test-0001 (HIGH)\nRemote code execution via crafted input\nhttps://github.com/advisories/GHSA-test-0001" end test "aka: line respects line prefix" do advisory = Map.put(@advisory, :aliases, [%{id: "CVE-2026-0001", url: :undefined}]) assert render(advisory, " ") == "GHSA-test-0001 (HIGH)\n aka: CVE-2026-0001\n Remote code execution via crafted input\n https://github.com/advisories/GHSA-test-0001" end end describe "escape_terminal/1" do test "keeps printable text, newlines and tabs" do assert Hex.Utils.escape_terminal("hello world") == "hello world" assert Hex.Utils.escape_terminal("café ☃") == "café ☃" assert Hex.Utils.escape_terminal("line1\nline2\tend") == "line1\nline2\tend" end test "escapes ANSI escapes and other control characters" do assert Hex.Utils.escape_terminal("\e[31mred\e[0m") == "\\e[31mred\\e[0m" assert Hex.Utils.escape_terminal("a\rb\bc\ad") == "a\\rb\\bc\\ad" assert Hex.Utils.escape_terminal(<<0x01, 0x1F>>) == "\\x01\\x1F" assert Hex.Utils.escape_terminal(<<0x7F>>) == "\\x7F" end test "escapes bidirectional overrides and C1 control characters" do assert Hex.Utils.escape_terminal("a\u{202E}b") == "a\\u{202E}b" assert Hex.Utils.escape_terminal("\u{2066}") == "\\u{2066}" assert Hex.Utils.escape_terminal(<<0x85::utf8>>) == "\\u{85}" end end defp render(advisory, line_prefix \\ "") do advisory |> Hex.Utils.format_advisory_ansi(line_prefix) |> IO.ANSI.format(false) |> IO.iodata_to_binary() end end hex-2.5.1/test/mix/000077500000000000000000000000001522402544500140625ustar00rootroot00000000000000hex-2.5.1/test/mix/tasks/000077500000000000000000000000001522402544500152075ustar00rootroot00000000000000hex-2.5.1/test/mix/tasks/hex.audit_test.exs000066400000000000000000000274461522402544500206750ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.AuditTest do use HexTest.IntegrationCase @package :test_package @package_name Atom.to_string(@package) @advisory %{ id: "GHSA-test-0001", summary: "Remote code execution via crafted input", html_url: "https://github.com/advisories/GHSA-test-0001", severity: :SEVERITY_HIGH, api_url: "https://hex.pm/api/advisories/GHSA-test-0001" } 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_retired_section(@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_retired_section(@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 or security advisory packages found"]} end) end test "audit (package with a security advisory)", context do with_test_package("1.1.0", context, fn -> inject_advisory(@package_name, "1.1.0", [@advisory]) assert catch_throw(Mix.Task.run("hex.audit")) == {:exit_code, 1} assert_advisory_section(@package_name, "1.1.0", @advisory) assert_received {:mix_shell, :error, ["Found packages with security advisories"]} refute_received {:mix_shell, :error, ["Found retired packages"]} end) end test "audit (package with both retirement and security advisory)", context do with_test_package("1.2.0", context, fn -> :sys.replace_state(Hex.Registry.Server, fn %{ets: tid} = state -> :ets.insert( tid, {{:retired, "hexpm", @package_name, "1.2.0"}, %{reason: :RETIRED_SECURITY}} ) :ets.insert(tid, {{:advisories, "hexpm", @package_name, "1.2.0"}, [@advisory]}) state end) assert catch_throw(Mix.Task.run("hex.audit")) == {:exit_code, 1} assert_retired_section(@package_name, "1.2.0", "(security)") assert_advisory_section(@package_name, "1.2.0", @advisory) assert_received {:mix_shell, :error, ["Found retired packages"]} assert_received {:mix_shell, :error, ["Found packages with security advisories"]} end) end test "audit (package with multiple advisories)", context do other_advisory = %{ id: "GHSA-test-0002", summary: "Another vulnerability", html_url: "https://github.com/advisories/GHSA-test-0002", severity: :SEVERITY_MEDIUM } with_test_package("1.3.0", context, fn -> inject_advisory(@package_name, "1.3.0", [@advisory, other_advisory]) assert catch_throw(Mix.Task.run("hex.audit")) == {:exit_code, 1} assert_advisory_section(@package_name, "1.3.0", @advisory) assert_advisory_section(@package_name, "1.3.0", other_advisory) end) end test "audit (advisories sharing a CVE alias are deduplicated)", context do eef_advisory = %{ id: "EEF-CVE-2026-32689", summary: "EEF source", html_url: "https://osv.dev/vulnerability/EEF-CVE-2026-32689", severity: :SEVERITY_HIGH, api_url: "https://api.osv.dev/v1/vulns/EEF-CVE-2026-32689", aliases: ["CVE-2026-32689", "GHSA-628h-q48j-jr6q"], references: [] } ghsa_advisory = %{ id: "GHSA-628h-q48j-jr6q", summary: "GHSA source", html_url: "https://osv.dev/vulnerability/GHSA-628h-q48j-jr6q", severity: :SEVERITY_HIGH, api_url: "https://api.osv.dev/v1/vulns/GHSA-628h-q48j-jr6q", aliases: ["CVE-2026-32689"], references: [] } with_test_package("1.4.0", context, fn -> inject_advisory(@package_name, "1.4.0", [eef_advisory, ghsa_advisory]) assert catch_throw(Mix.Task.run("hex.audit")) == {:exit_code, 1} grouped_advisory = %{ id: "EEF-CVE-2026-32689", summary: "EEF source", html_url: "https://osv.dev/vulnerability/EEF-CVE-2026-32689", severity: :SEVERITY_HIGH, api_url: "https://api.osv.dev/v1/vulns/EEF-CVE-2026-32689", aliases: [ %{id: "CVE-2026-32689", url: :undefined}, %{ id: "GHSA-628h-q48j-jr6q", url: "https://osv.dev/vulnerability/GHSA-628h-q48j-jr6q" } ] } assert_advisory_section(@package_name, "1.4.0", grouped_advisory) refute_received {:mix_shell, :info, [" " <> @package_name <> " 1.4.0 - GHSA-628h-q48j-jr6q" <> _]} end) end test "audit (advisory ignored by primary id)", context do with_test_package("2.0.0", context, fn -> inject_advisory(@package_name, "2.0.0", [@advisory]) Hex.State.put(:ignore_advisories, ["GHSA-test-0001"]) Mix.Task.run("hex.audit") output = shell_output() assert output =~ "Ignored advisories:" assert output =~ "GHSA-test-0001" refute output =~ "Found packages with security advisories" end) end test "audit (advisory ignored by CVE alias, case-insensitively)", context do advisory = %{ id: "GHSA-test-0005", aliases: ["CVE-2026-11111"], summary: "Path traversal via crafted filename", html_url: "https://github.com/advisories/GHSA-test-0005", severity: :SEVERITY_LOW, api_url: "https://hex.pm/api/advisories/GHSA-test-0005" } with_test_package("2.1.0", context, fn -> inject_advisory(@package_name, "2.1.0", [advisory]) Hex.State.put(:ignore_advisories, ["cve-2026-11111"]) Mix.Task.run("hex.audit") output = shell_output() assert output =~ "Ignored advisories:" assert output =~ "GHSA-test-0005" refute output =~ "Found packages with security advisories" end) end test "audit (retirement ignored by package name)", context do with_test_package("2.2.0", context, fn -> retire_test_package("2.2.0", "security") Hex.State.put(:ignore_retirements, [{@package_name, nil}]) Mix.Task.run("hex.audit") output = shell_output() assert output =~ "Ignored retired:" assert output =~ "#{@package_name} 2.2.0 - (security)" refute output =~ "Found retired packages" end) end test "audit (retirement ignored by pinned version)", context do with_test_package("2.3.0", context, fn -> retire_test_package("2.3.0", "deprecated", "Use something else") Hex.State.put(:ignore_retirements, [{@package_name, "2.3.0"}]) Mix.Task.run("hex.audit") output = shell_output() assert output =~ "Ignored retired:" refute output =~ "Found retired packages" end) end test "audit (pinned ignore for a different version still fails)", context do with_test_package("2.4.0", context, fn -> retire_test_package("2.4.0", "security") Hex.State.put(:ignore_retirements, [{@package_name, "9.9.9"}]) assert catch_throw(Mix.Task.run("hex.audit")) == {:exit_code, 1} output = shell_output() assert output =~ "Retired:" assert output =~ "Found retired packages" assert output =~ "ignore_retirements entry #{@package_name} 9.9.9 does not match" end) end test "audit (mixed active and ignored advisories)", context do other_advisory = %{ id: "GHSA-test-0006", summary: "Denial of service via oversized payload", html_url: "https://github.com/advisories/GHSA-test-0006", severity: :SEVERITY_CRITICAL, api_url: "https://hex.pm/api/advisories/GHSA-test-0006" } with_test_package("2.5.0", context, fn -> inject_advisory(@package_name, "2.5.0", [@advisory, other_advisory]) Hex.State.put(:ignore_advisories, ["GHSA-test-0001"]) assert catch_throw(Mix.Task.run("hex.audit")) == {:exit_code, 1} output = shell_output() assert output =~ "Advisories:" assert output =~ "GHSA-test-0006" assert output =~ "Ignored advisories:" assert output =~ "GHSA-test-0001" assert output =~ "Found packages with security advisories" end) end test "audit (stale ignore entries warn without failing)", context do with_test_package("2.6.0", context, fn -> Hex.State.put(:ignore_advisories, ["CVE-2020-00000"]) Hex.State.put(:ignore_retirements, [{"nonexistent_package", nil}]) Mix.Task.run("hex.audit") output = shell_output() assert output =~ "No retired or security advisory packages found" assert output =~ ~s(ignore_advisories entry "CVE-2020-00000" does not match any advisory) assert output =~ "ignore_retirements entry nonexistent_package does not match any retired" end) end test "audit (ignoring one advisory of an aliased group suppresses the group)", context do eef_advisory = %{ id: "EEF-test-0001", aliases: ["CVE-2026-33333"], summary: "Remote code execution via crafted input", html_url: "https://cna.erlef.org/advisories/EEF-test-0001", severity: :SEVERITY_HIGH, api_url: "https://hex.pm/api/advisories/EEF-test-0001" } ghsa_advisory = %{ id: "GHSA-test-0008", aliases: ["CVE-2026-33333"], summary: "Remote code execution via crafted input", html_url: "https://github.com/advisories/GHSA-test-0008", severity: :SEVERITY_HIGH, api_url: "https://hex.pm/api/advisories/GHSA-test-0008" } with_test_package("2.7.0", context, fn -> inject_advisory(@package_name, "2.7.0", [eef_advisory, ghsa_advisory]) Hex.State.put(:ignore_advisories, ["EEF-test-0001"]) Mix.Task.run("hex.audit") output = shell_output() assert output =~ "Ignored advisories:" refute output =~ "Found packages with security advisories" 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 inject_advisory(package, version, advisories) do :sys.replace_state(Hex.Registry.Server, fn %{ets: tid} = state -> :ets.insert(tid, {{:advisories, "hexpm", package, version}, advisories}) state end) end defp assert_retired_section(package, version, message) do header = render([:bright, "Retired:", :reset]) row = render([" #{package} #{version} - ", :yellow, message, :reset]) assert_received {:mix_shell, :info, [^header]} assert_received {:mix_shell, :info, [^row]} end defp assert_advisory_section(package, version, advisory) do block = render([ " #{package} #{version} - " | Hex.Utils.format_advisory_ansi(advisory, " ") ]) assert_received {:mix_shell, :info, [^block]} end defp render(message) do message |> Hex.Shell.format() |> IO.iodata_to_binary() end end hex-2.5.1/test/mix/tasks/hex.build_test.exs000066400000000000000000000415221522402544500206550ustar00rootroot00000000000000defmodule 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 "vendored licenses accept custom LicenseRef identifiers" do assert :mix_hex_licenses.valid("LicenseRef-Journey") assert :mix_hex_licenses.valid("LicenseRef-acme.1-2") refute :mix_hex_licenses.valid("LicenseRef-") refute :mix_hex_licenses.valid("LicenseRef-Journey License") refute :mix_hex_licenses.valid("LicenseRef-Journey_License") 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 with custom LicenseRef license" do Process.put(:hex_test_app_name, :release_license_ref) Mix.Project.push(ReleaseLicenseRef.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) File.write!("myfile.txt", "hello") File.write!("LICENSE", "Journey License") Mix.Tasks.Hex.Build.run([]) assert package_created?("release_license_ref-0.0.1") end) after purge([ReleaseLicenseRef.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.write!("dir/.DS_Store", "junk") 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" refute File.exists?("unzip/dir/.DS_Store") 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 "errors when package file escapes project root" do Process.put(:hex_test_app_name, :build_with_escaping_files) Mix.Project.push(ReleaseEscapingFiles.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) File.write!("../../README.md", "outside") outside_readme = Path.expand("../../README.md") error_msg = "Creating tarball failed: unsafe path in tarball: #{outside_readme}" assert_raise Mix.Error, error_msg, fn -> Mix.Tasks.Hex.Build.run([]) end end) after purge([ReleaseEscapingFiles.MixProject]) end test "errors when package symlink escapes project root" do Process.put(:hex_test_app_name, :build_with_escaping_symlink) Mix.Project.push(ReleaseEscapingSymlink.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) File.write!("../../README.md", "outside") File.ln_s!("../../README.md", "README.md") error_msg = "Creating tarball failed: unsafe symlink in tarball: README.md -> ../../README.md" assert_raise Mix.Error, error_msg, fn -> Mix.Tasks.Hex.Build.run([]) end end) after purge([ReleaseEscapingSymlink.MixProject]) end test "errors when package file resolves through escaping symlink directory" do Process.put(:hex_test_app_name, :build_with_escaping_symlink_directory) Mix.Project.push(ReleaseEscapingSymlinkDirectory.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) File.mkdir!("../outside") File.write!("../outside/secret.txt", "outside") File.ln_s!("../outside", "link") error_msg = "Creating tarball failed: unsafe path in tarball: link/secret.txt" assert_raise Mix.Error, error_msg, fn -> Mix.Tasks.Hex.Build.run([]) end end) after purge([ReleaseEscapingSymlinkDirectory.MixProject]) end test "preserves package symlink resolving inside project root" do Process.put(:hex_test_app_name, :build_with_internal_symlink) Mix.Project.push(ReleaseInternalSymlink.MixProject) in_tmp(fn -> Hex.State.put(:cache_home, tmp_path()) File.write!("README.md", "inside") File.mkdir!("dir") File.ln_s!("../README.md", "dir/link") Mix.Tasks.Hex.Build.run([]) extract("build_with_internal_symlink-0.0.1.tar", "unzip") assert File.lstat!("unzip/dir/link").type == :symlink assert File.read_link!("unzip/dir/link") == "../README.md" end) after purge([ReleaseInternalSymlink.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.5.1/test/mix/tasks/hex.config_test.exs000066400000000000000000000107121522402544500210200ustar00rootroot00000000000000defmodule 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)"]} assert_received {:mix_shell, :info, ["cooldown: \"0d\" (default)"]} end) after purge([ReleaseCustomApiUrl.MixProject]) end test "cooldown config key" do in_tmp(fn -> System.put_env("HEX_HOME", File.cwd!()) Hex.State.refresh() Mix.Tasks.Hex.Config.run(["cooldown"]) assert_received {:mix_shell, :info, ["\"0d\""]} System.put_env("HEX_COOLDOWN", "7d") Hex.State.refresh() Mix.Tasks.Hex.Config.run(["cooldown"]) assert_received {:mix_shell, :info, ["\"7d\""]} System.delete_env("HEX_COOLDOWN") Hex.State.refresh() end) end test "ignore_advisories and ignore_retirements config keys" do in_tmp(fn -> System.put_env("HEX_HOME", File.cwd!()) Hex.State.refresh() Mix.Tasks.Hex.Config.run(["ignore_advisories"]) assert_received {:mix_shell, :info, ["[]"]} Mix.Tasks.Hex.Config.run(["ignore_retirements"]) assert_received {:mix_shell, :info, ["[]"]} System.put_env("HEX_IGNORE_ADVISORIES", "CVE-2026-32686, GHSA-xxxx-yyyy-zzzz") System.put_env("HEX_IGNORE_RETIREMENTS", "decimal,phoenix@1.0.0") Hex.State.refresh() assert Hex.State.fetch!(:ignore_advisories) == ["CVE-2026-32686", "GHSA-xxxx-yyyy-zzzz"] assert Hex.State.fetch!(:ignore_retirements) == [{"decimal", nil}, {"phoenix", "1.0.0"}] System.delete_env("HEX_IGNORE_ADVISORIES") System.delete_env("HEX_IGNORE_RETIREMENTS") Hex.State.refresh() end) 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.5.1/test/mix/tasks/hex.docs_test.exs000066400000000000000000000354421522402544500205120ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.DocsTest do use HexTest.IntegrationCase @org_dir "hexpm" 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 setup tags do data_home = tmp_path() Hex.State.put(:data_home, data_home) if tags[:mirror] do bypass_mirror() end {:ok, docs_home: Path.join(data_home, "docs")} end describe "fetch" do @tag mirror: true test "fetch and open all packages in this applications deps", %{docs_home: docs_home} do Mix.Project.push(ExampleDeps.MixProject) 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}/#{@org_dir}/docs_package/1.1.2" assert_received {:mix_shell, :info, [^fetched_msg]} assert File.exists?("#{docs_home}/#{@org_dir}/docs_package/1.1.2") end) end @tag mirror: true test "fetch the version of a dependency from this apps lock file", %{docs_home: docs_home} do Mix.Project.push(ExampleDeps.MixProject) 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}/#{@org_dir}/docs_package/1.1.2" assert_received {:mix_shell, :info, [^fetched_msg]} assert File.exists?("#{docs_home}/#{@org_dir}/docs_package/1.1.2") end) end @tag mirror: true test "fetch the latest version of a package", %{docs_home: docs_home} do package = "docs_package" latest_version = "1.1.2" 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 @tag mirror: true test "when the only release is a pre-release, return that version", %{docs_home: docs_home} do package = "pre_only_package" latest_version = "0.0.1-rc1" 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 @tag mirror: true test "fetch the latest version of a package using the latest flag", %{docs_home: docs_home} do Mix.Project.push(ExampleDeps.MixProject) 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}/#{@org_dir}/docs_package/1.1.2" assert_received {:mix_shell, :info, [^fetched_msg]} assert File.exists?("#{docs_home}/#{@org_dir}/docs_package/1.1.2") end) end @tag mirror: true test "fetch a specific version of a package", %{docs_home: docs_home} do package = "docs_package" version = "1.1.2" 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 @tag mirror: true test "fetch a specific version of a package that exists in the fallback location", %{ docs_home: docs_home } do package = "docs_package" version = "1.1.2" 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 end describe "arguments" do 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 end describe "offline" do 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 @tag mirror: true test "open latest version offline using offline task", %{docs_home: docs_home} do package = "docs_package" latest_version = "1.1.2" 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 @tag mirror: true test "open latest version in epub offline using offline task", %{docs_home: docs_home} do package = "docs_package" latest_version = "1.1.2" 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 @tag mirror: true test "open a specific html page offline using the page option", %{docs_home: docs_home} do package = "docs_package" latest_version = "1.1.2" in_tmp("docs", fn -> Mix.Tasks.Hex.Docs.run(["offline", package, "--page", "Some.Module"]) fetched_msg = "Docs fetched: #{docs_home}/#{@org_dir}/#{package}/#{latest_version}" browser_open_msg = "#{docs_home}/#{@org_dir}/#{package}/#{latest_version}/Some.Module.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 @tag mirror: true test "open llms markdown offline using the md format", %{docs_home: docs_home} do package = "docs_package" latest_version = "1.1.2" in_tmp("docs", fn -> Mix.Tasks.Hex.Docs.run(["offline", package, "--format", "md"]) fetched_msg = "Docs fetched: #{docs_home}/#{@org_dir}/#{package}/#{latest_version}" browser_open_msg = "#{docs_home}/#{@org_dir}/#{package}/#{latest_version}/llms.txt" 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 @tag mirror: true test "open a specific markdown page offline", %{docs_home: docs_home} do package = "docs_package" latest_version = "1.1.2" in_tmp("docs", fn -> Mix.Tasks.Hex.Docs.run(["offline", package, "--format", "md", "--page", "guide"]) fetched_msg = "Docs fetched: #{docs_home}/#{@org_dir}/#{package}/#{latest_version}" browser_open_msg = "#{docs_home}/#{@org_dir}/#{package}/#{latest_version}/guide.md" 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 @tag mirror: true test "offline package with version succeeds when package is available remotely", %{ docs_home: docs_home } do package = "docs_package" version = "1.1.2" 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 @tag mirror: true test "offline package with version uses fallback location", %{docs_home: docs_home} do package = "docs_package" version = "1.1.2" 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 @tag mirror: true test "open the version of a package this app uses offline", %{docs_home: docs_home} do Mix.Project.push(ExampleDeps.MixProject) package = "docs_package" version = "1.1.2" 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}/#{@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 assert File.exists?("#{docs_home}/#{@org_dir}/#{package}/#{version}") end) end end describe "online" do 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://ecto.hexdocs.pm" end test "open the version of a package this app uses online" do Mix.Project.push(ExampleDeps.MixProject) 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://docs-package.hexdocs.pm/1.1.1" end) end test "open latest version of a package this app uses online" do Mix.Project.push(ExampleDeps.MixProject) 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://docs-package.hexdocs.pm" end) end test "open a specific page online using the page option" do Mix.Tasks.Hex.Docs.run(["online", "ecto", "--page", "Ecto.Repo"]) assert_received {:hex_system_cmd, _cmd, browser_open_cmd} assert Enum.fetch!(browser_open_cmd, -1) == "https://ecto.hexdocs.pm/Ecto.Repo.html" end end describe "--module deprecation" do test "online --module is treated as --page with a deprecation warning" do Mix.Tasks.Hex.Docs.run(["online", "ecto", "--module", "Ecto.Repo"]) assert_received {:mix_shell, :error, ["--module is deprecated, use --page instead"]} assert_received {:hex_system_cmd, _cmd, browser_open_cmd} assert Enum.fetch!(browser_open_cmd, -1) == "https://ecto.hexdocs.pm/Ecto.Repo.html" end @tag mirror: true test "offline --module is treated as --page with a deprecation warning", %{ docs_home: docs_home } do package = "docs_package" latest_version = "1.1.2" in_tmp("docs", fn -> Mix.Tasks.Hex.Docs.run(["offline", package, "--module", "Some.Module"]) assert_received {:mix_shell, :error, ["--module is deprecated, use --page instead"]} browser_open_msg = "#{docs_home}/#{@org_dir}/#{package}/#{latest_version}/Some.Module.html" assert_received {:hex_system_cmd, _cmd, browser_open_cmd} assert Enum.fetch!(browser_open_cmd, -1) == browser_open_msg end) end end end hex-2.5.1/test/mix/tasks/hex.info_test.exs000066400000000000000000000137511522402544500205140ustar00rootroot00000000000000defmodule 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.5.1/test/mix/tasks/hex.organization_test.exs000066400000000000000000000200041522402544500222520ustar00rootroot00000000000000defmodule 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" 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 without --key warns about deprecation" do in_tmp(fn -> set_home_cwd() auth = Hexpm.new_user("orgauthdeprecated", "orgauthdeprecated@mail.com", "password", "key") Hex.State.put(:api_key, auth[:key]) Hexpm.new_repo("myorgauthdeprecated", auth) send(self(), {:mix_shell_input, :yes?, true}) send(self(), {:mix_shell_input, :prompt, "password"}) Mix.Tasks.Hex.Organization.run(["auth", "myorgauthdeprecated"]) output = Case.shell_output() assert output =~ "deprecated" assert output =~ "mix hex.user auth" # Still authorizes (warning only, no behavior change) assert is_binary(Hex.Repo.get_repo("hexpm:myorgauthdeprecated").auth_key) 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" 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" 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" 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.5.1/test/mix/tasks/hex.outdated_test.exs000066400000000000000000001003401522402544500213610ustar00rootroot00000000000000defmodule 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 SingleDep.MixProject do def project do [ app: :outdated_app, version: "0.0.1", deps: [{:foo, ">= 0.0.0"}] ] end end defmodule UnsafeLockedDeps.MixProject do def project do [ app: :outdated_app, version: "0.0.1", deps: [ {:tired, ">= 0.0.0"}, {:foo, ">= 0.0.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 "outdated app --within-requirements still exits when latest does not match" 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", ["postgrex", "--within-requirements"])) == {:exit_code, 1} end) end test "outdated app --only does not filter out the requested dependency" 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() Mix.Task.run("hex.outdated", ["beta", "--only", "test"]) msg = ["Current version ", :bright, "1.0.0", :reset, " of dependency is up to date!"] |> IO.ANSI.format_fragment() |> List.to_string() assert_received {:mix_shell, :info, [^msg]} end) end @tag :requires_json test "outdated app --json" 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", "--json"])) == {:exit_code, 1} expected = [ %{ package: "ex_doc", only: "", lock_version: "0.0.1", latest_version: "0.1.0", requirements: [ %{source: "mix.exs", requirement: ">= 0.0.0", up_to_date: true}, %{source: "ecto", requirement: "~> 0.0.1", up_to_date: false}, %{source: "postgrex", requirement: "0.0.1", up_to_date: false} ], outdated: true, cooldown: nil } ] |> :json.encode() |> IO.iodata_to_binary() assert_received {:mix_shell, :info, [^expected]} 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 describe "cooldown" do defp inject_published_at(repo, package, version, published_at) do :sys.replace_state(Hex.Registry.Server, fn state -> :ets.insert(state.ets, {{:published_at, repo, package, version}, published_at}) state end) end defp clear_all_published_at do :sys.replace_state(Hex.Registry.Server, fn state -> :ets.match_delete(state.ets, {{:published_at, :_, :_, :_}, :_}) state end) end defp collect_shell_lines do for {:mix_shell, :info, [line]} <- flush(), is_binary(line), do: line end test "annotates row when latest version is within the cooldown window" 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() clear_all_published_at() inject_published_at("hexpm", "foo", "0.1.1", System.system_time(:second) - 86_400) Hex.State.put(:cooldown, "7d") assert catch_throw(Mix.Task.run("hex.outdated", ["--all"])) == {:exit_code, 1} lines = collect_shell_lines() assert Enum.any?(lines, &(&1 =~ "foo" and &1 =~ "Update possible" and &1 =~ "(cooldown)")) assert Enum.any?(lines, &(&1 == "Versions in cooldown:")) assert Enum.any?(lines, &(&1 =~ "foo 0.1.1" and &1 =~ "eligible")) end) end test "does not annotate when latest version is older than the cooldown window" 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() clear_all_published_at() inject_published_at("hexpm", "foo", "0.1.1", System.system_time(:second) - 30 * 86_400) Hex.State.put(:cooldown, "7d") assert catch_throw(Mix.Task.run("hex.outdated", ["--all"])) == {:exit_code, 1} lines = collect_shell_lines() refute Enum.any?(lines, &(&1 =~ "(cooldown)")) refute Enum.any?(lines, &(&1 == "Versions in cooldown:")) end) end test "does not annotate when cooldown is disabled" 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() clear_all_published_at() inject_published_at("hexpm", "foo", "0.1.1", System.system_time(:second) - 86_400) Hex.State.put(:cooldown, "0d") assert catch_throw(Mix.Task.run("hex.outdated", ["--all"])) == {:exit_code, 1} lines = collect_shell_lines() refute Enum.any?(lines, &(&1 =~ "(cooldown)")) refute Enum.any?(lines, &(&1 == "Versions in cooldown:")) end) end test "treats missing published_at as eligible" 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() clear_all_published_at() Hex.State.put(:cooldown, "1mo") assert catch_throw(Mix.Task.run("hex.outdated", ["--all"])) == {:exit_code, 1} lines = collect_shell_lines() refute Enum.any?(lines, &(&1 =~ "(cooldown)")) refute Enum.any?(lines, &(&1 == "Versions in cooldown:")) end) end test "does not annotate up-to-date dependencies even when fresh" 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() clear_all_published_at() inject_published_at("hexpm", "bar", "0.1.0", System.system_time(:second) - 86_400) Hex.State.put(:cooldown, "7d") assert catch_throw(Mix.Task.run("hex.outdated", ["--all"])) == {:exit_code, 1} lines = collect_shell_lines() refute Enum.any?(lines, &(&1 =~ "bar" and &1 =~ "(cooldown)")) end) end test "single-package mode annotates output and does not exit with error when latest is in cooldown" 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() clear_all_published_at() inject_published_at("hexpm", "foo", "0.1.1", System.system_time(:second) - 86_400) Hex.State.put(:cooldown, "7d") assert Mix.Task.run("hex.outdated", ["foo"]) == nil lines = collect_shell_lines() assert Enum.any?(lines, &(&1 =~ "Version 0.1.1 is in cooldown" and &1 =~ "eligible")) end) end test "does not exit with error when every outdated dep is cooldown-held" do Mix.Project.push(SingleDep.MixProject) in_tmp(fn -> set_home_tmp() Mix.Dep.Lock.write(%{foo: {:hex, :foo, "0.1.0"}}) Mix.Task.run("deps.get") flush() clear_all_published_at() inject_published_at("hexpm", "foo", "0.1.1", System.system_time(:second) - 86_400) Hex.State.put(:cooldown, "7d") assert Mix.Task.run("hex.outdated") == nil end) end test "still exits with error when at least one outdated dep is not cooldown-held" 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() clear_all_published_at() inject_published_at("hexpm", "foo", "0.1.1", System.system_time(:second) - 86_400) Hex.State.put(:cooldown, "7d") assert catch_throw(Mix.Task.run("hex.outdated", ["--all"])) == {:exit_code, 1} end) end test "--sort status orders cooldown-annotated rows by their base status" 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() clear_all_published_at() inject_published_at("hexpm", "foo", "0.1.1", System.system_time(:second) - 86_400) Hex.State.put(:cooldown, "7d") assert catch_throw(Mix.Task.run("hex.outdated", ["--all", "--sort", "status"])) == {:exit_code, 1} assert extract_statuses(flush()) == [ "Up-to-date", "Update not possible", "Update possible" ] end) end test "does not annotate when locked version is retired (unsafe-lock bypass)" do Mix.Project.push(UnsafeLockedDeps.MixProject) in_tmp(fn -> set_home_tmp() # tired 0.1.0 is retired in setup_hexpm.exs; foo 0.1.0 is not. Mix.Dep.Lock.write(%{ tired: {:hex, :tired, "0.1.0"}, foo: {:hex, :foo, "0.1.0"} }) Mix.Task.run("deps.get") flush() clear_all_published_at() inject_published_at("hexpm", "tired", "0.2.0", System.system_time(:second) - 86_400) inject_published_at("hexpm", "foo", "0.1.1", System.system_time(:second) - 86_400) Hex.State.put(:cooldown, "7d") assert catch_throw(Mix.Task.run("hex.outdated", ["--all"])) == {:exit_code, 1} lines = collect_shell_lines() refute Enum.any?(lines, &(&1 =~ "tired" and &1 =~ "(cooldown)")) assert Enum.any?(lines, &(&1 =~ "foo" and &1 =~ "(cooldown)")) end) end test "does not annotate when the update is not possible due to requirements" do Mix.Project.push(OutdatedDeps.MixProject) in_tmp(fn -> set_home_tmp() # ex_doc's locked version is 0.0.1; OutdatedDeps requires ~> 0.0.1, # which doesn't match the latest 0.1.0. Even with cooldown set on # the latest, the user can't take the upgrade — don't annotate. Mix.Dep.Lock.write(%{bar: {:hex, :bar, "0.1.0"}, foo: {:hex, :foo, "0.1.0"}}) Mix.Task.run("deps.get") flush() clear_all_published_at() inject_published_at("hexpm", "ex_doc", "0.1.0", System.system_time(:second) - 86_400) Hex.State.put(:cooldown, "7d") assert catch_throw(Mix.Task.run("hex.outdated", ["--all"])) == {:exit_code, 1} lines = collect_shell_lines() assert Enum.any?(lines, &(&1 =~ "ex_doc" and &1 =~ "Update not possible")) refute Enum.any?(lines, &(&1 =~ "ex_doc" and &1 =~ "(cooldown)")) refute Enum.any?(lines, &(&1 =~ "ex_doc 0.1.0" and &1 =~ "eligible")) end) end @tag :requires_json test "JSON output exposes published_at and eligible_on when latest is in cooldown" 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() clear_all_published_at() inject_published_at("hexpm", "foo", "0.1.1", System.system_time(:second) - 86_400) Hex.State.put(:cooldown, "7d") Mix.Task.run("hex.outdated", ["foo", "--json"]) [json] = collect_shell_lines() [decoded] = :json.decode(json) assert %{"published_at" => _, "eligible_on" => _} = decoded["cooldown"] end) end end end hex-2.5.1/test/mix/tasks/hex.owner_test.exs000066400000000000000000000141351522402544500207100ustar00rootroot00000000000000defmodule 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.5.1/test/mix/tasks/hex.package_test.exs000066400000000000000000000177521522402544500211610ustar00rootroot00000000000000defmodule 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 describe "search" do test "no results" do Mix.Tasks.Hex.Package.run(["search", "bloopdoopbloop"]) assert_received {:mix_shell, :info, ["No packages found"]} end test "public packages" do Mix.Tasks.Hex.Package.run(["search", "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.Package.run(["search", "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.Package.run(["search", "doc", "--organization", "searchrepo2"]) refute_received {:mix_shell, :info, ["ex_doc" <> _]} refute_received {:mix_shell, :info, ["only_doc" <> _]} end) end end end hex-2.5.1/test/mix/tasks/hex.policy_test.exs000066400000000000000000000072371522402544500210620ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.PolicyTest do use HexTest.Case, async: false import ExUnit.CaptureIO setup do Hex.State.put(:active_policy, nil) Mix.shell(Mix.Shell.IO) on_exit(fn -> Mix.shell(Hex.Shell.Process) end) :ok end describe "show" do test "prints 'no active policy' message when empty" do out = capture_io(fn -> Mix.Tasks.Hex.Policy.run(["show"]) end) assert out =~ "No active policy" end test "prints the active policy with key fields" do Hex.State.put(:active_policy, %{ repository: "myorg", name: "strict-prod", visibility: :VISIBILITY_PUBLIC, repositories: [ %{ repository: "hexpm", restriction: %{ cooldown: "14d", advisory_min_severity: :SEVERITY_HIGH, retirement_reasons: [:RETIRED_INVALID, :RETIRED_SECURITY] }, overrides: [] } ] }) out = capture_io(fn -> Mix.Tasks.Hex.Policy.run(["show"]) end) assert out =~ "Active policy" assert out =~ "myorg/strict-prod" assert out =~ "public" assert out =~ "14d" assert out =~ "HIGH" end test "lists each package override with its action" do Hex.State.put(:active_policy, %{ repository: "myorg", name: "strict-prod", visibility: :VISIBILITY_PUBLIC, repositories: [ %{ repository: "hexpm", restriction: %{}, overrides: [ %{action: :OVERRIDE_ACTION_ALLOW, ref: %{package: "plug", requirement: ">= 1.0.0"}}, %{action: :OVERRIDE_ACTION_DENY, ref: %{package: "evil_dep"}} ] } ] }) out = capture_io(fn -> Mix.Tasks.Hex.Policy.run(["show"]) end) assert out =~ "plug" assert out =~ ">= 1.0.0" assert out =~ "ALLOW" assert out =~ "evil_dep" assert out =~ "DENY" end test "renders a (none) placeholder when a repository has no overrides" do Hex.State.put(:active_policy, %{ repository: "myorg", name: "strict-prod", visibility: :VISIBILITY_PUBLIC, repositories: [ %{repository: "hexpm", restriction: %{}, overrides: []} ] }) out = capture_io(fn -> Mix.Tasks.Hex.Policy.run(["show"]) end) assert out =~ "Overrides:" assert out =~ "(none)" end end describe "why" do test "a bare invocation (no subcommand) raises the usage message" do assert_raise Mix.Error, ~r/Invalid arguments, expected one of:/, fn -> Mix.Tasks.Hex.Policy.run([]) end end test "complains when package name is missing" do assert_raise Mix.Error, ~r/Invalid arguments, expected one of:/, fn -> Mix.Tasks.Hex.Policy.run(["why"]) end end test "raises a short usage message on unknown arguments" do assert_raise Mix.Error, ~r/Invalid arguments, expected one of:/, fn -> Mix.Tasks.Hex.Policy.run(["bogus"]) end end test "rejects empty halves like myorg/ or /pkg" do Hex.State.put(:active_policy, %{ repository: "myorg", name: "strict-prod", visibility: :VISIBILITY_PUBLIC }) assert_raise Mix.Error, ~r/Invalid package argument/, fn -> Mix.Tasks.Hex.Policy.run(["why", "myorg/"]) end assert_raise Mix.Error, ~r/Invalid package argument/, fn -> Mix.Tasks.Hex.Policy.run(["why", "/foo"]) end end # Note: a full `why ` test would require a registry fixture with # advisory metadata. Skip for now; the rendering surface is covered # indirectly by Hex.Policy.Filter and Hex.Policy.Diagnostics tests. end end hex-2.5.1/test/mix/tasks/hex.publish_test.exs000066400000000000000000000530671522402544500212330ustar00rootroot00000000000000defmodule 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://invalid-filename.hexdocs.pm/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://invalid-dirname.hexdocs.pm/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://ex-doc.hexdocs.pm/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.5.1/test/mix/tasks/hex.registry_test.exs000066400000000000000000000174261522402544500214340ustar00rootroot00000000000000defmodule 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"], with_advisories: []}], 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"], with_advisories: []} ], 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"], with_advisories: [] } ], 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.5.1/test/mix/tasks/hex.repo_test.exs000066400000000000000000000253541522402544500205300ustar00rootroot00000000000000defmodule 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.5.1/test/mix/tasks/hex.retire_test.exs000066400000000000000000000026161522402544500210510ustar00rootroot00000000000000defmodule 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.5.1/test/mix/tasks/hex.search_test.exs000066400000000000000000000172541522402544500210300ustar00rootroot00000000000000defmodule Mix.Tasks.Hex.SearchTest do use HexTest.IntegrationCase import Mox setup :verify_on_exit! 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 test "--packages opens explicit package list" do Mix.Tasks.Hex.Search.run(["--packages", "foo,bar"]) assert_received {:hex_system_cmd, _, ["https://hexdocs.pm/?packages=foo%2Cbar&q="]} end test "--packages with --print-url prints explicit package list" do Mix.Tasks.Hex.Search.run(["--packages", "foo,bar", "--print-url"]) assert_received {:mix_shell, :info, ["https://hexdocs.pm/?packages=foo%2Cbar&q="]} 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 "docs query" do @tag :requires_json test "prints formatted results for project dependencies" do Mix.Project.push(SearchDeps.MixProject) in_tmp(fn -> write_search_deps() mock_search_http(fn url -> assert URI.parse(url).host == "search.hexdocs.pm" query = URI.decode_query(URI.parse(url).query) assert query["q"] == "ecto changeset" assert query["query_by"] == "doc,title" refute Map.has_key?(query, "per_page") vsn = System.version() assert query["filter_by"] == "package:=[bar-0.1.0,eex-#{vsn},elixir-#{vsn},ex_unit-#{vsn},foo-0.1.0,iex-#{vsn},logger-#{vsn},mix-#{vsn}]" {:ok, {200, %{}, ~s({"found":2,"hits":[{"document":{"doc":"Cast changesets.","package":"ecto-3.13.4","ref":"Ecto.Changeset.html#cast/4","title":"cast/4"}},{"document":{"doc":"Tree docs.","package":"mix-1.20.0-rc.5","ref":"Mix.Tasks.Deps.Tree.html#module-examples","title":"Examples - mix deps.tree"}}]})}} end) Mix.Tasks.Hex.Search.run(["ecto changeset"]) assert_received {:mix_shell, :info, [ "# cast/4 (1/2)\nhttps://ecto.hexdocs.pm/3.13.4/Ecto.Changeset.html#cast/4\n\nCast changesets.\n\n" ]} assert_received {:mix_shell, :info, [ "# Examples - mix deps.tree (2/2)\nhttps://mix.hexdocs.pm/1.20.0-rc.5/Mix.Tasks.Deps.Tree.html#module-examples\n\nTree docs.\n\n" ]} end) end @tag :requires_json test "uses explicit package filters" do in_tmp(fn -> set_home_tmp() mock_search_http(fn url -> query = URI.decode_query(URI.parse(url).query) assert query["q"] == "query" assert query["filter_by"] == "package:=[foo-0.1.1,bar-0.1.0]" {:ok, {200, %{}, ~s({"found":0,"hits":[]})}} end) Mix.Tasks.Hex.Search.run(["query", "--packages", "foo,bar"]) assert_received {:mix_shell, :info, ["No results found"]} end) end @tag :requires_json test "strips html comments from results" do in_tmp(fn -> set_home_tmp() mock_search_http(fn _url -> {:ok, {200, %{}, ~s({"found":1,"hits":[{"document":{"doc":"AlphaBetaGamma","package":"ecto-3.13.4","ref":"Ecto.Changeset.html#cast/4","title":"cast/4"}}]})}} end) Mix.Tasks.Hex.Search.run(["query", "--packages", "foo"]) assert_received {:mix_shell, :info, [message]} assert message =~ "\n\nAlphaBetaGamma\n\n" refute message =~ "