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 ctxt(&AnalysisContext::from_raw(analysis))
73 }
74 }
75 let config = config.as_config();
76 let result = unsafe {
77 BNCreateActivity(
78 config.as_ptr(),
79 &mut action as *mut F as *mut c_void,
80 Some(cb_action::<F>),
81 )
82 };
83 unsafe { Activity::ref_from_raw(NonNull::new(result).unwrap()) }
84 }
85
86 pub fn name(&self) -> String {
87 let result = unsafe { BNActivityGetName(self.handle.as_ptr()) };
88 assert!(!result.is_null());
89 unsafe { BnString::into_string(result) }
90 }
91}
92
93impl ToOwned for Activity {
94 type Owned = Ref<Self>;
95
96 fn to_owned(&self) -> Self::Owned {
97 unsafe { RefCountable::inc_ref(self) }
98 }
99}
100
101unsafe impl RefCountable for Activity {
102 unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
103 Ref::new(Self {
104 handle: NonNull::new(BNNewActivityReference(handle.handle.as_ptr()))
105 .expect("valid handle"),
106 })
107 }
108
109 unsafe fn dec_ref(handle: &Self) {
110 BNFreeActivity(handle.handle.as_ptr());
111 }
112}
113
114pub trait AsConfig {
115 fn as_config(&self) -> CString;
116}
117
118impl AsConfig for &str {
119 fn as_config(&self) -> std::ffi::CString {
120 self.to_cstr()
121 }
122}
123
124#[must_use]
126#[derive(Deserialize, Serialize, Debug)]
127pub struct Config {
128 pub name: String,
130
131 pub title: String,
133
134 pub description: String,
136
137 #[serde(default)]
139 pub role: Role,
140
141 #[serde(skip_serializing_if = "Vec::is_empty", default)]
143 pub aliases: Vec<String>,
144
145 #[serde(default)]
147 pub eligibility: Eligibility,
148
149 #[serde(skip_serializing_if = "Option::is_none")]
150 pub dependencies: Option<Dependencies>,
151}
152
153#[derive(Deserialize, Serialize, Debug)]
154pub struct Dependencies {
155 #[serde(skip_serializing_if = "Vec::is_empty", default)]
156 pub downstream: Vec<String>,
157}
158
159impl Config {
160 pub fn action(
162 name: impl Into<String>,
163 title: impl Into<String>,
164 description: impl Into<String>,
165 ) -> Self {
166 Self {
167 name: name.into(),
168 title: title.into(),
169 description: description.into(),
170 role: Role::Action,
171 aliases: Vec::new(),
172 eligibility: Eligibility::default(),
173 dependencies: None,
174 }
175 }
176
177 pub fn aliases<I, S>(mut self, aliases: I) -> Self
179 where
180 I: IntoIterator<Item = S>,
181 S: Into<String>,
182 {
183 self.aliases = aliases.into_iter().map(|s| s.into()).collect();
184 self
185 }
186
187 pub fn eligibility(mut self, eligibility: Eligibility) -> Self {
189 self.eligibility = eligibility;
190 self
191 }
192
193 pub fn downstream_dependencies<I, S>(mut self, dependencies: I) -> Self
195 where
196 I: IntoIterator<Item = S>,
197 S: Into<String>,
198 {
199 self.dependencies = Some(Dependencies {
200 downstream: dependencies.into_iter().map(|s| s.into()).collect(),
201 });
202 self
203 }
204}
205
206impl AsConfig for &Config {
207 fn as_config(&self) -> CString {
208 serde_json::to_string(self)
209 .expect("Failed to serialize Config")
210 .to_cstr()
211 }
212}
213
214impl AsConfig for Config {
215 fn as_config(&self) -> CString {
216 (&self).as_config()
217 }
218}
219
220#[derive(Deserialize, Serialize, Debug)]
225#[serde(rename_all = "camelCase")]
226#[derive(Default)]
227pub enum Role {
228 #[default]
230 Action,
231
232 Selector,
235
236 Subflow,
242
243 Task,
248
249 Sequence,
250 Listener,
251}
252
253#[must_use]
255#[derive(Deserialize, Serialize, Debug)]
256#[serde(rename_all = "camelCase")]
257pub struct Eligibility {
258 #[serde(skip_serializing_if = "Option::is_none")]
260 pub auto: Option<Auto>,
261
262 #[serde(skip_serializing_if = "Option::is_none")]
267 pub run_once: Option<bool>,
268
269 #[serde(skip_serializing_if = "Option::is_none")]
274 pub run_once_per_session: Option<bool>,
275
276 #[serde(skip_serializing_if = "Option::is_none")]
278 pub continuation: Option<bool>,
279
280 #[serde(skip_serializing_if = "Vec::is_empty")]
282 pub predicates: Vec<Predicate>,
283
284 #[serde(skip_serializing_if = "Option::is_none")]
286 pub logical_operator: Option<PredicateLogicalOperator>,
287}
288
289impl Eligibility {
290 pub fn without_setting() -> Self {
293 Eligibility {
294 auto: None,
295 run_once: None,
296 run_once_per_session: None,
297 continuation: None,
298 predicates: vec![],
299 logical_operator: None,
300 }
301 }
302
303 pub fn auto() -> Self {
306 Eligibility {
307 auto: Some(Auto::new()),
308 run_once: None,
309 run_once_per_session: None,
310 continuation: None,
311 predicates: vec![],
312 logical_operator: None,
313 }
314 }
315
316 pub fn auto_with_default(value: bool) -> Self {
319 Eligibility {
320 auto: Some(Auto::new().default(value)),
321 run_once: None,
322 run_once_per_session: None,
323 continuation: None,
324 predicates: vec![],
325 logical_operator: None,
326 }
327 }
328
329 pub fn run_once(mut self, value: bool) -> Self {
331 self.run_once = Some(value);
332 self
333 }
334
335 pub fn run_once_per_session(mut self, value: bool) -> Self {
337 self.run_once_per_session = Some(value);
338 self
339 }
340
341 pub fn continuation(mut self, value: bool) -> Self {
343 self.continuation = Some(value);
344 self
345 }
346
347 pub fn predicate(mut self, predicate: impl Into<Predicate>) -> Self {
349 self.predicates = vec![predicate.into()];
350 self
351 }
352
353 pub fn matching_any_predicate(mut self, predicates: &[Predicate]) -> Self {
356 self.predicates = predicates.to_vec();
357 self.logical_operator = Some(PredicateLogicalOperator::Or);
358 self
359 }
360
361 pub fn matching_all_predicates(mut self, predicates: &[Predicate]) -> Self {
364 self.predicates = predicates.to_vec();
365 self.logical_operator = Some(PredicateLogicalOperator::And);
366 self
367 }
368}
369
370impl Default for Eligibility {
371 fn default() -> Self {
372 Self::auto()
373 }
374}
375
376#[must_use]
378#[derive(Deserialize, Serialize, Debug, Default)]
379pub struct Auto {
380 #[serde(skip_serializing_if = "Option::is_none")]
382 pub default: Option<bool>,
383}
384
385impl Auto {
386 pub fn new() -> Self {
388 Self { default: None }
389 }
390
391 pub fn default(mut self, value: bool) -> Self {
393 self.default = Some(value);
394 self
395 }
396}
397
398#[must_use]
402#[derive(Deserialize, Serialize, Debug, Clone)]
403pub struct Predicate {
404 #[serde(flatten)]
405 predicate_type: PredicateType,
406 operator: Operator,
407 value: serde_json::Value,
408}
409
410#[must_use]
412pub enum ViewType {
413 In(Vec<String>),
414 NotIn(Vec<String>),
415}
416
417impl ViewType {
418 pub fn in_<I, S>(values: I) -> Self
421 where
422 I: IntoIterator<Item = S>,
423 S: AsRef<str>,
424 {
425 ViewType::In(values.into_iter().map(|s| s.as_ref().to_string()).collect())
426 }
427
428 pub fn not_in<I, S>(values: I) -> Self
431 where
432 I: IntoIterator<Item = S>,
433 S: AsRef<str>,
434 {
435 ViewType::NotIn(values.into_iter().map(|s| s.as_ref().to_string()).collect())
436 }
437}
438
439impl From<ViewType> for Predicate {
440 fn from(predicate: ViewType) -> Self {
441 match predicate {
442 ViewType::In(value) => Predicate {
443 predicate_type: PredicateType::ViewType,
444 operator: Operator::In,
445 value: serde_json::json!(value),
446 },
447 ViewType::NotIn(value) => Predicate {
448 predicate_type: PredicateType::ViewType,
449 operator: Operator::NotIn,
450 value: serde_json::json!(value),
451 },
452 }
453 }
454}
455
456#[must_use]
458pub enum Platform {
459 In(Vec<String>),
460 NotIn(Vec<String>),
461}
462
463impl Platform {
464 pub fn in_<I, S>(values: I) -> Self
467 where
468 I: IntoIterator<Item = S>,
469 S: AsRef<str>,
470 {
471 Platform::In(values.into_iter().map(|s| s.as_ref().to_string()).collect())
472 }
473
474 pub fn not_in<I, S>(values: I) -> Self
477 where
478 I: IntoIterator<Item = S>,
479 S: AsRef<str>,
480 {
481 Platform::NotIn(values.into_iter().map(|s| s.as_ref().to_string()).collect())
482 }
483}
484
485impl From<Platform> for Predicate {
486 fn from(predicate: Platform) -> Self {
487 match predicate {
488 Platform::In(value) => Predicate {
489 predicate_type: PredicateType::Platform,
490 operator: Operator::In,
491 value: serde_json::json!(value),
492 },
493 Platform::NotIn(value) => Predicate {
494 predicate_type: PredicateType::Platform,
495 operator: Operator::NotIn,
496 value: serde_json::json!(value),
497 },
498 }
499 }
500}
501
502#[must_use]
504pub struct Setting {
505 identifier: String,
506 operator: Operator,
507 value: serde_json::Value,
508}
509
510impl Setting {
511 pub fn new(
513 identifier: impl Into<String>,
514 operator: Operator,
515 value: impl serde::Serialize,
516 ) -> Self {
517 Self {
518 identifier: identifier.into(),
519 operator,
520 value: serde_json::json!(value),
521 }
522 }
523
524 pub fn eq(identifier: impl Into<String>, value: impl serde::Serialize) -> Self {
526 Self::new(identifier, Operator::Eq, value)
527 }
528
529 pub fn ne(identifier: impl Into<String>, value: impl serde::Serialize) -> Self {
531 Self::new(identifier, Operator::Ne, value)
532 }
533
534 pub fn lt(identifier: impl Into<String>, value: impl serde::Serialize) -> Self {
536 Self::new(identifier, Operator::Lt, value)
537 }
538
539 pub fn lte(identifier: impl Into<String>, value: impl serde::Serialize) -> Self {
541 Self::new(identifier, Operator::Lte, value)
542 }
543
544 pub fn gt(identifier: impl Into<String>, value: impl serde::Serialize) -> Self {
546 Self::new(identifier, Operator::Gt, value)
547 }
548
549 pub fn gte(identifier: impl Into<String>, value: impl serde::Serialize) -> Self {
551 Self::new(identifier, Operator::Gte, value)
552 }
553
554 pub fn in_(identifier: impl Into<String>, value: impl serde::Serialize) -> Self {
556 Self::new(identifier, Operator::In, value)
557 }
558
559 pub fn not_in(identifier: impl Into<String>, value: impl serde::Serialize) -> Self {
561 Self::new(identifier, Operator::NotIn, value)
562 }
563}
564
565impl From<Setting> for Predicate {
566 fn from(setting: Setting) -> Self {
567 Predicate {
568 predicate_type: PredicateType::Setting {
569 identifier: setting.identifier,
570 },
571 operator: setting.operator,
572 value: setting.value,
573 }
574 }
575}
576
577#[derive(Deserialize, Serialize, Debug, Clone)]
578#[serde(rename_all = "camelCase", tag = "type")]
579enum PredicateType {
580 Setting { identifier: String },
581 ViewType,
582 Platform,
583}
584
585#[derive(Deserialize, Serialize, Debug, Copy, Clone)]
586pub enum Operator {
587 #[serde(rename = "==")]
588 Eq,
589 #[serde(rename = "!=")]
590 Ne,
591 #[serde(rename = "<")]
592 Lt,
593 #[serde(rename = "<=")]
594 Lte,
595 #[serde(rename = ">")]
596 Gt,
597 #[serde(rename = ">=")]
598 Gte,
599 #[serde(rename = "in")]
600 In,
601 #[serde(rename = "not in")]
602 NotIn,
603}
604
605#[derive(Deserialize, Serialize, Debug, Clone, Copy)]
606#[serde(rename_all = "camelCase")]
607pub enum PredicateLogicalOperator {
608 And,
609 Or,
610}