1use crate::model::account::{Account, MAX_USERNAME_LENGTH};
2use crate::model::api::{
3 DeleteAccountRequest, GetPublicKeyRequest, GetUsernameRequest, NewAccountRequestV2,
4};
5use crate::model::errors::{LbErrKind, LbResult, core_err_unexpected};
6use crate::model::file_like::FileLike;
7use crate::model::file_metadata::{FileType, Owner};
8use crate::model::meta::Meta;
9use crate::service::events::Actor;
10use crate::{DEFAULT_API_LOCATION, Lb};
11use libsecp256k1::SecretKey;
12use qrcode_generator::QrCodeEcc;
13
14use crate::io::network::ApiError;
15
16impl Lb {
17 #[instrument(level = "debug", skip(self), err(Debug))]
24 pub async fn create_account(
25 &self, username: &str, api_url: &str, welcome_doc: bool,
26 ) -> LbResult<Account> {
27 let username = String::from(username).to_lowercase();
28
29 if username.len() > MAX_USERNAME_LENGTH {
30 return Err(LbErrKind::UsernameInvalid.into());
31 }
32
33 let mut tx = self.begin_tx().await;
34 let db = tx.db();
35
36 if db.account.get().is_some() {
37 return Err(LbErrKind::AccountExists.into());
38 }
39
40 let account = Account::new(username.clone(), api_url.to_string());
41
42 let root = Meta::create_root(&account)?.sign_with(&account)?;
43 let root_id = *root.id();
44
45 let last_synced = self
46 .client
47 .request(&account, NewAccountRequestV2::new(&account, &root))
48 .await?
49 .last_synced;
50
51 db.account.insert(account.clone())?;
52 db.base_metadata.insert(root_id, root)?;
53 db.last_synced.insert(last_synced as i64)?;
54 db.root.insert(root_id)?;
55 db.pub_key_lookup
56 .insert(Owner(account.public_key()), account.username.clone())?;
57
58 self.keychain.cache_account(account.clone()).await?;
59
60 tx.end();
61
62 if welcome_doc {
63 let welcome_doc = self
64 .create_file("welcome.md", &root_id, FileType::Document)
65 .await?;
66 self.write_document(welcome_doc.id, Self::WELCOME_MESSAGE.as_bytes())
67 .await?;
68 self.sync().await?;
69 }
70
71 self.events.meta_changed(Actor::User);
72 self.events.signed_in();
73
74 Ok(account)
75 }
76
77 #[instrument(level = "debug", skip(self, key), err(Debug))]
78 pub async fn import_account(&self, key: &str, api_url: Option<&str>) -> LbResult<Account> {
79 if self.get_account().is_ok() {
80 warn!("tried to import an account, but account exists already.");
81 return Err(LbErrKind::AccountExists.into());
82 }
83
84 if let Ok(key) = base64::decode(key) {
85 if let Ok(account) = bincode::deserialize(&key[..]) {
86 return self.import_account_private_key_v1(account).await;
87 } else if let Ok(key) = SecretKey::parse_slice(&key) {
88 return self
89 .import_account_private_key_v2(key, api_url.unwrap_or(DEFAULT_API_LOCATION))
90 .await;
91 }
92 }
93
94 let phrase: [&str; 24] = key
95 .split([' ', ','])
96 .filter(|maybe_word| !maybe_word.is_empty())
97 .collect::<Vec<_>>()
98 .try_into()
99 .map_err(|_| LbErrKind::AccountStringCorrupted)?;
100
101 self.import_account_phrase(phrase, api_url.unwrap_or(DEFAULT_API_LOCATION))
102 .await
103 }
104
105 pub async fn import_account_private_key_v1(&self, account: Account) -> LbResult<Account> {
106 let server_public_key = self
107 .client
108 .request(&account, GetPublicKeyRequest { username: account.username.clone() })
109 .await?
110 .key;
111
112 let account_public_key = account.public_key();
113
114 if account_public_key != server_public_key {
115 return Err(LbErrKind::UsernamePublicKeyMismatch.into());
116 }
117
118 let mut tx = self.begin_tx().await;
119 let db = tx.db();
120 db.account.insert(account.clone())?;
121 self.keychain.cache_account(account.clone()).await?;
122
123 Ok(account)
124 }
125
126 pub async fn import_account_private_key_v2(
127 &self, private_key: SecretKey, api_url: &str,
128 ) -> LbResult<Account> {
129 let mut account =
130 Account { username: "".to_string(), api_url: api_url.to_string(), private_key };
131 let public_key = account.public_key();
132
133 account.username = self
134 .client
135 .request(&account, GetUsernameRequest { key: public_key })
136 .await?
137 .username;
138
139 let mut tx = self.begin_tx().await;
140 let db = tx.db();
141 db.account.insert(account.clone())?;
142 self.keychain.cache_account(account.clone()).await?;
143
144 Ok(account)
145 }
146
147 pub async fn import_account_phrase(
148 &self, phrase: [&str; 24], api_url: &str,
149 ) -> LbResult<Account> {
150 let private_key = Account::phrase_to_private_key(phrase)?;
151 self.import_account_private_key_v2(private_key, api_url)
152 .await
153 }
154
155 #[instrument(level = "debug", skip(self), err(Debug))]
156 pub fn export_account_private_key(&self) -> LbResult<String> {
157 self.export_account_private_key_v2()
158 }
159
160 #[allow(dead_code)]
161 pub(crate) fn export_account_private_key_v1(&self) -> LbResult<String> {
162 let account = self.get_account()?;
163 let encoded: Vec<u8> = bincode::serialize(account).map_err(core_err_unexpected)?;
164 Ok(base64::encode(encoded))
165 }
166
167 pub(crate) fn export_account_private_key_v2(&self) -> LbResult<String> {
168 let account = self.get_account()?;
169 Ok(base64::encode(account.private_key.serialize()))
170 }
171
172 pub fn export_account_phrase(&self) -> LbResult<String> {
173 let account = self.get_account()?;
174 Ok(account.get_phrase()?.join(" "))
175 }
176
177 pub fn export_account_qr(&self) -> LbResult<Vec<u8>> {
178 let acct_secret = self.export_account_private_key_v2()?;
179 qrcode_generator::to_png_to_vec(acct_secret, QrCodeEcc::Low, 1024)
180 .map_err(|err| core_err_unexpected(err).into())
181 }
182
183 #[instrument(level = "debug", skip(self), err(Debug))]
184 pub async fn delete_account(&self) -> LbResult<()> {
185 let account = self.get_account()?;
186
187 self.client
188 .request(account, DeleteAccountRequest {})
189 .await
190 .map_err(|err| match err {
191 ApiError::SendFailed(_) => LbErrKind::ServerUnreachable,
192 ApiError::ClientUpdateRequired => LbErrKind::ClientUpdateRequired,
193 _ => core_err_unexpected(err),
194 })?;
195
196 let mut tx = self.begin_tx().await;
197 let db = tx.db();
198
199 db.account.clear()?;
200 db.last_synced.clear()?;
201 db.base_metadata.clear()?;
202 db.root.clear()?;
203 db.local_metadata.clear()?;
204 db.pub_key_lookup.clear()?;
205
206 Ok(())
209 }
210
211 const WELCOME_MESSAGE: &'static str = r#"# Markdown Syntax
212Markdown is a language for easily formatting your documents. This document can help you get started.
213
214## Styled Text
215To style text, wrap your text in the corresponding characters.
216| Style | Syntax | Example |
217|---------------|---------------------|-------------------|
218| emphasis | `*emphasis*` | *emphasis* |
219| strong | `**strong**` | **strong** |
220| strikethrough | `~~strikethrough~~` | ~~strikethrough~~ |
221| underline | `__underline__` | __underline__ |
222| code | ``code`` | `code` |
223| highlight | `==highlight==` | ==highlight== |
224| spoiler | `||spoiler||` | ||spoiler|| |
225| superscript | `^superscript^` | ^superscript^ |
226| subscript | `~subscript~` | ~subscript~ |
227
228## Links
229To make text into a link, wrap it with `[` `]`, add a link destination to the end , and wrap the destination with `(` `)`. The link destination can be a web URL or a relative path to another Lockbook file.
230```md
231[Lockbook's website](https://lockbook.net)
232```
233> [Lockbook's website](https://lockbook.net)
234
235## Images
236To embed an image, add a `!` to the beginning of the link syntax.
237```md
238
239```
240> 
241
242## Headings
243To create a heading, add up to six `#`'s plus a space before your text. More `#`'s create a smaller heading.
244```md
245# Heading 1
246## Heading 2
247### Heading 3
248#### Heading 4
249##### Heading 5
250###### Heading 6
251```
252> # Heading 1
253> ## Heading 2
254> ### Heading 3
255> #### Heading 4
256> ##### Heading 5
257> ###### Heading 6
258
259## Lists
260Create a list item by adding `- `, `+ `, or `* ` for a bulleted list, `1. ` for a numbered list, or `- [ ] `, `+ [ ] `, or `* [ ] ` for a task list at the start of the line. The added characters are called the *list marker*.
261```md
262* bulleted list item
263- bulleted list item
264+ bulleted list item
265
2661. numbered list item
2671. numbered list item
2681. numbered list item
269
270- [ ] task list item
271- [x] task list item
272```
273>* bulleted list item
274>- bulleted list item
275>+ bulleted list item
276>
277>1. numbered list item
278>1. numbered list item
279>1. numbered list item
280>
281>- [ ] task list item
282>- [x] task list item
283
284List items can be nested. To nest an inner item in an outer one, the inner item's line must start with at least one space for each character in the outer item's list marker: usually 2 for bulleted lists, 3 for numbered lists, or 2 for tasks lists (the trailing `[ ] ` is excluded).
285```md
286* This is a bulleted list
287 * An inner item needs at least 2 spaces
2881. This is a numbered list
289 1. An inner item needs at least 3 spaces
290* [ ] This is a task list
291 * [ ] An inner item needs at least 2 spaces
292```
293> * This is a bulleted list
294> * An inner item needs at least 2 spaces
295> 1. This is a numbered list
296> 1. An inner item needs at least 3 spaces
297> * [ ] This is a task list
298> * [ ] An inner item needs at least 2 spaces
299
300List items can contain formatted content. For non-text content, each line must start with the same number of spaces as an inner list item would.
301```md
302* This item contains text,
303 > a quote
304 ### and a heading.
305* This item contains two lines of text.
306The second line doesn't need spaces.
307```
308> * This item contains text,
309> > a quote
310> ### and a heading.
311> * This item contains two lines of text.
312> The second line doesn't need spaces.
313
314## Quotes
315To create a block quote, add `> ` to each line.
316```md
317> This is a quote
318```
319> This is a quote
320
321Like list items, block quotes can contain formatted content.
322```md
323> This quote contains some text,
324> ```rust
325> // some code
326> fn main() { println!("Hello, world!"); }
327> ```
328> ### and a heading.
329
330> This quote contains two lines of text.
331The second line doesn't need added characters.
332```
333> This quote contains some text,
334> ```rust
335> // some code
336> fn main() { println!("Hello, world!"); }
337> ```
338> ### and a heading.
339
340> This quote contains two lines of text.
341The second line doesn't need added characters.
342
343## Alerts
344To create an alert, add one of 5 tags to the first line of a quote: `[!NOTE]`, `[!TIP]`, `[!IMPORTANT]`, `[!WARNING]`, or `[!CAUTION]`. An alternate title can be added after the tag.
345```md
346> [!NOTE]
347> This is a note.
348
349> [!TIP]
350> This is a tip.
351
352> [!IMPORTANT]
353> This is important.
354
355> [!WARNING]
356> This is a warning.
357
358> [!CAUTION] Caution!!!!!
359> This is a caution.
360```
361> [!NOTE]
362> This is a note.
363
364> [!TIP]
365> This is a tip.
366
367> [!IMPORTANT]
368> This is important.
369
370> [!WARNING]
371> This is a warning.
372
373> [!CAUTION] Caution!!!!!
374> This is a caution.
375
376## Tables
377A table is written with `|`'s between columns and a row after the header row whose cell's contents are `-`'s.
378```md
379| Style | Syntax | Example |
380|---------------|---------------------|-------------------|
381| emphasis | `*emphasis*` | *emphasis* |
382| strong | `**strong**` | **strong** |
383```
384> | Style | Syntax | Example |
385> |---------------|---------------------|-------------------|
386> | emphasis | `*emphasis*` | *emphasis* |
387> | strong | `**strong**` | **strong** |
388
389## Code
390A code block is wrapped by two lines containing three backticks. A language can be added after the opening backticks.
391```md
392 ```rust
393 // some code
394 fn main() { println!("Hello, world!"); }
395 ```
396```
397> ```rust
398> // some code
399> fn main() { println!("Hello, world!"); }
400> ```
401
402You can also create a code block by indenting each line with four spaces. Indented code blocks cannot have a language.
403```md
404 // some code
405 fn main() { println!("Hello, world!"); }
406```
407> // some code
408> fn main() { println!("Hello, world!"); }
409
410## Thematic Breaks
411A thematic break is written with `***`, `---`, or `___` and shows a horizontal line across the page.
412```md
413***
414---
415___
416```
417> ***
418> ---
419> ___
420"#;
421}