binaryninja/
collaboration.rs

1//! The collaboration API is **unstable** and as such will undergo breaking changes in the near future!
2
3mod changeset;
4mod file;
5mod folder;
6mod group;
7mod merge;
8mod permission;
9mod project;
10mod remote;
11mod snapshot;
12mod sync;
13mod undo;
14mod user;
15
16pub use changeset::*;
17pub use file::*;
18pub use folder::*;
19pub use group::*;
20pub use merge::*;
21pub use permission::*;
22pub use project::*;
23pub use remote::*;
24pub use snapshot::*;
25use std::ffi::c_char;
26use std::ptr::NonNull;
27pub use sync::*;
28pub use user::*;
29
30use binaryninjacore_sys::*;
31
32use crate::rc::{Array, Ref};
33use crate::string::{BnString, IntoCStr};
34
35// TODO: Should we pull metadata and information required to call a function? Or should we add documentation
36// TODO: on what functions need to have been called prior? I feel like we should make the user have to pull
37// TODO: the data because they have a greater understanding of where the function is being called from.
38
39/// Check whether the client has collaboration support.
40///
41/// Call this if you intend on providing divergent behavior, as otherwise you will likely
42/// crash calling collaboration APIs on unsupported builds of Binary Ninja.
43pub fn has_collaboration_support() -> bool {
44    let mut count = 0;
45    let value = unsafe { BNCollaborationGetRemotes(&mut count) };
46    !value.is_null()
47}
48
49/// Get the single actively connected Remote (for ux simplification), if any
50pub fn active_remote() -> Option<Ref<Remote>> {
51    let value = unsafe { BNCollaborationGetActiveRemote() };
52    NonNull::new(value).map(|h| unsafe { Remote::ref_from_raw(h) })
53}
54
55/// Get the enterprise remote.
56///
57/// NOTE: There can only be one because that it is associated with the enterprise client user.
58pub fn enterprise_remote() -> Option<Ref<Remote>> {
59    for remote in &known_remotes() {
60        if remote.is_enterprise().unwrap_or(false) {
61            return Some(remote.clone());
62        }
63    }
64    None
65}
66
67/// Set the single actively connected Remote
68pub fn set_active_remote(remote: Option<&Remote>) {
69    let remote_ptr = remote.map_or(std::ptr::null_mut(), |r| r.handle.as_ptr());
70    unsafe { BNCollaborationSetActiveRemote(remote_ptr) }
71}
72
73/// Load the list of known Remotes from local Settings
74pub fn load_remotes() -> Result<(), ()> {
75    let success = unsafe { BNCollaborationLoadRemotes() };
76    success.then_some(()).ok_or(())
77}
78
79/// List of known/connected Remotes
80pub fn known_remotes() -> Array<Remote> {
81    let mut count = 0;
82    let value = unsafe { BNCollaborationGetRemotes(&mut count) };
83    assert!(!value.is_null());
84    unsafe { Array::new(value, count, ()) }
85}
86
87/// Get Remote by unique `id`
88pub fn get_remote_by_id(id: &str) -> Option<Ref<Remote>> {
89    let id = id.to_cstr();
90    let value = unsafe { BNCollaborationGetRemoteById(id.as_ptr()) };
91    NonNull::new(value).map(|h| unsafe { Remote::ref_from_raw(h) })
92}
93
94/// Get Remote by `address`
95pub fn get_remote_by_address(address: &str) -> Option<Ref<Remote>> {
96    let address = address.to_cstr();
97    let value = unsafe { BNCollaborationGetRemoteByAddress(address.as_ptr()) };
98    NonNull::new(value).map(|h| unsafe { Remote::ref_from_raw(h) })
99}
100
101/// Get Remote by `name`
102pub fn get_remote_by_name(name: &str) -> Option<Ref<Remote>> {
103    let name = name.to_cstr();
104    let value = unsafe { BNCollaborationGetRemoteByName(name.as_ptr()) };
105    NonNull::new(value).map(|h| unsafe { Remote::ref_from_raw(h) })
106}
107
108/// Remove a Remote from the list of known remotes (saved to Settings)
109pub fn remove_known_remote(remote: &Remote) {
110    unsafe { BNCollaborationRemoveRemote(remote.handle.as_ptr()) }
111}
112
113/// Save the list of known Remotes to local Settings
114pub fn save_remotes() {
115    unsafe { BNCollaborationSaveRemotes() }
116}
117
118pub fn store_data_in_keychain<I>(key: &str, data: I) -> bool
119where
120    I: IntoIterator<Item = (String, String)>,
121{
122    let key = key.to_cstr();
123    let (data_keys, data_values): (Vec<_>, Vec<_>) = data
124        .into_iter()
125        .map(|(k, v)| (k.to_cstr(), v.to_cstr()))
126        .unzip();
127    let data_keys_ptr: Box<[*const c_char]> = data_keys.iter().map(|k| k.as_ptr()).collect();
128    let data_values_ptr: Box<[*const c_char]> = data_values.iter().map(|v| v.as_ptr()).collect();
129    unsafe {
130        BNCollaborationStoreDataInKeychain(
131            key.as_ptr(),
132            data_keys_ptr.as_ptr() as *mut _,
133            data_values_ptr.as_ptr() as *mut _,
134            data_keys.len(),
135        )
136    }
137}
138
139pub fn has_data_in_keychain(key: &str) -> bool {
140    let key = key.to_cstr();
141    unsafe { BNCollaborationHasDataInKeychain(key.as_ptr()) }
142}
143
144pub fn get_data_from_keychain(key: &str) -> Option<(Array<BnString>, Array<BnString>)> {
145    let key = key.to_cstr();
146    let mut keys = std::ptr::null_mut();
147    let mut values = std::ptr::null_mut();
148    let count = unsafe { BNCollaborationGetDataFromKeychain(key.as_ptr(), &mut keys, &mut values) };
149    let keys = (!keys.is_null()).then(|| unsafe { Array::new(keys, count, ()) });
150    let values = (!values.is_null()).then(|| unsafe { Array::new(values, count, ()) });
151    keys.zip(values)
152}
153
154pub fn delete_data_from_keychain(key: &str) -> bool {
155    let key = key.to_cstr();
156    unsafe { BNCollaborationDeleteDataFromKeychain(key.as_ptr()) }
157}