binaryninja/collaboration/
group.rs

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