diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/rebar_pkg_resource.erl | 121 |
1 files changed, 118 insertions, 3 deletions
diff --git a/src/rebar_pkg_resource.erl b/src/rebar_pkg_resource.erl index f77a81a..8806c8b 100644 --- a/src/rebar_pkg_resource.erl +++ b/src/rebar_pkg_resource.erl @@ -19,9 +19,26 @@ -include("rebar.hrl"). -include_lib("public_key/include/OTP-PUB-KEY.hrl"). +-type cached_result() :: {'bad_checksum',string()} | + {'bad_registry_checksum',string()} | + {'failed_extract',string()} | + {'ok','true'} | + {'unexpected_hash',string(),_,binary()}. + +-type download_result() :: {bad_download, binary() | string()} | + {fetch_fail, _, _} | cached_result(). + +-spec lock(AppDir, Source) -> Res when + AppDir :: file:name(), + Source :: tuple(), + Res :: {atom(), string(), any()}. lock(_AppDir, Source) -> Source. +-spec needs_update(Dir, Pkg) -> Res when + Dir :: file:name(), + Pkg :: {pkg, Name :: binary(), Vsn :: binary(), Hash :: binary()}, + Res :: boolean(). needs_update(Dir, {pkg, _Name, Vsn, _Hash}) -> [AppInfo] = rebar_app_discover:find_apps([Dir], all), case rebar_app_info:original_vsn(AppInfo) =:= rebar_utils:to_list(Vsn) of @@ -31,9 +48,20 @@ needs_update(Dir, {pkg, _Name, Vsn, _Hash}) -> true end. +-spec download(TmpDir, Pkg, State) -> Res when + TmpDir :: file:name(), + Pkg :: {pkg, Name :: binary(), Vsn :: binary(), Hash :: binary()}, + State :: rebar_state:t(), + Res :: {'error',_} | {'ok',_} | {'tarball',binary() | string()}. download(TmpDir, Pkg, State) -> download(TmpDir, Pkg, State, true). +-spec download(TmpDir, Pkg, State, UpdateETag) -> Res when + TmpDir :: file:name(), + Pkg :: {pkg, Name :: binary(), Vsn :: binary(), Hash :: binary()}, + State :: rebar_state:t(), + UpdateETag :: boolean(), + Res :: download_result(). download(TmpDir, Pkg={pkg, Name, Vsn, _Hash}, State, UpdateETag) -> CDN = rebar_state:get(State, rebar_packages_cdn, ?DEFAULT_CDN), {ok, PackageDir} = rebar_packages:package_dir(State), @@ -48,6 +76,16 @@ download(TmpDir, Pkg={pkg, Name, Vsn, _Hash}, State, UpdateETag) -> {fetch_fail, Name, Vsn} end. +-spec cached_download(TmpDir, CachePath, Pkg, Url, ETag, State, ETagPath, UpdateETag) -> Res when + TmpDir :: file:name(), + CachePath :: file:name(), + Pkg :: {pkg, Name :: binary(), Vsn :: binary(), Hash :: binary()}, + Url :: string(), + ETag :: false | string(), + State :: rebar_state:t(), + ETagPath :: file:name(), + UpdateETag :: boolean(), + Res :: download_result(). cached_download(TmpDir, CachePath, Pkg={pkg, Name, Vsn, _Hash}, Url, ETag, State, ETagPath, UpdateETag) -> case request(Url, ETag) of {ok, cached} -> @@ -65,6 +103,12 @@ cached_download(TmpDir, CachePath, Pkg={pkg, Name, Vsn, _Hash}, Url, ETag, State {fetch_fail, Name, Vsn} end. +-spec serve_from_cache(TmpDir, CachePath, Pkg, State) -> Res when + TmpDir :: file:name(), + CachePath :: file:name(), + Pkg :: {pkg, Name :: binary(), Vsn :: binary(), Hash :: binary()}, + State :: rebar_state:t(), + Res :: cached_result(). serve_from_cache(TmpDir, CachePath, Pkg, State) -> {Files, Contents, Version, Meta} = extract(TmpDir, CachePath), case checksums(Pkg, Files, Contents, Version, Meta, State) of @@ -85,6 +129,15 @@ serve_from_cache(TmpDir, CachePath, Pkg, State) -> {bad_checksum, CachePath} end. +-spec serve_from_download(TmpDir, CachePath, Package, ETag, Binary, State, ETagPath) -> Res when + TmpDir :: file:name(), + CachePath :: file:name(), + Package :: {pkg, Name :: binary(), Vsn :: binary(), Hash :: binary()}, + ETag :: string(), + Binary :: binary(), + State :: rebar_state:t(), + ETagPath :: file:name(), + Res :: download_result(). serve_from_download(TmpDir, CachePath, Package, ETag, Binary, State, ETagPath) -> ?DEBUG("Writing ~p to cache at ~ts", [Package, CachePath]), file:write_file(CachePath, Binary), @@ -96,6 +149,14 @@ serve_from_download(TmpDir, CachePath, Package, ETag, Binary, State, ETagPath) - {bad_download, CachePath} end. +-spec extract(TmpDir, CachePath) -> Res when + TmpDir :: file:name(), + CachePath :: file:name(), + Res :: {Files, Contents, Version, Meta}, + Files :: list({file:name(), binary()}), + Contents :: binary(), + Version :: binary(), + Meta :: binary(). extract(TmpDir, CachePath) -> ec_file:mkdir_p(TmpDir), {ok, Files} = erl_tar:extract(CachePath, [memory]), @@ -104,6 +165,18 @@ extract(TmpDir, CachePath) -> {"metadata.config", Meta} = lists:keyfind("metadata.config", 1, Files), {Files, Contents, Version, Meta}. +-spec checksums(Pkg, Files, Contents, Version, Meta, State) -> Res when + Pkg :: {pkg, Name :: binary(), Vsn :: binary(), Hash :: binary()}, + Files :: list({file:name(), binary()}), + Contents :: binary(), + Version :: binary(), + Meta :: binary(), + State :: rebar_state:t(), + Res :: {Hash, BinChecksum, RegistryChecksum, TarChecksum}, + Hash :: binary(), + BinChecksum :: binary(), + RegistryChecksum :: any(), + TarChecksum :: binary(). checksums(Pkg={pkg, _Name, _Vsn, Hash}, Files, Contents, Version, Meta, State) -> Blob = <<Version/binary, Meta/binary, Contents/binary>>, <<X:256/big-unsigned>> = crypto:hash(sha256, Blob), @@ -112,12 +185,18 @@ checksums(Pkg={pkg, _Name, _Vsn, Hash}, Files, Contents, Version, Meta, State) - {"CHECKSUM", TarChecksum} = lists:keyfind("CHECKSUM", 1, Files), {Hash, BinChecksum, RegistryChecksum, TarChecksum}. +-spec make_vsn(Vsn) -> Res when + Vsn :: any(), + Res :: {'error',[1..255,...]}. make_vsn(_) -> {error, "Replacing version of type pkg not supported."}. +-spec request(Url, ETag) -> Res when + Url :: string(), + ETag :: false | string(), + Res :: 'error' | {ok, cached} | {ok, any(), string()}. request(Url, ETag) -> HttpOptions = [{ssl, ssl_opts(Url)}, {relaxed, true} | rebar_utils:get_proxy_auth()], - case httpc:request(get, {Url, [{"if-none-match", "\"" ++ ETag ++ "\""} || ETag =/= false]++ [{"User-Agent", rebar_utils:user_agent()}]}, HttpOptions, @@ -138,6 +217,9 @@ request(Url, ETag) -> error end. +-spec etag(Path) -> Res when + Path :: file:name(), + Res :: false | string(). etag(Path) -> case file:read_file(Path) of {ok, Bin} -> @@ -148,7 +230,9 @@ etag(Path) -> %% @doc returns the SSL options adequate for the project based on %% its configuration, including for validation of certs. --spec ssl_opts(string()) -> [term()]. +-spec ssl_opts(Url) -> Res when + Url :: string(), + Res :: proplists:proplist(). ssl_opts(Url) -> case get_ssl_config() of ssl_verify_enabled -> @@ -159,7 +243,10 @@ ssl_opts(Url) -> %% @doc returns the SSL options adequate for the project based on %% its configuration, including for validation of certs. --spec ssl_opts(atom(), string()) -> [term()]. +-spec ssl_opts(Enabled, Url) -> Res when + Enabled :: atom(), + Url :: string(), + Res :: proplists:proplist(). ssl_opts(ssl_verify_enabled, Url) -> case check_ssl_version() of true -> @@ -173,6 +260,9 @@ ssl_opts(ssl_verify_enabled, Url) -> [{verify, verify_none}] end. +-spec partial_chain(Certs) -> Res when + Certs :: list(any()), + Res :: unknown_ca | {trusted_ca, any()}. partial_chain(Certs) -> Certs1 = [{Cert, public_key:pkix_decode_cert(Cert, otp)} || Cert <- Certs], CACerts = certifi:cacerts(), @@ -187,14 +277,23 @@ partial_chain(Certs) -> unknown_ca end. +-spec extract_public_key_info(Cert) -> Res when + Cert :: #'OTPCertificate'{tbsCertificate::#'OTPTBSCertificate'{}}, + Res :: any(). extract_public_key_info(Cert) -> ((Cert#'OTPCertificate'.tbsCertificate)#'OTPTBSCertificate'.subjectPublicKeyInfo). +-spec check_cert(CACerts, Cert) -> Res when + CACerts :: list(any()), + Cert :: any(), + Res :: boolean(). check_cert(CACerts, Cert) -> lists:any(fun(CACert) -> extract_public_key_info(CACert) == extract_public_key_info(Cert) end, CACerts). +-spec check_ssl_version() -> + boolean(). check_ssl_version() -> case application:get_key(ssl, vsn) of {ok, Vsn} -> @@ -203,6 +302,8 @@ check_ssl_version() -> false end. +-spec get_ssl_config() -> + ssl_verify_disabled | ssl_verify_enabled. get_ssl_config() -> GlobalConfigFile = rebar_dir:global_config(), Config = rebar_config:consult_file(GlobalConfigFile), @@ -213,9 +314,14 @@ get_ssl_config() -> ssl_verify_enabled end. +-spec parse_vsn(Vsn) -> Res when + Vsn :: string(), + Res :: {integer(), integer(), integer()}. parse_vsn(Vsn) -> version_pad(rebar_string:lexemes(Vsn, ".-")). +-spec version_pad(list(nonempty_string())) -> Res when + Res :: {integer(), integer(), integer()}. version_pad([Major]) -> {list_to_integer(Major), 0, 0}; version_pad([Major, Minor]) -> @@ -225,10 +331,19 @@ version_pad([Major, Minor, Patch]) -> version_pad([Major, Minor, Patch | _]) -> {list_to_integer(Major), list_to_integer(Minor), list_to_integer(Patch)}. +-spec maybe_store_etag_in_cache(UpdateETag, Path, ETag) -> Res when + UpdateETag :: boolean(), + Path :: file:name(), + ETag :: string(), + Res :: ok. maybe_store_etag_in_cache(false = _UpdateETag, _Path, _ETag) -> ok; maybe_store_etag_in_cache(true = _UpdateETag, Path, ETag) -> store_etag_in_cache(Path, ETag). +-spec store_etag_in_cache(File, ETag) -> Res when + File :: file:name(), + ETag :: string(), + Res :: ok. store_etag_in_cache(Path, ETag) -> ok = file:write_file(Path, ETag). |