summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src/repository/config
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix/src/repository/config')
-rw-r--r--vendor/gix/src/repository/config/mod.rs191
-rw-r--r--vendor/gix/src/repository/config/transport.rs425
2 files changed, 616 insertions, 0 deletions
diff --git a/vendor/gix/src/repository/config/mod.rs b/vendor/gix/src/repository/config/mod.rs
new file mode 100644
index 000000000..92b2618cc
--- /dev/null
+++ b/vendor/gix/src/repository/config/mod.rs
@@ -0,0 +1,191 @@
+use std::collections::BTreeSet;
+
+use crate::{bstr::ByteSlice, config};
+
+/// General Configuration
+impl crate::Repository {
+ /// Return a snapshot of the configuration as seen upon opening the repository.
+ pub fn config_snapshot(&self) -> config::Snapshot<'_> {
+ config::Snapshot { repo: self }
+ }
+
+ /// Return a mutable snapshot of the configuration as seen upon opening the repository, starting a transaction.
+ /// When the returned instance is dropped, it is applied in full, even if the reason for the drop is an error.
+ ///
+ /// Note that changes to the configuration are in-memory only and are observed only the this instance
+ /// of the [`Repository`][crate::Repository].
+ pub fn config_snapshot_mut(&mut self) -> config::SnapshotMut<'_> {
+ let config = self.config.resolved.as_ref().clone();
+ config::SnapshotMut {
+ repo: Some(self),
+ config,
+ }
+ }
+
+ /// The options used to open the repository.
+ pub fn open_options(&self) -> &crate::open::Options {
+ &self.options
+ }
+
+ /// Obtain options for use when connecting via `ssh`.
+ #[cfg(feature = "blocking-network-client")]
+ pub fn ssh_connect_options(
+ &self,
+ ) -> Result<gix_protocol::transport::client::ssh::connect::Options, config::ssh_connect_options::Error> {
+ use crate::config::{
+ cache::util::ApplyLeniency,
+ tree::{gitoxide, Core, Ssh},
+ };
+
+ let config = &self.config.resolved;
+ let mut trusted = self.filter_config_section();
+ let mut fallback_active = false;
+ let ssh_command = config
+ .string_filter("core", None, Core::SSH_COMMAND.name, &mut trusted)
+ .or_else(|| {
+ fallback_active = true;
+ config.string_filter(
+ "gitoxide",
+ Some("ssh".into()),
+ gitoxide::Ssh::COMMAND_WITHOUT_SHELL_FALLBACK.name,
+ &mut trusted,
+ )
+ })
+ .map(|cmd| gix_path::from_bstr(cmd).into_owned().into());
+ let opts = gix_protocol::transport::client::ssh::connect::Options {
+ disallow_shell: fallback_active,
+ command: ssh_command,
+ kind: config
+ .string_filter_by_key("ssh.variant", &mut trusted)
+ .and_then(|variant| Ssh::VARIANT.try_into_variant(variant).transpose())
+ .transpose()
+ .with_leniency(self.options.lenient_config)?,
+ };
+ Ok(opts)
+ }
+
+ /// The kind of object hash the repository is configured to use.
+ pub fn object_hash(&self) -> gix_hash::Kind {
+ self.config.object_hash
+ }
+}
+
+#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))]
+mod transport;
+
+mod remote {
+ use std::{borrow::Cow, collections::BTreeSet};
+
+ use crate::{bstr::ByteSlice, remote};
+
+ impl crate::Repository {
+ /// Returns a sorted list unique of symbolic names of remotes that
+ /// we deem [trustworthy][crate::open::Options::filter_config_section()].
+ // TODO: Use `remote::Name` here
+ pub fn remote_names(&self) -> BTreeSet<&str> {
+ self.subsection_names_of("remote")
+ }
+
+ /// Obtain the branch-independent name for a remote for use in the given `direction`, or `None` if it could not be determined.
+ ///
+ /// For _fetching_, use the only configured remote, or default to `origin` if it exists.
+ /// For _pushing_, use the `remote.pushDefault` trusted configuration key, or fall back to the rules for _fetching_.
+ ///
+ /// # Notes
+ ///
+ /// It's up to the caller to determine what to do if the current `head` is unborn or detached.
+ // TODO: use remote::Name here
+ pub fn remote_default_name(&self, direction: remote::Direction) -> Option<Cow<'_, str>> {
+ let name = (direction == remote::Direction::Push)
+ .then(|| {
+ self.config
+ .resolved
+ .string_filter("remote", None, "pushDefault", &mut self.filter_config_section())
+ .and_then(|s| match s {
+ Cow::Borrowed(s) => s.to_str().ok().map(Cow::Borrowed),
+ Cow::Owned(s) => s.to_str().ok().map(|s| Cow::Owned(s.into())),
+ })
+ })
+ .flatten();
+ name.or_else(|| {
+ let names = self.remote_names();
+ match names.len() {
+ 0 => None,
+ 1 => names.iter().next().copied().map(Cow::Borrowed),
+ _more_than_one => names.get("origin").copied().map(Cow::Borrowed),
+ }
+ })
+ }
+ }
+}
+
+mod branch {
+ use std::{borrow::Cow, collections::BTreeSet, convert::TryInto};
+
+ use gix_ref::FullNameRef;
+ use gix_validate::reference::name::Error as ValidateNameError;
+
+ use crate::bstr::BStr;
+
+ impl crate::Repository {
+ /// Return a set of unique short branch names for which custom configuration exists in the configuration,
+ /// if we deem them [trustworthy][crate::open::Options::filter_config_section()].
+ pub fn branch_names(&self) -> BTreeSet<&str> {
+ self.subsection_names_of("branch")
+ }
+
+ /// Returns the validated reference on the remote associated with the given `short_branch_name`,
+ /// always `main` instead of `refs/heads/main`.
+ ///
+ /// The returned reference is the one we track on the remote side for merging and pushing.
+ /// Returns `None` if the remote reference was not found.
+ /// May return an error if the reference is invalid.
+ pub fn branch_remote_ref<'a>(
+ &self,
+ short_branch_name: impl Into<&'a BStr>,
+ ) -> Option<Result<Cow<'_, FullNameRef>, ValidateNameError>> {
+ self.config
+ .resolved
+ .string("branch", Some(short_branch_name.into()), "merge")
+ .map(crate::config::tree::branch::Merge::try_into_fullrefname)
+ }
+
+ /// Returns the unvalidated name of the remote associated with the given `short_branch_name`,
+ /// typically `main` instead of `refs/heads/main`.
+ /// In some cases, the returned name will be an URL.
+ /// Returns `None` if the remote was not found or if the name contained illformed UTF-8.
+ ///
+ /// See also [Reference::remote_name()][crate::Reference::remote_name()] for a more typesafe version
+ /// to be used when a `Reference` is available.
+ pub fn branch_remote_name<'a>(
+ &self,
+ short_branch_name: impl Into<&'a BStr>,
+ ) -> Option<crate::remote::Name<'_>> {
+ self.config
+ .resolved
+ .string("branch", Some(short_branch_name.into()), "remote")
+ .and_then(|name| name.try_into().ok())
+ }
+ }
+}
+
+impl crate::Repository {
+ pub(crate) fn filter_config_section(&self) -> fn(&gix_config::file::Metadata) -> bool {
+ self.options
+ .filter_config_section
+ .unwrap_or(config::section::is_trusted)
+ }
+
+ fn subsection_names_of<'a>(&'a self, header_name: &'a str) -> BTreeSet<&'a str> {
+ self.config
+ .resolved
+ .sections_by_name(header_name)
+ .map(|it| {
+ let filter = self.filter_config_section();
+ it.filter(move |s| filter(s.meta()))
+ .filter_map(|section| section.header().subsection_name().and_then(|b| b.to_str().ok()))
+ .collect()
+ })
+ .unwrap_or_default()
+ }
+}
diff --git a/vendor/gix/src/repository/config/transport.rs b/vendor/gix/src/repository/config/transport.rs
new file mode 100644
index 000000000..dcfbc0bf6
--- /dev/null
+++ b/vendor/gix/src/repository/config/transport.rs
@@ -0,0 +1,425 @@
+#![allow(clippy::result_large_err)]
+use std::any::Any;
+
+use crate::bstr::BStr;
+
+impl crate::Repository {
+ /// Produce configuration suitable for `url`, as differentiated by its protocol/scheme, to be passed to a transport instance via
+ /// [configure()][gix_transport::client::TransportWithoutIO::configure()] (via `&**config` to pass the contained `Any` and not the `Box`).
+ /// `None` is returned if there is no known configuration. If `remote_name` is not `None`, the remote's name may contribute to
+ /// configuration overrides, typically for the HTTP transport.
+ ///
+ /// Note that the caller may cast the instance themselves to modify it before passing it on.
+ ///
+ /// For transports that support proxy authentication, the
+ /// [default authentication method](crate::config::Snapshot::credential_helpers()) will be used with the url of the proxy
+ /// if it contains a user name.
+ #[cfg_attr(
+ not(any(
+ feature = "blocking-http-transport-reqwest",
+ feature = "blocking-http-transport-curl"
+ )),
+ allow(unused_variables)
+ )]
+ pub fn transport_options<'a>(
+ &self,
+ url: impl Into<&'a BStr>,
+ remote_name: Option<&BStr>,
+ ) -> Result<Option<Box<dyn Any>>, crate::config::transport::Error> {
+ let url = gix_url::parse(url.into())?;
+ use gix_url::Scheme::*;
+
+ match &url.scheme {
+ Http | Https => {
+ #[cfg(not(any(
+ feature = "blocking-http-transport-reqwest",
+ feature = "blocking-http-transport-curl"
+ )))]
+ {
+ Ok(None)
+ }
+ #[cfg(any(
+ feature = "blocking-http-transport-reqwest",
+ feature = "blocking-http-transport-curl"
+ ))]
+ {
+ use std::{
+ borrow::Cow,
+ sync::{Arc, Mutex},
+ };
+
+ use gix_transport::client::{
+ http,
+ http::options::{ProxyAuthMethod, SslVersion, SslVersionRangeInclusive},
+ };
+
+ use crate::{
+ config,
+ config::{
+ cache::util::ApplyLeniency,
+ tree::{gitoxide, Key, Remote},
+ },
+ };
+ fn try_cow_to_string(
+ v: Cow<'_, BStr>,
+ lenient: bool,
+ key_str: impl Into<Cow<'static, BStr>>,
+ key: &'static config::tree::keys::String,
+ ) -> Result<Option<String>, config::transport::Error> {
+ key.try_into_string(v)
+ .map_err(|err| config::transport::Error::IllformedUtf8 {
+ source: err,
+ key: key_str.into(),
+ })
+ .map(Some)
+ .with_leniency(lenient)
+ }
+
+ fn cow_bstr(v: &str) -> Cow<'_, BStr> {
+ Cow::Borrowed(v.into())
+ }
+
+ fn proxy_auth_method(
+ value_and_key: Option<(
+ Cow<'_, BStr>,
+ Cow<'static, BStr>,
+ &'static config::tree::http::ProxyAuthMethod,
+ )>,
+ ) -> Result<ProxyAuthMethod, config::transport::Error> {
+ let value = value_and_key
+ .map(|(method, key, key_type)| {
+ key_type.try_into_proxy_auth_method(method).map_err(|err| {
+ config::transport::http::Error::InvalidProxyAuthMethod { source: err, key }
+ })
+ })
+ .transpose()?
+ .unwrap_or_default();
+ Ok(value)
+ }
+
+ fn ssl_version(
+ config: &gix_config::File<'static>,
+ key_str: &'static str,
+ key: &'static config::tree::http::SslVersion,
+ mut filter: fn(&gix_config::file::Metadata) -> bool,
+ lenient: bool,
+ ) -> Result<Option<SslVersion>, config::transport::Error> {
+ debug_assert_eq!(
+ key_str,
+ key.logical_name(),
+ "BUG: hardcoded and generated key names must match"
+ );
+ config
+ .string_filter_by_key(key_str, &mut filter)
+ .filter(|v| !v.is_empty())
+ .map(|v| {
+ key.try_into_ssl_version(v)
+ .map_err(crate::config::transport::http::Error::from)
+ })
+ .transpose()
+ .with_leniency(lenient)
+ .map_err(Into::into)
+ }
+
+ fn proxy(
+ value: Option<(Cow<'_, BStr>, Cow<'static, BStr>, &'static config::tree::keys::String)>,
+ lenient: bool,
+ ) -> Result<Option<String>, config::transport::Error> {
+ Ok(value
+ .and_then(|(v, k, key)| try_cow_to_string(v, lenient, k.clone(), key).transpose())
+ .transpose()?
+ .map(|mut proxy| {
+ if !proxy.trim().is_empty() && !proxy.contains("://") {
+ proxy.insert_str(0, "http://");
+ proxy
+ } else {
+ proxy
+ }
+ }))
+ }
+
+ let mut opts = http::Options::default();
+ let config = &self.config.resolved;
+ let mut trusted_only = self.filter_config_section();
+ let lenient = self.config.lenient_config;
+ opts.extra_headers = {
+ let key = "http.extraHeader";
+ debug_assert_eq!(key, &config::tree::Http::EXTRA_HEADER.logical_name());
+ config
+ .strings_filter_by_key(key, &mut trusted_only)
+ .map(|values| config::tree::Http::EXTRA_HEADER.try_into_extra_header(values))
+ .transpose()
+ .map_err(|err| config::transport::Error::IllformedUtf8 {
+ source: err,
+ key: Cow::Borrowed(key.into()),
+ })?
+ .unwrap_or_default()
+ };
+
+ opts.follow_redirects = {
+ let key = "http.followRedirects";
+
+ config::tree::Http::FOLLOW_REDIRECTS
+ .try_into_follow_redirects(
+ config.string_filter_by_key(key, &mut trusted_only).unwrap_or_default(),
+ || {
+ config
+ .boolean_filter_by_key(key, &mut trusted_only)
+ .transpose()
+ .with_leniency(lenient)
+ },
+ )
+ .map_err(config::transport::http::Error::InvalidFollowRedirects)?
+ };
+
+ opts.low_speed_time_seconds = config
+ .integer_filter_by_key("http.lowSpeedTime", &mut trusted_only)
+ .map(|value| config::tree::Http::LOW_SPEED_TIME.try_into_u64(value))
+ .transpose()
+ .with_leniency(lenient)
+ .map_err(config::transport::http::Error::from)?
+ .unwrap_or_default();
+ opts.low_speed_limit_bytes_per_second = config
+ .integer_filter_by_key("http.lowSpeedLimit", &mut trusted_only)
+ .map(|value| config::tree::Http::LOW_SPEED_LIMIT.try_into_u32(value))
+ .transpose()
+ .with_leniency(lenient)
+ .map_err(config::transport::http::Error::from)?
+ .unwrap_or_default();
+ opts.proxy = proxy(
+ remote_name
+ .and_then(|name| {
+ config
+ .string_filter("remote", Some(name), Remote::PROXY.name, &mut trusted_only)
+ .map(|v| (v, Cow::Owned(format!("remote.{name}.proxy").into()), &Remote::PROXY))
+ })
+ .or_else(|| {
+ let key = "http.proxy";
+ debug_assert_eq!(key, config::tree::Http::PROXY.logical_name());
+ let http_proxy = config
+ .string_filter_by_key(key, &mut trusted_only)
+ .map(|v| (v, cow_bstr(key), &config::tree::Http::PROXY))
+ .or_else(|| {
+ let key = "gitoxide.http.proxy";
+ debug_assert_eq!(key, gitoxide::Http::PROXY.logical_name());
+ config
+ .string_filter_by_key(key, &mut trusted_only)
+ .map(|v| (v, cow_bstr(key), &gitoxide::Http::PROXY))
+ });
+ if url.scheme == Https {
+ http_proxy.or_else(|| {
+ let key = "gitoxide.https.proxy";
+ debug_assert_eq!(key, gitoxide::Https::PROXY.logical_name());
+ config
+ .string_filter_by_key(key, &mut trusted_only)
+ .map(|v| (v, cow_bstr(key), &gitoxide::Https::PROXY))
+ })
+ } else {
+ http_proxy
+ }
+ })
+ .or_else(|| {
+ let key = "gitoxide.http.allProxy";
+ debug_assert_eq!(key, gitoxide::Http::ALL_PROXY.logical_name());
+ config
+ .string_filter_by_key(key, &mut trusted_only)
+ .map(|v| (v, cow_bstr(key), &gitoxide::Http::ALL_PROXY))
+ }),
+ lenient,
+ )?;
+ {
+ let key = "gitoxide.http.noProxy";
+ debug_assert_eq!(key, gitoxide::Http::NO_PROXY.logical_name());
+ opts.no_proxy = config
+ .string_filter_by_key(key, &mut trusted_only)
+ .and_then(|v| {
+ try_cow_to_string(v, lenient, Cow::Borrowed(key.into()), &gitoxide::Http::NO_PROXY)
+ .transpose()
+ })
+ .transpose()?;
+ }
+ opts.proxy_auth_method = proxy_auth_method({
+ let key = "gitoxide.http.proxyAuthMethod";
+ debug_assert_eq!(key, gitoxide::Http::PROXY_AUTH_METHOD.logical_name());
+ config
+ .string_filter_by_key(key, &mut trusted_only)
+ .map(|v| (v, Cow::Borrowed(key.into()), &gitoxide::Http::PROXY_AUTH_METHOD))
+ .or_else(|| {
+ remote_name
+ .and_then(|name| {
+ config
+ .string_filter("remote", Some(name), "proxyAuthMethod", &mut trusted_only)
+ .map(|v| {
+ (
+ v,
+ Cow::Owned(format!("remote.{name}.proxyAuthMethod").into()),
+ &Remote::PROXY_AUTH_METHOD,
+ )
+ })
+ })
+ .or_else(|| {
+ let key = "http.proxyAuthMethod";
+ debug_assert_eq!(key, config::tree::Http::PROXY_AUTH_METHOD.logical_name());
+ config.string_filter_by_key(key, &mut trusted_only).map(|v| {
+ (v, Cow::Borrowed(key.into()), &config::tree::Http::PROXY_AUTH_METHOD)
+ })
+ })
+ })
+ })?;
+ opts.proxy_authenticate = opts
+ .proxy
+ .as_deref()
+ .filter(|url| !url.is_empty())
+ .map(|url| gix_url::parse(url.into()))
+ .transpose()?
+ .filter(|url| url.user().is_some())
+ .map(|url| -> Result<_, config::transport::http::Error> {
+ let (mut cascade, action_with_normalized_url, prompt_opts) =
+ self.config_snapshot().credential_helpers(url)?;
+ Ok((
+ action_with_normalized_url,
+ Arc::new(Mutex::new(move |action| cascade.invoke(action, prompt_opts.clone())))
+ as Arc<Mutex<http::options::AuthenticateFn>>,
+ ))
+ })
+ .transpose()?;
+ opts.connect_timeout = {
+ let key = "gitoxide.http.connectTimeout";
+ config
+ .integer_filter_by_key(key, &mut trusted_only)
+ .map(|v| {
+ debug_assert_eq!(key, gitoxide::Http::CONNECT_TIMEOUT.logical_name());
+ gitoxide::Http::CONNECT_TIMEOUT
+ .try_into_duration(v)
+ .map_err(crate::config::transport::http::Error::from)
+ })
+ .transpose()
+ .with_leniency(lenient)?
+ };
+ {
+ let key = "http.userAgent";
+ opts.user_agent = config
+ .string_filter_by_key(key, &mut trusted_only)
+ .and_then(|v| {
+ try_cow_to_string(
+ v,
+ lenient,
+ Cow::Borrowed(key.into()),
+ &config::tree::Http::USER_AGENT,
+ )
+ .transpose()
+ })
+ .transpose()?
+ .or_else(|| Some(crate::env::agent().into()));
+ }
+
+ {
+ let key = "http.version";
+ opts.http_version = config
+ .string_filter_by_key(key, &mut trusted_only)
+ .map(|v| {
+ config::tree::Http::VERSION
+ .try_into_http_version(v)
+ .map_err(config::transport::http::Error::InvalidHttpVersion)
+ })
+ .transpose()?;
+ }
+
+ {
+ opts.verbose = config
+ .boolean_filter(
+ "gitoxide",
+ Some("http".into()),
+ gitoxide::Http::VERBOSE.name,
+ &mut trusted_only,
+ )
+ .and_then(Result::ok)
+ .unwrap_or_default();
+ }
+
+ let may_use_cainfo = {
+ let key = "http.schannelUseSSLCAInfo";
+ config
+ .boolean_filter_by_key(key, &mut trusted_only)
+ .map(|value| config::tree::Http::SCHANNEL_USE_SSL_CA_INFO.enrich_error(value))
+ .transpose()
+ .with_leniency(lenient)
+ .map_err(config::transport::http::Error::from)?
+ .unwrap_or(true)
+ };
+
+ if may_use_cainfo {
+ let key = "http.sslCAInfo";
+ debug_assert_eq!(key, config::tree::Http::SSL_CA_INFO.logical_name());
+ opts.ssl_ca_info = config
+ .path_filter_by_key(key, &mut trusted_only)
+ .map(|p| {
+ use crate::config::cache::interpolate_context;
+ p.interpolate(interpolate_context(
+ self.install_dir().ok().as_deref(),
+ self.config.home_dir().as_deref(),
+ ))
+ .map(|cow| cow.into_owned())
+ })
+ .transpose()
+ .with_leniency(lenient)
+ .map_err(|err| config::transport::Error::InterpolatePath { source: err, key })?;
+ }
+
+ {
+ opts.ssl_version = ssl_version(
+ config,
+ "http.sslVersion",
+ &config::tree::Http::SSL_VERSION,
+ trusted_only,
+ lenient,
+ )?
+ .map(|v| SslVersionRangeInclusive { min: v, max: v });
+ let min_max = ssl_version(
+ config,
+ "gitoxide.http.sslVersionMin",
+ &gitoxide::Http::SSL_VERSION_MIN,
+ trusted_only,
+ lenient,
+ )
+ .and_then(|min| {
+ ssl_version(
+ config,
+ "gitoxide.http.sslVersionMax",
+ &gitoxide::Http::SSL_VERSION_MAX,
+ trusted_only,
+ lenient,
+ )
+ .map(|max| min.and_then(|min| max.map(|max| (min, max))))
+ })?;
+ if let Some((min, max)) = min_max {
+ let v = opts.ssl_version.get_or_insert(SslVersionRangeInclusive {
+ min: SslVersion::TlsV1_3,
+ max: SslVersion::TlsV1_3,
+ });
+ v.min = min;
+ v.max = max;
+ }
+ }
+
+ #[cfg(feature = "blocking-http-transport-curl")]
+ {
+ let key = "http.schannelCheckRevoke";
+ let schannel_check_revoke = config
+ .boolean_filter_by_key(key, &mut trusted_only)
+ .map(|value| config::tree::Http::SCHANNEL_CHECK_REVOKE.enrich_error(value))
+ .transpose()
+ .with_leniency(lenient)
+ .map_err(config::transport::http::Error::from)?;
+ let backend = gix_protocol::transport::client::http::curl::Options { schannel_check_revoke };
+ opts.backend =
+ Some(Arc::new(Mutex::new(backend)) as Arc<Mutex<dyn Any + Send + Sync + 'static>>);
+ }
+
+ Ok(Some(Box::new(opts)))
+ }
+ }
+ File | Git | Ssh | Ext(_) => Ok(None),
+ }
+ }
+}