binaryninja/collaboration/
snapshot.rsuse std::ffi::{c_char, c_void};
use std::ptr::NonNull;
use std::time::SystemTime;
use super::{sync, Remote, RemoteFile, RemoteProject};
use crate::binary_view::{BinaryView, BinaryViewExt};
use crate::collaboration::undo::{RemoteUndoEntry, RemoteUndoEntryId};
use crate::database::snapshot::Snapshot;
use crate::progress::{NoProgressCallback, ProgressCallback};
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
use crate::string::{BnStrCompatible, BnString};
use binaryninjacore_sys::*;
#[repr(transparent)]
pub struct RemoteSnapshot {
pub(crate) handle: NonNull<BNCollaborationSnapshot>,
}
impl RemoteSnapshot {
pub(crate) unsafe fn from_raw(handle: NonNull<BNCollaborationSnapshot>) -> Self {
Self { handle }
}
pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNCollaborationSnapshot>) -> Ref<Self> {
Ref::new(Self { handle })
}
pub fn get_for_local_snapshot(snapshot: &Snapshot) -> Result<Option<Ref<RemoteSnapshot>>, ()> {
sync::get_remote_snapshot_from_local(snapshot)
}
pub fn file(&self) -> Result<Ref<RemoteFile>, ()> {
let result = unsafe { BNCollaborationSnapshotGetFile(self.handle.as_ptr()) };
let raw = NonNull::new(result).ok_or(())?;
Ok(unsafe { RemoteFile::ref_from_raw(raw) })
}
pub fn project(&self) -> Result<Ref<RemoteProject>, ()> {
let result = unsafe { BNCollaborationSnapshotGetProject(self.handle.as_ptr()) };
let raw = NonNull::new(result).ok_or(())?;
Ok(unsafe { RemoteProject::ref_from_raw(raw) })
}
pub fn remote(&self) -> Result<Ref<Remote>, ()> {
let result = unsafe { BNCollaborationSnapshotGetRemote(self.handle.as_ptr()) };
let raw = NonNull::new(result).ok_or(())?;
Ok(unsafe { Remote::ref_from_raw(raw) })
}
pub fn url(&self) -> BnString {
let value = unsafe { BNCollaborationSnapshotGetUrl(self.handle.as_ptr()) };
assert!(!value.is_null());
unsafe { BnString::from_raw(value) }
}
pub fn id(&self) -> BnString {
let value = unsafe { BNCollaborationSnapshotGetId(self.handle.as_ptr()) };
assert!(!value.is_null());
unsafe { BnString::from_raw(value) }
}
pub fn name(&self) -> BnString {
let value = unsafe { BNCollaborationSnapshotGetName(self.handle.as_ptr()) };
assert!(!value.is_null());
unsafe { BnString::from_raw(value) }
}
pub fn title(&self) -> BnString {
let value = unsafe { BNCollaborationSnapshotGetTitle(self.handle.as_ptr()) };
assert!(!value.is_null());
unsafe { BnString::from_raw(value) }
}
pub fn description(&self) -> BnString {
let value = unsafe { BNCollaborationSnapshotGetDescription(self.handle.as_ptr()) };
assert!(!value.is_null());
unsafe { BnString::from_raw(value) }
}
pub fn author(&self) -> BnString {
let value = unsafe { BNCollaborationSnapshotGetAuthor(self.handle.as_ptr()) };
assert!(!value.is_null());
unsafe { BnString::from_raw(value) }
}
pub fn author_username(&self) -> BnString {
let value = unsafe { BNCollaborationSnapshotGetAuthorUsername(self.handle.as_ptr()) };
assert!(!value.is_null());
unsafe { BnString::from_raw(value) }
}
pub fn created(&self) -> SystemTime {
let timestamp = unsafe { BNCollaborationSnapshotGetCreated(self.handle.as_ptr()) };
crate::ffi::time_from_bn(timestamp.try_into().unwrap())
}
pub fn last_modified(&self) -> SystemTime {
let timestamp = unsafe { BNCollaborationSnapshotGetLastModified(self.handle.as_ptr()) };
crate::ffi::time_from_bn(timestamp.try_into().unwrap())
}
pub fn hash(&self) -> BnString {
let value = unsafe { BNCollaborationSnapshotGetHash(self.handle.as_ptr()) };
assert!(!value.is_null());
unsafe { BnString::from_raw(value) }
}
pub fn snapshot_file_hash(&self) -> BnString {
let value = unsafe { BNCollaborationSnapshotGetSnapshotFileHash(self.handle.as_ptr()) };
assert!(!value.is_null());
unsafe { BnString::from_raw(value) }
}
pub fn has_pulled_undo_entries(&self) -> bool {
unsafe { BNCollaborationSnapshotHasPulledUndoEntries(self.handle.as_ptr()) }
}
pub fn is_finalized(&self) -> bool {
unsafe { BNCollaborationSnapshotIsFinalized(self.handle.as_ptr()) }
}
pub fn parent_ids(&self) -> Result<Array<BnString>, ()> {
let mut count = 0;
let raw = unsafe { BNCollaborationSnapshotGetParentIds(self.handle.as_ptr(), &mut count) };
(!raw.is_null())
.then(|| unsafe { Array::new(raw, count, ()) })
.ok_or(())
}
pub fn child_ids(&self) -> Result<Array<BnString>, ()> {
let mut count = 0;
let raw = unsafe { BNCollaborationSnapshotGetChildIds(self.handle.as_ptr(), &mut count) };
(!raw.is_null())
.then(|| unsafe { Array::new(raw, count, ()) })
.ok_or(())
}
pub fn parents(&self) -> Result<Array<RemoteSnapshot>, ()> {
let mut count = 0;
let raw = unsafe { BNCollaborationSnapshotGetParents(self.handle.as_ptr(), &mut count) };
(!raw.is_null())
.then(|| unsafe { Array::new(raw, count, ()) })
.ok_or(())
}
pub fn children(&self) -> Result<Array<RemoteSnapshot>, ()> {
let mut count = 0;
let raw = unsafe { BNCollaborationSnapshotGetChildren(self.handle.as_ptr(), &mut count) };
(!raw.is_null())
.then(|| unsafe { Array::new(raw, count, ()) })
.ok_or(())
}
pub fn undo_entries(&self) -> Result<Array<RemoteUndoEntry>, ()> {
if !self.has_pulled_undo_entries() {
self.pull_undo_entries()?;
}
let mut count = 0;
let raw =
unsafe { BNCollaborationSnapshotGetUndoEntries(self.handle.as_ptr(), &mut count) };
(!raw.is_null())
.then(|| unsafe { Array::new(raw, count, ()) })
.ok_or(())
}
pub fn get_undo_entry_by_id(
&self,
id: RemoteUndoEntryId,
) -> Result<Option<Ref<RemoteUndoEntry>>, ()> {
if !self.has_pulled_undo_entries() {
self.pull_undo_entries()?;
}
let raw = unsafe { BNCollaborationSnapshotGetUndoEntryById(self.handle.as_ptr(), id.0) };
Ok(NonNull::new(raw).map(|handle| unsafe { RemoteUndoEntry::ref_from_raw(handle) }))
}
pub fn pull_undo_entries(&self) -> Result<(), ()> {
self.pull_undo_entries_with_progress(NoProgressCallback)
}
pub fn pull_undo_entries_with_progress<P: ProgressCallback>(
&self,
mut progress: P,
) -> Result<(), ()> {
let success = unsafe {
BNCollaborationSnapshotPullUndoEntries(
self.handle.as_ptr(),
Some(P::cb_progress_callback),
&mut progress as *mut P as *mut c_void,
)
};
success.then_some(()).ok_or(())
}
pub fn create_undo_entry<S: BnStrCompatible>(
&self,
parent: Option<u64>,
data: S,
) -> Result<Ref<RemoteUndoEntry>, ()> {
let data = data.into_bytes_with_nul();
let value = unsafe {
BNCollaborationSnapshotCreateUndoEntry(
self.handle.as_ptr(),
parent.is_some(),
parent.unwrap_or(0),
data.as_ref().as_ptr() as *const c_char,
)
};
let handle = NonNull::new(value).ok_or(())?;
Ok(unsafe { RemoteUndoEntry::ref_from_raw(handle) })
}
pub fn finalize(&self) -> Result<(), ()> {
let success = unsafe { BNCollaborationSnapshotFinalize(self.handle.as_ptr()) };
success.then_some(()).ok_or(())
}
pub fn get_local_snapshot(&self, bv: &BinaryView) -> Result<Option<Ref<Snapshot>>, ()> {
let Some(db) = bv.file().database() else {
return Ok(None);
};
sync::get_local_snapshot_for_remote(self, &db)
}
pub fn analysis_cache_build_id(&self) -> u64 {
unsafe { BNCollaborationSnapshotGetAnalysisCacheBuildId(self.handle.as_ptr()) }
}
}
impl PartialEq for RemoteSnapshot {
fn eq(&self, other: &Self) -> bool {
self.id() == other.id()
}
}
impl Eq for RemoteSnapshot {}
impl ToOwned for RemoteSnapshot {
type Owned = Ref<Self>;
fn to_owned(&self) -> Self::Owned {
unsafe { RefCountable::inc_ref(self) }
}
}
unsafe impl RefCountable for RemoteSnapshot {
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
Ref::new(Self {
handle: NonNull::new(BNNewCollaborationSnapshotReference(handle.handle.as_ptr()))
.unwrap(),
})
}
unsafe fn dec_ref(handle: &Self) {
BNFreeCollaborationSnapshot(handle.handle.as_ptr());
}
}
impl CoreArrayProvider for RemoteSnapshot {
type Raw = *mut BNCollaborationSnapshot;
type Context = ();
type Wrapped<'a> = Guard<'a, RemoteSnapshot>;
}
unsafe impl CoreArrayProviderInner for RemoteSnapshot {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeCollaborationSnapshotList(raw, count)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
let raw_ptr = NonNull::new(*raw).unwrap();
Guard::new(Self::from_raw(raw_ptr), context)
}
}