aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rust/Cargo.toml2
-rw-r--r--rust/src/getpass.rs65
-rw-r--r--rust/src/main.rs54
3 files changed, 117 insertions, 4 deletions
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 869baeb..bd0e51c 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -14,3 +14,5 @@ unicode-width = "^0.1.8"
users = "0.10"
dirs = "3.0"
rand = "0.7"
+argparse = "0.2"
+libc = "^0.2.79"
diff --git a/rust/src/getpass.rs b/rust/src/getpass.rs
new file mode 100644
index 0000000..f40da0e
--- /dev/null
+++ b/rust/src/getpass.rs
@@ -0,0 +1,65 @@
+extern crate libc;
+
+use std::io::{self, Write};
+use std::mem;
+use std::str;
+
+fn error_checker(ret: i32) -> io::Result<()> {
+ if ret == 0 {
+ Ok(())
+ } else {
+ Err(io::Error::last_os_error())
+ }
+}
+
+fn tcgetattr(fd: i32) -> io::Result<libc::termios> {
+ unsafe {
+ let mut tios: libc::termios = mem::zeroed();
+ error_checker(libc::tcgetattr(fd, &mut tios))?;
+ Ok(tios)
+ }
+}
+
+fn tcsetattr(fd: i32, other_options: i32, tios: &libc::termios) -> io::Result<()> {
+ unsafe {
+ error_checker(libc::tcsetattr(fd, other_options, tios))?;
+ Ok(())
+ }
+}
+
+fn without_echo<T, F: FnOnce() -> io::Result<T>>(f: F) -> io::Result<T> {
+ let tios_old = tcgetattr(0)?;
+
+ let mut tios_new = tios_old;
+ tios_new.c_lflag &= !libc::ECHO;
+ tios_new.c_lflag |= libc::ECHONL;
+
+ match tcsetattr(0, libc::TCSANOW, &tios_new) {
+ Ok(()) => {},
+ Err(e) => {
+ // Just blindly try to reset the terminal, in case something changed
+ let _ = tcsetattr(0, libc::TCSANOW, &tios_old);
+ return Err(e);
+ }
+ }
+
+ let ret = f()?;
+
+ tcsetattr(0, libc::TCSANOW, &tios_old)?;
+
+ Ok(ret)
+}
+
+pub fn get_pass(prompt: &str) -> io::Result<String> {
+ print!("{}", prompt);
+ io::stdout().flush()?;
+
+ without_echo(|| {
+ let mut line = String::new();
+ io::stdin().read_line(&mut line)?;
+ if line.ends_with('\n') {
+ line.pop();
+ }
+ Ok(line)
+ })
+}
diff --git a/rust/src/main.rs b/rust/src/main.rs
index f7a2660..8c7e617 100644
--- a/rust/src/main.rs
+++ b/rust/src/main.rs
@@ -3,6 +3,7 @@ use std::collections::{HashMap, HashSet};
use std::io::{self, Write};
use std::sync::{Arc, Mutex};
use std::convert::TryFrom;
+use argparse::{ArgumentParser, Store, StoreOption};
use futures::channel::mpsc;
use futures::stream::StreamExt;
use once_cell::sync::Lazy;
@@ -22,6 +23,7 @@ mod auth;
mod bel;
mod editor;
mod error;
+mod getpass;
static DEBUG_FILE: Lazy<Mutex<Option<File>>> = Lazy::new(|| {
Mutex::new(
@@ -624,14 +626,58 @@ async fn pushchan_thread(mut chan: mpsc::Receiver<PushMessage>, app: Arc<App>) {
}
}
+struct Options {
+ server: String,
+ username: Option<String>,
+}
+
+impl Default for Options {
+ fn default() -> Self {
+ Self { server: String::from("127.0.0.1:29538"), username: None }
+ }
+}
+
+fn parse_options() -> Options {
+ let mut options = Options::default();
+ let defserver = options.server.clone();
+ let server_descr = format!("Address (host:port) of tomsg server (default: {})", defserver);
+
+ {
+ let mut ap = ArgumentParser::new();
+ ap.set_description("Simple client for tomsg with auto-generated credentials.");
+
+ ap.refer(&mut options.server)
+ .add_option(&["-s", "--server"], Store, &server_descr)
+ .metavar("ADDR");
+ ap.refer(&mut options.username)
+ .add_option(&["-u", "--user"], StoreOption,
+ "Username to connect as (if given, will prompt for password on stdin) (default: current system username)")
+ .metavar("USER");
+
+ ap.parse_args_or_exit();
+ }
+ options
+}
+
async fn async_main() -> io::Result<()> {
- let addr = ("127.0.0.1", 29538);
- let (conn, pushchan) = Connection::connect(connection::Type::Plain, addr).await?;
+ let options = parse_options();
+
+ let (conn, pushchan) = Connection::connect(connection::Type::Plain, options.server).await?;
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();
+ let (user, pass) = match options.username {
+ None => (Word::try_from(user.to_string()).unwrap()
+ ,Line::try_from(pass.to_string()).unwrap()),
+ Some(user) => {
+ let pass = getpass::get_pass(&format!("Password for user '{}': ", user))?;
+ if pass.len() == 0 {
+ println!("Cancelled.");
+ std::process::exit(1);
+ }
+ (Word::try_from(user).unwrap(), Line::try_from(pass).unwrap())
+ }
+ };
send_command(&conn, Command::Register { username: user.clone(), password: pass.clone() }).await?;