aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rust/Cargo.toml3
-rw-r--r--rust/src/auth.rs84
-rw-r--r--rust/src/bel.rs6
-rw-r--r--rust/src/editor.rs5
-rw-r--r--rust/src/main.rs8
5 files changed, 101 insertions, 5 deletions
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 2c95d3c..869baeb 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -11,3 +11,6 @@ tokio = { version = "0.2", features = ["rt-threaded"] }
futures = "0.3"
once_cell = "1.4"
unicode-width = "^0.1.8"
+users = "0.10"
+dirs = "3.0"
+rand = "0.7"
diff --git a/rust/src/auth.rs b/rust/src/auth.rs
new file mode 100644
index 0000000..51b4820
--- /dev/null
+++ b/rust/src/auth.rs
@@ -0,0 +1,84 @@
+// use users::get_current_username;
+// use dirs::data_dir;
+use std::borrow::Borrow;
+use std::ops::Deref;
+use std::{fs, io};
+use std::path::{Path, PathBuf};
+use rand::{Rng, CryptoRng};
+use crate::error::{IntoIOError, IntoIOResult};
+
+pub struct User(String);
+pub struct Pass(String);
+
+impl Borrow<str> for User { fn borrow(&self) -> &str { &self.0 } }
+impl Borrow<str> for Pass { fn borrow(&self) -> &str { &self.0 } }
+
+impl Deref for User { type Target = str; fn deref(&self) -> &Self::Target { &self.0 } }
+impl Deref for Pass { type Target = str; fn deref(&self) -> &Self::Target { &self.0 } }
+
+static PASSWORD_FILE_SUBDIR: &str = "tomsg_client"; // ~/.local/share/PASSWORD_FILE_SUBDIR/...
+static PASSWORD_FILE_SUBDIR_HOME: &str = ".tomsg_client"; // ~/PASSWORD_FILE_SUBDIR_HOME/...
+static PASSWORD_FILE_NAME: &str = "password.txt";
+
+pub fn get_auth_info() -> io::Result<(User, Pass)> {
+ Ok((get_username()?, obtain_password()?))
+}
+
+fn get_username() -> io::Result<User> {
+ match users::get_current_username().map(|u| u.to_str().map(|u| u.to_string())) {
+ Some(Some(u)) => Ok(User(u)),
+ Some(None) => Err("Username not valid UTF-8".ioerr()),
+ None => Err("Cannot obtain username".ioerr()),
+ }
+}
+
+fn obtain_password() -> io::Result<Pass> {
+ let dir = storage_directory()?;
+ if let Ok(pass) = read_password(&dir) {
+ return Ok(pass);
+ }
+
+ let pass = generate_password();
+ write_password(&dir, &pass)
+ .map_err(|e| format!("Cannot write password to home directory: {}", e).ioerr())?;
+ Ok(pass)
+}
+
+fn read_password(data_dir: &Path) -> io::Result<Pass> {
+ fs::read(data_dir.join(PASSWORD_FILE_NAME))
+ .and_then(|mut v| {
+ // Trim final newline if there is one
+ if v.last() == Some(&b'\n') {
+ v.pop();
+ }
+ String::from_utf8(v).map(Pass).iores()
+ })
+}
+
+fn write_password(data_dir: &Path, pass: &Pass) -> io::Result<()> {
+ fs::create_dir_all(data_dir)?;
+ fs::write(data_dir.join(PASSWORD_FILE_NAME), pass.as_bytes())
+}
+
+fn storage_directory() -> io::Result<PathBuf> {
+ dirs::data_dir()
+ .map(|mut dir| { dir.push(PASSWORD_FILE_SUBDIR); dir })
+ .or_else(|| dirs::home_dir()
+ .map(|mut dir| {
+ dir.push(PASSWORD_FILE_SUBDIR_HOME); dir
+ }))
+ .ok_or("Cannot determine home directory".ioerr())
+}
+
+fn generate_password() -> Pass {
+ Pass(secure_rng()
+ .sample_iter(rand::distributions::Alphanumeric)
+ .take(32)
+ .collect::<String>()
+ )
+}
+
+// Wrapper for thread_rng() that statically enforces that the generator is CryptoRng.
+fn secure_rng() -> impl Rng + CryptoRng {
+ rand::thread_rng()
+}
diff --git a/rust/src/bel.rs b/rust/src/bel.rs
new file mode 100644
index 0000000..fec5a3c
--- /dev/null
+++ b/rust/src/bel.rs
@@ -0,0 +1,6 @@
+use std::io::Write;
+
+pub fn bel() {
+ print!("\x07");
+ std::io::stdout().flush().unwrap();
+}
diff --git a/rust/src/editor.rs b/rust/src/editor.rs
index 7d83904..6284570 100644
--- a/rust/src/editor.rs
+++ b/rust/src/editor.rs
@@ -1,5 +1,5 @@
-use std::io::Write;
use termion::event::Key;
+use crate::bel;
#[derive(Debug, Default)]
pub struct Editor {
@@ -56,8 +56,7 @@ impl Editor {
}
_ => {
- print!("\x07");
- std::io::stdout().flush().unwrap();
+ bel::bel();
}
};
diff --git a/rust/src/main.rs b/rust/src/main.rs
index 6e10938..5a50f3b 100644
--- a/rust/src/main.rs
+++ b/rust/src/main.rs
@@ -18,6 +18,8 @@ use unicode_width::UnicodeWidthChar;
use crate::editor::Editor;
use crate::error::IntoIOError;
+mod auth;
+mod bel;
mod editor;
mod error;
@@ -537,8 +539,10 @@ async fn async_main() -> io::Result<()> {
let addr = ("127.0.0.1", 29538);
let (conn, pushchan) = Connection::connect(connection::Type::Plain, addr).await?;
- let user = Word::try_from(String::from("tom")).unwrap();
- let pass = Line::try_from(String::from("kaas")).unwrap();
+ let (user, pass) = auth::get_auth_info()?;
+
+ let user = Word::try_from(user.to_string()).unwrap();
+ let pass = Line::try_from(pass.to_string()).unwrap();
send_command(&conn, Command::Register { username: user.clone(), password: pass.clone() }).await?;