diff options
Diffstat (limited to 'vendor/gix/src/remote/url')
-rw-r--r-- | vendor/gix/src/remote/url/mod.rs | 7 | ||||
-rw-r--r-- | vendor/gix/src/remote/url/rewrite.rs | 100 | ||||
-rw-r--r-- | vendor/gix/src/remote/url/scheme_permission.rs | 120 |
3 files changed, 227 insertions, 0 deletions
diff --git a/vendor/gix/src/remote/url/mod.rs b/vendor/gix/src/remote/url/mod.rs new file mode 100644 index 000000000..7b8815812 --- /dev/null +++ b/vendor/gix/src/remote/url/mod.rs @@ -0,0 +1,7 @@ +mod rewrite; +/// +#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))] +pub mod scheme_permission; +pub(crate) use rewrite::Rewrite; +#[cfg(any(feature = "blocking-network-client", feature = "async-network-client"))] +pub(crate) use scheme_permission::SchemePermission; diff --git a/vendor/gix/src/remote/url/rewrite.rs b/vendor/gix/src/remote/url/rewrite.rs new file mode 100644 index 000000000..ae0eee426 --- /dev/null +++ b/vendor/gix/src/remote/url/rewrite.rs @@ -0,0 +1,100 @@ +use gix_features::threading::OwnShared; + +use crate::{ + bstr::{BStr, BString, ByteVec}, + config, + remote::Direction, +}; + +#[derive(Debug, Clone)] +struct Replace { + find: BString, + with: OwnShared<BString>, +} + +#[derive(Default, Debug, Clone)] +pub(crate) struct Rewrite { + url_rewrite: Vec<Replace>, + push_url_rewrite: Vec<Replace>, +} + +/// Init +impl Rewrite { + pub fn from_config( + config: &gix_config::File<'static>, + mut filter: fn(&gix_config::file::Metadata) -> bool, + ) -> Rewrite { + config + .sections_by_name_and_filter("url", &mut filter) + .map(|sections| { + let mut url_rewrite = Vec::new(); + let mut push_url_rewrite = Vec::new(); + for section in sections { + let replace = match section.header().subsection_name() { + Some(base) => OwnShared::new(base.to_owned()), + None => continue, + }; + + for instead_of in section.values(config::tree::Url::INSTEAD_OF.name) { + url_rewrite.push(Replace { + with: OwnShared::clone(&replace), + find: instead_of.into_owned(), + }); + } + for instead_of in section.values(config::tree::Url::PUSH_INSTEAD_OF.name) { + push_url_rewrite.push(Replace { + with: OwnShared::clone(&replace), + find: instead_of.into_owned(), + }); + } + } + Rewrite { + url_rewrite, + push_url_rewrite, + } + }) + .unwrap_or_default() + } +} + +/// Access +impl Rewrite { + fn replacements_for(&self, direction: Direction) -> &[Replace] { + match direction { + Direction::Fetch => &self.url_rewrite, + Direction::Push => &self.push_url_rewrite, + } + } + + pub fn longest(&self, url: &gix_url::Url, direction: Direction) -> Option<BString> { + if self.replacements_for(direction).is_empty() { + None + } else { + let mut url = url.to_bstring(); + self.rewrite_url_in_place(&mut url, direction).then_some(url) + } + } + + /// Rewrite the given `url` of `direction` and return `true` if a replacement happened. + /// + /// Note that the result must still be checked for validity, it might not be a valid URL as we do a syntax-unaware replacement. + pub fn rewrite_url_in_place(&self, url: &mut BString, direction: Direction) -> bool { + self.replacements_for(direction) + .iter() + .fold(None::<(usize, &BStr)>, |mut acc, replace| { + if url.starts_with(replace.find.as_ref()) { + let (bytes_matched, prev_rewrite_with) = + acc.get_or_insert((replace.find.len(), replace.with.as_slice().into())); + if *bytes_matched < replace.find.len() { + *bytes_matched = replace.find.len(); + *prev_rewrite_with = replace.with.as_slice().into(); + } + }; + acc + }) + .map(|(bytes_matched, replace_with)| { + url.replace_range(..bytes_matched, replace_with); + }) + .is_some() + } +} diff --git a/vendor/gix/src/remote/url/scheme_permission.rs b/vendor/gix/src/remote/url/scheme_permission.rs new file mode 100644 index 000000000..ddb87e111 --- /dev/null +++ b/vendor/gix/src/remote/url/scheme_permission.rs @@ -0,0 +1,120 @@ +use std::{borrow::Cow, collections::BTreeMap, convert::TryFrom}; + +use crate::{ + bstr::{BStr, BString, ByteSlice}, + config, + config::tree::{gitoxide, Key, Protocol}, +}; + +/// All allowed values of the `protocol.allow` key. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum Allow { + /// Allow use this protocol. + Always, + /// Forbid using this protocol + Never, + /// Only supported if the `GIT_PROTOCOL_FROM_USER` is unset or is set to `1`. + User, +} + +impl Allow { + /// Return true if we represent something like 'allow == true'. + pub fn to_bool(self, user_allowed: Option<bool>) -> bool { + match self { + Allow::Always => true, + Allow::Never => false, + Allow::User => user_allowed.unwrap_or(true), + } + } +} + +impl<'a> TryFrom<Cow<'a, BStr>> for Allow { + type Error = BString; + + fn try_from(v: Cow<'a, BStr>) -> Result<Self, Self::Error> { + Ok(match v.as_ref().as_bytes() { + b"never" => Allow::Never, + b"always" => Allow::Always, + b"user" => Allow::User, + unknown => return Err(unknown.into()), + }) + } +} + +#[derive(Debug, Clone)] +pub(crate) struct SchemePermission { + /// `None`, env-var is unset or wasn't queried, otherwise true if `GIT_PROTOCOL_FROM_USER` is `1`. + user_allowed: Option<bool>, + /// The general allow value from `protocol.allow`. + allow: Option<Allow>, + /// Per scheme allow information + allow_per_scheme: BTreeMap<gix_url::Scheme, Allow>, +} + +/// Init +impl SchemePermission { + /// NOTE: _intentionally without leniency_ + pub fn from_config( + config: &gix_config::File<'static>, + mut filter: fn(&gix_config::file::Metadata) -> bool, + ) -> Result<Self, config::protocol::allow::Error> { + let allow: Option<Allow> = config + .string_filter_by_key("protocol.allow", &mut filter) + .map(|value| Protocol::ALLOW.try_into_allow(value, None)) + .transpose()?; + + let mut saw_user = allow.map_or(false, |allow| allow == Allow::User); + let allow_per_scheme = match config.sections_by_name_and_filter("protocol", &mut filter) { + Some(it) => { + let mut map = BTreeMap::default(); + for (section, scheme) in it.filter_map(|section| { + section.header().subsection_name().and_then(|scheme| { + scheme + .to_str() + .ok() + .and_then(|scheme| gix_url::Scheme::try_from(scheme).ok().map(|scheme| (section, scheme))) + }) + }) { + if let Some(value) = section + .value("allow") + .map(|value| Protocol::ALLOW.try_into_allow(value, Some(scheme.as_str()))) + .transpose()? + { + saw_user |= value == Allow::User; + map.insert(scheme, value); + } + } + map + } + None => Default::default(), + }; + + let user_allowed = saw_user.then(|| { + config + .string_filter_by_key(gitoxide::Allow::PROTOCOL_FROM_USER.logical_name().as_str(), &mut filter) + .map_or(true, |val| val.as_ref() == "1") + }); + Ok(SchemePermission { + allow, + allow_per_scheme, + user_allowed, + }) + } +} + +/// Access +impl SchemePermission { + pub fn allow(&self, scheme: &gix_url::Scheme) -> bool { + self.allow_per_scheme.get(scheme).or(self.allow.as_ref()).map_or_else( + || { + use gix_url::Scheme::*; + match scheme { + File | Git | Ssh | Http | Https => true, + Ext(_) => false, + // TODO: figure out what 'ext' really entails, and what 'other' protocols are which aren't representable for us yet + } + }, + |allow| allow.to_bool(self.user_allowed), + ) + } +} |