From 7549d361927e74a87207fd2568713f7e4c1a12b0 Mon Sep 17 00:00:00 2001 From: Ade Attwood Date: Fri, 21 Jun 2024 18:56:09 +0100 Subject: [PATCH] feat: support port config and cert file Summary: This will now support a port config in the IMAPStore of the config. This is still optional and will fall back to 993 if you don't have it. It will also ignore tls verification if we have a CertificateFile config option. This will take a bit of refactoring to implement our own tls context so, for now we will disable it. As we have put in the comment, if the user really wants this then they can configure this in there root store. Test Plan: N / A this tool is not tested yet --- src/config.rs | 21 +++++++++++++++++++++ src/main.rs | 44 ++++++++++++++++++++++++++------------------ 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/config.rs b/src/config.rs index 6a24f35..e34d3e5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -5,12 +5,21 @@ use std::process::Command; pub struct ImapStoreConfig { pub name: String, pub host: String, + pub port: Option, pub user: String, pub pass: Option, pub pass_command: Option, + pub cert_file: Option, } impl ImapStoreConfig { + pub fn port(&self) -> u16 { + if let Some(port) = self.port { + return port; + } + + 993 + } pub fn password(&self) -> String { if let Some(pass) = &self.pass { return pass.to_string(); @@ -56,9 +65,11 @@ impl Config { enum ConfigLine { ImapStore(String), Host(String), + Port(u16), User(String), Pass(String), PassCommand(String), + CertFile(String), Channel(String), Near(String), @@ -76,9 +87,11 @@ impl TryFrom<&str> for ConfigLine { match key { "IMAPStore" => Ok(ConfigLine::ImapStore(value)), "Host" => Ok(ConfigLine::Host(value)), + "Port" => Ok(ConfigLine::Port(value.parse().unwrap())), "User" => Ok(ConfigLine::User(value)), "Pass" => Ok(ConfigLine::Pass(remove_quotes(value))), "PassCmd" => Ok(ConfigLine::PassCommand(remove_quotes(value))), + "CertificateFile" => Ok(ConfigLine::CertFile(value)), "Channel" => Ok(ConfigLine::Channel(value)), "Near" => Ok(ConfigLine::Near(value)), @@ -136,6 +149,10 @@ pub fn from_file(config_path: &str) -> Config { let store = config.imap_stores.last_mut().unwrap(); store.host = host; } + ConfigLine::Port(port) => { + let store = config.imap_stores.last_mut().unwrap(); + store.port = Some(port); + } ConfigLine::User(user) => { let store = config.imap_stores.last_mut().unwrap(); store.user = user; @@ -148,6 +165,10 @@ pub fn from_file(config_path: &str) -> Config { let store = config.imap_stores.last_mut().unwrap(); store.pass_command = Some(command); } + ConfigLine::CertFile(cert_file) => { + let store = config.imap_stores.last_mut().unwrap(); + store.cert_file = Some(cert_file); + } ConfigLine::Channel(name) => { let mut channel_config = ChannelConfig::default(); diff --git a/src/main.rs b/src/main.rs index b48c77f..fc52d0f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,24 +3,36 @@ mod config; use std::process::Command; use std::thread; +use config::ImapStoreConfig; use imap::{ImapConnection, Session}; -fn connect(host: String, user: String, pass: String) -> Option>> { - let client = imap::ClientBuilder::new(host.clone(), 993) +fn connect(config: &ImapStoreConfig) -> Option>> { + let mut client_builder = imap::ClientBuilder::new(config.host.clone(), config.port()) .mode(imap::ConnectionMode::AutoTls) - .tls_kind(imap::TlsKind::Rust) + .tls_kind(imap::TlsKind::Rust); + + // For now disable skipping tls verification if we have a cert file. I will need to refactor + // this to setup the TPC connection manually to support this. As we have passed in a + // certificate we are assuming its all good. If we really want this to be secure for now we + // need to setup the certificates in the system root store. + if config.cert_file.is_some() { + log::warn!("Skipping tls verification for {}", &config.host); + client_builder = client_builder.danger_skip_tls_verify(true); + } + + let client = client_builder .connect() .expect("Could not connect to the server"); let mut session = client - .login(user, pass) + .login(&config.user, config.password()) .expect("Unable to login please ensure yor credentials are correct"); let capabilities = session.capabilities().expect("Unable to get capabilities"); if !capabilities.has_str("IDLE") { log::info!( "Skipping connection, {} dose not support idle connections", - &host + &config.host ); return None; @@ -55,12 +67,13 @@ fn main() { let channel_name = channel.name.clone(); watchers.push(thread::spawn(move || { - let mut session = connect( - imap_store.host.clone(), - imap_store.user.clone(), - imap_store.password().clone(), - ) - .unwrap(); + let mut session = match connect(&imap_store) { + Some(session) => session, + None => { + log::error!("Unable to connect to channel {}", channel_name); + return; + } + }; log::info!("Watching for messages on channel {}", channel_name); @@ -71,14 +84,9 @@ fn main() { if result.is_err() { log::error!("Error while idling: {:?}", result); - thread::sleep(std::time::Duration::from_secs(10)); - session = connect( - imap_store.host.clone(), - imap_store.user.clone(), - imap_store.password().clone(), - ) - .unwrap(); + thread::sleep(std::time::Duration::from_secs(10)); + session = connect(&imap_store).unwrap(); continue; }