restructure configs
This commit is contained in:
parent
5ad3a844e1
commit
4ed955fb93
4 changed files with 62 additions and 26 deletions
11
notes.md
Normal file
11
notes.md
Normal file
|
@ -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?)
|
||||||
|
|
|
@ -14,11 +14,30 @@ use tokio::{sync::watch, task::JoinHandle};
|
||||||
use xdg::BaseDirectories;
|
use xdg::BaseDirectories;
|
||||||
|
|
||||||
/// Alias for a MailConfig receiver.
|
/// Alias for a MailConfig receiver.
|
||||||
pub type ConfigWatcher = watch::Receiver<Option<MailConfig>>;
|
pub type ConfigWatcher = watch::Receiver<Option<Config>>;
|
||||||
|
|
||||||
/// Configuration
|
/// Configuration
|
||||||
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
|
#[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<MailAccountConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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)
|
/// Host of the IMAP server (needs to be hostname for TLS)
|
||||||
pub server: String,
|
pub server: String,
|
||||||
|
|
||||||
|
@ -48,7 +67,7 @@ fn start_watcher() -> Result<(RecommendedWatcher, Receiver<DebouncedEvent>)> {
|
||||||
Ok((watcher, rx))
|
Ok((watcher, rx))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read_config(path: impl AsRef<Path>) -> Result<MailConfig> {
|
async fn read_config(path: impl AsRef<Path>) -> Result<Config> {
|
||||||
let mut file = File::open(path.as_ref())?;
|
let mut file = File::open(path.as_ref())?;
|
||||||
let mut contents = Vec::new();
|
let mut contents = Vec::new();
|
||||||
file.read_to_end(&mut contents)?;
|
file.read_to_end(&mut contents)?;
|
||||||
|
@ -63,20 +82,29 @@ async fn read_config(path: impl AsRef<Path>) -> Result<MailConfig> {
|
||||||
/// This exists so all errors are able to be caught in one go.
|
/// This exists so all errors are able to be caught in one go.
|
||||||
async fn watcher_loop(
|
async fn watcher_loop(
|
||||||
fs_events: Receiver<DebouncedEvent>,
|
fs_events: Receiver<DebouncedEvent>,
|
||||||
config_tx: watch::Sender<Option<MailConfig>>,
|
config_tx: watch::Sender<Option<Config>>,
|
||||||
) -> Result<()> {
|
) -> 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)
|
// (so the config isn't blank until the user touches the config file)
|
||||||
let xdg = BaseDirectories::new()?;
|
let xdg = BaseDirectories::new()?;
|
||||||
if let Some(config_path) = xdg.find_config_file("panorama/panorama.toml") {
|
if let Some(config_path) = xdg.find_config_file("panorama/panorama.toml") {
|
||||||
debug!("found config at {:?}", config_path);
|
debug!("found config at {:?}", config_path);
|
||||||
let config = read_config(config_path).await?;
|
let config = read_config(config_path).await?;
|
||||||
|
debug!("read config: {:?}, sending to output", config);
|
||||||
config_tx.send(Some(config))?;
|
config_tx.send(Some(config))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// start listening for events from the notify::Watcher
|
||||||
for event in fs_events {
|
for event in fs_events {
|
||||||
debug!("new event: {:?}", event);
|
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(())
|
Ok(())
|
||||||
|
@ -93,7 +121,7 @@ pub fn spawn_config_watcher() -> Result<(JoinHandle<()>, ConfigWatcher)> {
|
||||||
match watcher_loop(config_rx, config_tx).await {
|
match watcher_loop(config_rx, config_tx).await {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
debug!("config watcher died: {:?}", err);
|
debug!("config watcher bugged: {:?}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
17
src/mail.rs
17
src/mail.rs
|
@ -25,7 +25,7 @@ use tokio_rustls::{rustls::ClientConfig, webpki::DNSNameRef, TlsConnector};
|
||||||
use tokio_stream::wrappers::WatchStream;
|
use tokio_stream::wrappers::WatchStream;
|
||||||
use tokio_util::codec::{Decoder, LinesCodec, LinesCodecError};
|
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)
|
/// Command sent to the mail thread by something else (i.e. UI)
|
||||||
pub enum MailCommand {
|
pub enum MailCommand {
|
||||||
|
@ -45,7 +45,10 @@ pub async fn run_mail(
|
||||||
|
|
||||||
let mut config_watcher = WatchStream::new(config_watcher);
|
let mut config_watcher = WatchStream::new(config_watcher);
|
||||||
loop {
|
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,
|
Some(Some(v)) => v,
|
||||||
_ => break,
|
_ => break,
|
||||||
};
|
};
|
||||||
|
@ -57,14 +60,18 @@ pub async fn run_mail(
|
||||||
curr_conn.abort();
|
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);
|
curr_conn = Some(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn open_imap_connection(config: MailConfig) -> Result<()> {
|
async fn open_imap_connection(config: ImapConfig) -> Result<()> {
|
||||||
debug!(
|
debug!(
|
||||||
"Opening imap connection to {}:{}",
|
"Opening imap connection to {}:{}",
|
||||||
config.server, config.port
|
config.server, config.port
|
||||||
|
@ -115,7 +122,7 @@ enum LoopExit<S, S2> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn listen_loop<S, S2>(
|
async fn listen_loop<S, S2>(
|
||||||
config: MailConfig,
|
config: ImapConfig,
|
||||||
st: &mut State,
|
st: &mut State,
|
||||||
sink: S2,
|
sink: S2,
|
||||||
mut stream: S,
|
mut stream: S,
|
||||||
|
|
18
src/main.rs
18
src/main.rs
|
@ -8,7 +8,7 @@ use std::path::PathBuf;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use futures::future::TryFutureExt;
|
use futures::future::TryFutureExt;
|
||||||
use panorama::{
|
use panorama::{
|
||||||
config::{spawn_config_watcher, MailConfig},
|
config::{spawn_config_watcher, Config},
|
||||||
mail, ui,
|
mail, ui,
|
||||||
};
|
};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
@ -38,26 +38,16 @@ async fn main() -> Result<()> {
|
||||||
let xdg = BaseDirectories::new()?;
|
let xdg = BaseDirectories::new()?;
|
||||||
let (config_thread, config_update) = spawn_config_watcher()?;
|
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
|
// used to notify the runtime that the process should exit
|
||||||
let (exit_tx, mut exit_rx) = mpsc::channel::<()>(1);
|
let (exit_tx, mut exit_rx) = mpsc::channel::<()>(1);
|
||||||
|
|
||||||
// used to send commands to the mail service
|
// used to send commands to the mail service
|
||||||
let (mail_tx, mail_rx) = mpsc::unbounded_channel();
|
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 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;
|
exit_rx.recv().await;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue