1pub mod filters;
2pub mod sorts;
3
4use dco3_crypto::DracoonCrypto;
5use dco3_crypto::DracoonRSACrypto;
6use dco3_crypto::PlainUserKeyPairContainer;
7use dco3_derive::FromResponse;
8pub use filters::*;
9pub use sorts::*;
10use tracing::debug;
11use tracing::error;
12
13use std::fmt::Debug;
14use std::fmt::Display;
15use std::fmt::Formatter;
16use std::sync::Arc;
17use std::sync::Mutex;
18
19use crate::client::DracoonClient;
20use crate::{
21 client::{errors::DracoonClientError, models::DracoonErrorResponse},
22 models::{ObjectExpiration, Range, RangedItems},
23 utils::parse_body,
24 utils::FromResponse,
25};
26
27use async_trait::async_trait;
28use chrono::{DateTime, Utc};
29use dco3_crypto::FileKey;
30use dco3_crypto::PublicKeyContainer;
31use reqwest::{Response, StatusCode};
32use serde::{Deserialize, Serialize};
33
34use super::rooms::models::NodePermissionsBuilder;
35
36#[derive(Clone)]
37pub struct NodesEndpoint<S> {
38 client: Arc<DracoonClient<S>>,
39 state: std::marker::PhantomData<S>,
40}
41
42impl<S> NodesEndpoint<S> {
43 pub fn new(client: Arc<DracoonClient<S>>) -> Self {
44 Self {
45 client,
46 state: std::marker::PhantomData,
47 }
48 }
49
50 pub fn client(&self) -> &Arc<DracoonClient<S>> {
51 &self.client
52 }
53}
54
55pub type DownloadProgressCallback = Box<dyn FnMut(u64, u64) + Send + Sync>;
57
58pub type UploadProgressCallback = Box<dyn FnMut(u64, u64) + Send + Sync>;
60
61pub struct CloneableUploadProgressCallback(Arc<Mutex<UploadProgressCallback>>);
63
64impl Clone for CloneableUploadProgressCallback {
65 fn clone(&self) -> Self {
66 Self(self.0.clone())
67 }
68}
69
70impl CloneableUploadProgressCallback {
71 pub fn new<F>(callback: F) -> Self
72 where
73 F: 'static + FnMut(u64, u64) + Send + Sync,
74 {
75 Self(Arc::new(Mutex::new(Box::new(callback))))
76 }
77
78 pub fn call(&self, bytes_read: u64, total_size: u64) {
79 (self.0.lock().unwrap())(bytes_read, total_size);
80 }
81}
82
83#[derive(Debug, Clone)]
85pub struct FileMeta {
86 pub name: String,
87 pub size: u64,
88 pub timestamp_creation: Option<DateTime<Utc>>,
89 pub timestamp_modification: Option<DateTime<Utc>>,
90}
91
92#[derive(Default)]
93pub struct FileMetaBuilder {
94 name: String,
95 size: u64,
96 timestamp_creation: Option<DateTime<Utc>>,
97 timestamp_modification: Option<DateTime<Utc>>,
98}
99
100impl FileMeta {
101 pub fn builder(name: impl Into<String>, size: u64) -> FileMetaBuilder {
102 FileMetaBuilder::new(name, size)
103 }
104}
105
106impl FileMetaBuilder {
107 pub fn new(name: impl Into<String>, size: u64) -> Self {
108 Self {
109 name: name.into(),
110 size,
111 timestamp_creation: None,
112 timestamp_modification: None,
113 }
114 }
115
116 pub fn with_timestamp_creation(mut self, timestamp_creation: DateTime<Utc>) -> Self {
117 self.timestamp_creation = Some(timestamp_creation);
118 self
119 }
120
121 pub fn with_timestamp_modification(mut self, timestamp_modification: DateTime<Utc>) -> Self {
122 self.timestamp_modification = Some(timestamp_modification);
123 self
124 }
125
126 pub fn build(self) -> FileMeta {
127 FileMeta {
128 name: self.name,
129 size: self.size,
130 timestamp_creation: self.timestamp_creation,
131 timestamp_modification: self.timestamp_modification,
132 }
133 }
134}
135
136#[derive(Debug, Clone)]
138pub struct UploadOptions {
139 pub expiration: Option<ObjectExpiration>,
140 pub classification: Option<u8>,
141 pub keep_share_links: Option<bool>,
142 pub resolution_strategy: Option<ResolutionStrategy>,
143 pub file_meta: FileMeta,
144}
145
146impl UploadOptions {
147 pub fn builder(file_meta: FileMeta) -> UploadOptionsBuilder {
148 UploadOptionsBuilder::new(file_meta)
149 }
150}
151
152pub struct UploadOptionsBuilder {
153 file_meta: FileMeta,
154 expiration: Option<ObjectExpiration>,
155 classification: Option<u8>,
156 keep_share_links: Option<bool>,
157 resolution_strategy: Option<ResolutionStrategy>,
158}
159
160impl UploadOptionsBuilder {
161 pub fn new(file_meta: FileMeta) -> Self {
162 Self {
163 expiration: None,
164 classification: None,
165 keep_share_links: None,
166 resolution_strategy: None,
167 file_meta,
168 }
169 }
170
171 pub fn with_expiration(mut self, expiration: impl Into<ObjectExpiration>) -> Self {
172 self.expiration = Some(expiration.into());
173 self
174 }
175
176 pub fn with_classification(mut self, classification: u8) -> Self {
177 self.classification = Some(classification);
178 self
179 }
180
181 pub fn with_keep_share_links(mut self, keep_share_links: bool) -> Self {
182 self.keep_share_links = Some(keep_share_links);
183 self
184 }
185
186 pub fn with_resolution_strategy(mut self, resolution_strategy: ResolutionStrategy) -> Self {
187 self.resolution_strategy = Some(resolution_strategy);
188 self
189 }
190
191 pub fn build(self) -> UploadOptions {
192 UploadOptions {
193 expiration: self.expiration,
194 classification: self.classification,
195 keep_share_links: self.keep_share_links,
196 resolution_strategy: self.resolution_strategy,
197 file_meta: self.file_meta,
198 }
199 }
200}
201
202pub type NodeList = RangedItems<Node>;
204
205impl NodeList {
206 pub fn get_files(&self) -> Vec<Node> {
207 self.items
208 .iter()
209 .filter(|node| node.node_type == NodeType::File)
210 .cloned()
211 .collect()
212 }
213
214 pub fn get_folders(&self) -> Vec<Node> {
215 self.items
216 .iter()
217 .filter(|node| node.node_type == NodeType::Folder)
218 .cloned()
219 .collect()
220 }
221
222 pub fn get_rooms(&self) -> Vec<Node> {
223 self.items
224 .iter()
225 .filter(|node| node.node_type == NodeType::Room)
226 .cloned()
227 .collect()
228 }
229}
230
231#[derive(Debug, Deserialize, Clone, FromResponse)]
233#[serde(rename_all = "camelCase")]
234pub struct Node {
235 pub id: u64,
236 pub reference_id: Option<u64>,
237 #[serde(rename = "type")]
238 pub node_type: NodeType,
239 pub name: String,
240 pub timestamp_creation: Option<DateTime<Utc>>,
241 pub timestamp_modification: Option<DateTime<Utc>>,
242 pub parent_id: Option<u64>,
243 pub parent_path: Option<String>,
244 pub created_at: Option<DateTime<Utc>>,
245 pub created_by: Option<UserInfo>,
246 pub updated_at: Option<DateTime<Utc>>,
247 pub updated_by: Option<UserInfo>,
248 pub expire_at: Option<DateTime<Utc>>,
249 pub hash: Option<String>,
250 pub file_type: Option<String>,
251 pub media_type: Option<String>,
252 pub size: Option<u64>,
253 pub classification: Option<u64>,
254 pub notes: Option<String>,
255 pub permissions: Option<NodePermissions>,
256 pub inherit_permissions: Option<bool>,
257 pub is_encrypted: Option<bool>,
258 pub encryption_info: Option<EncryptionInfo>,
259 pub cnt_deleted_versions: Option<u64>,
260 pub cnt_comments: Option<u64>,
261 pub cnt_upload_shares: Option<u64>,
262 pub cnt_download_shares: Option<u64>,
263 pub recycle_bin_retention_period: Option<u64>,
264 pub has_activities_log: Option<bool>,
265 pub quota: Option<u64>,
266 pub is_favorite: Option<bool>,
267 pub branch_version: Option<u64>,
268 pub media_token: Option<String>,
269 pub is_browsable: Option<bool>,
270 pub cnt_rooms: Option<u64>,
271 pub cnt_folders: Option<u64>,
272 pub cnt_files: Option<u64>,
273 pub auth_parent_id: Option<u64>,
274}
275
276#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
277pub enum NodeType {
278 #[serde(rename = "room")]
279 Room,
280 #[serde(rename = "folder")]
281 Folder,
282 #[serde(rename = "file")]
283 File,
284}
285
286impl From<NodeType> for String {
287 fn from(node_type: NodeType) -> Self {
288 match node_type {
289 NodeType::Room => "room".to_string(),
290 NodeType::Folder => "folder".to_string(),
291 NodeType::File => "file".to_string(),
292 }
293 }
294}
295
296impl From<&NodeType> for String {
297 fn from(node_type: &NodeType) -> Self {
298 match node_type {
299 NodeType::Room => "room".to_string(),
300 NodeType::Folder => "folder".to_string(),
301 NodeType::File => "file".to_string(),
302 }
303 }
304}
305
306#[derive(Debug, Serialize, Deserialize, Clone)]
308#[serde(rename_all = "camelCase")]
309#[allow(clippy::struct_excessive_bools)]
310pub struct NodePermissions {
311 pub manage: bool,
312 pub read: bool,
313 pub create: bool,
314 pub change: bool,
315 pub delete: bool,
316 pub manage_download_share: bool,
317 pub manage_upload_share: bool,
318 pub read_recycle_bin: bool,
319 pub restore_recycle_bin: bool,
320 pub delete_recycle_bin: bool,
321}
322
323impl NodePermissions {
324 pub fn builder() -> NodePermissionsBuilder {
325 NodePermissionsBuilder::new()
326 }
327
328 pub fn new_with_edit_permissions() -> Self {
329 Self {
330 manage: false,
331 read: true,
332 create: true,
333 change: true,
334 delete: true,
335 manage_download_share: true,
336 manage_upload_share: true,
337 read_recycle_bin: true,
338 restore_recycle_bin: true,
339 delete_recycle_bin: false,
340 }
341 }
342
343 pub fn new_with_read_permissions() -> Self {
344 Self {
345 manage: false,
346 read: true,
347 create: false,
348 change: false,
349 delete: false,
350 manage_download_share: false,
351 manage_upload_share: false,
352 read_recycle_bin: false,
353 restore_recycle_bin: false,
354 delete_recycle_bin: false,
355 }
356 }
357
358 pub fn new_with_manage_permissions() -> Self {
359 Self {
360 manage: true,
361 read: true,
362 create: true,
363 change: true,
364 delete: true,
365 manage_download_share: true,
366 manage_upload_share: true,
367 read_recycle_bin: true,
368 restore_recycle_bin: true,
369 delete_recycle_bin: true,
370 }
371 }
372}
373
374impl Display for NodePermissions {
375 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
376 let mapping = [
377 (self.manage, 'm'),
378 (self.read, 'r'),
379 (self.create, 'w'),
380 (self.change, 'c'),
381 (self.delete, 'd'),
382 (self.manage_download_share, 'm'),
383 (self.manage_upload_share, 'm'),
384 (self.read_recycle_bin, 'r'),
385 (self.restore_recycle_bin, 'r'),
386 (self.delete_recycle_bin, 'd'),
387 ];
388
389 let mut perms = String::with_capacity(mapping.len());
390
391 for (i, &(flag, ch)) in mapping.iter().enumerate() {
392 perms.push(if flag { ch } else { '-' });
393
394 if i == 4 {
396 perms.push('-');
397 }
398 }
399
400 formatter.write_str(&perms)?;
401
402 Ok(())
403 }
404}
405
406#[derive(Debug, Serialize, Deserialize, Clone)]
408#[serde(rename_all = "camelCase")]
409pub struct EncryptionInfo {
410 user_key_state: String,
411 room_key_state: String,
412 data_space_key_state: String,
413}
414
415#[derive(Debug, Deserialize, Clone)]
417#[serde(rename_all = "camelCase")]
418pub struct UserInfo {
419 pub id: i64,
420 pub user_type: UserType,
421 pub user_name: Option<String>,
422 pub avatar_uuid: String,
423 pub first_name: Option<String>,
424 pub last_name: Option<String>,
425 pub email: Option<String>,
426}
427
428#[derive(Deserialize, Debug, Clone, PartialEq)]
429pub enum UserType {
430 #[serde(rename = "internal")]
431 Internal,
432 #[serde(rename = "external")]
433 External,
434 #[serde(rename = "system")]
435 System,
436 #[serde(rename = "deleted")]
437 Deleted,
438}
439
440#[async_trait]
441impl FromResponse for NodeList {
442 async fn from_response(res: Response) -> Result<Self, DracoonClientError> {
444 parse_body::<Self, DracoonErrorResponse>(res).await
445 }
446}
447
448#[derive(Serialize, Deserialize, Debug, FromResponse)]
450#[serde(rename_all = "camelCase")]
451pub struct DownloadUrlResponse {
452 pub download_url: String,
453}
454
455#[derive(Debug, Deserialize, PartialEq, Clone)]
457#[serde(rename_all = "PascalCase")]
458pub struct S3XmlError {
459 code: Option<String>,
460 request_id: Option<String>,
461 host_id: Option<String>,
462 message: Option<String>,
463 argument_name: Option<String>,
464}
465
466#[derive(Debug, PartialEq, Clone)]
468pub struct S3ErrorResponse {
469 pub status: StatusCode,
470 pub error: S3XmlError,
471}
472
473impl Display for S3ErrorResponse {
474 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
475 write!(
476 f,
477 "Error: {} ({})",
478 self.error
479 .message
480 .as_ref()
481 .unwrap_or(&String::from("Unknown S3 error")),
482 self.status,
483 )
484 }
485}
486
487impl S3ErrorResponse {
488 pub fn from_xml_error(status: StatusCode, error: S3XmlError) -> Self {
490 Self { status, error }
491 }
492}
493
494#[async_trait]
495impl FromResponse for FileKey {
496 async fn from_response(res: Response) -> Result<Self, DracoonClientError> {
498 parse_body::<Self, DracoonErrorResponse>(res).await
499 }
500}
501
502#[derive(Debug, Deserialize, FromResponse)]
503#[serde(rename_all = "camelCase")]
504pub struct CreateFileUploadResponse {
505 pub upload_url: String,
506 pub upload_id: String,
507 pub token: String,
508}
509
510#[derive(Debug, Deserialize, Clone)]
511#[serde(rename_all = "camelCase")]
512pub struct PresignedUrl {
513 pub url: String,
514 pub part_number: u32,
515}
516
517#[derive(Debug, Deserialize, FromResponse, Clone)]
518#[serde(rename_all = "camelCase")]
519pub struct PresignedUrlList {
520 pub urls: Vec<PresignedUrl>,
521}
522
523#[derive(Debug, Deserialize, FromResponse, Clone)]
524#[serde(rename_all = "camelCase")]
525pub struct S3FileUploadStatus {
526 pub status: S3UploadStatus,
527 pub node: Option<Node>,
528 pub error_details: Option<DracoonErrorResponse>,
529}
530
531#[derive(Debug, Deserialize, PartialEq, Clone)]
532pub enum S3UploadStatus {
533 #[serde(rename = "transfer")]
534 Transfer,
535 #[serde(rename = "finishing")]
536 Finishing,
537 #[serde(rename = "done")]
538 Done,
539 #[serde(rename = "error")]
540 Error,
541}
542
543#[derive(Debug, Serialize, Clone)]
544#[serde(rename_all = "camelCase")]
545#[allow(non_snake_case)]
546pub struct CreateFileUploadRequest {
547 parent_id: u64,
548 name: String,
549 #[serde(skip_serializing_if = "Option::is_none")]
550 size: Option<u64>,
551 #[serde(skip_serializing_if = "Option::is_none")]
552 classification: Option<u8>,
553 #[serde(skip_serializing_if = "Option::is_none")]
554 expiration: Option<ObjectExpiration>,
555 #[serde(skip_serializing_if = "Option::is_none")]
556 direct_S3_upload: Option<bool>,
557 #[serde(skip_serializing_if = "Option::is_none")]
558 timestamp_creation: Option<String>,
559 #[serde(skip_serializing_if = "Option::is_none")]
560 timestamp_modification: Option<String>,
561}
562
563impl CreateFileUploadRequest {
564 pub fn builder(parent_id: u64, name: String) -> CreateFileUploadRequestBuilder {
565 CreateFileUploadRequestBuilder {
566 parent_id,
567 name,
568 size: None,
569 classification: None,
570 expiration: None,
571 direct_s3_upload: Some(true),
572 timestamp_creation: None,
573 timestamp_modification: None,
574 }
575 }
576
577 pub fn from_upload_options(
578 parent_id: u64,
579 upload_options: &UploadOptions,
580 is_s3_upload: Option<bool>,
581 ) -> Self {
582 let req = Self::builder(parent_id, upload_options.file_meta.name.clone())
583 .with_size(upload_options.file_meta.size)
584 .with_classification(upload_options.classification.unwrap_or(1))
585 .with_expiration(
586 upload_options
587 .expiration
588 .clone()
589 .unwrap_or_default()
590 .clone(),
591 );
592 let req = if let Some(timestamp_creation) = upload_options.file_meta.timestamp_creation {
593 req.with_timestamp_creation(timestamp_creation)
594 } else {
595 req
596 };
597
598 let mut req =
599 if let Some(timestamp_modification) = upload_options.file_meta.timestamp_modification {
600 req.with_timestamp_modification(timestamp_modification)
601 } else {
602 req
603 };
604
605 req.direct_s3_upload = is_s3_upload;
606
607 req.build()
608 }
609}
610
611pub struct CreateFileUploadRequestBuilder {
612 parent_id: u64,
613 name: String,
614 size: Option<u64>,
615 classification: Option<u8>,
616 expiration: Option<ObjectExpiration>,
617 direct_s3_upload: Option<bool>,
618 timestamp_creation: Option<String>,
619 timestamp_modification: Option<String>,
620}
621
622impl CreateFileUploadRequestBuilder {
623 pub fn with_size(mut self, size: u64) -> Self {
624 self.size = Some(size);
625 self
626 }
627
628 pub fn with_classification(mut self, classification: u8) -> Self {
629 self.classification = Some(classification);
630 self
631 }
632
633 pub fn with_expiration(mut self, expiration: impl Into<ObjectExpiration>) -> Self {
634 self.expiration = Some(expiration.into());
635 self
636 }
637 pub fn with_timestamp_creation(mut self, timestamp_creation: DateTime<Utc>) -> Self {
638 self.timestamp_creation = Some(timestamp_creation.to_rfc3339());
639 self
640 }
641 pub fn with_timestamp_modification(mut self, timestamp_modification: DateTime<Utc>) -> Self {
642 self.timestamp_modification = Some(timestamp_modification.to_rfc3339());
643 self
644 }
645
646 pub fn with_direct_s3_upload(mut self, direct_s3_upload: bool) -> Self {
647 if !direct_s3_upload {
648 self.direct_s3_upload = None;
649 } else {
650 self.direct_s3_upload = Some(direct_s3_upload);
651 }
652 self
653 }
654
655 pub fn build(self) -> CreateFileUploadRequest {
656 CreateFileUploadRequest {
657 parent_id: self.parent_id,
658 name: self.name,
659 size: self.size,
660 classification: self.classification,
661 expiration: self.expiration,
662 direct_S3_upload: self.direct_s3_upload,
663 timestamp_creation: self.timestamp_creation,
664 timestamp_modification: self.timestamp_modification,
665 }
666 }
667}
668
669#[derive(Debug, Serialize, Clone)]
670#[serde(rename_all = "camelCase")]
671pub struct GeneratePresignedUrlsRequest {
672 size: u64,
673 first_part_number: u32,
674 last_part_number: u32,
675}
676
677impl GeneratePresignedUrlsRequest {
678 pub fn new(size: u64, first_part_number: u32, last_part_number: u32) -> Self {
679 Self {
680 size,
681 first_part_number,
682 last_part_number,
683 }
684 }
685}
686
687#[derive(Debug, Serialize, Clone)]
688#[serde(rename_all = "camelCase")]
689pub struct CompleteS3FileUploadRequest {
690 parts: Vec<S3FileUploadPart>,
691 #[serde(skip_serializing_if = "Option::is_none")]
692 resolution_strategy: Option<ResolutionStrategy>,
693 #[serde(skip_serializing_if = "Option::is_none")]
694 file_name: Option<String>,
695 #[serde(skip_serializing_if = "Option::is_none")]
696 keep_share_links: Option<bool>,
697 #[serde(skip_serializing_if = "Option::is_none")]
698 file_key: Option<FileKey>,
699}
700
701pub struct CompleteS3FileUploadRequestBuilder {
702 parts: Vec<S3FileUploadPart>,
703 resolution_strategy: Option<ResolutionStrategy>,
704 file_name: Option<String>,
705 keep_share_links: Option<bool>,
706 file_key: Option<FileKey>,
707}
708
709impl CompleteS3FileUploadRequest {
710 pub fn builder(parts: Vec<S3FileUploadPart>) -> CompleteS3FileUploadRequestBuilder {
711 CompleteS3FileUploadRequestBuilder {
712 parts,
713 resolution_strategy: None,
714 file_name: None,
715 keep_share_links: None,
716 file_key: None,
717 }
718 }
719}
720
721impl CompleteS3FileUploadRequestBuilder {
722 pub fn with_resolution_strategy(mut self, resolution_strategy: ResolutionStrategy) -> Self {
723 self.resolution_strategy = Some(resolution_strategy);
724 self
725 }
726
727 pub fn with_file_name(mut self, file_name: String) -> Self {
728 self.file_name = Some(file_name);
729 self
730 }
731
732 pub fn with_keep_share_links(mut self, keep_share_links: bool) -> Self {
733 self.keep_share_links = Some(keep_share_links);
734 self
735 }
736
737 pub fn with_file_key(mut self, file_key: FileKey) -> Self {
738 self.file_key = Some(file_key);
739 self
740 }
741
742 pub fn build(self) -> CompleteS3FileUploadRequest {
743 CompleteS3FileUploadRequest {
744 parts: self.parts,
745 resolution_strategy: self.resolution_strategy,
746 file_name: self.file_name,
747 keep_share_links: self.keep_share_links,
748 file_key: self.file_key,
749 }
750 }
751}
752
753#[derive(Debug, Serialize, Clone)]
754#[serde(rename_all = "camelCase")]
755pub struct CompleteUploadRequest {
756 #[serde(skip_serializing_if = "Option::is_none")]
757 resolution_strategy: Option<ResolutionStrategy>,
758 #[serde(skip_serializing_if = "Option::is_none")]
759 file_name: Option<String>,
760 #[serde(skip_serializing_if = "Option::is_none")]
761 keep_share_links: Option<bool>,
762 #[serde(skip_serializing_if = "Option::is_none")]
763 file_key: Option<FileKey>,
764}
765
766pub struct CompleteUploadRequestBuilder {
767 resolution_strategy: Option<ResolutionStrategy>,
768 file_name: Option<String>,
769 keep_share_links: Option<bool>,
770 file_key: Option<FileKey>,
771}
772
773impl CompleteUploadRequest {
774 pub fn builder() -> CompleteUploadRequestBuilder {
775 CompleteUploadRequestBuilder {
776 resolution_strategy: None,
777 file_name: None,
778 keep_share_links: None,
779 file_key: None,
780 }
781 }
782}
783
784impl CompleteUploadRequestBuilder {
785 pub fn with_resolution_strategy(mut self, resolution_strategy: ResolutionStrategy) -> Self {
786 self.resolution_strategy = Some(resolution_strategy);
787 self
788 }
789
790 pub fn with_file_name(mut self, file_name: String) -> Self {
791 self.file_name = Some(file_name);
792 self
793 }
794
795 pub fn with_keep_share_links(mut self, keep_share_links: bool) -> Self {
796 self.keep_share_links = Some(keep_share_links);
797 self
798 }
799
800 pub fn with_file_key(mut self, file_key: FileKey) -> Self {
801 self.file_key = Some(file_key);
802 self
803 }
804
805 pub fn build(self) -> CompleteUploadRequest {
806 CompleteUploadRequest {
807 resolution_strategy: self.resolution_strategy,
808 file_name: self.file_name,
809 keep_share_links: self.keep_share_links,
810 file_key: self.file_key,
811 }
812 }
813}
814
815#[derive(Debug, Serialize, Clone, Default)]
816pub enum ResolutionStrategy {
817 #[default]
818 #[serde(rename = "autorename")]
819 AutoRename,
820 #[serde(rename = "overwrite")]
821 Overwrite,
822 #[serde(rename = "fail")]
823 Fail,
824}
825
826#[derive(Debug, Serialize, Clone)]
827#[serde(rename_all = "camelCase")]
828pub struct S3FileUploadPart {
829 part_number: u32,
830 part_etag: String,
831}
832
833impl S3FileUploadPart {
834 pub fn new(part_number: u32, part_etag: String) -> Self {
835 Self {
836 part_number,
837 part_etag,
838 }
839 }
840}
841
842#[derive(Debug, Serialize, Clone)]
843#[serde(rename_all = "camelCase")]
844pub struct DeleteNodesRequest {
845 node_ids: Vec<u64>,
846}
847
848impl From<Vec<u64>> for DeleteNodesRequest {
849 fn from(node_ids: Vec<u64>) -> Self {
850 Self { node_ids }
851 }
852}
853
854#[derive(Debug, Serialize, Clone)]
855#[serde(rename_all = "camelCase")]
856pub struct TransferNodesRequest {
857 items: Vec<TransferNode>,
858 #[serde(skip_serializing_if = "Option::is_none")]
859 resolution_strategy: Option<ResolutionStrategy>,
860 #[serde(skip_serializing_if = "Option::is_none")]
861 keep_share_links: Option<bool>,
862}
863
864#[derive(Debug, Serialize, Clone)]
865#[serde(rename_all = "camelCase")]
866pub struct TransferNode {
867 id: u64,
868 name: Option<String>,
869 timestamp_creation: Option<String>,
870 timestamp_modification: Option<String>,
871}
872
873impl From<u64> for TransferNode {
874 fn from(node_id: u64) -> Self {
875 Self {
876 id: node_id,
877 name: None,
878 timestamp_creation: None,
879 timestamp_modification: None,
880 }
881 }
882}
883
884impl From<Vec<u64>> for TransferNodesRequest {
885 fn from(node_ids: Vec<u64>) -> Self {
886 Self {
887 items: node_ids.into_iter().map(std::convert::Into::into).collect(),
888 resolution_strategy: None,
889 keep_share_links: None,
890 }
891 }
892}
893
894pub struct TransferNodesRequestBuilder {
895 items: Vec<TransferNode>,
896 resolution_strategy: Option<ResolutionStrategy>,
897 keep_share_links: Option<bool>,
898}
899
900impl TransferNodesRequest {
901 pub fn builder(items: Vec<TransferNode>) -> TransferNodesRequestBuilder {
902 TransferNodesRequestBuilder {
903 items,
904 resolution_strategy: None,
905 keep_share_links: None,
906 }
907 }
908
909 pub fn new_from_ids(node_ids: Vec<u64>) -> TransferNodesRequestBuilder {
910 TransferNodesRequestBuilder {
911 items: node_ids.into_iter().map(std::convert::Into::into).collect(),
912 resolution_strategy: None,
913 keep_share_links: None,
914 }
915 }
916
917 pub fn with_resolution_strategy(mut self, resolution_strategy: ResolutionStrategy) -> Self {
918 self.resolution_strategy = Some(resolution_strategy);
919 self
920 }
921
922 pub fn with_keep_share_links(mut self, keep_share_links: bool) -> Self {
923 self.keep_share_links = Some(keep_share_links);
924 self
925 }
926
927 pub fn build(self) -> TransferNodesRequest {
928 TransferNodesRequest {
929 items: self.items,
930 resolution_strategy: self.resolution_strategy,
931 keep_share_links: self.keep_share_links,
932 }
933 }
934}
935
936pub struct TransferNodeBuilder {
937 id: u64,
938 name: Option<String>,
939 timestamp_creation: Option<String>,
940 timestamp_modification: Option<String>,
941}
942
943impl TransferNode {
944 pub fn builder(id: u64) -> TransferNodeBuilder {
945 TransferNodeBuilder {
946 id,
947 name: None,
948 timestamp_creation: None,
949 timestamp_modification: None,
950 }
951 }
952
953 pub fn with_name(mut self, name: String) -> Self {
954 self.name = Some(name);
955 self
956 }
957
958 pub fn with_timestamp_creation(mut self, timestamp_creation: String) -> Self {
959 self.timestamp_creation = Some(timestamp_creation);
960 self
961 }
962
963 pub fn with_timestamp_modification(mut self, timestamp_modification: String) -> Self {
964 self.timestamp_modification = Some(timestamp_modification);
965 self
966 }
967
968 pub fn build(self) -> TransferNode {
969 TransferNode {
970 id: self.id,
971 name: self.name,
972 timestamp_creation: self.timestamp_creation,
973 timestamp_modification: self.timestamp_modification,
974 }
975 }
976}
977
978#[derive(Debug, Serialize, Clone)]
979#[serde(rename_all = "camelCase")]
980pub struct CreateFolderRequest {
981 name: String,
982 parent_id: u64,
983 #[serde(skip_serializing_if = "Option::is_none")]
984 notes: Option<String>,
985 #[serde(skip_serializing_if = "Option::is_none")]
986 timestamp_creation: Option<String>,
987 #[serde(skip_serializing_if = "Option::is_none")]
988 timestamp_modification: Option<String>,
989 #[serde(skip_serializing_if = "Option::is_none")]
990 classification: Option<u8>,
991}
992
993pub struct CreateFolderRequestBuilder {
994 name: String,
995 parent_id: u64,
996 notes: Option<String>,
997 timestamp_creation: Option<String>,
998 timestamp_modification: Option<String>,
999 classification: Option<u8>,
1000}
1001
1002impl CreateFolderRequest {
1003 pub fn builder(name: impl Into<String>, parent_id: u64) -> CreateFolderRequestBuilder {
1004 CreateFolderRequestBuilder {
1005 name: name.into(),
1006 parent_id,
1007 notes: None,
1008 timestamp_creation: None,
1009 timestamp_modification: None,
1010 classification: None,
1011 }
1012 }
1013}
1014
1015impl CreateFolderRequestBuilder {
1016 pub fn with_notes(mut self, notes: impl Into<String>) -> Self {
1017 self.notes = Some(notes.into());
1018 self
1019 }
1020
1021 pub fn with_timestamp_creation(mut self, timestamp_creation: impl Into<String>) -> Self {
1022 self.timestamp_creation = Some(timestamp_creation.into());
1023 self
1024 }
1025
1026 pub fn with_timestamp_modification(
1027 mut self,
1028 timestamp_modification: impl Into<String>,
1029 ) -> Self {
1030 self.timestamp_modification = Some(timestamp_modification.into());
1031 self
1032 }
1033
1034 pub fn with_classification(mut self, classification: u8) -> Self {
1035 self.classification = Some(classification);
1036 self
1037 }
1038
1039 pub fn build(self) -> CreateFolderRequest {
1040 CreateFolderRequest {
1041 name: self.name,
1042 parent_id: self.parent_id,
1043 notes: self.notes,
1044 timestamp_creation: self.timestamp_creation,
1045 timestamp_modification: self.timestamp_modification,
1046 classification: self.classification,
1047 }
1048 }
1049}
1050
1051#[derive(Debug, Serialize, Clone)]
1052#[serde(rename_all = "camelCase")]
1053pub struct UpdateFolderRequest {
1054 #[serde(skip_serializing_if = "Option::is_none")]
1055 name: Option<String>,
1056 #[serde(skip_serializing_if = "Option::is_none")]
1057 notes: Option<String>,
1058 #[serde(skip_serializing_if = "Option::is_none")]
1059 timestamp_creation: Option<String>,
1060 #[serde(skip_serializing_if = "Option::is_none")]
1061 timestamp_modification: Option<String>,
1062 #[serde(skip_serializing_if = "Option::is_none")]
1063 classification: Option<u8>,
1064}
1065
1066pub struct UpdateFolderRequestBuilder {
1067 name: Option<String>,
1068 notes: Option<String>,
1069 timestamp_creation: Option<String>,
1070 timestamp_modification: Option<String>,
1071 classification: Option<u8>,
1072}
1073
1074impl UpdateFolderRequest {
1075 pub fn builder() -> UpdateFolderRequestBuilder {
1076 UpdateFolderRequestBuilder {
1077 name: None,
1078 notes: None,
1079 timestamp_creation: None,
1080 timestamp_modification: None,
1081 classification: None,
1082 }
1083 }
1084}
1085
1086impl UpdateFolderRequestBuilder {
1087 pub fn with_name(mut self, name: impl Into<String>) -> Self {
1088 self.name = Some(name.into());
1089 self
1090 }
1091
1092 pub fn with_notes(mut self, notes: impl Into<String>) -> Self {
1093 self.notes = Some(notes.into());
1094 self
1095 }
1096
1097 pub fn with_timestamp_creation(mut self, timestamp_creation: impl Into<String>) -> Self {
1098 self.timestamp_creation = Some(timestamp_creation.into());
1099 self
1100 }
1101
1102 pub fn with_timestamp_modification(
1103 mut self,
1104 timestamp_modification: impl Into<String>,
1105 ) -> Self {
1106 self.timestamp_modification = Some(timestamp_modification.into());
1107 self
1108 }
1109
1110 pub fn with_classification(mut self, classification: u8) -> Self {
1111 self.classification = Some(classification);
1112 self
1113 }
1114
1115 pub fn build(self) -> UpdateFolderRequest {
1116 UpdateFolderRequest {
1117 name: self.name,
1118 notes: self.notes,
1119 timestamp_creation: self.timestamp_creation,
1120 timestamp_modification: self.timestamp_modification,
1121 classification: self.classification,
1122 }
1123 }
1124}
1125
1126#[derive(Debug, Deserialize, Clone)]
1127#[serde(rename_all = "camelCase")]
1128pub struct UserIdFileItem {
1129 pub user_id: u64,
1130 pub file_id: u64,
1131}
1132
1133#[derive(Debug, Deserialize, Clone)]
1134#[serde(rename_all = "camelCase")]
1135pub struct UserUserPublicKey {
1136 pub id: u64,
1137 pub public_key_container: PublicKeyContainer,
1138}
1139
1140#[derive(Debug, Deserialize, Clone)]
1141#[serde(rename_all = "camelCase")]
1142pub struct FileFileKeys {
1143 pub id: u64,
1144 pub file_key_container: FileKey,
1145}
1146
1147#[derive(Debug, Deserialize, FromResponse, Clone)]
1148#[serde(rename_all = "camelCase")]
1149pub struct MissingKeysResponse {
1150 pub range: Option<Range>,
1151 pub items: Vec<UserIdFileItem>,
1152 pub users: Vec<UserUserPublicKey>,
1153 pub files: Vec<FileFileKeys>,
1154}
1155
1156#[derive(Debug, Serialize, Default, Clone)]
1157#[serde(rename_all = "camelCase")]
1158pub struct UserFileKeySetBatchRequest {
1159 items: Vec<UserFileKeySetRequest>,
1160}
1161
1162impl UserFileKeySetBatchRequest {
1163 pub fn new() -> Self {
1164 UserFileKeySetBatchRequest { items: Vec::new() }
1165 }
1166
1167 pub fn add(&mut self, user_id: u64, file_id: u64, file_key: FileKey) {
1168 self.items
1169 .push(UserFileKeySetRequest::new(user_id, file_id, file_key));
1170 }
1171
1172 pub fn is_empty(&self) -> bool {
1173 self.items.is_empty()
1174 }
1175
1176 pub fn try_new_from_missing_keys(
1177 missing_keys: MissingKeysResponse,
1178 keypair: &PlainUserKeyPairContainer,
1179 ) -> Result<Self, DracoonClientError> {
1180 let reqs = missing_keys
1181 .items
1182 .into_iter()
1183 .flat_map::<Result<UserFileKeySetRequest, DracoonClientError>, _>(|item| {
1184 let file_id = item.file_id;
1185 let user_id = item.user_id;
1186 let public_key = missing_keys
1187 .users
1188 .iter()
1189 .find(|u| u.id == user_id)
1190 .ok_or_else(|| {
1191 error!("User not found in response: {}", user_id);
1192 DracoonClientError::Unknown
1193 })? .public_key_container
1195 .clone();
1196 let file_key = missing_keys
1197 .files
1198 .iter()
1199 .find(|f| f.id == file_id)
1200 .ok_or_else(|| {
1201 error!("File not found in response: {}", file_id);
1202 DracoonClientError::Unknown
1203 })? .file_key_container
1205 .clone();
1206
1207 let plain_file_key = DracoonCrypto::decrypt_file_key(file_key, keypair.clone())
1208 .map_err(|err| {
1209 error!("Could not decrypt file key: {:?}", err);
1210 DracoonClientError::CryptoError(err)
1211 })?;
1212 let file_key = DracoonCrypto::encrypt_file_key(plain_file_key, public_key)
1213 .map_err(|err| {
1214 error!("Could not encrypt file key: {:?}", err);
1215 DracoonClientError::CryptoError(err)
1216 })?;
1217 let set_key_req = UserFileKeySetRequest::new(user_id, file_id, file_key);
1218 Ok(set_key_req)
1219 })
1220 .collect::<Vec<_>>();
1221
1222 debug!("Built {} key requests", reqs.len());
1223
1224 Ok(reqs.into())
1225 }
1226}
1227
1228impl From<Vec<UserFileKeySetRequest>> for UserFileKeySetBatchRequest {
1229 fn from(items: Vec<UserFileKeySetRequest>) -> Self {
1230 UserFileKeySetBatchRequest { items }
1231 }
1232}
1233
1234#[derive(Debug, Serialize, Clone)]
1235#[serde(rename_all = "camelCase")]
1236pub struct UserFileKeySetRequest {
1237 user_id: u64,
1238 file_id: u64,
1239 file_key: FileKey,
1240}
1241
1242impl UserFileKeySetRequest {
1243 pub fn new(user_id: u64, file_id: u64, file_key: FileKey) -> Self {
1244 UserFileKeySetRequest {
1245 user_id,
1246 file_id,
1247 file_key,
1248 }
1249 }
1250}
1251
1252#[derive(Debug, Clone)]
1253pub enum UseKey {
1254 RoomRescueKey,
1255 SystemRescueKey,
1256 PreviousUserKey,
1257 PreviousRoomRescueKey,
1258 PreviousSystemRescueKey,
1259}
1260
1261impl From<UseKey> for String {
1262 fn from(use_key: UseKey) -> Self {
1263 match use_key {
1264 UseKey::RoomRescueKey => "room_rescue_key".to_string(),
1265 UseKey::SystemRescueKey => "system_rescue_key".to_string(),
1266 UseKey::PreviousUserKey => "previous_user_key".to_string(),
1267 UseKey::PreviousRoomRescueKey => "previous_room_rescue_key".to_string(),
1268 UseKey::PreviousSystemRescueKey => "previous_system_rescue_key".to_string(),
1269 }
1270 }
1271}