1use std::ffi::OsString;
6pub type Result<T> = std::result::Result<T, Error>;
7pub type ApiResult<T> = std::result::Result<T, LoginsApiError>;
9
10pub use error_support::{breadcrumb, handle_error, report_error};
11pub use error_support::{debug, error, info, trace, warn};
12
13use error_support::{ErrorHandling, GetErrorHandling};
14use jwcrypto::JwCryptoError;
15use sync15::Error as Sync15Error;
16
17#[derive(Debug, thiserror::Error)]
19pub enum LoginsApiError {
20 #[error("NSS not initialized")]
21 NSSUninitialized,
22
23 #[error("NSS error during authentication: {reason}")]
24 NSSAuthenticationError { reason: String },
25
26 #[error("error during authentication: {reason}")]
27 AuthenticationError { reason: String },
28
29 #[error("authentication cancelled")]
30 AuthenticationCanceled,
31
32 #[error("Invalid login: {reason}")]
33 InvalidRecord { reason: String },
34
35 #[error("No record with guid exists (when one was required): {reason:?}")]
36 NoSuchRecord { reason: String },
37
38 #[error("Encryption key is missing.")]
39 MissingKey,
40
41 #[error("Encryption key is not valid.")]
42 InvalidKey,
43
44 #[error("encryption failed: {reason}")]
45 EncryptionFailed { reason: String },
46
47 #[error("decryption failed: {reason}")]
48 DecryptionFailed { reason: String },
49
50 #[error("{reason}")]
51 Interrupted { reason: String },
52
53 #[error("SyncAuthInvalid error {reason}")]
54 SyncAuthInvalid { reason: String },
55
56 #[error("Unexpected Error: {reason}")]
57 UnexpectedLoginsApiError { reason: String },
58}
59
60#[derive(Debug, thiserror::Error)]
64pub enum Error {
65 #[error("Database is closed")]
66 DatabaseClosed,
67
68 #[error("Malformed incoming record")]
69 MalformedIncomingRecord,
70
71 #[error("Invalid login: {0}")]
72 InvalidLogin(#[from] InvalidLogin),
73
74 #[error("The `sync_status` column in DB has an illegal value: {0}")]
75 BadSyncStatus(u8),
76
77 #[error("No record with guid exists (when one was required): {0:?}")]
78 NoSuchRecord(String),
79
80 #[error("The logins tables are not empty")]
82 NonEmptyTable,
83
84 #[error("encryption failed: {0:?}")]
85 EncryptionFailed(String),
86
87 #[error("decryption failed: {0:?}")]
88 DecryptionFailed(String),
89
90 #[error("Error synchronizing: {0}")]
91 SyncAdapterError(#[from] sync15::Error),
92
93 #[error("Error parsing JSON data: {0}")]
94 JsonError(#[from] serde_json::Error),
95
96 #[error("Error executing SQL: {0}")]
97 SqlError(#[from] rusqlite::Error),
98
99 #[error("Error parsing URL: {0}")]
100 UrlParseError(#[from] url::ParseError),
101
102 #[error("Invalid path: {0:?}")]
103 InvalidPath(OsString),
104
105 #[error("CryptoError({0})")]
106 CryptoError(#[from] JwCryptoError),
107
108 #[error("{0}")]
109 Interrupted(#[from] interrupt_support::Interrupted),
110
111 #[error("IOError: {0}")]
112 IOError(#[from] std::io::Error),
113
114 #[error("Migration Error: {0}")]
115 MigrationError(String),
116
117 #[error("IncompatibleVersion: {0}")]
118 IncompatibleVersion(i64),
119}
120
121#[derive(Debug, thiserror::Error)]
123pub enum InvalidLogin {
124 #[error("Origin is empty")]
126 EmptyOrigin,
127 #[error("Password is empty")]
128 EmptyPassword,
129 #[error("Login already exists")]
130 DuplicateLogin,
131 #[error("Both `formActionOrigin` and `httpRealm` are present")]
132 BothTargets,
133 #[error("Neither `formActionOrigin` or `httpRealm` are present")]
134 NoTarget,
135 #[error("Login has illegal origin: {reason}")]
138 IllegalOrigin { reason: String },
139 #[error("Login has illegal field: {field_info}")]
140 IllegalFieldValue { field_info: String },
141}
142
143impl GetErrorHandling for Error {
146 type ExternalError = LoginsApiError;
147
148 fn get_error_handling(&self) -> ErrorHandling<Self::ExternalError> {
149 match self {
150 Self::InvalidLogin(why) => ErrorHandling::convert(LoginsApiError::InvalidRecord {
151 reason: why.to_string(),
152 }),
153 Self::MalformedIncomingRecord => {
154 ErrorHandling::convert(LoginsApiError::InvalidRecord {
155 reason: "invalid incoming record".to_string(),
156 })
157 }
158 Self::NoSuchRecord(guid) => ErrorHandling::convert(LoginsApiError::NoSuchRecord {
160 reason: guid.to_string(),
161 }),
162 Self::NonEmptyTable => {
166 ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
167 reason: "must be an empty DB to migrate".to_string(),
168 })
169 .report_error("logins-migration")
170 }
171 Self::Interrupted(_) => ErrorHandling::convert(LoginsApiError::Interrupted {
172 reason: self.to_string(),
173 }),
174 Self::SyncAdapterError(e) => match e {
175 Sync15Error::TokenserverHttpError(401) | Sync15Error::BadKeyLength(..) => {
176 ErrorHandling::convert(LoginsApiError::SyncAuthInvalid {
177 reason: e.to_string(),
178 })
179 .log_warning()
180 }
181 Sync15Error::RequestError(_) => {
182 ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
183 reason: e.to_string(),
184 })
185 .log_warning()
186 }
187 _ => ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
188 reason: self.to_string(),
189 })
190 .report_error("logins-sync"),
191 },
192 Error::SqlError(rusqlite::Error::SqliteFailure(err, _)) => match err.code {
193 rusqlite::ErrorCode::DatabaseCorrupt => {
194 ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
195 reason: self.to_string(),
196 })
197 .report_error("logins-db-corrupt")
198 }
199 rusqlite::ErrorCode::DiskFull => {
200 ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
201 reason: self.to_string(),
202 })
203 .report_error("logins-db-disk-full")
204 }
205 _ => ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
206 reason: self.to_string(),
207 })
208 .report_error("logins-unexpected"),
209 },
210 _ => ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
216 reason: self.to_string(),
217 })
218 .report_error("logins-unexpected"),
219 }
220 }
221}
222
223impl From<uniffi::UnexpectedUniFFICallbackError> for LoginsApiError {
224 fn from(error: uniffi::UnexpectedUniFFICallbackError) -> Self {
225 LoginsApiError::UnexpectedLoginsApiError {
226 reason: error.to_string(),
227 }
228 }
229}