2021-07-30 03:47:53 +00:00
|
|
|
#[macro_use]
|
|
|
|
extern crate serde;
|
|
|
|
#[macro_use]
|
2021-08-09 11:50:19 +00:00
|
|
|
extern crate anyhow;
|
|
|
|
#[macro_use]
|
2021-07-30 03:47:53 +00:00
|
|
|
extern crate log;
|
|
|
|
#[macro_use]
|
|
|
|
extern crate futures;
|
|
|
|
|
|
|
|
mod config;
|
2021-08-06 02:26:39 +00:00
|
|
|
mod mail;
|
2021-07-30 03:47:53 +00:00
|
|
|
|
|
|
|
use anyhow::Result;
|
|
|
|
use clap::Clap;
|
2021-08-06 02:26:39 +00:00
|
|
|
use futures::future::{
|
|
|
|
select,
|
|
|
|
Either::{Left, Right},
|
|
|
|
FutureExt,
|
|
|
|
};
|
2021-08-09 11:50:19 +00:00
|
|
|
use panorama_imap::client::ConfigBuilder;
|
2021-07-30 03:47:53 +00:00
|
|
|
use tokio::sync::oneshot;
|
|
|
|
|
2021-08-06 02:26:39 +00:00
|
|
|
use crate::config::{Config, MailAccountConfig, TlsMethod};
|
2021-07-30 03:47:53 +00:00
|
|
|
|
|
|
|
type ExitListener = oneshot::Receiver<()>;
|
|
|
|
|
2021-08-09 11:50:19 +00:00
|
|
|
/// The panorama daemon runs in the background and communicates with other
|
|
|
|
/// panorama components over Unix sockets.
|
2021-07-30 03:47:53 +00:00
|
|
|
#[derive(Debug, Clap)]
|
|
|
|
struct Options {
|
|
|
|
// /// Config file path (defaults to XDG)
|
|
|
|
// #[clap(long = "config", short = 'c')]
|
|
|
|
// config_file: Option<PathBuf>,
|
|
|
|
/// 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();
|
|
|
|
|
|
|
|
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<()> {
|
2021-08-06 02:26:39 +00:00
|
|
|
debug!("new config");
|
2021-07-30 03:47:53 +00:00
|
|
|
|
2021-08-06 02:26:39 +00:00
|
|
|
let mut notify_mail_threads = Vec::new();
|
|
|
|
for (account_name, account) in config.mail_accounts {
|
|
|
|
let (exit_tx, exit_rx) = oneshot::channel();
|
|
|
|
tokio::spawn(run_single_mail_account(account_name, account, exit_rx));
|
|
|
|
notify_mail_threads.push(exit_tx);
|
|
|
|
}
|
|
|
|
|
|
|
|
exit.await?;
|
|
|
|
for exit_tx in notify_mail_threads {
|
|
|
|
let _ = exit_tx.send(());
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn run_single_mail_account(
|
|
|
|
account_name: String,
|
|
|
|
account: MailAccountConfig,
|
|
|
|
exit: ExitListener,
|
|
|
|
) -> Result<()> {
|
|
|
|
debug!("connecting to account {}", account_name);
|
|
|
|
|
2021-08-09 11:50:19 +00:00
|
|
|
// set up the connection
|
|
|
|
let mut builder = ConfigBuilder::default();
|
|
|
|
let imap_cookie = builder
|
|
|
|
.hostname(account.imap.server.clone())
|
|
|
|
.port(account.imap.port)
|
2021-08-09 11:50:32 +00:00
|
|
|
.tls(matches!(account.imap.tls, TlsMethod::On))
|
|
|
|
.open();
|
2021-08-06 02:26:39 +00:00
|
|
|
|
|
|
|
pin_mut!(imap_cookie);
|
|
|
|
pin_mut!(exit);
|
|
|
|
|
|
|
|
let (imap, exit) = match select(imap_cookie, exit).await {
|
|
|
|
Left(res) => res,
|
|
|
|
Right(_) => return Ok(()),
|
|
|
|
};
|
|
|
|
|
|
|
|
debug!("connected to {}", account.imap.server);
|
2021-08-09 11:50:19 +00:00
|
|
|
let _imap = imap?;
|
2021-07-30 03:47:53 +00:00
|
|
|
let mut exit = exit.fuse();
|
2021-08-06 02:26:39 +00:00
|
|
|
|
2021-07-30 03:47:53 +00:00
|
|
|
loop {
|
|
|
|
select! {
|
|
|
|
_ = exit => break,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-06 02:26:39 +00:00
|
|
|
debug!("disconnecting from account {}", account_name);
|
2021-07-30 03:47:53 +00:00
|
|
|
Ok(())
|
|
|
|
}
|