summaryrefslogtreecommitdiff
path: root/rust/src/collaboration/group.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src/collaboration/group.rs')
-rw-r--r--rust/src/collaboration/group.rs196
1 files changed, 196 insertions, 0 deletions
diff --git a/rust/src/collaboration/group.rs b/rust/src/collaboration/group.rs
new file mode 100644
index 00000000..bad09d6c
--- /dev/null
+++ b/rust/src/collaboration/group.rs
@@ -0,0 +1,196 @@
+use super::Remote;
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
+use crate::string::{BnStrCompatible, BnString};
+use binaryninjacore_sys::*;
+use std::ffi::c_char;
+use std::fmt;
+use std::fmt::{Display, Formatter};
+use std::ptr::NonNull;
+
+#[repr(transparent)]
+pub struct RemoteGroup {
+ pub(crate) handle: NonNull<BNCollaborationGroup>,
+}
+
+impl RemoteGroup {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNCollaborationGroup>) -> Self {
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNCollaborationGroup>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ /// Owning Remote
+ pub fn remote(&self) -> Result<Ref<Remote>, ()> {
+ let value = unsafe { BNCollaborationGroupGetRemote(self.handle.as_ptr()) };
+ NonNull::new(value)
+ .map(|handle| unsafe { Remote::ref_from_raw(handle) })
+ .ok_or(())
+ }
+
+ /// Web api endpoint url
+ pub fn url(&self) -> BnString {
+ let value = unsafe { BNCollaborationGroupGetUrl(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// Unique id
+ pub fn id(&self) -> GroupId {
+ GroupId(unsafe { BNCollaborationGroupGetId(self.handle.as_ptr()) })
+ }
+
+ /// Group name
+ pub fn name(&self) -> BnString {
+ let value = unsafe { BNCollaborationGroupGetName(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// Set group name
+ /// You will need to push the group to update the Remote.
+ pub fn set_name<U: BnStrCompatible>(&self, name: U) {
+ let name = name.into_bytes_with_nul();
+ unsafe {
+ BNCollaborationGroupSetName(
+ self.handle.as_ptr(),
+ name.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ /// Get list of users in the group
+ pub fn users(&self) -> Result<(Array<BnString>, Array<BnString>), ()> {
+ let mut usernames = std::ptr::null_mut();
+ let mut user_ids = std::ptr::null_mut();
+ let mut count = 0;
+ // TODO: This should only fail if collaboration is not supported.
+ // TODO: Because you should not have a RemoteGroup at that point we can ignore?
+ let success = unsafe {
+ BNCollaborationGroupGetUsers(
+ self.handle.as_ptr(),
+ &mut user_ids,
+ &mut usernames,
+ &mut count,
+ )
+ };
+ success
+ .then(|| unsafe {
+ let ids = Array::new(user_ids, count, ());
+ let users = Array::new(usernames, count, ());
+ (ids, users)
+ })
+ .ok_or(())
+ }
+
+ // TODO: Are any permissions required to the set the remote group users?
+ /// Set the list of users in a group by their usernames.
+ /// You will need to push the group to update the Remote.
+ pub fn set_users<I>(&self, usernames: I) -> Result<(), ()>
+ where
+ I: IntoIterator,
+ I::Item: BnStrCompatible,
+ {
+ let usernames: Vec<_> = usernames
+ .into_iter()
+ .map(|u| u.into_bytes_with_nul())
+ .collect();
+ let mut usernames_raw: Vec<_> = usernames
+ .iter()
+ .map(|s| s.as_ref().as_ptr() as *const c_char)
+ .collect();
+ // TODO: This should only fail if collaboration is not supported.
+ // TODO: Because you should not have a RemoteGroup at that point we can ignore?
+ // TODO: Do you need any permissions to do this?
+ let success = unsafe {
+ BNCollaborationGroupSetUsernames(
+ self.handle.as_ptr(),
+ usernames_raw.as_mut_ptr(),
+ usernames_raw.len(),
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Test if a group has a user with the given username
+ pub fn contains_user<U: BnStrCompatible>(&self, username: U) -> bool {
+ let username = username.into_bytes_with_nul();
+ unsafe {
+ BNCollaborationGroupContainsUser(
+ self.handle.as_ptr(),
+ username.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+}
+
+impl PartialEq for RemoteGroup {
+ fn eq(&self, other: &Self) -> bool {
+ self.id() == other.id()
+ }
+}
+impl Eq for RemoteGroup {}
+
+impl ToOwned for RemoteGroup {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for RemoteGroup {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewCollaborationGroupReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeCollaborationGroup(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for RemoteGroup {
+ type Raw = *mut BNCollaborationGroup;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, Self>;
+}
+
+unsafe impl CoreArrayProviderInner for RemoteGroup {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeCollaborationGroupList(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)
+ }
+}
+
+#[repr(transparent)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct GroupId(pub u64);
+
+impl Display for GroupId {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_fmt(format_args!("{}", self.0))
+ }
+}
+
+impl CoreArrayProvider for GroupId {
+ type Raw = u64;
+ type Context = ();
+ type Wrapped<'a> = GroupId;
+}
+
+unsafe impl CoreArrayProviderInner for GroupId {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNCollaborationFreeIdList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ GroupId(*raw)
+ }
+}