Skip to content

Commit 7498194

Browse files
spoonmilkCouleeApps
authored andcommitted
Add Checkbox input support to python api
1 parent 99965f6 commit 7498194

File tree

6 files changed

+212
-1
lines changed

6 files changed

+212
-1
lines changed

binaryninjaapi.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2049,6 +2049,22 @@ namespace BinaryNinja {
20492049
*/
20502050
bool GetDirectoryNameInput(std::string& result, const std::string& prompt, const std::string& defaultName = "");
20512051

2052+
/*! Prompts the user for a checkbox input
2053+
\ingroup interaction
2054+
2055+
\param[out] result Reference to the integer the result will be copied to
2056+
\param[in] prompt Prompt for the dialog
2057+
\param[in] title Title for the input popup when used in UI
2058+
\param[in] defaultChoice Default checkbox state (0 == unchecked, 1 == checked)
2059+
\return Whether a checkbox input was successfully received
2060+
*/
2061+
bool GetCheckboxInput(
2062+
int64_t& result,
2063+
const std::string& prompt,
2064+
const std::string& title,
2065+
const int64_t& defaultChoice
2066+
);
2067+
20522068
/*! Prompts the user for a set of inputs specified in `fields` with given title.
20532069
The fields parameter is a list containing FieldInputFields
20542070

@@ -17027,6 +17043,7 @@ namespace BinaryNinja {
1702717043
static FormInputField SaveFileName(
1702817044
const std::string& prompt, const std::string& ext, const std::string& defaultName = "");
1702917045
static FormInputField DirectoryName(const std::string& prompt, const std::string& defaultName = "");
17046+
static FormInputField Checkbox(const std::string& prompt, const bool& defaultChoice = false);
1703017047
};
1703117048

1703217049
/*!
@@ -17086,6 +17103,13 @@ namespace BinaryNinja {
1708617103
const std::string& defaultName = "");
1708717104
virtual bool GetDirectoryNameInput(
1708817105
std::string& result, const std::string& prompt, const std::string& defaultName = "");
17106+
virtual bool GetCheckboxInput(
17107+
int64_t& result,
17108+
const std::string& prompt,
17109+
const std::string& title,
17110+
const int64_t
17111+
& defaultChoice = 0
17112+
);
1708917113
virtual bool GetFormInput(std::vector<FormInputField>& fields, const std::string& title) = 0;
1709017114

1709117115
virtual BNMessageBoxButtonResult ShowMessageBox(const std::string& title, const std::string& text,

binaryninjacore.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3037,7 +3037,8 @@ extern "C"
30373037
ChoiceFormField,
30383038
OpenFileNameFormField,
30393039
SaveFileNameFormField,
3040-
DirectoryNameFormField
3040+
DirectoryNameFormField,
3041+
CheckboxFormField
30413042
} BNFormInputFieldType;
30423043

30433044
typedef struct BNFormInputField
@@ -3083,6 +3084,7 @@ extern "C"
30833084
bool (*getSaveFileNameInput)(
30843085
void* ctxt, char** result, const char* prompt, const char* ext, const char* defaultName);
30853086
bool (*getDirectoryNameInput)(void* ctxt, char** result, const char* prompt, const char* defaultName);
3087+
bool (*getCheckboxInput)(void* ctxt, int64_t* result, const char* prompt, const char* title, const int64_t* defaultChoice);
30863088
bool (*getFormInput)(void* ctxt, BNFormInputField* fields, size_t count, const char* title);
30873089
BNMessageBoxButtonResult (*showMessageBox)(
30883090
void* ctxt, const char* title, const char* text, BNMessageBoxButtonSet buttons, BNMessageBoxIcon icon);
@@ -7312,6 +7314,7 @@ extern "C"
73127314
BINARYNINJACOREAPI bool BNGetSaveFileNameInput(
73137315
char** result, const char* prompt, const char* ext, const char* defaultName);
73147316
BINARYNINJACOREAPI bool BNGetDirectoryNameInput(char** result, const char* prompt, const char* defaultName);
7317+
BINARYNINJACOREAPI bool BNGetCheckboxInput(int64_t* result, const char* prompt, const char* title, const int64_t* defaultChoice);
73157318
BINARYNINJACOREAPI bool BNGetFormInput(BNFormInputField* fields, size_t count, const char* title);
73167319
BINARYNINJACOREAPI void BNFreeFormInputResults(BNFormInputField* fields, size_t count);
73177320
BINARYNINJACOREAPI BNMessageBoxButtonResult BNShowMessageBox(

interaction.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,17 @@ FormInputField FormInputField::DirectoryName(const string& prompt, const string&
112112
}
113113

114114

115+
FormInputField FormInputField::Checkbox(const string& prompt, const bool& defaultChoice)
116+
{
117+
FormInputField result;
118+
result.type = CheckboxFormField;
119+
result.prompt = prompt;
120+
result.intDefault = defaultChoice;
121+
result.hasDefault = true;
122+
return result;
123+
}
124+
125+
115126
void InteractionHandler::ShowMarkdownReport(
116127
Ref<BinaryView> view, const string& title, const string& contents, const string& plainText)
117128
{
@@ -189,6 +200,12 @@ bool InteractionHandler::GetDirectoryNameInput(string& result, const string& pro
189200
}
190201

191202

203+
bool InteractionHandler::GetCheckboxInput(int64_t& result, const std::string& prompt, const std::string& title, const int64_t& defaultChoice)
204+
{
205+
return BinaryNinja::GetCheckboxInput(result, prompt, "Select an option", defaultChoice);
206+
}
207+
208+
192209
static void ShowPlainTextReportCallback(void* ctxt, BNBinaryView* view, const char* title, const char* contents)
193210
{
194211
InteractionHandler* handler = (InteractionHandler*)ctxt;
@@ -310,6 +327,13 @@ static bool GetDirectoryNameInputCallback(void* ctxt, char** result, const char*
310327
}
311328

312329

330+
static bool GetCheckboxInputCallback(void* ctxt, int64_t* result, const char* prompt, const char* title, const int64_t* defaultChoice)
331+
{
332+
InteractionHandler* handler = (InteractionHandler*)ctxt;
333+
return handler->GetCheckboxInput(*result, prompt, title, *defaultChoice);
334+
}
335+
336+
313337
static bool GetFormInputCallback(void* ctxt, BNFormInputField* fieldBuf, size_t count, const char* title)
314338
{
315339
InteractionHandler* handler = (InteractionHandler*)ctxt;
@@ -353,6 +377,9 @@ static bool GetFormInputCallback(void* ctxt, BNFormInputField* fieldBuf, size_t
353377
case DirectoryNameFormField:
354378
fields.push_back(FormInputField::DirectoryName(fieldBuf[i].prompt, fieldBuf[i].defaultName));
355379
break;
380+
case CheckboxFormField:
381+
fields.push_back(FormInputField::Checkbox(fieldBuf[i].prompt, fieldBuf[i].intDefault));
382+
break;
356383
default:
357384
fields.push_back(FormInputField::Label(fieldBuf[i].prompt));
358385
break;
@@ -369,6 +396,7 @@ static bool GetFormInputCallback(void* ctxt, BNFormInputField* fieldBuf, size_t
369396
case DirectoryNameFormField:
370397
fields.back().stringDefault = fieldBuf[i].stringDefault;
371398
break;
399+
case CheckboxFormField:
372400
case IntegerFormField:
373401
fields.back().intDefault = fieldBuf[i].intDefault;
374402
break;
@@ -399,6 +427,7 @@ static bool GetFormInputCallback(void* ctxt, BNFormInputField* fieldBuf, size_t
399427
case DirectoryNameFormField:
400428
fieldBuf[i].stringResult = BNAllocString(fields[i].stringResult.c_str());
401429
break;
430+
case CheckboxFormField:
402431
case IntegerFormField:
403432
fieldBuf[i].intResult = fields[i].intResult;
404433
break;
@@ -460,6 +489,7 @@ void BinaryNinja::RegisterInteractionHandler(InteractionHandler* handler)
460489
cb.getOpenFileNameInput = GetOpenFileNameInputCallback;
461490
cb.getSaveFileNameInput = GetSaveFileNameInputCallback;
462491
cb.getDirectoryNameInput = GetDirectoryNameInputCallback;
492+
cb.getCheckboxInput = GetCheckboxInputCallback;
463493
cb.getFormInput = GetFormInputCallback;
464494
cb.showMessageBox = ShowMessageBoxCallback;
465495
cb.openUrl = OpenUrlCallback;
@@ -590,6 +620,12 @@ bool BinaryNinja::GetDirectoryNameInput(string& result, const string& prompt, co
590620
}
591621

592622

623+
bool BinaryNinja::GetCheckboxInput(int64_t& result, const std::string& prompt, const std::string& title, const int64_t& defaultChoice)
624+
{
625+
return BNGetCheckboxInput(&result, prompt.c_str(), title.c_str(), &defaultChoice);
626+
}
627+
628+
593629
bool BinaryNinja::GetFormInput(vector<FormInputField>& fields, const string& title)
594630
{
595631
// Construct field list in core format
@@ -635,6 +671,8 @@ bool BinaryNinja::GetFormInput(vector<FormInputField>& fields, const string& tit
635671
case DirectoryNameFormField:
636672
fieldBuf[i].stringDefault = fields[i].stringDefault.c_str();
637673
break;
674+
case CheckboxFormField:
675+
fieldBuf[i].intDefault = fields[i].intDefault;
638676
case IntegerFormField:
639677
fieldBuf[i].intDefault = fields[i].intDefault;
640678
break;
@@ -678,6 +716,7 @@ bool BinaryNinja::GetFormInput(vector<FormInputField>& fields, const string& tit
678716
case DirectoryNameFormField:
679717
fields[i].stringResult = fieldBuf[i].stringResult;
680718
break;
719+
case CheckboxFormField:
681720
case IntegerFormField:
682721
fields[i].intResult = fieldBuf[i].intResult;
683722
break;

python/interaction.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,56 @@ def result(self, value):
486486
self._result = value
487487

488488

489+
class CheckboxField:
490+
"""
491+
``CheckboxField`` prompts the user to choose a yes/no option in a checkbox.
492+
Result is stored in self.result as a boolean value.
493+
494+
:param str prompt: Prompt to be presented to the user
495+
:param bool default: Default state of the checkbox (False == unchecked, True == checked)
496+
"""
497+
def __init__(self, prompt, default):
498+
self._prompt = prompt
499+
self._result = None
500+
self._default = default
501+
502+
def _fill_core_struct(self, value):
503+
value.type = FormInputFieldType.CheckboxFormField
504+
value.prompt = self._prompt
505+
value.hasDefault = True
506+
value.intDefault = 1 if self._default else 0
507+
508+
def _fill_core_result(self, value):
509+
value.intResult = 1 if self.result else 0
510+
511+
def _get_result(self, value):
512+
self._result = value.intResult != 0
513+
514+
@property
515+
def prompt(self):
516+
return self._prompt
517+
518+
@prompt.setter
519+
def prompt(self, value):
520+
self._prompt = value
521+
522+
@property
523+
def result(self):
524+
return self._result
525+
526+
@result.setter
527+
def result(self, value):
528+
self._result = value
529+
530+
@property
531+
def default(self):
532+
return self._default
533+
534+
@default.setter
535+
def default(self, value):
536+
self._default = value
537+
538+
489539
class InteractionHandler:
490540
_interaction_handler = None
491541

@@ -505,6 +555,7 @@ def __init__(self):
505555
self._cb.getOpenFileNameInput = self._cb.getOpenFileNameInput.__class__(self._get_open_filename_input)
506556
self._cb.getSaveFileNameInput = self._cb.getSaveFileNameInput.__class__(self._get_save_filename_input)
507557
self._cb.getDirectoryNameInput = self._cb.getDirectoryNameInput.__class__(self._get_directory_name_input)
558+
self._cb.getCheckboxInput = self._cb.getCheckboxInput.__class__(self._get_checkbox_input)
508559
self._cb.getFormInput = self._cb.getFormInput.__class__(self._get_form_input)
509560
self._cb.showMessageBox = self._cb.showMessageBox.__class__(self._show_message_box)
510561
self._cb.openUrl = self._cb.openUrl.__class__(self._open_url)
@@ -650,6 +701,16 @@ def _get_directory_name_input(self, ctxt, result, prompt, default_name):
650701
except:
651702
log_error(traceback.format_exc())
652703

704+
def _get_checkbox_input(self, ctxt, result, prompt, default_choice):
705+
try:
706+
value = self.get_checkbox_input(prompt, default_choice)
707+
if value is None:
708+
return False
709+
result[0] = value
710+
return True
711+
except:
712+
log_error(traceback.format_exc())
713+
653714
def _get_form_input(self, ctxt, fields, count, title):
654715
try:
655716
field_objs = []
@@ -714,6 +775,14 @@ def _get_form_input(self, ctxt, fields, count, title):
714775
default=fields[i].stringDefault if fields[i].hasDefault else None
715776
)
716777
)
778+
elif fields[i].type == FormInputFieldType.CheckboxFormField:
779+
log_error(fields[i].hasDefault)
780+
field_objs.append(
781+
CheckboxField(
782+
fields[i].prompt,
783+
default=fields[i].intDefault != 0 if fields[i].hasDefault else None
784+
)
785+
)
717786
else:
718787
field_objs.append(LabelField(fields[i].prompt))
719788
if not self.get_form_input(field_objs, title):
@@ -795,6 +864,9 @@ def get_save_filename_input(self, prompt, ext, default_name):
795864
def get_directory_name_input(self, prompt, default_name):
796865
return get_text_line_input(prompt, "Select Directory")
797866

867+
def get_checkbox_input(self, prompt, default_choice):
868+
return get_checkbox_input(prompt, "Choose Option(s)", default_choice)
869+
798870
def get_form_input(self, fields, title):
799871
return False
800872

@@ -1361,6 +1433,22 @@ def get_directory_name_input(prompt: str, default_name: str = ""):
13611433
core.free_string(value)
13621434
return result.decode("utf-8")
13631435

1436+
def get_checkbox_input(prompt: str, title: str, default: bool = False):
1437+
"""
1438+
``get_checkbox_input`` prompts the user for a checkbox input
1439+
:param prompt: String to prompt with
1440+
:param title: Title of the window when executed in the UI
1441+
:param default: Optional default state for the checkbox (false == unchecked, true == checked), False if not set.
1442+
:rtype: bool indicating the state of the checkbox
1443+
"""
1444+
default_state = ctypes.c_int64()
1445+
default_state.value = 1 if default else 0
1446+
value = ctypes.c_int64()
1447+
if not core.BNGetCheckboxInput(value, prompt, title, default_state):
1448+
return None
1449+
result = value.value
1450+
assert result is not None
1451+
return result != 0
13641452

13651453
def get_form_input(fields, title):
13661454
"""
@@ -1382,6 +1470,7 @@ def get_form_input(fields, title):
13821470
OpenFileNameField Prompt for file to open
13831471
SaveFileNameField Prompt for file to save to
13841472
DirectoryNameField Prompt for directory name
1473+
CheckboxFormField Prompt for a checkbox
13851474
===================== ===================================================
13861475
13871476
This API is flexible and works both in the UI via a pop-up dialog and on the command-line.

rust/src/interaction/form.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ pub enum FormInputField {
112112
default: Option<String>,
113113
value: Option<String>,
114114
},
115+
Checkbox {
116+
prompt: String,
117+
default: Option<bool>,
118+
value: bool,
119+
},
115120
}
116121

117122
impl FormInputField {
@@ -130,6 +135,7 @@ impl FormInputField {
130135
let int_default = value.hasDefault.then_some(value.intDefault);
131136
let address_default = value.hasDefault.then_some(value.addressDefault);
132137
let index_default = value.hasDefault.then_some(value.indexDefault);
138+
let bool_default = value.hasDefault.then_some(value.intResult != 0);
133139
let extension = raw_to_string(value.ext);
134140
let current_address = value.currentAddress;
135141
let string_result = raw_to_string(value.stringResult);
@@ -186,6 +192,11 @@ impl FormInputField {
186192
default: string_default,
187193
value: string_result,
188194
},
195+
BNFormInputFieldType::CheckboxFormField => Self::Checkbox {
196+
prompt,
197+
default: bool_default,
198+
value: value.intResult != 0,
199+
},
189200
}
190201
}
191202

@@ -258,6 +269,7 @@ impl FormInputField {
258269
FormInputField::OpenFileName { .. } => BNFormInputFieldType::OpenFileNameFormField,
259270
FormInputField::SaveFileName { .. } => BNFormInputFieldType::SaveFileNameFormField,
260271
FormInputField::DirectoryName { .. } => BNFormInputFieldType::DirectoryNameFormField,
272+
FormInputField::Checkbox { .. } => BNFormInputFieldType::CheckboxFormField,
261273
}
262274
}
263275

@@ -274,6 +286,7 @@ impl FormInputField {
274286
FormInputField::OpenFileName { prompt, .. } => Some(prompt.clone()),
275287
FormInputField::SaveFileName { prompt, .. } => Some(prompt.clone()),
276288
FormInputField::DirectoryName { prompt, .. } => Some(prompt.clone()),
289+
FormInputField::Checkbox { prompt, .. } => Some(prompt.clone()),
277290
}
278291
}
279292

@@ -325,6 +338,7 @@ impl FormInputField {
325338
FormInputField::OpenFileName { default, .. } => Some(default.is_some()),
326339
FormInputField::SaveFileName { default, .. } => Some(default.is_some()),
327340
FormInputField::DirectoryName { default, .. } => Some(default.is_some()),
341+
FormInputField::Checkbox { default, .. } => Some(default.is_some()),
328342
}
329343
}
330344

0 commit comments

Comments
 (0)