From 4ed955fb9364c4a9a1bfaf2570b9989e8bfd4498 Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Sun, 14 Feb 2021 17:49:54 -0600 Subject: [PATCH] restructure configs --- notes.md | 11 +++++++++++ src/config.rs | 42 +++++++++++++++++++++++++++++++++++------- src/mail.rs | 17 ++++++++++++----- src/main.rs | 18 ++++-------------- 4 files changed, 62 insertions(+), 26 deletions(-) create mode 100644 notes.md diff --git a/notes.md b/notes.md new file mode 100644 index 0000000..0629c09 --- /dev/null +++ b/notes.md @@ -0,0 +1,11 @@ +imap routine +--- + +- basic tcp connection is opened +- if tls is "on", then immediately perform tls handshake with the server +- if tls is "starttls", check starttls capability + - if the server doesn't have starttls capability, die and report to the user + - if the server _does_ have starttls, exit the read loop and perform tls handshake over current connection +- at this point, tls should be figured out, so moving on to auth +- check if the auth type that the user specified is in the list of auth types (prob support plain and oauth2?) + diff --git a/src/config.rs b/src/config.rs index e75a978..5ecc5a5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,11 +14,30 @@ use tokio::{sync::watch, task::JoinHandle}; use xdg::BaseDirectories; /// Alias for a MailConfig receiver. -pub type ConfigWatcher = watch::Receiver>; +pub type ConfigWatcher = watch::Receiver>; /// Configuration #[derive(Default, Serialize, Deserialize, Clone, Debug)] -pub struct MailConfig { +pub struct Config { + /// Version of the config to use + /// (potentially for migration later?) + pub version: String, + + /// Mail accounts + #[serde(rename = "mail")] + pub mail_accounts: Vec, +} + +/// Configuration for a single mail account +#[derive(Default, Serialize, Deserialize, Clone, Debug)] +pub struct MailAccountConfig { + /// Imap + pub imap: ImapConfig, +} + +/// Configuring an IMAP server +#[derive(Default, Serialize, Deserialize, Clone, Debug)] +pub struct ImapConfig { /// Host of the IMAP server (needs to be hostname for TLS) pub server: String, @@ -48,7 +67,7 @@ fn start_watcher() -> Result<(RecommendedWatcher, Receiver)> { Ok((watcher, rx)) } -async fn read_config(path: impl AsRef) -> Result { +async fn read_config(path: impl AsRef) -> Result { let mut file = File::open(path.as_ref())?; let mut contents = Vec::new(); file.read_to_end(&mut contents)?; @@ -63,20 +82,29 @@ async fn read_config(path: impl AsRef) -> Result { /// This exists so all errors are able to be caught in one go. async fn watcher_loop( fs_events: Receiver, - config_tx: watch::Sender>, + config_tx: watch::Sender>, ) -> Result<()> { - // first try opening the config file directly on load + // first try opening the config file directly when the program is opened // (so the config isn't blank until the user touches the config file) let xdg = BaseDirectories::new()?; if let Some(config_path) = xdg.find_config_file("panorama/panorama.toml") { debug!("found config at {:?}", config_path); let config = read_config(config_path).await?; + debug!("read config: {:?}, sending to output", config); config_tx.send(Some(config))?; } + // start listening for events from the notify::Watcher for event in fs_events { debug!("new event: {:?}", event); - // config_tx.send(Some(config))?; + use notify::DebouncedEvent::*; + match event { + NoticeWrite(path) | Write(path) => { + let config = read_config(path).await?; + config_tx.send(Some(config))?; + } + _ => {} + } } Ok(()) @@ -93,7 +121,7 @@ pub fn spawn_config_watcher() -> Result<(JoinHandle<()>, ConfigWatcher)> { match watcher_loop(config_rx, config_tx).await { Ok(_) => {} Err(err) => { - debug!("config watcher died: {:?}", err); + debug!("config watcher bugged: {:?}", err); } } }); diff --git a/src/mail.rs b/src/mail.rs index ca80f2d..f717b12 100644 --- a/src/mail.rs +++ b/src/mail.rs @@ -25,7 +25,7 @@ use tokio_rustls::{rustls::ClientConfig, webpki::DNSNameRef, TlsConnector}; use tokio_stream::wrappers::WatchStream; use tokio_util::codec::{Decoder, LinesCodec, LinesCodecError}; -use crate::config::{ConfigWatcher, MailConfig}; +use crate::config::{Config, ConfigWatcher, ImapConfig}; /// Command sent to the mail thread by something else (i.e. UI) pub enum MailCommand { @@ -45,7 +45,10 @@ pub async fn run_mail( let mut config_watcher = WatchStream::new(config_watcher); loop { - let config: MailConfig = match config_watcher.next().await { + debug!("listening for configs"); + let a = config_watcher.next().await; + debug!("got config {:?}", a); + let config: Config = match a { Some(Some(v)) => v, _ => break, }; @@ -57,14 +60,18 @@ pub async fn run_mail( curr_conn.abort(); } - let handle = tokio::spawn(open_imap_connection(config)); + let handle = tokio::spawn(async { + for acct in config.mail_accounts.into_iter() { + open_imap_connection(acct.imap); + } + }); curr_conn = Some(handle); } Ok(()) } -async fn open_imap_connection(config: MailConfig) -> Result<()> { +async fn open_imap_connection(config: ImapConfig) -> Result<()> { debug!( "Opening imap connection to {}:{}", config.server, config.port @@ -115,7 +122,7 @@ enum LoopExit { } async fn listen_loop( - config: MailConfig, + config: ImapConfig, st: &mut State, sink: S2, mut stream: S, diff --git a/src/main.rs b/src/main.rs index 7999dd7..40a1a39 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ use std::path::PathBuf; use anyhow::Result; use futures::future::TryFutureExt; use panorama::{ - config::{spawn_config_watcher, MailConfig}, + config::{spawn_config_watcher, Config}, mail, ui, }; use structopt::StructOpt; @@ -38,26 +38,16 @@ async fn main() -> Result<()> { let xdg = BaseDirectories::new()?; let (config_thread, config_update) = spawn_config_watcher()?; - // let config: MailConfig = { - // let config_path = opt - // .config_path - // .clone() - // .unwrap_or_else(|| "config.toml".into()); - // let mut config_file = File::open(config_path)?; - // let mut contents = Vec::new(); - // config_file.read_to_end(&mut contents)?; - // toml::from_slice(&contents)? - // }; - // used to notify the runtime that the process should exit let (exit_tx, mut exit_rx) = mpsc::channel::<()>(1); // used to send commands to the mail service let (mail_tx, mail_rx) = mpsc::unbounded_channel(); - let mail_thread = tokio::spawn(mail::run_mail(config_update.clone(), mail_rx).unwrap_or_else(report_err)); + tokio::spawn(mail::run_mail(config_update.clone(), mail_rx).unwrap_or_else(report_err)); + let stdout = std::io::stdout(); - let ui_thread = tokio::spawn(ui::run_ui(stdout, exit_tx).unwrap_or_else(report_err)); + tokio::spawn(ui::run_ui(stdout, exit_tx).unwrap_or_else(report_err)); exit_rx.recv().await;