1use std::path::PathBuf;
4
5use pyo3::{pyclass, pymethods, PyResult};
6use uuid::Uuid;
7
8#[pyclass(frozen)]
10#[derive(Debug, Clone)]
11pub struct Label {
12 #[pyo3(get)]
14 pub id: u64,
15
16 #[pyo3(get)]
18 pub name: String,
19
20 #[pyo3(get)]
22 pub parent: Option<String>,
23}
24
25#[pymethods]
26impl Label {
27 #[must_use]
29 pub fn __repr__(&self) -> String {
30 if let Some(parent) = &self.parent {
31 format!("Label {}: {parent}/{}", self.id, self.name)
32 } else {
33 format!("Label {}: {}", self.id, self.name)
34 }
35 }
36
37 #[must_use]
39 pub fn __str__(&self) -> String {
40 self.name.clone()
41 }
42}
43
44impl From<malwaredb_client::malwaredb_api::Label> for Label {
45 fn from(label: malwaredb_client::malwaredb_api::Label) -> Self {
46 Self {
47 id: label.id,
48 name: label.name,
49 parent: label.parent,
50 }
51 }
52}
53
54#[pyclass(frozen)]
56#[derive(Debug, Clone)]
57pub struct ServerInfo {
58 #[pyo3(get)]
60 pub os_name: String,
61
62 #[pyo3(get)]
64 pub memory_used: String,
65
66 #[pyo3(get)]
68 pub mdb_version: String,
69
70 #[pyo3(get)]
72 pub db_version: String,
73
74 #[pyo3(get)]
76 pub db_size: String,
77
78 #[pyo3(get)]
80 pub num_samples: u64,
81
82 #[pyo3(get)]
84 pub num_users: u32,
85
86 #[pyo3(get)]
88 pub uptime: String,
89
90 #[pyo3(get)]
92 pub instance_name: String,
93}
94
95#[pymethods]
96impl ServerInfo {
97 #[must_use]
99 pub fn __repr__(&self) -> String {
100 format!(
101 "MalwareDB {} running on {} for {}",
102 self.mdb_version, self.os_name, self.uptime
103 )
104 }
105
106 #[must_use]
108 pub fn __str__(&self) -> String {
109 format!("MalwareDB {}", self.mdb_version)
110 }
111}
112
113impl From<malwaredb_client::malwaredb_api::ServerInfo> for ServerInfo {
114 fn from(value: malwaredb_client::malwaredb_api::ServerInfo) -> Self {
115 Self {
116 os_name: value.os_name,
117 memory_used: value.memory_used,
118 mdb_version: value.mdb_version.to_string(),
119 db_version: value.db_version,
120 db_size: value.db_size,
121 num_samples: value.num_samples,
122 num_users: value.num_users,
123 uptime: value.uptime,
124 instance_name: value.instance_name,
125 }
126 }
127}
128
129#[pyclass(frozen)]
131#[derive(Debug, Clone)]
132pub struct Source {
133 #[pyo3(get)]
135 pub id: u32,
136
137 #[pyo3(get)]
139 pub name: String,
140
141 #[pyo3(get)]
143 pub description: Option<String>,
144
145 #[pyo3(get)]
147 pub url: Option<String>,
148
149 #[pyo3(get)]
151 pub first_acquisition: String,
152
153 #[pyo3(get)]
155 pub malicious: Option<bool>,
156}
157
158#[pymethods]
159impl Source {
160 #[must_use]
162 pub fn __repr__(&self) -> String {
163 let url = if let Some(url) = &self.url {
164 format!(" from {url}")
165 } else {
166 String::new()
167 };
168
169 let desc = if let Some(desc) = &self.description {
170 format!(" -- {desc}")
171 } else {
172 String::new()
173 };
174
175 format!("{}({}){url}{desc}", self.name, self.id)
176 }
177
178 #[must_use]
180 pub fn __str__(&self) -> String {
181 self.name.clone()
182 }
183}
184
185impl From<malwaredb_client::malwaredb_api::SourceInfo> for Source {
186 fn from(value: malwaredb_client::malwaredb_api::SourceInfo) -> Self {
187 Self {
188 id: value.id,
189 name: value.name,
190 description: value.description,
191 url: value.url,
192 first_acquisition: value.first_acquisition.to_rfc3339(),
193 malicious: value.malicious,
194 }
195 }
196}
197
198#[pyclass(frozen)]
200#[derive(Debug, Clone)]
201pub struct SupportedFileType {
202 #[pyo3(get)]
204 pub name: String,
205
206 #[pyo3(get)]
208 pub magic: Vec<String>,
209
210 #[pyo3(get)]
212 pub is_executable: bool,
213
214 #[pyo3(get)]
216 pub description: Option<String>,
217}
218
219#[pymethods]
220impl SupportedFileType {
221 #[must_use]
223 pub fn __repr__(&self) -> String {
224 format!("{}, starting with {}", self.name, self.magic.join(" or "))
225 }
226
227 #[must_use]
229 pub fn __str__(&self) -> String {
230 self.name.clone()
231 }
232}
233
234impl From<malwaredb_client::malwaredb_api::SupportedFileType> for SupportedFileType {
235 fn from(value: malwaredb_client::malwaredb_api::SupportedFileType) -> Self {
236 Self {
237 name: value.name,
238 magic: value.magic,
239 is_executable: value.is_executable,
240 description: value.description,
241 }
242 }
243}
244
245#[pyclass(frozen)]
247#[derive(Debug, Clone)]
248pub struct UserInfo {
249 #[pyo3(get)]
251 pub id: u32,
252
253 #[pyo3(get)]
255 pub username: String,
256
257 #[pyo3(get)]
259 pub groups: Vec<String>,
260
261 #[pyo3(get)]
263 pub sources: Vec<String>,
264
265 #[pyo3(get)]
267 pub is_admin: bool,
268
269 #[pyo3(get)]
271 pub created: String,
272
273 #[pyo3(get)]
275 pub is_readonly: bool,
276}
277
278#[pymethods]
279impl UserInfo {
280 #[must_use]
282 pub fn __str__(&self) -> String {
283 self.username.clone()
284 }
285}
286
287impl From<malwaredb_client::malwaredb_api::GetUserInfoResponse> for UserInfo {
288 fn from(value: malwaredb_client::malwaredb_api::GetUserInfoResponse) -> Self {
289 Self {
290 id: value.id,
291 username: value.username,
292 groups: value.groups,
293 sources: value.sources,
294 is_admin: value.is_admin,
295 created: value.created.to_rfc3339(),
296 is_readonly: value.is_readonly,
297 }
298 }
299}
300
301#[pyclass(frozen)]
303#[derive(Debug, Clone)]
304pub struct SearchResults {
305 #[pyo3(get)]
307 pub hashes: Vec<String>,
308
309 #[pyo3(get)]
311 pub results: u64,
312
313 #[pyo3(get)]
315 pub pagination: Option<Uuid>,
316
317 #[pyo3(get)]
319 pub message: Option<String>,
320}
321
322#[pymethods]
323impl SearchResults {
324 #[must_use]
326 pub fn __repr__(&self) -> String {
327 if self.pagination.is_some() && self.results > self.hashes.len() as u64 {
328 format!(
329 "Search results showing {} of {} available hashes",
330 self.hashes.len(),
331 self.results
332 )
333 } else if let Some(message) = &self.message {
334 format!(
335 "{message}: Search results showing {} hashes",
336 self.hashes.len()
337 )
338 } else {
339 format!("Search results showing {} hashes", self.hashes.len())
340 }
341 }
342
343 #[must_use]
345 pub fn __str__(&self) -> String {
346 if let Some(message) = &self.message {
347 format!(
348 "{message}: Search results with {} hashes",
349 self.hashes.len()
350 )
351 } else {
352 format!("Search results with {} hashes", self.hashes.len())
353 }
354 }
355}
356
357impl From<malwaredb_client::malwaredb_api::SearchResponse> for SearchResults {
358 fn from(value: malwaredb_client::malwaredb_api::SearchResponse) -> Self {
359 Self {
360 hashes: value.hashes,
361 results: value.total_results,
362 pagination: value.pagination,
363 message: value.message,
364 }
365 }
366}
367
368#[pyclass(frozen)]
370#[derive(Debug, Clone)]
371pub struct DiscoveredServer {
372 #[pyo3(get)]
374 pub host: String,
375
376 #[pyo3(get)]
378 pub port: u16,
379
380 #[pyo3(get)]
382 pub ssl: bool,
383
384 #[pyo3(get)]
386 pub name: String,
387
388 #[pyo3(get)]
390 pub info: Option<ServerInfo>,
391}
392
393#[pymethods]
394impl DiscoveredServer {
395 #[pyo3(signature = (username, password, save = false, cert_path = None))]
401 pub fn connect(
402 &self,
403 username: String,
404 password: String,
405 save: bool,
406 cert_path: Option<PathBuf>,
407 ) -> PyResult<crate::MalwareDBClient> {
408 crate::MalwareDBClient::login(self.__str__(), username, password, save, cert_path)
409 }
410
411 #[must_use]
413 pub fn __repr__(&self) -> String {
414 if self.ssl {
415 format!("{} at https://{}:{}", self.name, self.host, self.port)
416 } else {
417 format!("{} at http://{}:{}", self.name, self.host, self.port)
418 }
419 }
420
421 #[must_use]
423 pub fn __str__(&self) -> String {
424 if self.ssl {
425 format!("https://{}:{}", self.host, self.port)
426 } else {
427 format!("http://{}:{}", self.host, self.port)
428 }
429 }
430}
431
432impl From<malwaredb_client::MalwareDBServer> for DiscoveredServer {
433 fn from(value: malwaredb_client::MalwareDBServer) -> Self {
434 let info = value.server_info_blocking().ok();
435 Self {
436 host: value.host,
437 port: value.port,
438 ssl: value.ssl,
439 name: value.name,
440 info: info.map(Into::into),
441 }
442 }
443}