summaryrefslogtreecommitdiffstats
path: root/vendor/gix/src/remote/url
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix/src/remote/url')
-rw-r--r--vendor/gix/src/remote/url/mod.rs7
-rw-r--r--vendor/gix/src/remote/url/rewrite.rs100
-rw-r--r--vendor/gix/src/remote/url/scheme_permission.rs120
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),
+ )
+ }
+}