binaryninja/repository/
plugin.rs

1use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
2use crate::repository::{PluginStatus, PluginType};
3use crate::string::{raw_to_string, BnString, IntoCStr};
4use crate::VersionInfo;
5use binaryninjacore_sys::*;
6use std::ffi::c_char;
7use std::fmt::Debug;
8use std::path::PathBuf;
9use std::ptr::NonNull;
10use std::slice;
11
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct ExtensionVersionPlatform {
14    pub name: String,
15    pub download_url: String,
16    pub untracked_download_url: String,
17}
18
19impl ExtensionVersionPlatform {
20    pub(crate) fn from_raw(value: &BNPluginVersionPlatform) -> Self {
21        Self {
22            name: raw_to_string(value.name as *mut _).unwrap_or_default(),
23            download_url: raw_to_string(value.downloadUrl as *mut _).unwrap_or_default(),
24            untracked_download_url: raw_to_string(value.untrackedDownloadUrl as *mut _)
25                .unwrap_or_default(),
26        }
27    }
28}
29
30#[derive(Clone, Debug, PartialEq, Eq)]
31pub struct ExtensionVersion {
32    pub id: String,
33    pub version: String,
34    pub long_description: String,
35    pub changelog: String,
36    pub minimum_client_version: u64,
37    pub platforms: Vec<ExtensionVersionPlatform>,
38    pub created: String,
39}
40
41impl ExtensionVersion {
42    pub(crate) fn from_raw(value: &BNPluginVersion) -> Self {
43        let platforms = if value.platforms.is_null() || value.platformCount == 0 {
44            Vec::new()
45        } else {
46            unsafe { slice::from_raw_parts(value.platforms, value.platformCount) }
47                .iter()
48                .map(ExtensionVersionPlatform::from_raw)
49                .collect()
50        };
51
52        Self {
53            id: raw_to_string(value.id as *mut _).unwrap_or_default(),
54            version: raw_to_string(value.versionString as *mut _).unwrap_or_default(),
55            long_description: raw_to_string(value.longDescription as *mut _).unwrap_or_default(),
56            changelog: raw_to_string(value.changelog as *mut _).unwrap_or_default(),
57            minimum_client_version: value.minimumClientVersion,
58            platforms,
59            created: raw_to_string(value.created as *mut _).unwrap_or_default(),
60        }
61    }
62
63    pub(crate) fn from_owned_raw(value: BNPluginVersion) -> Self {
64        let owned = Self::from_raw(&value);
65        unsafe { BNPluginFreeVersion(value) };
66        owned
67    }
68}
69
70#[repr(transparent)]
71pub struct Extension {
72    handle: NonNull<BNPlugin>,
73}
74
75impl Extension {
76    pub(crate) unsafe fn from_raw(handle: NonNull<BNPlugin>) -> Self {
77        Self { handle }
78    }
79
80    pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNPlugin>) -> Ref<Self> {
81        Ref::new(Self { handle })
82    }
83
84    /// String indicating the API used by the plugin
85    pub fn apis(&self) -> Array<BnString> {
86        let mut count = 0;
87        let result = unsafe { BNPluginGetApis(self.handle.as_ptr(), &mut count) };
88        assert!(!result.is_null());
89        unsafe { Array::new(result, count, ()) }
90    }
91
92    /// String of the plugin author
93    pub fn author(&self) -> String {
94        let result = unsafe { BNPluginGetAuthor(self.handle.as_ptr()) };
95        assert!(!result.is_null());
96        unsafe { BnString::into_string(result as *mut c_char) }
97    }
98
99    /// String short description of the plugin
100    pub fn description(&self) -> String {
101        let result = unsafe { BNPluginGetDescription(self.handle.as_ptr()) };
102        assert!(!result.is_null());
103        unsafe { BnString::into_string(result as *mut c_char) }
104    }
105
106    /// String complete license text for the given plugin
107    pub fn license_text(&self) -> String {
108        let result = unsafe { BNPluginGetLicenseText(self.handle.as_ptr()) };
109        assert!(!result.is_null());
110        unsafe { BnString::into_string(result as *mut c_char) }
111    }
112
113    /// Minimum version info the plugin was tested on
114    pub fn minimum_version_info(&self) -> VersionInfo {
115        let result = unsafe { BNPluginGetMinimumVersionInfo(self.handle.as_ptr()) };
116        VersionInfo::from_owned_raw(result)
117    }
118
119    /// Maximum version info the plugin will support
120    pub fn maximum_version_info(&self) -> VersionInfo {
121        let result = unsafe { BNPluginGetMaximumVersionInfo(self.handle.as_ptr()) };
122        VersionInfo::from_owned_raw(result)
123    }
124
125    /// Metadata for all available versions of this plugin
126    pub fn versions(&self) -> Array<ExtensionVersion> {
127        let mut count = 0;
128        let result = unsafe { BNPluginGetVersions(self.handle.as_ptr(), &mut count) };
129        assert!(!result.is_null());
130        unsafe { Array::new(result, count, ()) }
131    }
132
133    /// Metadata for the currently selected version of this plugin
134    pub fn current_version(&self) -> ExtensionVersion {
135        let result = unsafe { BNPluginGetCurrentVersion(self.handle.as_ptr()) };
136        ExtensionVersion::from_owned_raw(result)
137    }
138
139    /// Latest version id available for this platform
140    pub fn latest_version_id(&self) -> String {
141        let result = unsafe { BNPluginGetLatestVersionID(self.handle.as_ptr()) };
142        assert!(!result.is_null());
143        unsafe { BnString::into_string(result as *mut c_char) }
144    }
145
146    /// String plugin name
147    pub fn name(&self) -> String {
148        let result = unsafe { BNPluginGetName(self.handle.as_ptr()) };
149        assert!(!result.is_null());
150        unsafe { BnString::into_string(result as *mut c_char) }
151    }
152
153    /// String URL of the plugin's git repository
154    pub fn project_url(&self) -> String {
155        let result = unsafe { BNPluginGetProjectUrl(self.handle.as_ptr()) };
156        assert!(!result.is_null());
157        unsafe { BnString::into_string(result as *mut c_char) }
158    }
159
160    /// String URL of the plugin's git repository
161    pub fn package_url(&self) -> String {
162        let result = unsafe { BNPluginGetPackageUrl(self.handle.as_ptr()) };
163        assert!(!result.is_null());
164        unsafe { BnString::into_string(result as *mut c_char) }
165    }
166
167    /// Boolean True if this plugin requires payment, False otherwise
168    pub fn is_paid(&self) -> bool {
169        unsafe { BNPluginGetIsPaid(self.handle.as_ptr()) }
170    }
171
172    /// String URL of the plugin author's url
173    pub fn author_url(&self) -> String {
174        let result = unsafe { BNPluginGetAuthorUrl(self.handle.as_ptr()) };
175        assert!(!result.is_null());
176        unsafe { BnString::into_string(result as *mut c_char) }
177    }
178
179    /// String of the commit of this plugin git repository
180    pub fn commit(&self) -> String {
181        let result = unsafe { BNPluginGetCommit(self.handle.as_ptr()) };
182        assert!(!result.is_null());
183        unsafe { BnString::into_string(result as *mut c_char) }
184    }
185
186    /// Relative path from the base of the repository to the actual plugin
187    pub fn path(&self) -> PathBuf {
188        let result = unsafe { BNPluginGetPath(self.handle.as_ptr()) };
189        assert!(!result.is_null());
190        let result_str = unsafe { BnString::into_string(result as *mut c_char) };
191        PathBuf::from(result_str)
192    }
193
194    /// Optional sub-directory the plugin code lives in as a relative path from the plugin root
195    pub fn subdir(&self) -> PathBuf {
196        let result = unsafe { BNPluginGetSubdir(self.handle.as_ptr()) };
197        assert!(!result.is_null());
198        let result_str = unsafe { BnString::into_string(result as *mut c_char) };
199        PathBuf::from(result_str)
200    }
201
202    /// Dependencies required for installing this plugin
203    pub fn dependencies(&self) -> String {
204        let result = unsafe { BNPluginGetDependencies(self.handle.as_ptr()) };
205        assert!(!result.is_null());
206        unsafe { BnString::into_string(result as *mut c_char) }
207    }
208
209    /// true if the plugin is installed, false otherwise
210    pub fn is_installed(&self) -> bool {
211        unsafe { BNPluginIsInstalled(self.handle.as_ptr()) }
212    }
213
214    /// true if the plugin is present in its repository's latest successful listing
215    pub fn is_listed(&self) -> bool {
216        unsafe { BNPluginIsListed(self.handle.as_ptr()) }
217    }
218
219    /// true if the plugin is marked deprecated by its repository
220    pub fn is_deprecated(&self) -> bool {
221        unsafe { BNPluginIsDeprecated(self.handle.as_ptr()) }
222    }
223
224    /// true if the plugin is enabled, false otherwise
225    pub fn is_enabled(&self) -> bool {
226        unsafe { BNPluginIsEnabled(self.handle.as_ptr()) }
227    }
228
229    pub fn status(&self) -> PluginStatus {
230        unsafe { BNPluginGetPluginStatus(self.handle.as_ptr()) }
231    }
232
233    /// List of PluginType enumeration objects indicating the plugin type(s)
234    pub fn types(&self) -> Array<PluginType> {
235        let mut count = 0;
236        let result = unsafe { BNPluginGetPluginTypes(self.handle.as_ptr(), &mut count) };
237        assert!(!result.is_null());
238        unsafe { Array::new(result, count, ()) }
239    }
240
241    /// Enable this plugin, optionally trying to force it.
242    /// Force loading a plugin with ignore platform and api constraints.
243    pub fn enable(&self, force: bool) -> bool {
244        unsafe { BNPluginEnable(self.handle.as_ptr(), force) }
245    }
246
247    pub fn disable(&self) -> bool {
248        unsafe { BNPluginDisable(self.handle.as_ptr()) }
249    }
250
251    /// Attempt to install the given plugin
252    pub fn install(&self, version_id: &str) -> bool {
253        let version_id_raw = version_id.to_cstr();
254        unsafe { BNPluginInstall(self.handle.as_ptr(), version_id_raw.as_ptr()) }
255    }
256
257    pub fn install_dependencies(&self) -> bool {
258        unsafe { BNPluginInstallDependencies(self.handle.as_ptr()) }
259    }
260
261    /// Attempt to uninstall the given plugin
262    pub fn uninstall(&self) -> bool {
263        unsafe { BNPluginUninstall(self.handle.as_ptr()) }
264    }
265
266    /// Cancel an uninstall that is pending until restart.
267    pub fn cancel_uninstall(&self) -> bool {
268        unsafe { BNPluginCancelUninstall(self.handle.as_ptr()) }
269    }
270
271    pub fn updated(&self, version_id: &str) -> bool {
272        let version_id_raw = version_id.to_cstr();
273        unsafe { BNPluginUpdate(self.handle.as_ptr(), version_id_raw.as_ptr()) }
274    }
275
276    /// List of platforms this plugin can execute on
277    pub fn platforms(&self) -> Array<BnString> {
278        let mut count = 0;
279        let result = unsafe { BNPluginGetPlatforms(self.handle.as_ptr(), &mut count) };
280        assert!(!result.is_null());
281        unsafe { Array::new(result, count, ()) }
282    }
283
284    pub fn repository(&self) -> String {
285        let result = unsafe { BNPluginGetRepository(self.handle.as_ptr()) };
286        assert!(!result.is_null());
287        unsafe { BnString::into_string(result as *mut c_char) }
288    }
289
290    /// Boolean status indicating that the plugin is being deleted
291    pub fn is_being_deleted(&self) -> bool {
292        unsafe { BNPluginIsBeingDeleted(self.handle.as_ptr()) }
293    }
294
295    /// Boolean status indicating that the plugin is being updated
296    pub fn is_being_updated(&self) -> bool {
297        unsafe { BNPluginIsBeingUpdated(self.handle.as_ptr()) }
298    }
299
300    /// Boolean status indicating that the plugin is currently running
301    pub fn is_running(&self) -> bool {
302        unsafe { BNPluginIsRunning(self.handle.as_ptr()) }
303    }
304
305    /// Boolean status indicating that the plugin has updates will be installed after the next restart
306    pub fn is_update_pending(&self) -> bool {
307        unsafe { BNPluginIsUpdatePending(self.handle.as_ptr()) }
308    }
309
310    /// Boolean status indicating that the plugin will be disabled after the next restart
311    pub fn is_disable_pending(&self) -> bool {
312        unsafe { BNPluginIsDisablePending(self.handle.as_ptr()) }
313    }
314
315    /// Boolean status indicating that the plugin will be deleted after the next restart
316    pub fn is_delete_pending(&self) -> bool {
317        unsafe { BNPluginIsDeletePending(self.handle.as_ptr()) }
318    }
319
320    /// Boolean status indicating that the plugin has updates available
321    pub fn is_updated_available(&self) -> bool {
322        unsafe { BNPluginIsUpdateAvailable(self.handle.as_ptr()) }
323    }
324
325    /// Boolean status indicating that the plugin's dependencies are currently being installed
326    pub fn are_dependencies_being_installed(&self) -> bool {
327        unsafe { BNPluginAreDependenciesBeingInstalled(self.handle.as_ptr()) }
328    }
329
330    /// Gets a json object of the project data field
331    pub fn project_data(&self) -> String {
332        let result = unsafe { BNPluginGetProjectData(self.handle.as_ptr()) };
333        assert!(!result.is_null());
334        unsafe { BnString::into_string(result) }
335    }
336}
337
338impl Debug for Extension {
339    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
340        f.debug_struct("Extension")
341            .field("name", &self.name())
342            .field("author", &self.author())
343            .field("description", &self.description())
344            .field("minimum_version_info", &self.minimum_version_info())
345            .field("maximum_version_info", &self.maximum_version_info())
346            .field("status", &self.status())
347            .finish()
348    }
349}
350
351impl ToOwned for Extension {
352    type Owned = Ref<Self>;
353
354    fn to_owned(&self) -> Self::Owned {
355        unsafe { RefCountable::inc_ref(self) }
356    }
357}
358
359unsafe impl RefCountable for Extension {
360    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
361        Self::ref_from_raw(NonNull::new(BNNewPluginReference(handle.handle.as_ptr())).unwrap())
362    }
363
364    unsafe fn dec_ref(handle: &Self) {
365        BNFreePlugin(handle.handle.as_ptr())
366    }
367}
368
369impl CoreArrayProvider for Extension {
370    type Raw = *mut BNPlugin;
371    type Context = ();
372    type Wrapped<'a> = Guard<'a, Self>;
373}
374
375unsafe impl CoreArrayProviderInner for Extension {
376    unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
377        BNFreeRepositoryPluginList(raw)
378    }
379
380    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
381        Guard::new(Self::from_raw(NonNull::new(*raw).unwrap()), context)
382    }
383}
384
385impl CoreArrayProvider for ExtensionVersion {
386    type Raw = BNPluginVersion;
387    type Context = ();
388    type Wrapped<'a> = Self;
389}
390
391unsafe impl CoreArrayProviderInner for ExtensionVersion {
392    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
393        BNFreePluginVersions(raw, count)
394    }
395
396    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
397        ExtensionVersion::from_raw(raw)
398    }
399}