start 2nd (cleaner) imap implementation

This commit is contained in:
Michael Zhang 2021-02-16 06:34:48 -06:00
parent e089d76fc8
commit 9116898a21
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
10 changed files with 118 additions and 62 deletions

View file

@ -26,7 +26,7 @@ jobs:
- name: build api docs
run: |
cargo doc --workspace --no-deps
cargo doc --workspace --no-deps --document-private-items
cp -r target/doc public/api
- name: deploy

1
.gitignore vendored
View file

@ -2,3 +2,4 @@
/.env
/output.log
/config.toml
/public

11
Cargo.lock generated
View file

@ -232,15 +232,6 @@ dependencies = [
"ascii_utils",
]
[[package]]
name = "fern"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9a4820f0ccc8a7afd67c39a0f1a0f4b07ca1725164271a64939d7aeb9af065"
dependencies = [
"log",
]
[[package]]
name = "foreign-types"
version = "0.3.2"
@ -654,11 +645,9 @@ dependencies = [
"cfg-if",
"chrono",
"crossterm",
"fern",
"futures",
"inotify",
"lettre",
"log",
"panorama-imap",
"pin-project",
"rustls-connector",

View file

@ -12,16 +12,17 @@ license = "GPL-3.0-or-later"
members = ["imap"]
[dependencies]
# log = "0.4.14"
# fern = "0.6.0"
anyhow = "1.0.38"
async-trait = "0.1.42"
cfg-if = "1.0.0"
chrono = "0.4.19"
crossterm = "0.19.0"
fern = "0.6.0"
futures = "0.3.12"
panorama-imap = { path = "imap", version = "0" }
inotify = { version = "0.9.2", features = ["stream"] }
lettre = "0.9.5"
log = "0.4.14"
panorama-imap = { path = "imap", version = "0" }
pin-project = "1.0.4"
rustls-connector = "0.13.1"
serde = { version = "1.0.123", features = ["derive"] }
@ -31,12 +32,11 @@ tokio-rustls = "0.22.0"
tokio-stream = { version = "0.1.3", features = ["sync"] }
tokio-util = { version = "0.6.3", features = ["full"] }
toml = "0.5.8"
tracing = "0.1.23"
tracing-appender = "0.1.2"
tracing-subscriber = "0.2.15"
webpki-roots = "0.21.0"
xdg = "2.2.0"
tracing = "0.1.23"
tracing-subscriber = "0.2.15"
inotify = { version = "0.9.2", features = ["stream"] }
tracing-appender = "0.1.2"
[features]
clippy = []

View file

@ -1,5 +1,8 @@
doc:
cargo doc --document-private-items
doc-open:
cargo doc --document-private-items --open
watch:
cargo watch -x 'clippy --all --all-features'

View file

@ -2,14 +2,15 @@
//!
//! One of the primary goals of panorama is to be able to always hot-reload configuration files.
use std::fs::File;
use std::fs::{self, File};
use std::io::Read;
use std::path::{Path, PathBuf};
use anyhow::Result;
use anyhow::{Context, Result};
use futures::{future::TryFutureExt, stream::StreamExt};
use inotify::{Inotify, WatchMask};
use tokio::{sync::watch, task::JoinHandle};
use xdg::BaseDirectories;
use crate::report_err;
@ -81,19 +82,39 @@ async fn read_config(path: impl AsRef<Path>) -> Result<Config> {
async fn start_inotify_stream(
mut inotify: Inotify,
config_home: impl AsRef<Path>,
config_tx: watch::Sender<Config>,
) -> Result<()> {
let mut buffer = vec![0; 1024];
let mut event_stream = inotify.event_stream(&mut buffer)?;
let config_home = config_home.as_ref().to_path_buf();
let config_path = config_home.join("panorama.toml");
while let Some(v) = event_stream.next().await {
let event = v?;
debug!("event: {:?}", event);
let event = v.context("event")?;
if let Some(name) = event.name {
let path = PathBuf::from(name);
let config = read_config(path).await?;
let path_c = config_home
.clone()
.join(path.clone())
.canonicalize()
.context("osu")?;
if !path_c.exists() {
debug!("path {:?} doesn't exist", path_c);
continue;
}
// TODO: any better way to do this?
let config_path_c = config_path.canonicalize().context("cfg_path")?;
if config_path_c != path_c {
debug!("did not match {:?} {:?}", config_path_c, path_c);
continue;
}
debug!("reading config from {:?}", path_c);
let config = read_config(path_c).await.context("read")?;
debug!("sending config {:?}", config);
config_tx.send(config)?;
}
}
@ -105,9 +126,18 @@ async fn start_inotify_stream(
/// which is a cloneable receiver of config update events.
pub fn spawn_config_watcher_system() -> Result<(JoinHandle<()>, ConfigWatcher)> {
let mut inotify = Inotify::init()?;
inotify.add_watch(".", WatchMask::all())?;
let xdg = BaseDirectories::new()?;
let config_home = xdg.get_config_home().join("panorama");
if !config_home.exists() {
fs::create_dir_all(&config_home)?;
}
inotify.add_watch(&config_home, WatchMask::all())?;
debug!("watching {:?}", config_home);
let (config_tx, config_update) = watch::channel(Config::default());
let handle = tokio::spawn(start_inotify_stream(inotify, config_tx).unwrap_or_else(report_err));
let handle = tokio::spawn(
start_inotify_stream(inotify, config_home, config_tx).unwrap_or_else(report_err),
);
Ok((handle, config_update))
}

View file

@ -8,8 +8,6 @@ extern crate anyhow;
#[macro_use]
extern crate crossterm;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde;
#[macro_use]
extern crate tracing;
@ -23,5 +21,5 @@ pub type ExitSender = tokio::sync::mpsc::Sender<()>;
/// Consumes any error and dumps it to the logger.
pub fn report_err(err: anyhow::Error) {
error!("error: {:?}", err);
error!("error: {}", err);
}

View file

@ -1,9 +1,56 @@
// let's try this again
use anyhow::Result;
use std::sync::Arc;
use crate::config::ImapConfig;
use anyhow::Result;
use futures::stream::Stream;
use tokio::{
io::{AsyncRead, AsyncWrite},
net::TcpStream,
};
use tokio_rustls::{rustls::ClientConfig, webpki::DNSNameRef, TlsConnector};
use crate::config::{ImapConfig, TlsMethod};
pub async fn open_imap_connection(config: ImapConfig) -> Result<()> {
let server = config.server.as_str();
let port = config.port;
let stream = TcpStream::connect((server, port)).await?;
match config.tls {
TlsMethod::Off => begin_authentication(config, stream).await,
TlsMethod::On => {
let stream = perform_tls_negotiation(server.to_owned(), stream).await?;
begin_authentication(config, stream).await
}
TlsMethod::Starttls => {
todo!()
}
}
}
/// Performs TLS negotiation, using the webpki_roots and verifying the server name
async fn perform_tls_negotiation(
server_name: impl AsRef<str>,
stream: impl AsyncRead + AsyncWrite + Unpin,
) -> Result<impl AsyncRead + AsyncWrite> {
let server_name = server_name.as_ref();
let mut tls_config = ClientConfig::new();
tls_config
.root_store
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
let tls_config = TlsConnector::from(Arc::new(tls_config));
let dnsname = DNSNameRef::try_from_ascii_str(server_name).unwrap();
let stream = tls_config.connect(dnsname, stream).await?;
Ok(stream)
}
async fn begin_authentication(
config: ImapConfig,
stream: impl AsyncRead + AsyncWrite,
) -> Result<()> {
Ok(())
}

View file

@ -1,6 +1,7 @@
#[macro_use]
extern crate log;
extern crate tracing;
use std::fs::{File, OpenOptions};
use std::path::PathBuf;
use anyhow::Result;
@ -13,10 +14,6 @@ use xdg::BaseDirectories;
#[derive(Debug, StructOpt)]
#[structopt(author, about)]
struct Opt {
/// Config file
#[structopt(long = "config-file", short = "c")]
config_path: Option<PathBuf>,
/// The path to the log file. By default, does not log.
#[structopt(long = "log-file")]
log_file: Option<PathBuf>,
@ -28,7 +25,21 @@ async fn main() -> Result<()> {
let opt = Opt::from_args();
// print logs to file as directed by command line options
setup_logger(&opt)?;
use tracing_subscriber::filter::LevelFilter;
let file = tracing_appender::rolling::daily("public", "lol");
let (non_blocking, _guard) = tracing_appender::non_blocking(file);
tracing_subscriber::fmt()
.with_max_level(LevelFilter::TRACE)
.with_writer(non_blocking)
.with_thread_ids(true)
.init();
debug!("shiet");
// TODO: debug
let x = span!(tracing::Level::WARN, "ouais");
let _y = x.enter();
let _xdg = BaseDirectories::new()?;
let (_config_thread, config_update) = spawn_config_watcher_system()?;
@ -59,24 +70,6 @@ async fn main() -> Result<()> {
}
fn setup_logger(opt: &Opt) -> Result<()> {
let mut fern = fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"{}[{}][{}] {}",
chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
record.target(),
record.level(),
message
))
})
.level(log::LevelFilter::Debug);
if let Some(path) = &opt.log_file {
fern = fern.chain(fern::log_file(path)?);
}
// fern.apply()?;
tracing_subscriber::fmt::init();
debug!("logging set up.");
Ok(())
}

View file

@ -29,11 +29,6 @@ pub struct Rect(u16, u16, u16, u16);
/// UI entrypoint.
#[instrument]
pub async fn run_ui(mut w: impl Write + Debug, exit: ExitSender) -> Result<()> {
loop {
tokio::time::sleep(Duration::from_secs(5)).await;
debug!("1");
}
execute!(w, cursor::Hide, terminal::EnterAlternateScreen)?;
terminal::enable_raw_mode()?;