Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions binaryninjaapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -2010,6 +2010,16 @@ namespace BinaryNinja {
*/
bool GetDirectoryNameInput(std::string& result, const std::string& prompt, const std::string& defaultName = "");

/*! Prompts the user for a checkbox input
\ingroup interaction

\param[out] result Reference to the integer the result will be copied to
\param[in] prompt Prompt for the dialog
\param[in] title Title for the input popup when used in UI
\return Whether a checkbox input was successfully received
*/
bool GetCheckboxInput(int64_t& result, const std::string& prompt, const std::string& title);

/*! Prompts the user for a set of inputs specified in `fields` with given title.
The fields parameter is a list containing FieldInputFields

Expand Down Expand Up @@ -16799,6 +16809,7 @@ namespace BinaryNinja {
static FormInputField SaveFileName(
const std::string& prompt, const std::string& ext, const std::string& defaultName = "");
static FormInputField DirectoryName(const std::string& prompt, const std::string& defaultName = "");
static FormInputField Checkbox(const std::string& prompt);
};

/*!
Expand Down Expand Up @@ -16858,6 +16869,7 @@ namespace BinaryNinja {
const std::string& defaultName = "");
virtual bool GetDirectoryNameInput(
std::string& result, const std::string& prompt, const std::string& defaultName = "");
virtual bool GetCheckboxInput(int64_t& result, const std::string& prompt, const std::string& title);
virtual bool GetFormInput(std::vector<FormInputField>& fields, const std::string& title) = 0;

virtual BNMessageBoxButtonResult ShowMessageBox(const std::string& title, const std::string& text,
Expand Down
5 changes: 4 additions & 1 deletion binaryninjacore.h
Original file line number Diff line number Diff line change
Expand Up @@ -2975,7 +2975,8 @@ extern "C"
ChoiceFormField,
OpenFileNameFormField,
SaveFileNameFormField,
DirectoryNameFormField
DirectoryNameFormField,
CheckboxFormField
} BNFormInputFieldType;

typedef struct BNFormInputField
Expand Down Expand Up @@ -3021,6 +3022,7 @@ extern "C"
bool (*getSaveFileNameInput)(
void* ctxt, char** result, const char* prompt, const char* ext, const char* defaultName);
bool (*getDirectoryNameInput)(void* ctxt, char** result, const char* prompt, const char* defaultName);
bool (*getCheckboxInput)(void* ctxt, int64_t* result, const char* prompt, const char* title);
bool (*getFormInput)(void* ctxt, BNFormInputField* fields, size_t count, const char* title);
BNMessageBoxButtonResult (*showMessageBox)(
void* ctxt, const char* title, const char* text, BNMessageBoxButtonSet buttons, BNMessageBoxIcon icon);
Expand Down Expand Up @@ -7200,6 +7202,7 @@ extern "C"
BINARYNINJACOREAPI bool BNGetSaveFileNameInput(
char** result, const char* prompt, const char* ext, const char* defaultName);
BINARYNINJACOREAPI bool BNGetDirectoryNameInput(char** result, const char* prompt, const char* defaultName);
BINARYNINJACOREAPI bool BNGetCheckboxInput(int64_t* result, const char* prompt, const char* title);
BINARYNINJACOREAPI bool BNGetFormInput(BNFormInputField* fields, size_t count, const char* title);
BINARYNINJACOREAPI void BNFreeFormInputResults(BNFormInputField* fields, size_t count);
BINARYNINJACOREAPI BNMessageBoxButtonResult BNShowMessageBox(
Expand Down
34 changes: 34 additions & 0 deletions interaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,15 @@ FormInputField FormInputField::DirectoryName(const string& prompt, const string&
return result;
}

FormInputField FormInputField::Checkbox(const string& prompt)
{
FormInputField result;
result.type = CheckboxFormField;
result.prompt = prompt;
result.hasDefault = false;
return result;
}


void InteractionHandler::ShowMarkdownReport(
Ref<BinaryView> view, const string& title, const string& contents, const string& plainText)
Expand Down Expand Up @@ -188,6 +197,12 @@ bool InteractionHandler::GetDirectoryNameInput(string& result, const string& pro
return GetTextLineInput(result, prompt, "Select Directory");
}

bool InteractionHandler::GetCheckboxInput(int64_t& result, const std::string& prompt, const std::string& title)
{
return GetCheckboxInput(result, prompt, "Select an option");
}



static void ShowPlainTextReportCallback(void* ctxt, BNBinaryView* view, const char* title, const char* contents)
{
Expand Down Expand Up @@ -309,6 +324,12 @@ static bool GetDirectoryNameInputCallback(void* ctxt, char** result, const char*
return true;
}

static bool GetCheckboxInputCallback(void* ctxt, int64_t* result, const char* prompt, const char* title)
{
InteractionHandler* handler = (InteractionHandler*)ctxt;
return handler->GetCheckboxInput(*result, prompt, title);
}


static bool GetFormInputCallback(void* ctxt, BNFormInputField* fieldBuf, size_t count, const char* title)
{
Expand Down Expand Up @@ -353,6 +374,9 @@ static bool GetFormInputCallback(void* ctxt, BNFormInputField* fieldBuf, size_t
case DirectoryNameFormField:
fields.push_back(FormInputField::DirectoryName(fieldBuf[i].prompt, fieldBuf[i].defaultName));
break;
case CheckboxFormField:
fields.push_back(FormInputField::Checkbox(fieldBuf[i].prompt));
break;
default:
fields.push_back(FormInputField::Label(fieldBuf[i].prompt));
break;
Expand All @@ -369,6 +393,7 @@ static bool GetFormInputCallback(void* ctxt, BNFormInputField* fieldBuf, size_t
case DirectoryNameFormField:
fields.back().stringDefault = fieldBuf[i].stringDefault;
break;
case CheckboxFormField:
case IntegerFormField:
fields.back().intDefault = fieldBuf[i].intDefault;
break;
Expand Down Expand Up @@ -399,6 +424,7 @@ static bool GetFormInputCallback(void* ctxt, BNFormInputField* fieldBuf, size_t
case DirectoryNameFormField:
fieldBuf[i].stringResult = BNAllocString(fields[i].stringResult.c_str());
break;
case CheckboxFormField:
case IntegerFormField:
fieldBuf[i].intResult = fields[i].intResult;
break;
Expand Down Expand Up @@ -460,6 +486,7 @@ void BinaryNinja::RegisterInteractionHandler(InteractionHandler* handler)
cb.getOpenFileNameInput = GetOpenFileNameInputCallback;
cb.getSaveFileNameInput = GetSaveFileNameInputCallback;
cb.getDirectoryNameInput = GetDirectoryNameInputCallback;
cb.getCheckboxInput = GetCheckboxInputCallback;
cb.getFormInput = GetFormInputCallback;
cb.showMessageBox = ShowMessageBoxCallback;
cb.openUrl = OpenUrlCallback;
Expand Down Expand Up @@ -589,6 +616,11 @@ bool BinaryNinja::GetDirectoryNameInput(string& result, const string& prompt, co
return true;
}

bool BinaryNinja::GetCheckboxInput(int64_t& result, const std::string& prompt, const std::string& title)
{
return BNGetCheckboxInput(&result, prompt.c_str(), title.c_str());
}


bool BinaryNinja::GetFormInput(vector<FormInputField>& fields, const string& title)
{
Expand Down Expand Up @@ -635,6 +667,7 @@ bool BinaryNinja::GetFormInput(vector<FormInputField>& fields, const string& tit
case DirectoryNameFormField:
fieldBuf[i].stringDefault = fields[i].stringDefault.c_str();
break;
case CheckboxFormField:
case IntegerFormField:
fieldBuf[i].intDefault = fields[i].intDefault;
break;
Expand Down Expand Up @@ -678,6 +711,7 @@ bool BinaryNinja::GetFormInput(vector<FormInputField>& fields, const string& tit
case DirectoryNameFormField:
fields[i].stringResult = fieldBuf[i].stringResult;
break;
case CheckboxFormField:
case IntegerFormField:
fields[i].intResult = fieldBuf[i].intResult;
break;
Expand Down
71 changes: 71 additions & 0 deletions python/interaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,43 @@ def result(self):
def result(self, value):
self._result = value

class CheckboxField:
"""
``CheckboxField`` prompts the user to choose a yes/no option in a checkbox.
Result is stored in self.result as a boolean value.

:param str prompt: Prompt to be presented to the user
"""
def __init__(self, prompt, default=None):
self._prompt = prompt
self._result = None

def _fill_core_struct(self, value):
value.type = FormInputFieldType.CheckboxFormField
value.prompt = self._prompt

def _fill_core_result(self, value):
value.intResult = self._result

def _get_result(self, value):
self._result = value.intResult

@property
def prompt(self):
return self._prompt

@prompt.setter
def prompt(self, value):
self._prompt = value

@property
def result(self):
return self._result

@result.setter
def result(self, value):
self._result = value


class InteractionHandler:
_interaction_handler = None
Expand All @@ -505,6 +542,7 @@ def __init__(self):
self._cb.getOpenFileNameInput = self._cb.getOpenFileNameInput.__class__(self._get_open_filename_input)
self._cb.getSaveFileNameInput = self._cb.getSaveFileNameInput.__class__(self._get_save_filename_input)
self._cb.getDirectoryNameInput = self._cb.getDirectoryNameInput.__class__(self._get_directory_name_input)
self._cb.getCheckboxInput = self._cb.getCheckboxInput.__class__(self._get_checkbox_input)
self._cb.getFormInput = self._cb.getFormInput.__class__(self._get_form_input)
self._cb.showMessageBox = self._cb.showMessageBox.__class__(self._show_message_box)
self._cb.openUrl = self._cb.openUrl.__class__(self._open_url)
Expand Down Expand Up @@ -650,6 +688,16 @@ def _get_directory_name_input(self, ctxt, result, prompt, default_name):
except:
log_error(traceback.format_exc())

def _get_checkbox_input(self, ctxt, result, prompt):
try:
value = self.get_checkbox_input(prompt)
if value is None:
return False
result[0] = value
return True
except:
log_error(traceback.format_exc())

def _get_form_input(self, ctxt, fields, count, title):
try:
field_objs = []
Expand Down Expand Up @@ -714,6 +762,12 @@ def _get_form_input(self, ctxt, fields, count, title):
default=fields[i].stringDefault if fields[i].hasDefault else None
)
)
elif fields[i].type == FormInputFieldType.CheckboxFormField:
field_objs.append(
CheckboxField(
fields[i].prompt
)
)
else:
field_objs.append(LabelField(fields[i].prompt))
if not self.get_form_input(field_objs, title):
Expand Down Expand Up @@ -795,6 +849,9 @@ def get_save_filename_input(self, prompt, ext, default_name):
def get_directory_name_input(self, prompt, default_name):
return get_text_line_input(prompt, "Select Directory")

def get_checkbox_input(self, prompt):
return get_checkbox_input(prompt, "Choose Option(s)")

def get_form_input(self, fields, title):
return False

Expand Down Expand Up @@ -1361,6 +1418,19 @@ def get_directory_name_input(prompt: str, default_name: str = ""):
core.free_string(value)
return result.decode("utf-8")

def get_checkbox_input(prompt: str, title: str):
"""
``get_checkbox_input`` prompts the user for a checkbox input
:param prompt: String to prompt with
:param title: Title of the window when executed in the UI
:rtype: int indicating the state of the checkbox
"""
value = ctypes.c_bool()
if not core.BNGetCheckboxInput(value, prompt, title):
return None
result = value.value
assert result is not None
return result

def get_form_input(fields, title):
"""
Expand All @@ -1382,6 +1452,7 @@ def get_form_input(fields, title):
OpenFileNameField Prompt for file to open
SaveFileNameField Prompt for file to save to
DirectoryNameField Prompt for directory name
CheckboxFormField Prompt for a checkbox
===================== ===================================================

This API is flexible and works both in the UI via a pop-up dialog and on the command-line.
Expand Down
13 changes: 13 additions & 0 deletions rust/src/interaction/form.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ pub enum FormInputField {
default: Option<String>,
value: Option<String>,
},
Checkbox {
prompt: String,
default: Option<bool>,
value: bool,
}
}

impl FormInputField {
Expand Down Expand Up @@ -186,6 +191,11 @@ impl FormInputField {
default: string_default,
value: string_result,
},
BNFormInputFieldType::CheckboxFormField => Self::Checkbox {
prompt,
default: value.hasDefault.then_some(value.intResult != 0),
value: value.intResult != 0,
},
}
}

Expand Down Expand Up @@ -258,6 +268,7 @@ impl FormInputField {
FormInputField::OpenFileName { .. } => BNFormInputFieldType::OpenFileNameFormField,
FormInputField::SaveFileName { .. } => BNFormInputFieldType::SaveFileNameFormField,
FormInputField::DirectoryName { .. } => BNFormInputFieldType::DirectoryNameFormField,
FormInputField::Checkbox { .. } => BNFormInputFieldType::CheckboxFormField,
}
}

Expand All @@ -274,6 +285,7 @@ impl FormInputField {
FormInputField::OpenFileName { prompt, .. } => Some(prompt.clone()),
FormInputField::SaveFileName { prompt, .. } => Some(prompt.clone()),
FormInputField::DirectoryName { prompt, .. } => Some(prompt.clone()),
FormInputField::Checkbox { prompt, .. } => Some(prompt.clone()),
}
}

Expand Down Expand Up @@ -325,6 +337,7 @@ impl FormInputField {
FormInputField::OpenFileName { default, .. } => Some(default.is_some()),
FormInputField::SaveFileName { default, .. } => Some(default.is_some()),
FormInputField::DirectoryName { default, .. } => Some(default.is_some()),
FormInputField::Checkbox { default, .. } => Some(default.is_some()),
}
}

Expand Down
18 changes: 18 additions & 0 deletions rust/src/interaction/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub fn register_interaction_handler<R: InteractionHandler>(custom: R) {
getOpenFileNameInput: Some(cb_get_open_file_name_input::<R>),
getSaveFileNameInput: Some(cb_get_save_file_name_input::<R>),
getDirectoryNameInput: Some(cb_get_directory_name_input::<R>),
getCheckboxInput: None,
getFormInput: Some(cb_get_form_input::<R>),
showMessageBox: Some(cb_show_message_box::<R>),
openUrl: Some(cb_open_url::<R>),
Expand Down Expand Up @@ -245,6 +246,23 @@ pub trait InteractionHandler: Sync + Send + 'static {
form.get_field_with_name(prompt)
.and_then(|f| f.try_value_string())
}

fn get_choicebox_input(
&mut self,
prompt: &str,
title: &str,
) -> Option<i64> {
let mut form = Form::new(title.to_owned());
form.add_field(FormInputField::Checkbox {
prompt: prompt.to_string(),
value: false,
default: None,
});
if !self.get_form_input(&mut form) {
return None;
}
form.get_field_with_name(prompt).and_then(|f| f.try_value_int())
}
}

pub struct InteractionHandlerTask {
Expand Down
Loading