From 690cde974b7b7905e2ca4fd36b3f6905242935bd Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Wed, 3 Jan 2024 10:55:39 +0000 Subject: [PATCH] Support saving login details. We can now log in! --- src/config.rs | 28 ++++++++++++++++++++++++++++ src/login.rs | 24 ++++++++++++++++++++---- src/main.rs | 2 +- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/config.rs b/src/config.rs index 9d047aa..bd26ad6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,10 @@ use std::path::PathBuf; +use std::io::Write; + +use std::fs::OpenOptions; + +#[cfg(unix)] +use std::os::unix::fs::OpenOptionsExt; #[cfg(windows)] use std::str::FromStr; @@ -86,4 +92,26 @@ impl ConfigLocation { pub fn get_path(&self, leaf: &str) -> PathBuf { self.dir.join(leaf) } + + pub fn create_file(&self, leaf: &str, contents: &str) -> + Result<(), std::io::Error> + { + std::fs::create_dir_all(&self.dir)?; + + // FIXME: we could do better here, by creating a file under a + // separate name and then atomically renaming it + + let mut opts = OpenOptions::new(); + opts.write(true); + opts.create_new(true); + + // On Windows files are not-world-readable by default, but on + // Unix we must be careful about this + #[cfg(unix)] + opts.mode(0o600); + + let path = self.get_path(leaf); + opts.open(path)? + .write_all(contents.as_bytes()) + } } diff --git a/src/login.rs b/src/login.rs index 66c730e..67a00bf 100644 --- a/src/login.rs +++ b/src/login.rs @@ -1,7 +1,9 @@ use reqwest::Url; use std::io::Write; -use super::auth::AuthConfig; +use super::TopLevelError; +use super::auth::{AuthConfig, AuthError}; +use super::config::ConfigLocation; use super::client::{reqwest_client, Req, ClientError}; use super::types::{Account, Application, Instance, Token}; @@ -121,7 +123,19 @@ impl Login { } } -pub fn login(instance_url: &str) -> Result<(), ClientError> { +pub fn login(cfgloc: &ConfigLocation, instance_url: &str) -> + Result<(), TopLevelError> +{ + // First, check we aren't logged in already, and give some + // marginally useful advice on what to do if we are. + match AuthConfig::load(cfgloc) { + Err(AuthError::Nonexistent(..)) => Ok(()), + Ok(auth) => Err(TopLevelError::new("", &format!( + "you are already logged in as {0}@{1}! Use --config to specify a separate configuration directory for another login, or delete {2} to remove the existing login details", + auth.username, auth.instance_domain, cfgloc.get_path("auth").display()))), + Err(e) => Err(TopLevelError::from(e)), + }?; + // Ergonomics: parse the URL, adding a default https:// scheme if // it's just a bare hostname let urlstr = match instance_url.find('/') { @@ -172,7 +186,8 @@ pub fn login(instance_url: &str) -> Result<(), ClientError> { &user_token.access_token)?; println!(""); - println!("Successfully logged in as {}@{}", account.id, instance.domain); + println!("Successfully logged in as {}@{}", account.username, instance.domain); + println!("Now run 'mastodonochrome' without arguments to read and post!"); // Save everything! let auth = AuthConfig { @@ -184,7 +199,8 @@ pub fn login(instance_url: &str) -> Result<(), ClientError> { client_secret: app.client_secret.unwrap(), user_token: user_token.access_token, }; - println!("JSON: {}", serde_json::to_string_pretty(&auth).unwrap()); + + cfgloc.create_file("auth", &serde_json::to_string_pretty(&auth).unwrap())?; Ok(()) } diff --git a/src/main.rs b/src/main.rs index b07d56c..2545bb7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,7 +30,7 @@ fn main_inner() -> Result<(), TopLevelError> { }; match cli.login { None => Tui::run(&cfgloc, cli.readonly)?, - Some(ref server) => login(server)?, + Some(ref server) => login(&cfgloc, server)?, } Ok(()) } -- 2.30.2