binaryninja/collaboration/
group.rs

1use super::Remote;
2use crate::collaboration::RemoteUser;
3use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
4use crate::string::{BnString, IntoCStr};
5use binaryninjacore_sys::*;
6use std::fmt;
7use std::fmt::{Display, Formatter};
8use std::ptr::NonNull;
9
10#[repr(transparent)]
11pub struct RemoteGroup {
12    pub(crate) handle: NonNull<BNCollaborationGroup>,
13}
14
15impl RemoteGroup {
16    pub(crate) unsafe fn from_raw(handle: NonNull<BNCollaborationGroup>) -> Self {
17        Self { handle }
18    }
19
20    pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNCollaborationGroup>) -> Ref<Self> {
21        Ref::new(Self { handle })
22    }
23
24    /// Owning Remote
25    pub fn remote(&self) -> Result<Ref<Remote>, ()> {
26        let value = unsafe { BNCollaborationGroupGetRemote(self.handle.as_ptr()) };
27        NonNull::new(value)
28            .map(|handle| unsafe { Remote::ref_from_raw(handle) })
29            .ok_or(())
30    }
31
32    /// Web api endpoint url
33    pub fn url(&self) -> String {
34        let value = unsafe { BNCollaborationGroupGetUrl(self.handle.as_ptr()) };
35        assert!(!value.is_null());
36        unsafe { BnString::into_string(value) }
37    }
38
39    /// Unique id
40    pub fn id(&self) -> GroupId {
41        GroupId(unsafe { BNCollaborationGroupGetId(self.handle.as_ptr()) })
42    }
43
44    /// Group name
45    pub fn name(&self) -> String {
46        let value = unsafe { BNCollaborationGroupGetName(self.handle.as_ptr()) };
47        assert!(!value.is_null());
48        unsafe { BnString::into_string(value) }
49    }
50
51    /// Set group name
52    /// You will need to push the group to update the Remote.
53    pub fn set_name(&self, name: &str) {
54        let name = name.to_cstr();
55        unsafe { BNCollaborationGroupSetName(self.handle.as_ptr(), name.as_ptr()) }
56    }
57
58    /// Get list of users in the group
59    pub fn users(&self) -> Result<Array<RemoteUser>, ()> {
60        let mut count = 0;
61        // TODO: This should only fail if collaboration is not supported.
62        // TODO: Because you should not have a RemoteGroup at that point we can ignore?
63        let result = unsafe { BNCollaborationGroupGetUsers(self.handle.as_ptr(), &mut count) };
64        (!result.is_null())
65            .then(|| unsafe { Array::new(result, count, ()) })
66            .ok_or(())
67    }
68
69    // TODO: Are any permissions required to the set the remote group users?
70    /// Set the list of users in a group.
71    /// You will need to push the group to update the Remote.
72    pub fn set_users<I>(&self, users: I) -> Result<(), ()>
73    where
74        I: IntoIterator<Item = Ref<RemoteUser>>,
75    {
76        let mut users_raw: Vec<_> = users.into_iter().map(|s| s.handle.as_ptr()).collect();
77        // TODO: This should only fail if collaboration is not supported.
78        // TODO: Because you should not have a RemoteGroup at that point we can ignore?
79        // TODO: Do you need any permissions to do this?
80        let success = unsafe {
81            BNCollaborationGroupSetUsers(
82                self.handle.as_ptr(),
83                users_raw.as_mut_ptr(),
84                users_raw.len(),
85            )
86        };
87        success.then_some(()).ok_or(())
88    }
89
90    /// Test if a group contains a user
91    pub fn contains_user(&self, user: Ref<RemoteUser>) -> bool {
92        unsafe { BNCollaborationGroupContainsUser(self.handle.as_ptr(), user.handle.as_ptr()) }
93    }
94}
95
96impl PartialEq for RemoteGroup {
97    fn eq(&self, other: &Self) -> bool {
98        self.id() == other.id()
99    }
100}
101impl Eq for RemoteGroup {}
102
103impl ToOwned for RemoteGroup {
104    type Owned = Ref<Self>;
105
106    fn to_owned(&self) -> Self::Owned {
107        unsafe { RefCountable::inc_ref(self) }
108    }
109}
110
111unsafe impl RefCountable for RemoteGroup {
112    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
113        Ref::new(Self {
114            handle: NonNull::new(BNNewCollaborationGroupReference(handle.handle.as_ptr())).unwrap(),
115        })
116    }
117
118    unsafe fn dec_ref(handle: &Self) {
119        BNFreeCollaborationGroup(handle.handle.as_ptr());
120    }
121}
122
123impl CoreArrayProvider for RemoteGroup {
124    type Raw = *mut BNCollaborationGroup;
125    type Context = ();
126    type Wrapped<'a> = Guard<'a, Self>;
127}
128
129unsafe impl CoreArrayProviderInner for RemoteGroup {
130    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
131        BNFreeCollaborationGroupList(raw, count)
132    }
133
134    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
135        let raw_ptr = NonNull::new(*raw).unwrap();
136        Guard::new(Self::from_raw(raw_ptr), context)
137    }
138}
139
140#[repr(transparent)]
141#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
142pub struct GroupId(pub u64);
143
144impl Display for GroupId {
145    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
146        f.write_fmt(format_args!("{}", self.0))
147    }
148}
149
150impl CoreArrayProvider for GroupId {
151    type Raw = u64;
152    type Context = ();
153    type Wrapped<'a> = GroupId;
154}
155
156unsafe impl CoreArrayProviderInner for GroupId {
157    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
158        BNCollaborationFreeIdList(raw, count)
159    }
160
161    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
162        GroupId(*raw)
163    }
164}