diff options
| -rw-r--r-- | binaryninjaapi.cpp | 25 | ||||
| -rw-r--r-- | binaryninjaapi.h | 20 | ||||
| -rw-r--r-- | binaryninjacore.h | 23 | ||||
| -rw-r--r-- | pluginmanager.cpp | 33 | ||||
| -rw-r--r-- | python/pluginmanager.py | 92 | ||||
| -rw-r--r-- | rust/src/repository.rs | 2 | ||||
| -rw-r--r-- | rust/src/repository/plugin.rs | 91 |
7 files changed, 248 insertions, 38 deletions
diff --git a/binaryninjaapi.cpp b/binaryninjaapi.cpp index 58a4c0cd..34a86d41 100644 --- a/binaryninjaapi.cpp +++ b/binaryninjaapi.cpp @@ -207,31 +207,30 @@ string BinaryNinja::GetVersionString() } -VersionInfo BinaryNinja::GetVersionInfo() +static VersionInfo VersionInfoFromCoreStruct(const BNVersionInfo& result) { - BNVersionInfo result = BNGetVersionInfo(); VersionInfo info; info.major = result.major; info.minor = result.minor; info.build = result.build; - info.channel = ""; - if (result.channel) - info.channel = result.channel; + info.channel = result.channel ? result.channel : ""; + return info; +} + + +VersionInfo BinaryNinja::GetVersionInfo() +{ + BNVersionInfo result = BNGetVersionInfo(); + VersionInfo info = VersionInfoFromCoreStruct(result); BNFreeString(result.channel); return info; } -VersionInfo ParseVersionString(const string &version) +VersionInfo BinaryNinja::ParseVersionString(const string &version) { BNVersionInfo result = BNParseVersionString(version.c_str()); - VersionInfo info; - info.major = result.major; - info.minor = result.minor; - info.build = result.build; - info.channel = ""; - if (result.channel) - info.channel = result.channel; + VersionInfo info = VersionInfoFromCoreStruct(result); BNFreeString(result.channel); return info; } diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 57f896dd..fe57d92d 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -1962,8 +1962,16 @@ namespace BinaryNinja { { char* smallerChan = BNAllocString(channel.c_str()); char* largerChan = BNAllocString(other.channel.c_str()); - BNVersionInfo smaller = { major, minor, build, smallerChan }; - BNVersionInfo larger = { other.major, other.minor, other.build, largerChan }; + BNVersionInfo smaller = {}; + smaller.major = major; + smaller.minor = minor; + smaller.build = build; + smaller.channel = smallerChan; + BNVersionInfo larger = {}; + larger.major = other.major; + larger.minor = other.minor; + larger.build = other.build; + larger.channel = largerChan; bool result = BNVersionLessThan(smaller, larger); BNFreeString(smallerChan); BNFreeString(largerChan); @@ -19145,6 +19153,13 @@ namespace BinaryNinja { struct ExtensionVersion { + struct PlatformInfo + { + std::string name; + std::string downloadUrl; + std::string untrackedDownloadUrl; + }; + std::string id; std::string version; @@ -19152,6 +19167,7 @@ namespace BinaryNinja { std::string changelog; uint64_t minimumClientVersion; + std::vector<PlatformInfo> platforms; std::string created; }; diff --git a/binaryninjacore.h b/binaryninjacore.h index 879b7e1e..283524c8 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -354,6 +354,20 @@ extern "C" typedef struct BNStringRecognizer BNStringRecognizer; typedef struct BNCustomStringType BNCustomStringType; + typedef struct BNVersionInfo { + uint32_t major; + uint32_t minor; + uint32_t build; + char* channel; + } BNVersionInfo; + + typedef struct BNPluginVersionPlatform + { + char* name; + char* downloadUrl; + char* untrackedDownloadUrl; + } BNPluginVersionPlatform; + typedef struct BNPluginVersion { char* id; @@ -362,6 +376,8 @@ extern "C" char* changelog; uint64_t minimumClientVersion; + BNPluginVersionPlatform* platforms; + size_t platformCount; char* created; } BNPluginVersion; @@ -2743,13 +2759,6 @@ extern "C" char* latestVersion; } BNUpdateChannel; - typedef struct BNVersionInfo { - uint32_t major; - uint32_t minor; - uint32_t build; - char* channel; - } BNVersionInfo; - typedef struct BNChangelogEntry { BNVersionInfo version; char* notes; diff --git a/pluginmanager.cpp b/pluginmanager.cpp index 774e43d3..9b24a464 100644 --- a/pluginmanager.cpp +++ b/pluginmanager.cpp @@ -70,14 +70,29 @@ string Extension::GetDescription() const RETURN_STRING(BNPluginGetDescription(m_object)); } -VersionInfo Extension::GetMinimumVersionInfo() const +static VersionInfo ConvertVersionInfo(const BNVersionInfo& coreInfo) { - auto coreInfo = BNPluginGetMinimumVersionInfo(m_object); VersionInfo result; result.major = coreInfo.major; result.minor = coreInfo.minor; result.build = coreInfo.build; - result.channel = coreInfo.channel; + result.channel = coreInfo.channel ? coreInfo.channel : ""; + return result; +} + +static ExtensionVersion::PlatformInfo ConvertVersionPlatform(const BNPluginVersionPlatform& corePlatform) +{ + ExtensionVersion::PlatformInfo result; + result.name = corePlatform.name ? corePlatform.name : ""; + result.downloadUrl = corePlatform.downloadUrl ? corePlatform.downloadUrl : ""; + result.untrackedDownloadUrl = corePlatform.untrackedDownloadUrl ? corePlatform.untrackedDownloadUrl : ""; + return result; +} + +VersionInfo Extension::GetMinimumVersionInfo() const +{ + auto coreInfo = BNPluginGetMinimumVersionInfo(m_object); + VersionInfo result = ConvertVersionInfo(coreInfo); BNFreeString(coreInfo.channel); return result; } @@ -85,11 +100,7 @@ VersionInfo Extension::GetMinimumVersionInfo() const VersionInfo Extension::GetMaximumVersionInfo() const { auto coreInfo = BNPluginGetMaximumVersionInfo(m_object); - VersionInfo result; - result.major = coreInfo.major; - result.minor = coreInfo.minor; - result.build = coreInfo.build; - result.channel = coreInfo.channel; + VersionInfo result = ConvertVersionInfo(coreInfo); BNFreeString(coreInfo.channel); return result; } @@ -146,6 +157,9 @@ std::vector<ExtensionVersion> Extension::GetVersions() const version.longDescription = versionsPtr[i].longDescription ? versionsPtr[i].longDescription : ""; version.changelog = versionsPtr[i].changelog ? versionsPtr[i].changelog : ""; version.minimumClientVersion = versionsPtr[i].minimumClientVersion; + version.platforms.reserve(versionsPtr[i].platformCount); + for (size_t j = 0; j < versionsPtr[i].platformCount; j++) + version.platforms.push_back(ConvertVersionPlatform(versionsPtr[i].platforms[j])); version.created = versionsPtr[i].created ? versionsPtr[i].created : ""; versions.push_back(version); } @@ -163,6 +177,9 @@ ExtensionVersion Extension::GetCurrentVersion() const version.longDescription = currentVersion.longDescription ? currentVersion.longDescription : ""; version.changelog = currentVersion.changelog ? currentVersion.changelog : ""; version.minimumClientVersion = currentVersion.minimumClientVersion; + version.platforms.reserve(currentVersion.platformCount); + for (size_t i = 0; i < currentVersion.platformCount; i++) + version.platforms.push_back(ConvertVersionPlatform(currentVersion.platforms[i])); version.created = currentVersion.created ? currentVersion.created : ""; BNPluginFreeVersion(currentVersion); return version; diff --git a/python/pluginmanager.py b/python/pluginmanager.py index 5e32a79f..55315d12 100644 --- a/python/pluginmanager.py +++ b/python/pluginmanager.py @@ -20,6 +20,7 @@ import ctypes import json +from dataclasses import dataclass from datetime import datetime, date from typing import List, Dict, Optional @@ -29,6 +30,24 @@ from . import deprecation from .enums import PluginType +@dataclass(frozen=True) +class ExtensionVersionPlatform: + name: str + download_url: str + untracked_download_url: str + + +@dataclass(frozen=True) +class ExtensionVersion: + id: str + version: str + long_description: str + changelog: str + minimum_client_version: int + platforms: List[ExtensionVersionPlatform] + created: str + + class Extension: """ ``Extension`` is mostly read-only, however you can install/uninstall enable/disable plugins. Extensions are @@ -152,13 +171,23 @@ class Extension: def minimum_version_info(self) -> 'binaryninja.CoreVersionInfo': """Minimum version info the plugin was tested on""" core_version_info = core.BNPluginGetMinimumVersionInfo(self.handle) - return binaryninja.CoreVersionInfo(core_version_info.major, core_version_info.minor, core_version_info.build) + return binaryninja.CoreVersionInfo( + core_version_info.major, + core_version_info.minor, + core_version_info.build, + core_version_info.channel or "" + ) @property def maximum_version_info(self) -> 'binaryninja.CoreVersionInfo': """Maximum version info the plugin will support""" core_version_info = core.BNPluginGetMaximumVersionInfo(self.handle) - return binaryninja.CoreVersionInfo(core_version_info.major, core_version_info.minor, core_version_info.build) + return binaryninja.CoreVersionInfo( + core_version_info.major, + core_version_info.minor, + core_version_info.build, + core_version_info.channel or "" + ) @property def name(self) -> str: @@ -206,14 +235,65 @@ class Extension: @deprecation.deprecated(deprecated_in="5.3", details='Use :py:attr:`current_version` in combination with :py:attr:`versions` instead.') def version(self) -> Optional[str]: """String version of the plugin""" + return self.current_version.version + + @property + def current_version(self) -> ExtensionVersion: + """Current version metadata for the plugin""" version: core.BNPluginVersion = core.BNPluginGetCurrentVersion(self.handle) try: - version_string = version.versionString - except AttributeError: - version_string = "" + platforms = [] + for i in range(version.platformCount): + platform = version.platforms[i] + platforms.append(ExtensionVersionPlatform( + name=platform.name or "", + download_url=platform.downloadUrl or "", + untracked_download_url=platform.untrackedDownloadUrl or "" + )) + return ExtensionVersion( + id=version.id or "", + version=version.versionString or "", + long_description=version.longDescription or "", + changelog=version.changelog or "", + minimum_client_version=version.minimumClientVersion, + platforms=platforms, + created=version.created or "" + ) finally: core.BNPluginFreeVersion(version) - return version_string + + @property + def versions(self) -> List[ExtensionVersion]: + """Version metadata for all available plugin versions""" + result = [] + count = ctypes.c_ulonglong(0) + versions = core.BNPluginGetVersions(self.handle, count) + try: + if versions is None: + return result + for i in range(count.value): + version = versions[i] + platforms = [] + for j in range(version.platformCount): + platform = version.platforms[j] + platforms.append(ExtensionVersionPlatform( + name=platform.name or "", + download_url=platform.downloadUrl or "", + untracked_download_url=platform.untrackedDownloadUrl or "" + )) + result.append(ExtensionVersion( + id=version.id or "", + version=version.versionString or "", + long_description=version.longDescription or "", + changelog=version.changelog or "", + minimum_client_version=version.minimumClientVersion, + platforms=platforms, + created=version.created or "" + )) + return result + finally: + if versions is not None: + core.BNFreePluginVersions(versions, count.value) @property def install_platforms(self) -> List[str]: diff --git a/rust/src/repository.rs b/rust/src/repository.rs index 2f1ab267..18946eac 100644 --- a/rust/src/repository.rs +++ b/rust/src/repository.rs @@ -11,10 +11,10 @@ use std::ptr::NonNull; use binaryninjacore_sys::*; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; -use crate::repository::plugin::Extension; use crate::string::{BnString, IntoCStr}; pub use manager::RepositoryManager; +pub use plugin::{Extension, ExtensionVersion, ExtensionVersionPlatform}; pub type PluginType = BNPluginType; pub type PluginStatus = BNPluginStatus; diff --git a/rust/src/repository/plugin.rs b/rust/src/repository/plugin.rs index ba95c29c..ac7c9d20 100644 --- a/rust/src/repository/plugin.rs +++ b/rust/src/repository/plugin.rs @@ -1,12 +1,71 @@ use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; use crate::repository::{PluginStatus, PluginType}; -use crate::string::{BnString, IntoCStr}; +use crate::string::{raw_to_string, BnString, IntoCStr}; use crate::VersionInfo; use binaryninjacore_sys::*; use std::ffi::c_char; use std::fmt::Debug; use std::path::PathBuf; use std::ptr::NonNull; +use std::slice; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ExtensionVersionPlatform { + pub name: String, + pub download_url: String, + pub untracked_download_url: String, +} + +impl ExtensionVersionPlatform { + pub(crate) fn from_raw(value: &BNPluginVersionPlatform) -> Self { + Self { + name: raw_to_string(value.name as *mut _).unwrap_or_default(), + download_url: raw_to_string(value.downloadUrl as *mut _).unwrap_or_default(), + untracked_download_url: raw_to_string(value.untrackedDownloadUrl as *mut _) + .unwrap_or_default(), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ExtensionVersion { + pub id: String, + pub version: String, + pub long_description: String, + pub changelog: String, + pub minimum_client_version: u64, + pub platforms: Vec<ExtensionVersionPlatform>, + pub created: String, +} + +impl ExtensionVersion { + pub(crate) fn from_raw(value: &BNPluginVersion) -> Self { + let platforms = if value.platforms.is_null() || value.platformCount == 0 { + Vec::new() + } else { + unsafe { slice::from_raw_parts(value.platforms, value.platformCount) } + .iter() + .map(ExtensionVersionPlatform::from_raw) + .collect() + }; + + Self { + id: raw_to_string(value.id as *mut _).unwrap_or_default(), + version: raw_to_string(value.versionString as *mut _).unwrap_or_default(), + long_description: raw_to_string(value.longDescription as *mut _).unwrap_or_default(), + changelog: raw_to_string(value.changelog as *mut _).unwrap_or_default(), + minimum_client_version: value.minimumClientVersion, + platforms, + created: raw_to_string(value.created as *mut _).unwrap_or_default(), + } + } + + pub(crate) fn from_owned_raw(value: BNPluginVersion) -> Self { + let owned = Self::from_raw(&value); + unsafe { BNPluginFreeVersion(value) }; + owned + } +} #[repr(transparent)] pub struct Extension { @@ -56,6 +115,20 @@ impl Extension { VersionInfo::from_owned_raw(result) } + /// Metadata for all available versions of this plugin + pub fn versions(&self) -> Array<ExtensionVersion> { + let mut count = 0; + let result = unsafe { BNPluginGetVersions(self.handle.as_ptr(), &mut count) }; + assert!(!result.is_null()); + unsafe { Array::new(result, count, ()) } + } + + /// Metadata for the currently selected version of this plugin + pub fn current_version(&self) -> ExtensionVersion { + let result = unsafe { BNPluginGetCurrentVersion(self.handle.as_ptr()) }; + ExtensionVersion::from_owned_raw(result) + } + /// String plugin name pub fn name(&self) -> String { let result = unsafe { BNPluginGetName(self.handle.as_ptr()) }; @@ -274,3 +347,19 @@ unsafe impl CoreArrayProviderInner for Extension { Guard::new(Self::from_raw(NonNull::new(*raw).unwrap()), context) } } + +impl CoreArrayProvider for ExtensionVersion { + type Raw = BNPluginVersion; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for ExtensionVersion { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreePluginVersions(raw, count) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + ExtensionVersion::from_raw(raw) + } +} |
