1use std::backtrace::Backtrace;
2use std::fmt::{self, Display, Formatter};
3use std::io;
4use std::panic::Location;
5use std::sync::PoisonError;
6
7use hmac::crypto_mac::MacError;
8use serde::ser::SerializeStruct;
9use serde::{Serialize, Serializer};
10use tracing::error;
11use uuid::Uuid;
12
13use crate::io::network::ApiError;
14use crate::model::ValidationFailure;
15
16use super::api;
17
18pub type LbResult<T> = Result<T, LbErr>;
19
20#[derive(Debug)]
21pub struct LbErr {
22 pub kind: LbErrKind,
23 pub backtrace: Option<Backtrace>,
24}
25
26impl Display for LbErr {
30 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
31 write!(f, "{}", self.kind)
32 }
33}
34
35impl Display for LbErrKind {
42 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
43 match self {
44 LbErrKind::AccountExists => write!(f, "An account already exists"),
45 LbErrKind::AccountNonexistent => write!(f, "You need an account to do that"),
46 LbErrKind::AccountStringCorrupted => write!(f, "That account key is invalid"),
47 LbErrKind::AlreadyCanceled => write!(f, "Your subscription has already been cancelled"),
48 LbErrKind::AlreadyPremium => write!(f, "Your account is already premium"),
49 LbErrKind::AppStoreAccountAlreadyLinked => {
50 write!(f, "Your account is already linked to the App Store")
51 }
52 LbErrKind::CannotCancelSubscriptionForAppStore => {
53 write!(f, "You cannot cancel an app store subscription from here")
54 }
55 LbErrKind::CardDecline => write!(f, "Your card was declined"),
56 LbErrKind::CardExpired => write!(f, "Your card is expired"),
57 LbErrKind::CardInsufficientFunds => write!(f, "This card has insufficient funds"),
58 LbErrKind::CardInvalidCvc => write!(f, "Your CVC is invalid"),
59 LbErrKind::CardInvalidExpMonth => write!(f, "Your expiration month is invalid"),
60 LbErrKind::CardInvalidExpYear => write!(f, "Your expiration year is invalid"),
61 LbErrKind::CardInvalidNumber => write!(f, "Your card number is invalid"),
62 LbErrKind::CardNotSupported => write!(f, "That card is not supported by stripe"),
63 LbErrKind::ClientUpdateRequired => {
64 write!(f, "You must update your Lockbook to do that")
65 }
66 LbErrKind::CurrentUsageIsMoreThanNewTier => {
67 write!(f, "You need to delete some files before downgrading your usage")
68 }
69 LbErrKind::DiskPathInvalid => write!(f, "That disk path is invalid"),
70 LbErrKind::DiskPathTaken => write!(f, "That disk path is not available"),
71 LbErrKind::DrawingInvalid => write!(f, "That drawing is invalid"),
72 LbErrKind::ExistingRequestPending => {
73 write!(f, "Existing billing request in progress, please wait and try again")
74 }
75 LbErrKind::FileNameContainsSlash => write!(f, "A file name cannot contain slashes"),
76 LbErrKind::FileNameTooLong => write!(f, "That file name is too long"),
77 LbErrKind::FileNameEmpty => write!(f, "A file name cannot be empty"),
78 LbErrKind::FileNonexistent => write!(f, "That file does not exist"),
79 LbErrKind::FileNotDocument => write!(f, "That file is not a document"),
80 LbErrKind::FileParentNonexistent => write!(f, "Could not find that file parent"),
81 LbErrKind::InsufficientPermission => {
82 write!(f, "You don't have the permission to do that")
83 }
84 LbErrKind::InvalidPurchaseToken => write!(f, "That purchase token is invalid"),
85 LbErrKind::InvalidAuthDetails => {
86 write!(f, "Our server failed to authenticate your request, please try again")
87 }
88 LbErrKind::KeyPhraseInvalid => {
89 write!(f, "Your private key phrase is wrong")
90 }
91 LbErrKind::NotPremium => write!(f, "You do not have a premium subscription"),
92 LbErrKind::UsageIsOverDataCap => {
93 write!(f, "You're out of space")
94 }
95 LbErrKind::UsageIsOverFreeTierDataCap => {
96 write!(f, "You're out of space, you can purchase additional space")
97 }
98 LbErrKind::OldCardDoesNotExist => write!(f, "No existing card found"),
99 LbErrKind::PathContainsEmptyFileName => {
100 write!(f, "That path contains an empty file name")
101 }
102 LbErrKind::RootModificationInvalid => write!(f, "You cannot modify your root"),
103 LbErrKind::RootNonexistent => write!(f, "Could not find your root file"),
104 LbErrKind::ServerDisabled => write!(
105 f,
106 "The server is not accepting this action at the moment, please try again later"
107 ),
108 LbErrKind::ServerUnreachable => write!(f, "Could not reach server"),
109 LbErrKind::ShareAlreadyExists => write!(f, "That share already exists"),
110 LbErrKind::ShareNonexistent => write!(f, "That share does not exist"),
111 LbErrKind::TryAgain => write!(f, "Please try again"),
112 LbErrKind::UsernameInvalid => write!(f, "That username is invalid"),
113 LbErrKind::UsernameNotFound => write!(f, "That username is not found"),
114 LbErrKind::UsernamePublicKeyMismatch => {
115 write!(f, "That username doesn't match that public key")
116 }
117 LbErrKind::UsernameTaken => write!(f, "That username is not available"),
118 LbErrKind::Unexpected(msg) => write!(f, "Unexpected error: {msg}"),
119 LbErrKind::AlreadySyncing => {
120 write!(f, "A sync is already in progress, cannot begin another sync at this time!")
121 }
122 LbErrKind::ReReadRequired => {
123 write!(f, "This document changed since you last read it, please re-read it!")
124 }
125 LbErrKind::Validation(validation_failure) => match validation_failure {
126 ValidationFailure::Cycle(_) => write!(f, "Cannot move a folder into itself"),
127 ValidationFailure::NonFolderWithChildren(_) => {
128 write!(f, "A document or a link was treated as a folder.")
129 }
130 ValidationFailure::PathConflict(_) => {
131 write!(f, "A file already exists at that path.")
132 }
133 ValidationFailure::DeletedFileUpdated(id) => {
134 write!(f, "this file has been deleted {id}")
135 }
136 ValidationFailure::FileNameTooLong(_) => write!(f, "this filename is too long!"),
137 ValidationFailure::OwnedLink(_) => {
138 write!(f, "you cannot have a link to a file you own")
139 }
140 ValidationFailure::BrokenLink(_) => write!(f, "that link target does not exist!"),
141 ValidationFailure::DuplicateLink { .. } => {
142 write!(f, "you already have a link to that file")
143 }
144 ValidationFailure::SharedLink { .. } => {
145 write!(f, "you cannot place a link inside a shared folder!")
146 }
147 _ => write!(f, "unexpected validation failure: {validation_failure:?}"),
148 },
149 LbErrKind::Diff(diff_error) => {
150 write!(f, "unexpected diff error: {diff_error:?}")
151 }
152 LbErrKind::Sign(sign_error) => {
153 write!(f, "unexpected signing error: {sign_error:?}")
154 }
155 LbErrKind::Crypto(crypto_error) => {
156 write!(f, "unexpected crypto error: {crypto_error:?}")
157 }
158 }
159 }
160}
161
162impl From<LbErrKind> for LbErr {
163 fn from(kind: LbErrKind) -> Self {
164 Self { kind, backtrace: Some(Backtrace::force_capture()) }
165 }
166}
167
168pub trait Unexpected<T> {
169 fn log_and_ignore(self) -> Option<T>;
170 fn map_unexpected(self) -> LbResult<T>;
171}
172
173impl<T, E: std::fmt::Debug> Unexpected<T> for Result<T, E> {
174 #[track_caller]
175 fn map_unexpected(self) -> LbResult<T> {
176 let location = Location::caller();
177 self.map_err(|err| {
178 LbErrKind::Unexpected(format!(
179 "unexpected error at {}:{} {err:?}",
180 location.file(),
181 location.line(),
182 ))
183 .into()
184 })
185 }
186
187 #[track_caller]
188 fn log_and_ignore(self) -> Option<T> {
189 let location = Location::caller();
190 if let Err(e) = &self {
191 error!("error ignored at {}:{} {e:?}", location.file(), location.line());
192 }
193
194 self.ok()
195 }
196}
197
198#[derive(Debug)]
199pub struct UnexpectedError {
200 pub msg: String,
201 pub backtrace: Option<Backtrace>,
202}
203
204impl UnexpectedError {
205 pub fn new(s: impl ToString) -> Self {
206 Self { msg: s.to_string(), backtrace: Some(Backtrace::force_capture()) }
207 }
208}
209
210impl fmt::Display for UnexpectedError {
211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
212 write!(f, "unexpected error: {}", self.msg)
213 }
214}
215
216impl From<LbErr> for UnexpectedError {
217 fn from(err: LbErr) -> Self {
218 Self { msg: format!("{:?}", err.kind), backtrace: err.backtrace }
219 }
220}
221
222impl<T> From<PoisonError<T>> for UnexpectedError {
223 fn from(err: PoisonError<T>) -> Self {
224 Self::new(format!("{err:#?}"))
225 }
226}
227
228impl From<crossbeam::channel::RecvError> for UnexpectedError {
229 fn from(err: crossbeam::channel::RecvError) -> Self {
230 Self::new(format!("{err:#?}"))
231 }
232}
233
234impl From<crossbeam::channel::RecvTimeoutError> for UnexpectedError {
235 fn from(err: crossbeam::channel::RecvTimeoutError) -> Self {
236 Self::new(format!("{err:#?}"))
237 }
238}
239
240impl<T> From<crossbeam::channel::SendError<T>> for UnexpectedError {
241 fn from(err: crossbeam::channel::SendError<T>) -> Self {
242 Self::new(format!("{err:#?}"))
243 }
244}
245
246impl Serialize for UnexpectedError {
247 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
248 where
249 S: Serializer,
250 {
251 let mut state = serializer.serialize_struct("UnexpectedError", 2)?;
252 state.serialize_field("tag", "Unexpected")?;
253 state.serialize_field("content", &self.msg)?;
254 state.end()
255 }
256}
257
258#[macro_export]
259macro_rules! unexpected_only {
260 ($base:literal $(, $args:tt )*) => {{
261 debug!($base $(, $args )*);
262 debug!("{:?}", std::backtrace::Backtrace::force_capture());
263 debug!($base $(, $args )*);
264 UnexpectedError::new(format!($base $(, $args )*))
265 }};
266}
267
268#[derive(Debug, Clone, PartialEq, Eq)]
269pub enum LbErrKind {
270 AccountExists,
271 AccountNonexistent,
272 AccountStringCorrupted,
273 AlreadyCanceled,
274 AlreadyPremium,
275 AppStoreAccountAlreadyLinked,
276 AlreadySyncing,
277 CannotCancelSubscriptionForAppStore,
279 CardDecline,
280 CardExpired,
281 CardInsufficientFunds,
282 CardInvalidCvc,
283 CardInvalidExpMonth,
284 CardInvalidExpYear,
285 CardInvalidNumber,
286 CardNotSupported,
287 ClientUpdateRequired,
288 CurrentUsageIsMoreThanNewTier,
289 DiskPathInvalid,
290 DiskPathTaken,
291 DrawingInvalid,
292 ExistingRequestPending,
293 FileNameContainsSlash,
295 FileNameTooLong,
297 FileNameEmpty,
298 FileNonexistent,
299 FileNotDocument,
300 FileParentNonexistent,
301 InsufficientPermission,
302 InvalidPurchaseToken,
303 InvalidAuthDetails,
304 KeyPhraseInvalid,
305 NotPremium,
306 UsageIsOverDataCap,
307 UsageIsOverFreeTierDataCap,
308 OldCardDoesNotExist,
309 PathContainsEmptyFileName,
310 RootModificationInvalid,
311 RootNonexistent,
312 ServerDisabled,
313 ServerUnreachable,
314 ShareAlreadyExists,
315 ShareNonexistent,
316 TryAgain,
317 UsernameInvalid,
319 UsernameNotFound,
320 UsernamePublicKeyMismatch,
321 UsernameTaken,
322 ReReadRequired,
323 Diff(DiffError),
324
325 Validation(ValidationFailure),
327 Sign(SignError),
328 Crypto(CryptoError),
329
330 Unexpected(String),
335}
336
337#[derive(Debug, Clone, PartialEq, Eq)]
338pub enum DiffError {
339 OldVersionIncorrect,
340 OldFileNotFound,
341 OldVersionRequired,
342 DiffMalformed,
343 HmacModificationInvalid,
344}
345
346#[derive(Debug, Clone, PartialEq, Eq)]
347pub enum SignError {
348 SignatureInvalid,
349 SignatureParseError(libsecp256k1::Error),
351 WrongPublicKey,
352 SignatureInTheFuture(u64),
353 SignatureExpired(u64),
354}
355
356#[derive(Debug, Clone, PartialEq, Eq)]
357pub enum CryptoError {
358 Decryption(aead::Error),
359 HmacVerification(MacError),
360}
361
362impl From<bincode::Error> for LbErr {
363 fn from(err: bincode::Error) -> Self {
364 core_err_unexpected(err).into()
365 }
366}
367
368pub fn core_err_unexpected<T: fmt::Debug>(err: T) -> LbErrKind {
369 LbErrKind::Unexpected(format!("{err:?}"))
370}
371
372pub fn unexpected<T: fmt::Debug>(err: T) -> LbErr {
374 LbErrKind::Unexpected(format!("{err:?}")).into()
375}
376
377impl From<db_rs::DbError> for LbErr {
378 fn from(err: db_rs::DbError) -> Self {
379 core_err_unexpected(err).into()
380 }
381}
382
383impl<G> From<PoisonError<G>> for LbErr {
384 fn from(err: PoisonError<G>) -> Self {
385 core_err_unexpected(err).into()
386 }
387}
388
389impl From<io::Error> for LbErr {
390 fn from(e: io::Error) -> Self {
391 match e.kind() {
392 io::ErrorKind::NotFound
393 | io::ErrorKind::PermissionDenied
394 | io::ErrorKind::InvalidInput => LbErrKind::DiskPathInvalid,
395 io::ErrorKind::AlreadyExists => LbErrKind::DiskPathTaken,
396 _ => core_err_unexpected(e),
397 }
398 .into()
399 }
400}
401
402impl From<serde_json::Error> for LbErr {
403 fn from(err: serde_json::Error) -> Self {
404 LbErrKind::Unexpected(format!("{err}")).into()
405 }
406}
407
408impl From<ApiError<api::NewAccountError>> for LbErr {
409 fn from(err: ApiError<api::NewAccountError>) -> Self {
410 match err {
411 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
412 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
413 ApiError::Endpoint(api::NewAccountError::UsernameTaken) => LbErrKind::UsernameTaken,
414 ApiError::Endpoint(api::NewAccountError::InvalidUsername) => LbErrKind::UsernameInvalid,
415 ApiError::Endpoint(api::NewAccountError::Disabled) => LbErrKind::ServerDisabled,
416 e => core_err_unexpected(e),
417 }
418 .into()
419 }
420}
421
422impl From<ApiError<api::GetPublicKeyError>> for LbErr {
423 fn from(err: ApiError<api::GetPublicKeyError>) -> Self {
424 match err {
425 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
426 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
427 ApiError::Endpoint(api::GetPublicKeyError::UserNotFound) => {
428 LbErrKind::AccountNonexistent
429 }
430 e => core_err_unexpected(e),
431 }
432 .into()
433 }
434}
435
436impl From<ApiError<api::GetUsernameError>> for LbErr {
437 fn from(err: ApiError<api::GetUsernameError>) -> Self {
438 match err {
439 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
440 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
441 ApiError::Endpoint(api::GetUsernameError::UserNotFound) => {
442 LbErrKind::AccountNonexistent
443 }
444 e => core_err_unexpected(e),
445 }
446 .into()
447 }
448}
449
450impl From<ApiError<api::GetFileIdsError>> for LbErr {
451 fn from(e: ApiError<api::GetFileIdsError>) -> Self {
452 match e {
453 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
454 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
455 e => core_err_unexpected(e),
456 }
457 .into()
458 }
459}
460
461impl From<ApiError<api::GetUpdatesError>> for LbErr {
462 fn from(e: ApiError<api::GetUpdatesError>) -> Self {
463 match e {
464 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
465 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
466 e => core_err_unexpected(e),
467 }
468 .into()
469 }
470}
471
472impl From<ApiError<api::GetDocumentError>> for LbErr {
473 fn from(e: ApiError<api::GetDocumentError>) -> Self {
474 match e {
475 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
476 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
477 e => core_err_unexpected(e),
478 }
479 .into()
480 }
481}
482
483impl From<ApiError<api::UpsertError>> for LbErr {
484 fn from(e: ApiError<api::UpsertError>) -> Self {
485 match e {
486 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
487 ApiError::Endpoint(api::UpsertError::UsageIsOverDataCap) => {
488 LbErrKind::UsageIsOverDataCap
489 }
490 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
491 e => core_err_unexpected(e),
492 }
493 .into()
494 }
495}
496
497impl From<ApiError<api::ChangeDocError>> for LbErr {
498 fn from(e: ApiError<api::ChangeDocError>) -> Self {
499 match e {
500 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
501 ApiError::Endpoint(api::ChangeDocError::UsageIsOverDataCap) => {
502 LbErrKind::UsageIsOverDataCap
503 }
504 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
505 e => core_err_unexpected(e),
506 }
507 .into()
508 }
509}
510
511impl From<ApiError<api::GetUsageError>> for LbErr {
512 fn from(e: ApiError<api::GetUsageError>) -> Self {
513 match e {
514 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
515 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
516 e => core_err_unexpected(e),
517 }
518 .into()
519 }
520}
521
522#[derive(Debug, Clone, Serialize)]
523pub enum Warning {
524 EmptyFile(Uuid),
525 InvalidUTF8(Uuid),
526}
527
528impl fmt::Display for Warning {
529 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
530 match self {
531 Self::EmptyFile(id) => write!(f, "empty file: {id}"),
532 Self::InvalidUTF8(id) => write!(f, "invalid utf8 in file: {id}"),
533 }
534 }
535}