summaryrefslogtreecommitdiffstats
path: root/vendor/git2/src/remote_callbacks.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
commit10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch)
treebdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/git2/src/remote_callbacks.rs
parentReleasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff)
downloadrustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz
rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/git2/src/remote_callbacks.rs')
-rw-r--r--vendor/git2/src/remote_callbacks.rs518
1 files changed, 518 insertions, 0 deletions
diff --git a/vendor/git2/src/remote_callbacks.rs b/vendor/git2/src/remote_callbacks.rs
new file mode 100644
index 000000000..1169420bd
--- /dev/null
+++ b/vendor/git2/src/remote_callbacks.rs
@@ -0,0 +1,518 @@
+use libc::{c_char, c_int, c_uint, c_void, size_t};
+use std::ffi::{CStr, CString};
+use std::mem;
+use std::ptr;
+use std::slice;
+use std::str;
+
+use crate::cert::Cert;
+use crate::util::Binding;
+use crate::{
+ panic, raw, Cred, CredentialType, Error, IndexerProgress, Oid, PackBuilderStage, Progress,
+ PushUpdate,
+};
+
+/// A structure to contain the callbacks which are invoked when a repository is
+/// being updated or downloaded.
+///
+/// These callbacks are used to manage facilities such as authentication,
+/// transfer progress, etc.
+pub struct RemoteCallbacks<'a> {
+ push_progress: Option<Box<PushTransferProgress<'a>>>,
+ progress: Option<Box<IndexerProgress<'a>>>,
+ pack_progress: Option<Box<PackProgress<'a>>>,
+ credentials: Option<Box<Credentials<'a>>>,
+ sideband_progress: Option<Box<TransportMessage<'a>>>,
+ update_tips: Option<Box<UpdateTips<'a>>>,
+ certificate_check: Option<Box<CertificateCheck<'a>>>,
+ push_update_reference: Option<Box<PushUpdateReference<'a>>>,
+ push_negotiation: Option<Box<PushNegotiation<'a>>>,
+}
+
+/// Callback used to acquire credentials for when a remote is fetched.
+///
+/// * `url` - the resource for which the credentials are required.
+/// * `username_from_url` - the username that was embedded in the URL, or `None`
+/// if it was not included.
+/// * `allowed_types` - a bitmask stating which cred types are OK to return.
+pub type Credentials<'a> =
+ dyn FnMut(&str, Option<&str>, CredentialType) -> Result<Cred, Error> + 'a;
+
+/// Callback for receiving messages delivered by the transport.
+///
+/// The return value indicates whether the network operation should continue.
+pub type TransportMessage<'a> = dyn FnMut(&[u8]) -> bool + 'a;
+
+/// Callback for whenever a reference is updated locally.
+pub type UpdateTips<'a> = dyn FnMut(&str, Oid, Oid) -> bool + 'a;
+
+/// Callback for a custom certificate check.
+///
+/// The first argument is the certificate received on the connection.
+/// Certificates are typically either an SSH or X509 certificate.
+///
+/// The second argument is the hostname for the connection is passed as the last
+/// argument.
+pub type CertificateCheck<'a> =
+ dyn FnMut(&Cert<'_>, &str) -> Result<CertificateCheckStatus, Error> + 'a;
+
+/// The return value for the [`RemoteCallbacks::certificate_check`] callback.
+pub enum CertificateCheckStatus {
+ /// Indicates that the certificate should be accepted.
+ CertificateOk,
+ /// Indicates that the certificate callback is neither accepting nor
+ /// rejecting the certificate. The result of the certificate checks
+ /// built-in to libgit2 will be used instead.
+ CertificatePassthrough,
+}
+
+/// Callback for each updated reference on push.
+///
+/// The first argument here is the `refname` of the reference, and the second is
+/// the status message sent by a server. If the status is `Some` then the update
+/// was rejected by the remote server with a reason why.
+pub type PushUpdateReference<'a> = dyn FnMut(&str, Option<&str>) -> Result<(), Error> + 'a;
+
+/// Callback for push transfer progress
+///
+/// Parameters:
+/// * current
+/// * total
+/// * bytes
+pub type PushTransferProgress<'a> = dyn FnMut(usize, usize, usize) + 'a;
+
+/// Callback for pack progress
+///
+/// Parameters:
+/// * stage
+/// * current
+/// * total
+pub type PackProgress<'a> = dyn FnMut(PackBuilderStage, usize, usize) + 'a;
+
+/// Callback used to inform of upcoming updates.
+///
+/// The argument is a slice containing the updates which will be sent as
+/// commands to the destination.
+///
+/// The push is cancelled if an error is returned.
+pub type PushNegotiation<'a> = dyn FnMut(&[PushUpdate<'_>]) -> Result<(), Error> + 'a;
+
+impl<'a> Default for RemoteCallbacks<'a> {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl<'a> RemoteCallbacks<'a> {
+ /// Creates a new set of empty callbacks
+ pub fn new() -> RemoteCallbacks<'a> {
+ RemoteCallbacks {
+ credentials: None,
+ progress: None,
+ pack_progress: None,
+ sideband_progress: None,
+ update_tips: None,
+ certificate_check: None,
+ push_update_reference: None,
+ push_progress: None,
+ push_negotiation: None,
+ }
+ }
+
+ /// The callback through which to fetch credentials if required.
+ ///
+ /// # Example
+ ///
+ /// Prepare a callback to authenticate using the `$HOME/.ssh/id_rsa` SSH key, and
+ /// extracting the username from the URL (i.e. git@github.com:rust-lang/git2-rs.git):
+ ///
+ /// ```no_run
+ /// use git2::{Cred, RemoteCallbacks};
+ /// use std::env;
+ ///
+ /// let mut callbacks = RemoteCallbacks::new();
+ /// callbacks.credentials(|_url, username_from_url, _allowed_types| {
+ /// Cred::ssh_key(
+ /// username_from_url.unwrap(),
+ /// None,
+ /// std::path::Path::new(&format!("{}/.ssh/id_rsa", env::var("HOME").unwrap())),
+ /// None,
+ /// )
+ /// });
+ /// ```
+ pub fn credentials<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
+ where
+ F: FnMut(&str, Option<&str>, CredentialType) -> Result<Cred, Error> + 'a,
+ {
+ self.credentials = Some(Box::new(cb) as Box<Credentials<'a>>);
+ self
+ }
+
+ /// The callback through which progress is monitored.
+ pub fn transfer_progress<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
+ where
+ F: FnMut(Progress<'_>) -> bool + 'a,
+ {
+ self.progress = Some(Box::new(cb) as Box<IndexerProgress<'a>>);
+ self
+ }
+
+ /// Textual progress from the remote.
+ ///
+ /// Text sent over the progress side-band will be passed to this function
+ /// (this is the 'counting objects' output).
+ pub fn sideband_progress<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
+ where
+ F: FnMut(&[u8]) -> bool + 'a,
+ {
+ self.sideband_progress = Some(Box::new(cb) as Box<TransportMessage<'a>>);
+ self
+ }
+
+ /// Each time a reference is updated locally, the callback will be called
+ /// with information about it.
+ pub fn update_tips<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
+ where
+ F: FnMut(&str, Oid, Oid) -> bool + 'a,
+ {
+ self.update_tips = Some(Box::new(cb) as Box<UpdateTips<'a>>);
+ self
+ }
+
+ /// If certificate verification fails, then this callback will be invoked to
+ /// let the caller make the final decision of whether to allow the
+ /// connection to proceed.
+ pub fn certificate_check<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
+ where
+ F: FnMut(&Cert<'_>, &str) -> Result<CertificateCheckStatus, Error> + 'a,
+ {
+ self.certificate_check = Some(Box::new(cb) as Box<CertificateCheck<'a>>);
+ self
+ }
+
+ /// Set a callback to get invoked for each updated reference on a push.
+ ///
+ /// The first argument to the callback is the name of the reference and the
+ /// second is a status message sent by the server. If the status is `Some`
+ /// then the push was rejected.
+ pub fn push_update_reference<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
+ where
+ F: FnMut(&str, Option<&str>) -> Result<(), Error> + 'a,
+ {
+ self.push_update_reference = Some(Box::new(cb) as Box<PushUpdateReference<'a>>);
+ self
+ }
+
+ /// The callback through which progress of push transfer is monitored
+ pub fn push_transfer_progress<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
+ where
+ F: FnMut(usize, usize, usize) + 'a,
+ {
+ self.push_progress = Some(Box::new(cb) as Box<PushTransferProgress<'a>>);
+ self
+ }
+
+ /// Function to call with progress information during pack building.
+ /// Be aware that this is called inline with pack building operations,
+ /// so performance may be affected.
+ pub fn pack_progress<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
+ where
+ F: FnMut(PackBuilderStage, usize, usize) + 'a,
+ {
+ self.pack_progress = Some(Box::new(cb) as Box<PackProgress<'a>>);
+ self
+ }
+
+ /// The callback is called once between the negotiation step and the upload.
+ /// It provides information about what updates will be performed.
+ pub fn push_negotiation<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
+ where
+ F: FnMut(&[PushUpdate<'_>]) -> Result<(), Error> + 'a,
+ {
+ self.push_negotiation = Some(Box::new(cb) as Box<PushNegotiation<'a>>);
+ self
+ }
+}
+
+impl<'a> Binding for RemoteCallbacks<'a> {
+ type Raw = raw::git_remote_callbacks;
+ unsafe fn from_raw(_raw: raw::git_remote_callbacks) -> RemoteCallbacks<'a> {
+ panic!("unimplemented");
+ }
+
+ fn raw(&self) -> raw::git_remote_callbacks {
+ unsafe {
+ let mut callbacks: raw::git_remote_callbacks = mem::zeroed();
+ assert_eq!(
+ raw::git_remote_init_callbacks(&mut callbacks, raw::GIT_REMOTE_CALLBACKS_VERSION),
+ 0
+ );
+ if self.progress.is_some() {
+ callbacks.transfer_progress = Some(transfer_progress_cb);
+ }
+ if self.credentials.is_some() {
+ callbacks.credentials = Some(credentials_cb);
+ }
+ if self.sideband_progress.is_some() {
+ callbacks.sideband_progress = Some(sideband_progress_cb);
+ }
+ if self.certificate_check.is_some() {
+ callbacks.certificate_check = Some(certificate_check_cb);
+ }
+ if self.push_update_reference.is_some() {
+ callbacks.push_update_reference = Some(push_update_reference_cb);
+ }
+ if self.push_progress.is_some() {
+ callbacks.push_transfer_progress = Some(push_transfer_progress_cb);
+ }
+ if self.pack_progress.is_some() {
+ callbacks.pack_progress = Some(pack_progress_cb);
+ }
+ if self.update_tips.is_some() {
+ let f: extern "C" fn(
+ *const c_char,
+ *const raw::git_oid,
+ *const raw::git_oid,
+ *mut c_void,
+ ) -> c_int = update_tips_cb;
+ callbacks.update_tips = Some(f);
+ }
+ if self.push_negotiation.is_some() {
+ callbacks.push_negotiation = Some(push_negotiation_cb);
+ }
+ callbacks.payload = self as *const _ as *mut _;
+ callbacks
+ }
+ }
+}
+
+extern "C" fn credentials_cb(
+ ret: *mut *mut raw::git_cred,
+ url: *const c_char,
+ username_from_url: *const c_char,
+ allowed_types: c_uint,
+ payload: *mut c_void,
+) -> c_int {
+ unsafe {
+ let ok = panic::wrap(|| {
+ let payload = &mut *(payload as *mut RemoteCallbacks<'_>);
+ let callback = payload
+ .credentials
+ .as_mut()
+ .ok_or(raw::GIT_PASSTHROUGH as c_int)?;
+ *ret = ptr::null_mut();
+ let url = str::from_utf8(CStr::from_ptr(url).to_bytes())
+ .map_err(|_| raw::GIT_PASSTHROUGH as c_int)?;
+ let username_from_url = match crate::opt_bytes(&url, username_from_url) {
+ Some(username) => {
+ Some(str::from_utf8(username).map_err(|_| raw::GIT_PASSTHROUGH as c_int)?)
+ }
+ None => None,
+ };
+
+ let cred_type = CredentialType::from_bits_truncate(allowed_types as u32);
+
+ callback(url, username_from_url, cred_type).map_err(|e| {
+ let s = CString::new(e.to_string()).unwrap();
+ raw::git_error_set_str(e.class() as c_int, s.as_ptr());
+ e.raw_code() as c_int
+ })
+ });
+ match ok {
+ Some(Ok(cred)) => {
+ // Turns out it's a memory safety issue if we pass through any
+ // and all credentials into libgit2
+ if allowed_types & (cred.credtype() as c_uint) != 0 {
+ *ret = cred.unwrap();
+ 0
+ } else {
+ raw::GIT_PASSTHROUGH as c_int
+ }
+ }
+ Some(Err(e)) => e,
+ None => -1,
+ }
+ }
+}
+
+extern "C" fn transfer_progress_cb(
+ stats: *const raw::git_indexer_progress,
+ payload: *mut c_void,
+) -> c_int {
+ let ok = panic::wrap(|| unsafe {
+ let payload = &mut *(payload as *mut RemoteCallbacks<'_>);
+ let callback = match payload.progress {
+ Some(ref mut c) => c,
+ None => return true,
+ };
+ let progress = Binding::from_raw(stats);
+ callback(progress)
+ });
+ if ok == Some(true) {
+ 0
+ } else {
+ -1
+ }
+}
+
+extern "C" fn sideband_progress_cb(str: *const c_char, len: c_int, payload: *mut c_void) -> c_int {
+ let ok = panic::wrap(|| unsafe {
+ let payload = &mut *(payload as *mut RemoteCallbacks<'_>);
+ let callback = match payload.sideband_progress {
+ Some(ref mut c) => c,
+ None => return true,
+ };
+ let buf = slice::from_raw_parts(str as *const u8, len as usize);
+ callback(buf)
+ });
+ if ok == Some(true) {
+ 0
+ } else {
+ -1
+ }
+}
+
+extern "C" fn update_tips_cb(
+ refname: *const c_char,
+ a: *const raw::git_oid,
+ b: *const raw::git_oid,
+ data: *mut c_void,
+) -> c_int {
+ let ok = panic::wrap(|| unsafe {
+ let payload = &mut *(data as *mut RemoteCallbacks<'_>);
+ let callback = match payload.update_tips {
+ Some(ref mut c) => c,
+ None => return true,
+ };
+ let refname = str::from_utf8(CStr::from_ptr(refname).to_bytes()).unwrap();
+ let a = Binding::from_raw(a);
+ let b = Binding::from_raw(b);
+ callback(refname, a, b)
+ });
+ if ok == Some(true) {
+ 0
+ } else {
+ -1
+ }
+}
+
+extern "C" fn certificate_check_cb(
+ cert: *mut raw::git_cert,
+ _valid: c_int,
+ hostname: *const c_char,
+ data: *mut c_void,
+) -> c_int {
+ let ok = panic::wrap(|| unsafe {
+ let payload = &mut *(data as *mut RemoteCallbacks<'_>);
+ let callback = match payload.certificate_check {
+ Some(ref mut c) => c,
+ None => return Ok(CertificateCheckStatus::CertificatePassthrough),
+ };
+ let cert = Binding::from_raw(cert);
+ let hostname = str::from_utf8(CStr::from_ptr(hostname).to_bytes()).unwrap();
+ callback(&cert, hostname)
+ });
+ match ok {
+ Some(Ok(CertificateCheckStatus::CertificateOk)) => 0,
+ Some(Ok(CertificateCheckStatus::CertificatePassthrough)) => raw::GIT_PASSTHROUGH as c_int,
+ Some(Err(e)) => {
+ let s = CString::new(e.message()).unwrap();
+ unsafe {
+ raw::git_error_set_str(e.class() as c_int, s.as_ptr());
+ }
+ e.raw_code() as c_int
+ }
+ None => {
+ // Panic. The *should* get resumed by some future call to check().
+ -1
+ }
+ }
+}
+
+extern "C" fn push_update_reference_cb(
+ refname: *const c_char,
+ status: *const c_char,
+ data: *mut c_void,
+) -> c_int {
+ panic::wrap(|| unsafe {
+ let payload = &mut *(data as *mut RemoteCallbacks<'_>);
+ let callback = match payload.push_update_reference {
+ Some(ref mut c) => c,
+ None => return 0,
+ };
+ let refname = str::from_utf8(CStr::from_ptr(refname).to_bytes()).unwrap();
+ let status = if status.is_null() {
+ None
+ } else {
+ Some(str::from_utf8(CStr::from_ptr(status).to_bytes()).unwrap())
+ };
+ match callback(refname, status) {
+ Ok(()) => 0,
+ Err(e) => e.raw_code(),
+ }
+ })
+ .unwrap_or(-1)
+}
+
+extern "C" fn push_transfer_progress_cb(
+ progress: c_uint,
+ total: c_uint,
+ bytes: size_t,
+ data: *mut c_void,
+) -> c_int {
+ panic::wrap(|| unsafe {
+ let payload = &mut *(data as *mut RemoteCallbacks<'_>);
+ let callback = match payload.push_progress {
+ Some(ref mut c) => c,
+ None => return 0,
+ };
+
+ callback(progress as usize, total as usize, bytes as usize);
+
+ 0
+ })
+ .unwrap_or(-1)
+}
+
+extern "C" fn pack_progress_cb(
+ stage: raw::git_packbuilder_stage_t,
+ current: c_uint,
+ total: c_uint,
+ data: *mut c_void,
+) -> c_int {
+ panic::wrap(|| unsafe {
+ let payload = &mut *(data as *mut RemoteCallbacks<'_>);
+ let callback = match payload.pack_progress {
+ Some(ref mut c) => c,
+ None => return 0,
+ };
+
+ let stage = Binding::from_raw(stage);
+
+ callback(stage, current as usize, total as usize);
+
+ 0
+ })
+ .unwrap_or(-1)
+}
+
+extern "C" fn push_negotiation_cb(
+ updates: *mut *const raw::git_push_update,
+ len: size_t,
+ payload: *mut c_void,
+) -> c_int {
+ panic::wrap(|| unsafe {
+ let payload = &mut *(payload as *mut RemoteCallbacks<'_>);
+ let callback = match payload.push_negotiation {
+ Some(ref mut c) => c,
+ None => return 0,
+ };
+
+ let updates = slice::from_raw_parts(updates as *mut PushUpdate<'_>, len);
+ match callback(updates) {
+ Ok(()) => 0,
+ Err(e) => e.raw_code(),
+ }
+ })
+ .unwrap_or(-1)
+}