1use libc::{c_char, c_int, c_uint, c_void, size_t};
2use std::ffi::CStr;
3use std::mem;
4use std::ptr;
5use std::slice;
6use std::str;
7
8use crate::cert::Cert;
9use crate::util::Binding;
10use crate::{
11 panic, raw, Cred, CredentialType, Error, IndexerProgress, Oid, PackBuilderStage, Progress,
12 PushUpdate,
13};
14
15pub struct RemoteCallbacks<'a> {
21 push_progress: Option<Box<PushTransferProgress<'a>>>,
22 progress: Option<Box<IndexerProgress<'a>>>,
23 pack_progress: Option<Box<PackProgress<'a>>>,
24 credentials: Option<Box<Credentials<'a>>>,
25 sideband_progress: Option<Box<TransportMessage<'a>>>,
26 update_tips: Option<Box<UpdateTips<'a>>>,
27 certificate_check: Option<Box<CertificateCheck<'a>>>,
28 push_update_reference: Option<Box<PushUpdateReference<'a>>>,
29 push_negotiation: Option<Box<PushNegotiation<'a>>>,
30}
31
32pub type Credentials<'a> =
39 dyn FnMut(&str, Option<&str>, CredentialType) -> Result<Cred, Error> + 'a;
40
41pub type TransportMessage<'a> = dyn FnMut(&[u8]) -> bool + 'a;
45
46pub type UpdateTips<'a> = dyn FnMut(&str, Oid, Oid) -> bool + 'a;
48
49pub type CertificateCheck<'a> =
57 dyn FnMut(&Cert<'_>, &str) -> Result<CertificateCheckStatus, Error> + 'a;
58
59pub enum CertificateCheckStatus {
61 CertificateOk,
63 CertificatePassthrough,
67}
68
69pub type PushUpdateReference<'a> = dyn FnMut(&str, Option<&str>) -> Result<(), Error> + 'a;
75
76pub type PushTransferProgress<'a> = dyn FnMut(usize, usize, usize) + 'a;
83
84pub type PackProgress<'a> = dyn FnMut(PackBuilderStage, usize, usize) + 'a;
94
95pub type PushNegotiation<'a> = dyn FnMut(&[PushUpdate<'_>]) -> Result<(), Error> + 'a;
102
103impl<'a> Default for RemoteCallbacks<'a> {
104 fn default() -> Self {
105 Self::new()
106 }
107}
108
109impl<'a> RemoteCallbacks<'a> {
110 pub fn new() -> RemoteCallbacks<'a> {
112 RemoteCallbacks {
113 credentials: None,
114 progress: None,
115 pack_progress: None,
116 sideband_progress: None,
117 update_tips: None,
118 certificate_check: None,
119 push_update_reference: None,
120 push_progress: None,
121 push_negotiation: None,
122 }
123 }
124
125 pub fn credentials<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
147 where
148 F: FnMut(&str, Option<&str>, CredentialType) -> Result<Cred, Error> + 'a,
149 {
150 self.credentials = Some(Box::new(cb) as Box<Credentials<'a>>);
151 self
152 }
153
154 pub fn transfer_progress<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
156 where
157 F: FnMut(Progress<'_>) -> bool + 'a,
158 {
159 self.progress = Some(Box::new(cb) as Box<IndexerProgress<'a>>);
160 self
161 }
162
163 pub fn sideband_progress<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
168 where
169 F: FnMut(&[u8]) -> bool + 'a,
170 {
171 self.sideband_progress = Some(Box::new(cb) as Box<TransportMessage<'a>>);
172 self
173 }
174
175 pub fn update_tips<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
178 where
179 F: FnMut(&str, Oid, Oid) -> bool + 'a,
180 {
181 self.update_tips = Some(Box::new(cb) as Box<UpdateTips<'a>>);
182 self
183 }
184
185 pub fn certificate_check<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
189 where
190 F: FnMut(&Cert<'_>, &str) -> Result<CertificateCheckStatus, Error> + 'a,
191 {
192 self.certificate_check = Some(Box::new(cb) as Box<CertificateCheck<'a>>);
193 self
194 }
195
196 pub fn push_update_reference<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
202 where
203 F: FnMut(&str, Option<&str>) -> Result<(), Error> + 'a,
204 {
205 self.push_update_reference = Some(Box::new(cb) as Box<PushUpdateReference<'a>>);
206 self
207 }
208
209 pub fn push_transfer_progress<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
216 where
217 F: FnMut(usize, usize, usize) + 'a,
218 {
219 self.push_progress = Some(Box::new(cb) as Box<PushTransferProgress<'a>>);
220 self
221 }
222
223 pub fn pack_progress<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
233 where
234 F: FnMut(PackBuilderStage, usize, usize) + 'a,
235 {
236 self.pack_progress = Some(Box::new(cb) as Box<PackProgress<'a>>);
237 self
238 }
239
240 pub fn push_negotiation<F>(&mut self, cb: F) -> &mut RemoteCallbacks<'a>
247 where
248 F: FnMut(&[PushUpdate<'_>]) -> Result<(), Error> + 'a,
249 {
250 self.push_negotiation = Some(Box::new(cb) as Box<PushNegotiation<'a>>);
251 self
252 }
253}
254
255impl<'a> Binding for RemoteCallbacks<'a> {
256 type Raw = raw::git_remote_callbacks;
257 unsafe fn from_raw(_raw: raw::git_remote_callbacks) -> RemoteCallbacks<'a> {
258 panic!("unimplemented");
259 }
260
261 fn raw(&self) -> raw::git_remote_callbacks {
262 unsafe {
263 let mut callbacks: raw::git_remote_callbacks = mem::zeroed();
264 assert_eq!(
265 raw::git_remote_init_callbacks(&mut callbacks, raw::GIT_REMOTE_CALLBACKS_VERSION),
266 0
267 );
268 if self.progress.is_some() {
269 callbacks.transfer_progress = Some(transfer_progress_cb);
270 }
271 if self.credentials.is_some() {
272 callbacks.credentials = Some(credentials_cb);
273 }
274 if self.sideband_progress.is_some() {
275 callbacks.sideband_progress = Some(sideband_progress_cb);
276 }
277 if self.certificate_check.is_some() {
278 callbacks.certificate_check = Some(certificate_check_cb);
279 }
280 if self.push_update_reference.is_some() {
281 callbacks.push_update_reference = Some(push_update_reference_cb);
282 }
283 if self.push_progress.is_some() {
284 callbacks.push_transfer_progress = Some(push_transfer_progress_cb);
285 }
286 if self.pack_progress.is_some() {
287 callbacks.pack_progress = Some(pack_progress_cb);
288 }
289 if self.update_tips.is_some() {
290 let f: extern "C" fn(
291 *const c_char,
292 *const raw::git_oid,
293 *const raw::git_oid,
294 *mut c_void,
295 ) -> c_int = update_tips_cb;
296 callbacks.update_tips = Some(f);
297 }
298 if self.push_negotiation.is_some() {
299 callbacks.push_negotiation = Some(push_negotiation_cb);
300 }
301 callbacks.payload = self as *const _ as *mut _;
302 callbacks
303 }
304 }
305}
306
307extern "C" fn credentials_cb(
308 ret: *mut *mut raw::git_cred,
309 url: *const c_char,
310 username_from_url: *const c_char,
311 allowed_types: c_uint,
312 payload: *mut c_void,
313) -> c_int {
314 unsafe {
315 let ok = panic::wrap(|| {
316 let payload = &mut *(payload as *mut RemoteCallbacks<'_>);
317 let callback = payload
318 .credentials
319 .as_mut()
320 .ok_or(raw::GIT_PASSTHROUGH as c_int)?;
321 *ret = ptr::null_mut();
322 let url = str::from_utf8(CStr::from_ptr(url).to_bytes())
323 .map_err(|_| raw::GIT_PASSTHROUGH as c_int)?;
324 let username_from_url = match crate::opt_bytes(&url, username_from_url) {
325 Some(username) => {
326 Some(str::from_utf8(username).map_err(|_| raw::GIT_PASSTHROUGH as c_int)?)
327 }
328 None => None,
329 };
330
331 let cred_type = CredentialType::from_bits_truncate(allowed_types as u32);
332
333 callback(url, username_from_url, cred_type).map_err(|e| e.raw_set_git_error())
334 });
335 match ok {
336 Some(Ok(cred)) => {
337 if allowed_types & (cred.credtype() as c_uint) != 0 {
340 *ret = cred.unwrap();
341 0
342 } else {
343 raw::GIT_PASSTHROUGH as c_int
344 }
345 }
346 Some(Err(e)) => e,
347 None => -1,
348 }
349 }
350}
351
352extern "C" fn transfer_progress_cb(
353 stats: *const raw::git_indexer_progress,
354 payload: *mut c_void,
355) -> c_int {
356 let ok = panic::wrap(|| unsafe {
357 let payload = &mut *(payload as *mut RemoteCallbacks<'_>);
358 let callback = match payload.progress {
359 Some(ref mut c) => c,
360 None => return true,
361 };
362 let progress = Binding::from_raw(stats);
363 callback(progress)
364 });
365 if ok == Some(true) {
366 0
367 } else {
368 -1
369 }
370}
371
372extern "C" fn sideband_progress_cb(str: *const c_char, len: c_int, payload: *mut c_void) -> c_int {
373 let ok = panic::wrap(|| unsafe {
374 let payload = &mut *(payload as *mut RemoteCallbacks<'_>);
375 let callback = match payload.sideband_progress {
376 Some(ref mut c) => c,
377 None => return true,
378 };
379 let buf = slice::from_raw_parts(str as *const u8, len as usize);
380 callback(buf)
381 });
382 if ok == Some(true) {
383 0
384 } else {
385 -1
386 }
387}
388
389extern "C" fn update_tips_cb(
390 refname: *const c_char,
391 a: *const raw::git_oid,
392 b: *const raw::git_oid,
393 data: *mut c_void,
394) -> c_int {
395 let ok = panic::wrap(|| unsafe {
396 let payload = &mut *(data as *mut RemoteCallbacks<'_>);
397 let callback = match payload.update_tips {
398 Some(ref mut c) => c,
399 None => return true,
400 };
401 let refname = str::from_utf8(CStr::from_ptr(refname).to_bytes()).unwrap();
402 let a = Binding::from_raw(a);
403 let b = Binding::from_raw(b);
404 callback(refname, a, b)
405 });
406 if ok == Some(true) {
407 0
408 } else {
409 -1
410 }
411}
412
413extern "C" fn certificate_check_cb(
414 cert: *mut raw::git_cert,
415 _valid: c_int,
416 hostname: *const c_char,
417 data: *mut c_void,
418) -> c_int {
419 let ok = panic::wrap(|| unsafe {
420 let payload = &mut *(data as *mut RemoteCallbacks<'_>);
421 let callback = match payload.certificate_check {
422 Some(ref mut c) => c,
423 None => return Ok(CertificateCheckStatus::CertificatePassthrough),
424 };
425 let cert = Binding::from_raw(cert);
426 let hostname = str::from_utf8(CStr::from_ptr(hostname).to_bytes()).unwrap();
427 callback(&cert, hostname)
428 });
429 match ok {
430 Some(Ok(CertificateCheckStatus::CertificateOk)) => 0,
431 Some(Ok(CertificateCheckStatus::CertificatePassthrough)) => raw::GIT_PASSTHROUGH as c_int,
432 Some(Err(e)) => unsafe { e.raw_set_git_error() },
433 None => {
434 -1
436 }
437 }
438}
439
440extern "C" fn push_update_reference_cb(
441 refname: *const c_char,
442 status: *const c_char,
443 data: *mut c_void,
444) -> c_int {
445 panic::wrap(|| unsafe {
446 let payload = &mut *(data as *mut RemoteCallbacks<'_>);
447 let callback = match payload.push_update_reference {
448 Some(ref mut c) => c,
449 None => return 0,
450 };
451 let refname = str::from_utf8(CStr::from_ptr(refname).to_bytes()).unwrap();
452 let status = if status.is_null() {
453 None
454 } else {
455 Some(str::from_utf8(CStr::from_ptr(status).to_bytes()).unwrap())
456 };
457 match callback(refname, status) {
458 Ok(()) => 0,
459 Err(e) => e.raw_set_git_error(),
460 }
461 })
462 .unwrap_or(-1)
463}
464
465extern "C" fn push_transfer_progress_cb(
466 progress: c_uint,
467 total: c_uint,
468 bytes: size_t,
469 data: *mut c_void,
470) -> c_int {
471 panic::wrap(|| unsafe {
472 let payload = &mut *(data as *mut RemoteCallbacks<'_>);
473 let callback = match payload.push_progress {
474 Some(ref mut c) => c,
475 None => return 0,
476 };
477
478 callback(progress as usize, total as usize, bytes as usize);
479
480 0
481 })
482 .unwrap_or(-1)
483}
484
485extern "C" fn pack_progress_cb(
486 stage: raw::git_packbuilder_stage_t,
487 current: c_uint,
488 total: c_uint,
489 data: *mut c_void,
490) -> c_int {
491 panic::wrap(|| unsafe {
492 let payload = &mut *(data as *mut RemoteCallbacks<'_>);
493 let callback = match payload.pack_progress {
494 Some(ref mut c) => c,
495 None => return 0,
496 };
497
498 let stage = Binding::from_raw(stage);
499
500 callback(stage, current as usize, total as usize);
501
502 0
503 })
504 .unwrap_or(-1)
505}
506
507extern "C" fn push_negotiation_cb(
508 updates: *mut *const raw::git_push_update,
509 len: size_t,
510 payload: *mut c_void,
511) -> c_int {
512 panic::wrap(|| unsafe {
513 let payload = &mut *(payload as *mut RemoteCallbacks<'_>);
514 let callback = match payload.push_negotiation {
515 Some(ref mut c) => c,
516 None => return 0,
517 };
518
519 let updates = slice::from_raw_parts(updates as *mut PushUpdate<'_>, len);
520 match callback(updates) {
521 Ok(()) => 0,
522 Err(e) => e.raw_set_git_error(),
523 }
524 })
525 .unwrap_or(-1)
526}