1use std::{
2 ffi::{c_void, CString},
3 ptr::NonNull,
4};
5
6use binaryninjacore_sys::*;
7use serde_derive::{Deserialize, Serialize};
8
9use crate::{
10 rc::{Ref, RefCountable},
11 string::{BnString, IntoCStr},
12 workflow::AnalysisContext,
13};
14
15#[repr(transparent)]
40pub struct Activity {
41 pub(crate) handle: NonNull<BNActivity>,
42}
43
44impl Activity {
45 #[allow(unused)]
46 pub(crate) unsafe fn from_raw(handle: NonNull<BNActivity>) -> Self {
47 Self { handle }
48 }
49
50 pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNActivity>) -> Ref<Self> {
51 Ref::new(Self { handle })
52 }
53
54 pub fn new(config: impl AsConfig) -> Ref<Self> {
55 unsafe extern "C" fn cb_action_nop(_: *mut c_void, _: *mut BNAnalysisContext) {}
56 let config = config.as_config();
57 let result =
58 unsafe { BNCreateActivity(config.as_ptr(), std::ptr::null_mut(), Some(cb_action_nop)) };
59 unsafe { Activity::ref_from_raw(NonNull::new(result).unwrap()) }
60 }
61
62 pub fn new_with_action<F>(config: impl AsConfig, mut action: F) -> Ref<Self>
63 where
64 F: FnMut(&AnalysisContext),
65 {
66 unsafe extern "C" fn cb_action<F: FnMut(&AnalysisContext)>(
67 ctxt: *mut c_void,
68 analysis: *mut BNAnalysisContext,
69 ) {
70 let ctxt = &mut *(ctxt as *mut F);
71 if let Some(analysis) = NonNull::new(analysis) {
72 let analysis = AnalysisContext::from_raw(analysis);
73 let _span = ffi_span!("Activity::action", analysis.view());
74 ctxt(&analysis)
75 }
76 }
77 let config = config.as_config();
78 let result = unsafe {
79 BNCreateActivity(
80 config.as_ptr(),
81 &mut action as *mut F as *mut c_void,
82 Some(cb_action::<F>),
83 )
84 };
85 unsafe { Activity::ref_from_raw(NonNull::new(result).unwrap()) }
86 }
87
88 pub fn name(&self) -> String {
89 let result = unsafe { BNActivityGetName(self.handle.as_ptr()) };
90 assert!(!result.is_null());
91 unsafe { BnString::into_string(result) }
92 }
93}
94
95impl ToOwned for Activity {
96 type Owned = Ref<Self>;
97
98 fn to_owned(&self) -> Self::Owned {
99 unsafe { RefCountable::inc_ref(self) }
100 }
101}
102
103unsafe impl RefCountable for Activity {
104 unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
105 Ref::new(Self {
106 handle: NonNull::new(BNNewActivityReference(handle.handle.as_ptr()))
107 .expect("valid handle"),
108 })
109 }
110
111 unsafe fn dec_ref(handle: &Self) {
112 BNFreeActivity(handle.handle.as_ptr());
113 }
114}
115
116pub trait AsConfig {
117 fn as_config(&self) -> CString;
118}
119
120impl AsConfig for &str {
121 fn as_config(&self) -> std::ffi::CString {
122 self.to_cstr()
123 }
124}
125
126#[must_use]
128#[derive(Deserialize, Serialize, Debug)]
129pub struct Config {
130 pub name: String,
132
133 pub title: String,
135
136 pub description: String,
138
139 #[serde(default)]
141 pub role: Role,
142
143 #[serde(skip_serializing_if = "Vec::is_empty", default)]
145 pub aliases: Vec<String>,
146
147 #[serde(default)]
149 pub eligibility: Eligibility,
150
151 #[serde(skip_serializing_if = "Option::is_none")]
152 pub dependencies: Option<Dependencies>,
153}
154
155#[derive(Deserialize, Serialize, Debug)]
156pub struct Dependencies {
157 #[serde(skip_serializing_if = "Vec::is_empty", default)]
158 pub downstream: Vec<String>,
159}
160
161impl Config {
162 pub fn action(
164 name: impl Into<String>,
165 title: impl Into<String>,
166 description: impl Into<String>,
167 ) -> Self {
168 Self {
169 name: name.into(),
170 title: title.into(),
171 description: description.into(),
172 role: Role::Action,
173 aliases: Vec::new(),
174 eligibility: Eligibility::default(),
175 dependencies: None,
176 }
177 }
178
179 pub fn aliases<I, S>(mut self, aliases: I) -> Self
181 where
182 I: IntoIterator<Item = S>,
183 S: Into<String>,
184 {
185 self.aliases = aliases.into_iter().map(|s| s.into()).collect();
186 self
187 }
188
189 pub fn eligibility(mut self, eligibility: Eligibility) -> Self {
191 self.eligibility = eligibility;
192 self
193 }
194
195 pub fn downstream_dependencies<I, S>(mut self, dependencies: I) -> Self
197 where
198 I: IntoIterator<Item = S>,
199 S: Into<String>,
200 {
201 self.dependencies = Some(Dependencies {
202 downstream: dependencies.into_iter().map(|s| s.into()).collect(),
203 });
204 self
205 }
206}
207
208impl AsConfig for &Config {
209 fn as_config(&self) -> CString {
210 serde_json::to_string(self)
211 .expect("Failed to serialize Config")
212 .to_cstr()
213 }
214}
215
216impl AsConfig for Config {
217 fn as_config(&self) -> CString {
218 (&self).as_config()
219 }
220}
221
222#[derive(Deserialize, Serialize, Debug)]
227#[serde(rename_all = "camelCase")]
228#[derive(Default)]
229pub enum Role {
230 #[default]
232 Action,
233
234 Selector,
237
238 Subflow,
244
245 Task,
250
251 Sequence,
252 Listener,
253}
254
255#[must_use]
257#[derive(Deserialize, Serialize, Debug)]
258#[serde(rename_all = "camelCase")]
259pub struct Eligibility {
260 #[serde(skip_serializing_if = "Option::is_none")]
262 pub auto: Option<Auto>,
263
264 #[serde(skip_serializing_if = "Option::is_none")]
269 pub run_once: Option<bool>,
270
271 #[serde(skip_serializing_if = "Option::is_none")]
276 pub run_once_per_session: Option<bool>,
277
278 #[serde(skip_serializing_if = "Option::is_none")]
280 pub continuation: Option<bool>,
281
282 #[serde(skip_serializing_if = "Vec::is_empty")]
284 pub predicates: Vec<Predicate>,
285
286 #[serde(skip_serializing_if = "Option::is_none")]
288 pub logical_operator: Option<PredicateLogicalOperator>,
289}
290
291impl Eligibility {
292 pub fn without_setting() -> Self {
295 Eligibility {
296 auto: None,
297 run_once: None,
298 run_once_per_session: None,
299 continuation: None,
300 predicates: vec![],
301 logical_operator: None,
302 }
303 }
304
305 pub fn auto() -> Self {
308 Eligibility {
309 auto: Some(Auto::new()),
310 run_once: None,
311 run_once_per_session: None,
312 continuation: None,
313 predicates: vec![],
314 logical_operator: None,
315 }
316 }
317
318 pub fn auto_with_default(value: bool) -> Self {
321 Eligibility {
322 auto: Some(Auto::new().default(value)),
323 run_once: None,
324 run_once_per_session: None,
325 continuation: None,
326 predicates: vec![],
327 logical_operator: None,
328 }
329 }
330
331 pub fn run_once(mut self, value: bool) -> Self {
333 self.run_once = Some(value);
334 self
335 }
336
337 pub fn run_once_per_session(mut self, value: bool) -> Self {
339 self.run_once_per_session = Some(value);
340 self
341 }
342
343 pub fn continuation(mut self, value: bool) -> Self {
345 self.continuation = Some(value);
346 self
347 }
348
349 pub fn predicate(mut self, predicate: impl Into<Predicate>) -> Self {
351 self.predicates = vec![predicate.into()];
352 self
353 }
354
355 pub fn matching_any_predicate(mut self, predicates: &[Predicate]) -> Self {
358 self.predicates = predicates.to_vec();
359 self.logical_operator = Some(PredicateLogicalOperator::Or);
360 self
361 }
362
363 pub fn matching_all_predicates(mut self, predicates: &[Predicate]) -> Self {
366 self.predicates = predicates.to_vec();
367 self.logical_operator = Some(PredicateLogicalOperator::And);
368 self
369 }
370}
371
372impl Default for Eligibility {
373 fn default() -> Self {
374 Self::auto()
375 }
376}
377
378#[must_use]
380#[derive(Deserialize, Serialize, Debug, Default)]
381pub struct Auto {
382 #[serde(skip_serializing_if = "Option::is_none")]
384 pub default: Option<bool>,
385}
386
387impl Auto {
388 pub fn new() -> Self {
390 Self { default: None }
391 }
392
393 pub fn default(mut self, value: bool) -> Self {
395 self.default = Some(value);
396 self
397 }
398}
399
400#[must_use]
404#[derive(Deserialize, Serialize, Debug, Clone)]
405pub struct Predicate {
406 #[serde(flatten)]
407 predicate_type: PredicateType,
408 operator: Operator,
409 value: serde_json::Value,
410}
411
412#[must_use]
414pub enum ViewType {
415 In(Vec<String>),
416 NotIn(Vec<String>),
417}
418
419impl ViewType {
420 pub fn in_<I, S>(values: I) -> Self
423 where
424 I: IntoIterator<Item = S>,
425 S: AsRef<str>,
426 {
427 ViewType::In(values.into_iter().map(|s| s.as_ref().to_string()).collect())
428 }
429
430 pub fn not_in<I, S>(values: I) -> Self
433 where
434 I: IntoIterator<Item = S>,
435 S: AsRef<str>,
436 {
437 ViewType::NotIn(values.into_iter().map(|s| s.as_ref().to_string()).collect())
438 }
439}
440
441impl From<ViewType> for Predicate {
442 fn from(predicate: ViewType) -> Self {
443 match predicate {
444 ViewType::In(value) => Predicate {
445 predicate_type: PredicateType::ViewType,
446 operator: Operator::In,
447 value: serde_json::json!(value),
448 },
449 ViewType::NotIn(value) => Predicate {
450 predicate_type: PredicateType::ViewType,
451 operator: Operator::NotIn,
452 value: serde_json::json!(value),
453 },
454 }
455 }
456}
457
458#[must_use]
460pub enum Platform {
461 In(Vec<String>),
462 NotIn(Vec<String>),
463}
464
465impl Platform {
466 pub fn in_<I, S>(values: I) -> Self
469 where
470 I: IntoIterator<Item = S>,
471 S: AsRef<str>,
472 {
473 Platform::In(values.into_iter().map(|s| s.as_ref().to_string()).collect())
474 }
475
476 pub fn not_in<I, S>(values: I) -> Self
479 where
480 I: IntoIterator<Item = S>,
481 S: AsRef<str>,
482 {
483 Platform::NotIn(values.into_iter().map(|s| s.as_ref().to_string()).collect())
484 }
485}
486
487impl From<Platform> for Predicate {
488 fn from(predicate: Platform) -> Self {
489 match predicate {
490 Platform::In(value) => Predicate {
491 predicate_type: PredicateType::Platform,
492 operator: Operator::In,
493 value: serde_json::json!(value),
494 },
495 Platform::NotIn(value) => Predicate {
496 predicate_type: PredicateType::Platform,
497 operator: Operator::NotIn,
498 value: serde_json::json!(value),
499 },
500 }
501 }
502}
503
504#[must_use]
506pub struct Setting {
507 identifier: String,
508 operator: Operator,
509 value: serde_json::Value,
510}
511
512impl Setting {
513 pub fn new(
515 identifier: impl Into<String>,
516 operator: Operator,
517 value: impl serde::Serialize,
518 ) -> Self {
519 Self {
520 identifier: identifier.into(),
521 operator,
522 value: serde_json::json!(value),
523 }
524 }
525
526 pub fn eq(identifier: impl Into<String>, value: impl serde::Serialize) -> Self {
528 Self::new(identifier, Operator::Eq, value)
529 }
530
531 pub fn ne(identifier: impl Into<String>, value: impl serde::Serialize) -> Self {
533 Self::new(identifier, Operator::Ne, value)
534 }
535
536 pub fn lt(identifier: impl Into<String>, value: impl serde::Serialize) -> Self {
538 Self::new(identifier, Operator::Lt, value)
539 }
540
541 pub fn lte(identifier: impl Into<String>, value: impl serde::Serialize) -> Self {
543 Self::new(identifier, Operator::Lte, value)
544 }
545
546 pub fn gt(identifier: impl Into<String>, value: impl serde::Serialize) -> Self {
548 Self::new(identifier, Operator::Gt, value)
549 }
550
551 pub fn gte(identifier: impl Into<String>, value: impl serde::Serialize) -> Self {
553 Self::new(identifier, Operator::Gte, value)
554 }
555
556 pub fn in_(identifier: impl Into<String>, value: impl serde::Serialize) -> Self {
558 Self::new(identifier, Operator::In, value)
559 }
560
561 pub fn not_in(identifier: impl Into<String>, value: impl serde::Serialize) -> Self {
563 Self::new(identifier, Operator::NotIn, value)
564 }
565}
566
567impl From<Setting> for Predicate {
568 fn from(setting: Setting) -> Self {
569 Predicate {
570 predicate_type: PredicateType::Setting {
571 identifier: setting.identifier,
572 },
573 operator: setting.operator,
574 value: setting.value,
575 }
576 }
577}
578
579#[derive(Deserialize, Serialize, Debug, Clone)]
580#[serde(rename_all = "camelCase", tag = "type")]
581enum PredicateType {
582 Setting { identifier: String },
583 ViewType,
584 Platform,
585}
586
587#[derive(Deserialize, Serialize, Debug, Copy, Clone)]
588pub enum Operator {
589 #[serde(rename = "==")]
590 Eq,
591 #[serde(rename = "!=")]
592 Ne,
593 #[serde(rename = "<")]
594 Lt,
595 #[serde(rename = "<=")]
596 Lte,
597 #[serde(rename = ">")]
598 Gt,
599 #[serde(rename = ">=")]
600 Gte,
601 #[serde(rename = "in")]
602 In,
603 #[serde(rename = "not in")]
604 NotIn,
605}
606
607#[derive(Deserialize, Serialize, Debug, Clone, Copy)]
608#[serde(rename_all = "camelCase")]
609pub enum PredicateLogicalOperator {
610 And,
611 Or,
612}