1#![doc = include_str!("../README.md")]
2
3use std::fmt;
4
5use bitwarden_vault::{
6 CipherRepromptType, CipherView, Fido2CredentialFullView, LoginUriView, UriMatchType,
7};
8use chrono::{DateTime, Utc};
9use uuid::Uuid;
10
11#[cfg(feature = "uniffi")]
12uniffi::setup_scaffolding!();
13#[cfg(feature = "uniffi")]
14mod uniffi_support;
15
16mod csv;
17mod cxf;
18pub use cxf::Account;
19mod encrypted_json;
20mod exporter_client;
21mod json;
22mod models;
23pub use exporter_client::{ExporterClient, ExporterClientExt};
24mod error;
25mod export;
26pub use error::ExportError;
27
28#[allow(missing_docs)]
29#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
30#[cfg_attr(
31 feature = "wasm",
32 derive(serde::Serialize, serde::Deserialize, tsify_next::Tsify),
33 tsify(into_wasm_abi, from_wasm_abi)
34)]
35pub enum ExportFormat {
36 Csv,
37 Json,
38 EncryptedJson { password: String },
39}
40
41#[allow(missing_docs)]
46pub struct Folder {
47 pub id: Uuid,
48 pub name: String,
49}
50
51#[allow(missing_docs)]
56#[derive(Clone)]
57pub struct Cipher {
58 pub id: Uuid,
59 pub folder_id: Option<Uuid>,
60
61 pub name: String,
62 pub notes: Option<String>,
63
64 pub r#type: CipherType,
65
66 pub favorite: bool,
67 pub reprompt: u8,
68
69 pub fields: Vec<Field>,
70
71 pub revision_date: DateTime<Utc>,
72 pub creation_date: DateTime<Utc>,
73 pub deleted_date: Option<DateTime<Utc>>,
74}
75
76#[allow(missing_docs)]
81#[derive(Clone)]
82pub struct ImportingCipher {
83 pub folder_id: Option<Uuid>,
84
85 pub name: String,
86 pub notes: Option<String>,
87
88 pub r#type: CipherType,
89
90 pub favorite: bool,
91 pub reprompt: u8,
92
93 pub fields: Vec<Field>,
94
95 pub revision_date: DateTime<Utc>,
96 pub creation_date: DateTime<Utc>,
97 pub deleted_date: Option<DateTime<Utc>>,
98}
99
100impl From<ImportingCipher> for CipherView {
101 fn from(value: ImportingCipher) -> Self {
102 let login = match value.r#type {
103 CipherType::Login(login) => {
104 let l: Vec<LoginUriView> = login
105 .login_uris
106 .into_iter()
107 .map(LoginUriView::from)
108 .collect();
109
110 Some(bitwarden_vault::LoginView {
111 username: login.username,
112 password: login.password,
113 password_revision_date: None,
114 uris: if l.is_empty() { None } else { Some(l) },
115 totp: login.totp,
116 autofill_on_page_load: None,
117 fido2_credentials: None,
118 })
119 }
120 _ => None,
121 };
122
123 Self {
124 id: None,
125 organization_id: None,
126 folder_id: value.folder_id,
127 collection_ids: vec![],
128 key: None,
129 name: value.name,
130 notes: None,
131 r#type: bitwarden_vault::CipherType::Login,
132 login,
133 identity: None,
134 card: None,
135 secure_note: None,
136 ssh_key: None,
137 favorite: value.favorite,
138 reprompt: CipherRepromptType::None,
139 organization_use_totp: true,
140 edit: true,
141 permissions: None,
142 view_password: true,
143 local_data: None,
144 attachments: None,
145 fields: None,
146 password_history: None,
147 creation_date: value.creation_date,
148 deleted_date: None,
149 revision_date: value.revision_date,
150 }
151 }
152}
153
154impl From<LoginUri> for bitwarden_vault::LoginUriView {
155 fn from(value: LoginUri) -> Self {
156 Self {
157 uri: value.uri,
158 r#match: value.r#match.and_then(|m| match m {
159 0 => Some(UriMatchType::Domain),
160 1 => Some(UriMatchType::Host),
161 2 => Some(UriMatchType::StartsWith),
162 3 => Some(UriMatchType::Exact),
163 4 => Some(UriMatchType::RegularExpression),
164 5 => Some(UriMatchType::Never),
165 _ => None,
166 }),
167 uri_checksum: None,
168 }
169 }
170}
171
172#[allow(missing_docs)]
173#[derive(Clone)]
174pub struct Field {
175 pub name: Option<String>,
176 pub value: Option<String>,
177 pub r#type: u8,
178 pub linked_id: Option<u32>,
179}
180
181#[allow(missing_docs)]
182#[derive(Clone)]
183pub enum CipherType {
184 Login(Box<Login>),
185 SecureNote(Box<SecureNote>),
186 Card(Box<Card>),
187 Identity(Box<Identity>),
188 SshKey(Box<SshKey>),
189}
190
191impl fmt::Display for CipherType {
192 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
193 match self {
194 CipherType::Login(_) => write!(f, "login"),
195 CipherType::SecureNote(_) => write!(f, "note"),
196 CipherType::Card(_) => write!(f, "card"),
197 CipherType::Identity(_) => write!(f, "identity"),
198 CipherType::SshKey(_) => write!(f, "ssh_key"),
199 }
200 }
201}
202
203#[allow(missing_docs)]
204#[derive(Clone)]
205pub struct Login {
206 pub username: Option<String>,
207 pub password: Option<String>,
208 pub login_uris: Vec<LoginUri>,
209 pub totp: Option<String>,
210
211 pub fido2_credentials: Option<Vec<Fido2Credential>>,
212}
213
214#[allow(missing_docs)]
215#[derive(Clone)]
216pub struct LoginUri {
217 pub uri: Option<String>,
218 pub r#match: Option<u8>,
219}
220
221#[allow(missing_docs)]
222#[derive(Clone)]
223pub struct Fido2Credential {
224 pub credential_id: String,
225 pub key_type: String,
226 pub key_algorithm: String,
227 pub key_curve: String,
228 pub key_value: String,
229 pub rp_id: String,
230 pub user_handle: Option<String>,
231 pub user_name: Option<String>,
232 pub counter: u32,
233 pub rp_name: Option<String>,
234 pub user_display_name: Option<String>,
235 pub discoverable: String,
236 pub creation_date: DateTime<Utc>,
237}
238
239impl From<Fido2Credential> for Fido2CredentialFullView {
240 fn from(value: Fido2Credential) -> Self {
241 Fido2CredentialFullView {
242 credential_id: value.credential_id,
243 key_type: value.key_type,
244 key_algorithm: value.key_algorithm,
245 key_curve: value.key_curve,
246 key_value: value.key_value,
247 rp_id: value.rp_id,
248 user_handle: value.user_handle,
249 user_name: value.user_name,
250 counter: value.counter.to_string(),
251 rp_name: value.rp_name,
252 user_display_name: value.user_display_name,
253 discoverable: value.discoverable,
254 creation_date: value.creation_date,
255 }
256 }
257}
258
259#[allow(missing_docs)]
260#[derive(Clone)]
261pub struct Card {
262 pub cardholder_name: Option<String>,
263 pub exp_month: Option<String>,
264 pub exp_year: Option<String>,
265 pub code: Option<String>,
266 pub brand: Option<String>,
267 pub number: Option<String>,
268}
269
270#[allow(missing_docs)]
271#[derive(Clone)]
272pub struct SecureNote {
273 pub r#type: SecureNoteType,
274}
275
276#[allow(missing_docs)]
277#[derive(Clone)]
278pub enum SecureNoteType {
279 Generic = 0,
280}
281
282#[allow(missing_docs)]
283#[derive(Clone)]
284pub struct Identity {
285 pub title: Option<String>,
286 pub first_name: Option<String>,
287 pub middle_name: Option<String>,
288 pub last_name: Option<String>,
289 pub address1: Option<String>,
290 pub address2: Option<String>,
291 pub address3: Option<String>,
292 pub city: Option<String>,
293 pub state: Option<String>,
294 pub postal_code: Option<String>,
295 pub country: Option<String>,
296 pub company: Option<String>,
297 pub email: Option<String>,
298 pub phone: Option<String>,
299 pub ssn: Option<String>,
300 pub username: Option<String>,
301 pub passport_number: Option<String>,
302 pub license_number: Option<String>,
303}
304
305#[allow(missing_docs)]
306#[derive(Clone)]
307pub struct SshKey {
308 pub private_key: String,
310 pub public_key: String,
312 pub fingerprint: String,
314}