diff --git a/Cargo.lock b/Cargo.lock index 8be8d7d..6f72b74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,6 +43,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + [[package]] name = "clap" version = "3.0.0-beta.2" @@ -301,6 +314,25 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -324,15 +356,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85" [[package]] -name = "panorama-core" +name = "panorama-daemon" version = "0.1.0" dependencies = [ "anyhow", "clap", "futures", "inotify", + "log", "serde", + "stderrlog", "tokio", + "toml", + "xdg", ] [[package]] @@ -482,6 +518,19 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +[[package]] +name = "stderrlog" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a53e2eff3e94a019afa6265e8ee04cb05b9d33fe9f5078b14e4e391d155a38" +dependencies = [ + "atty", + "chrono", + "log", + "termcolor", + "thread_local", +] + [[package]] name = "strsim" version = "0.10.0" @@ -517,6 +566,26 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + [[package]] name = "tokio" version = "1.9.0" @@ -548,6 +617,15 @@ dependencies = [ "syn", ] +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + [[package]] name = "unicode-segmentation" version = "1.8.0" @@ -578,6 +656,12 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "winapi" version = "0.3.9" @@ -608,3 +692,9 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "xdg" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" diff --git a/Cargo.toml b/Cargo.toml index 3f3f176..7f470b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,4 @@ [workspace] members = [ - "core", + "daemon", ] diff --git a/core/src/imap.rs b/core/src/imap.rs deleted file mode 100644 index e69de29..0000000 diff --git a/core/src/main.rs b/core/src/main.rs deleted file mode 100644 index 89658c9..0000000 --- a/core/src/main.rs +++ /dev/null @@ -1,37 +0,0 @@ -#[macro_use] -extern crate serde; -extern crate futures; - -mod config; -mod imap; - -use std::path::PathBuf; - -use clap::Clap; -use anyhow::Result; - -use crate::config::Config; - -/// Panorama core -#[derive(Debug, Clap)] -struct Options { - /// Config file path (defaults to XDG) - #[clap(long = "config", short = 'c')] - config_file: Option, -} - -async fn run_with_config(config: Config) -> Result<()> { - Ok(()) -} - -#[tokio::main] -async fn main() -> Result<()> { - let opts = Options::parse(); - println!("{:?}", opts); - - let (tx, mut rx) = watch::channel(()); - - loop { - - } -} \ No newline at end of file diff --git a/core/Cargo.toml b/daemon/Cargo.toml similarity index 75% rename from core/Cargo.toml rename to daemon/Cargo.toml index f25273d..32ad41f 100644 --- a/core/Cargo.toml +++ b/daemon/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "panorama-core" +name = "panorama-daemon" version = "0.1.0" edition = "2018" @@ -10,3 +10,7 @@ tokio = { version = "1.9.0", features = ["full"] } clap = "3.0.0-beta.2" futures = "0.3.16" inotify = { version = "0.9.3", features = ["stream"] } +xdg = "2.2.0" +log = "0.4.14" +toml = "0.5.8" +stderrlog = "0.5.1" diff --git a/core/src/config/mod.rs b/daemon/src/config/mod.rs similarity index 82% rename from core/src/config/mod.rs rename to daemon/src/config/mod.rs index 0f520de..e63c3c2 100644 --- a/core/src/config/mod.rs +++ b/daemon/src/config/mod.rs @@ -1,33 +1,37 @@ mod watcher; -use std::path::{PathBuf, Path}; use std::collections::HashMap; use std::fs::File; use std::io::Read; +use std::path::{Path, PathBuf}; use anyhow::Result; +pub use self::watcher::spawn_config_watcher_system; + /// Configuration #[derive(Default, Serialize, Deserialize, Clone, Debug)] pub struct Config { /// Version of the config to use /// (potentially for migration later?) pub version: String, + /// Directory to store panorama-related data in pub data_dir: PathBuf, + /// Mail accounts #[serde(rename = "mail")] pub mail_accounts: HashMap, } impl Config { - pub async fn from_file(path: impl AsRef) -> Result { - let mut file = File::open(path.as_ref())?; - let mut contents = Vec::new(); - file.read_to_end(&mut contents)?; - let config = toml::from_slice(&contents)?; - Ok(config) - } + pub async fn from_file(path: impl AsRef) -> Result { + let mut file = File::open(path.as_ref())?; + let mut contents = Vec::new(); + file.read_to_end(&mut contents)?; + let config = toml::from_slice(&contents)?; + Ok(config) + } } /// Configuration for a single mail account @@ -42,10 +46,13 @@ pub struct MailAccountConfig { pub struct ImapConfig { /// Host of the IMAP server (needs to be hostname for TLS) pub server: String, + /// Port of the IMAP server pub port: u16, + /// TLS pub tls: TlsMethod, + /// Auth #[serde(flatten)] pub auth: ImapAuth, @@ -57,7 +64,6 @@ pub struct ImapConfig { pub enum ImapAuth { /// Use plain username/password authentication #[serde(rename = "plain")] - #[allow(missing_docs)] Plain { username: String, password: String }, } @@ -67,10 +73,12 @@ pub enum TlsMethod { /// Perform TLS handshake immediately upon connection #[serde(rename = "on")] On, + /// Perform TLS handshake after issuing the STARTTLS command #[serde(rename = "starttls")] Starttls, + /// Don't perform TLS handshake at all (unsecured) #[serde(rename = "off")] Off, -} \ No newline at end of file +} diff --git a/core/src/config/watcher.rs b/daemon/src/config/watcher.rs similarity index 87% rename from core/src/config/watcher.rs rename to daemon/src/config/watcher.rs index a3967c5..cd139a1 100644 --- a/core/src/config/watcher.rs +++ b/daemon/src/config/watcher.rs @@ -1,9 +1,11 @@ +use std::fs; use std::path::{Path, PathBuf}; -use anyhow::{Result, Context}; +use anyhow::{Context, Result}; use futures::{future::TryFutureExt, stream::StreamExt}; -use inotify::Inotify; -use tokio::{task::JoinHandle, sync::watch}; +use inotify::{Inotify, WatchMask}; +use tokio::{sync::watch, task::JoinHandle}; +use xdg::BaseDirectories; use super::Config; @@ -18,19 +20,15 @@ pub fn spawn_config_watcher_system() -> Result<(JoinHandle<()>, ConfigWatcher)> if !config_home.exists() { fs::create_dir_all(&config_home)?; } + inotify .add_watch(&config_home, WatchMask::CLOSE_WRITE) .context("adding watch for config home")?; - // let config_file_path = config_home.join("panorama.toml"); - // if config_file_path.exists() { - // inotify - // .add_watch(config_file_path, WatchMask::ALL_EVENTS) - // .context("adding watch for config file")?; - // } + debug!("watching {:?}", config_home); let (config_tx, config_update) = watch::channel(Config::default()); let handle = tokio::spawn( - start_inotify_stream(inotify, config_home, config_tx).unwrap_or_else(report_err), + start_inotify_stream(inotify, config_home, config_tx).unwrap_or_else(|_err| todo!()), ); Ok((handle, config_update)) } diff --git a/daemon/src/imap.rs b/daemon/src/imap.rs new file mode 100644 index 0000000..0d284f8 --- /dev/null +++ b/daemon/src/imap.rs @@ -0,0 +1 @@ +pub struct ImapClient {} diff --git a/daemon/src/main.rs b/daemon/src/main.rs new file mode 100644 index 0000000..ab4043f --- /dev/null +++ b/daemon/src/main.rs @@ -0,0 +1,66 @@ +#[macro_use] +extern crate serde; +#[macro_use] +extern crate log; +#[macro_use] +extern crate futures; + +mod config; +mod imap; + +use anyhow::Result; +use clap::Clap; +use futures::future::FutureExt; +use tokio::sync::oneshot; + +use crate::config::Config; + +type ExitListener = oneshot::Receiver<()>; + +/// The panorama daemon runs in the background and communicates with other panorama components over Unix sockets. +#[derive(Debug, Clap)] +struct Options { + // /// Config file path (defaults to XDG) + // #[clap(long = "config", short = 'c')] + // config_file: Option, + /// Verbose mode (-v, -vv, -vvv, etc) + #[clap(short = 'v', long = "verbose", parse(from_occurrences))] + verbose: usize, +} + +#[tokio::main] +async fn main() -> Result<()> { + let opt = Options::parse(); + println!("{:?}", opt); + + stderrlog::new() + .module(module_path!()) + .verbosity(opt.verbose) + .init() + .unwrap(); + + let (_, mut config_watcher) = config::spawn_config_watcher_system()?; + + loop { + let (exit_tx, exit_rx) = oneshot::channel(); + let new_config = config_watcher.borrow().clone(); + tokio::spawn(run_with_config(new_config, exit_rx)); + + // wait till the config has changed, then tell the current thread to stop + config_watcher.changed().await?; + let _ = exit_tx.send(()); + } +} + +async fn run_with_config(config: Config, exit: ExitListener) -> Result<()> { + println!("run with config: {:?}", config); + + let mut exit = exit.fuse(); + loop { + select! { + _ = exit => break, + } + } + + Ok(()) +}