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
118#[derive(Debug, thiserror::Error)]
120pub enum InvalidLogin {
121 #[error("Origin is empty")]
123 EmptyOrigin,
124 #[error("Password is empty")]
125 EmptyPassword,
126 #[error("Login already exists")]
127 DuplicateLogin,
128 #[error("Both `formActionOrigin` and `httpRealm` are present")]
129 BothTargets,
130 #[error("Neither `formActionOrigin` or `httpRealm` are present")]
131 NoTarget,
132 #[error("Login has illegal origin")]
135 IllegalOrigin,
136 #[error("Login has illegal field: {field_info}")]
137 IllegalFieldValue { field_info: String },
138}
139
140impl GetErrorHandling for Error {
143 type ExternalError = LoginsApiError;
144
145 fn get_error_handling(&self) -> ErrorHandling<Self::ExternalError> {
146 match self {
147 Self::InvalidLogin(why) => ErrorHandling::convert(LoginsApiError::InvalidRecord {
148 reason: why.to_string(),
149 }),
150 Self::MalformedIncomingRecord => {
151 ErrorHandling::convert(LoginsApiError::InvalidRecord {
152 reason: "invalid incoming record".to_string(),
153 })
154 }
155 Self::NoSuchRecord(guid) => ErrorHandling::convert(LoginsApiError::NoSuchRecord {
157 reason: guid.to_string(),
158 }),
159 Self::NonEmptyTable => {
163 ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
164 reason: "must be an empty DB to migrate".to_string(),
165 })
166 .report_error("logins-migration")
167 }
168 Self::Interrupted(_) => ErrorHandling::convert(LoginsApiError::Interrupted {
169 reason: self.to_string(),
170 }),
171 Self::SyncAdapterError(e) => match e {
172 Sync15Error::TokenserverHttpError(401) | Sync15Error::BadKeyLength(..) => {
173 ErrorHandling::convert(LoginsApiError::SyncAuthInvalid {
174 reason: e.to_string(),
175 })
176 .log_warning()
177 }
178 Sync15Error::RequestError(_) => {
179 ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
180 reason: e.to_string(),
181 })
182 .log_warning()
183 }
184 _ => ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
185 reason: self.to_string(),
186 })
187 .report_error("logins-sync"),
188 },
189 Error::SqlError(rusqlite::Error::SqliteFailure(err, _)) => match err.code {
190 rusqlite::ErrorCode::DatabaseCorrupt => {
191 ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
192 reason: self.to_string(),
193 })
194 .report_error("logins-db-corrupt")
195 }
196 rusqlite::ErrorCode::DiskFull => {
197 ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
198 reason: self.to_string(),
199 })
200 .report_error("logins-db-disk-full")
201 }
202 _ => ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
203 reason: self.to_string(),
204 })
205 .report_error("logins-unexpected"),
206 },
207 _ => ErrorHandling::convert(LoginsApiError::UnexpectedLoginsApiError {
213 reason: self.to_string(),
214 })
215 .report_error("logins-unexpected"),
216 }
217 }
218}
219
220impl From<uniffi::UnexpectedUniFFICallbackError> for LoginsApiError {
221 fn from(error: uniffi::UnexpectedUniFFICallbackError) -> Self {
222 LoginsApiError::UnexpectedLoginsApiError {
223 reason: error.to_string(),
224 }
225 }
226}