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