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 SizeModificationInvalid,
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}