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
This commit is contained in:
Ade Attwood 2024-06-21 18:56:09 +01:00
parent 5126594966
commit 7549d36192
2 changed files with 47 additions and 18 deletions

View file

@ -5,12 +5,21 @@ use std::process::Command;
pub struct ImapStoreConfig { pub struct ImapStoreConfig {
pub name: String, pub name: String,
pub host: String, pub host: String,
pub port: Option<u16>,
pub user: String, pub user: String,
pub pass: Option<String>, pub pass: Option<String>,
pub pass_command: Option<String>, pub pass_command: Option<String>,
pub cert_file: Option<String>,
} }
impl ImapStoreConfig { impl ImapStoreConfig {
pub fn port(&self) -> u16 {
if let Some(port) = self.port {
return port;
}
993
}
pub fn password(&self) -> String { pub fn password(&self) -> String {
if let Some(pass) = &self.pass { if let Some(pass) = &self.pass {
return pass.to_string(); return pass.to_string();
@ -56,9 +65,11 @@ impl Config {
enum ConfigLine { enum ConfigLine {
ImapStore(String), ImapStore(String),
Host(String), Host(String),
Port(u16),
User(String), User(String),
Pass(String), Pass(String),
PassCommand(String), PassCommand(String),
CertFile(String),
Channel(String), Channel(String),
Near(String), Near(String),
@ -76,9 +87,11 @@ impl TryFrom<&str> for ConfigLine {
match key { match key {
"IMAPStore" => Ok(ConfigLine::ImapStore(value)), "IMAPStore" => Ok(ConfigLine::ImapStore(value)),
"Host" => Ok(ConfigLine::Host(value)), "Host" => Ok(ConfigLine::Host(value)),
"Port" => Ok(ConfigLine::Port(value.parse().unwrap())),
"User" => Ok(ConfigLine::User(value)), "User" => Ok(ConfigLine::User(value)),
"Pass" => Ok(ConfigLine::Pass(remove_quotes(value))), "Pass" => Ok(ConfigLine::Pass(remove_quotes(value))),
"PassCmd" => Ok(ConfigLine::PassCommand(remove_quotes(value))), "PassCmd" => Ok(ConfigLine::PassCommand(remove_quotes(value))),
"CertificateFile" => Ok(ConfigLine::CertFile(value)),
"Channel" => Ok(ConfigLine::Channel(value)), "Channel" => Ok(ConfigLine::Channel(value)),
"Near" => Ok(ConfigLine::Near(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(); let store = config.imap_stores.last_mut().unwrap();
store.host = host; store.host = host;
} }
ConfigLine::Port(port) => {
let store = config.imap_stores.last_mut().unwrap();
store.port = Some(port);
}
ConfigLine::User(user) => { ConfigLine::User(user) => {
let store = config.imap_stores.last_mut().unwrap(); let store = config.imap_stores.last_mut().unwrap();
store.user = user; store.user = user;
@ -148,6 +165,10 @@ pub fn from_file(config_path: &str) -> Config {
let store = config.imap_stores.last_mut().unwrap(); let store = config.imap_stores.last_mut().unwrap();
store.pass_command = Some(command); 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) => { ConfigLine::Channel(name) => {
let mut channel_config = ChannelConfig::default(); let mut channel_config = ChannelConfig::default();

View file

@ -3,24 +3,36 @@ mod config;
use std::process::Command; use std::process::Command;
use std::thread; use std::thread;
use config::ImapStoreConfig;
use imap::{ImapConnection, Session}; use imap::{ImapConnection, Session};
fn connect(host: String, user: String, pass: String) -> Option<Session<Box<dyn ImapConnection>>> { fn connect(config: &ImapStoreConfig) -> Option<Session<Box<dyn ImapConnection>>> {
let client = imap::ClientBuilder::new(host.clone(), 993) let mut client_builder = imap::ClientBuilder::new(config.host.clone(), config.port())
.mode(imap::ConnectionMode::AutoTls) .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() .connect()
.expect("Could not connect to the server"); .expect("Could not connect to the server");
let mut session = client let mut session = client
.login(user, pass) .login(&config.user, config.password())
.expect("Unable to login please ensure yor credentials are correct"); .expect("Unable to login please ensure yor credentials are correct");
let capabilities = session.capabilities().expect("Unable to get capabilities"); let capabilities = session.capabilities().expect("Unable to get capabilities");
if !capabilities.has_str("IDLE") { if !capabilities.has_str("IDLE") {
log::info!( log::info!(
"Skipping connection, {} dose not support idle connections", "Skipping connection, {} dose not support idle connections",
&host &config.host
); );
return None; return None;
@ -55,12 +67,13 @@ fn main() {
let channel_name = channel.name.clone(); let channel_name = channel.name.clone();
watchers.push(thread::spawn(move || { watchers.push(thread::spawn(move || {
let mut session = connect( let mut session = match connect(&imap_store) {
imap_store.host.clone(), Some(session) => session,
imap_store.user.clone(), None => {
imap_store.password().clone(), log::error!("Unable to connect to channel {}", channel_name);
) return;
.unwrap(); }
};
log::info!("Watching for messages on channel {}", channel_name); log::info!("Watching for messages on channel {}", channel_name);
@ -71,14 +84,9 @@ fn main() {
if result.is_err() { if result.is_err() {
log::error!("Error while idling: {:?}", result); log::error!("Error while idling: {:?}", result);
thread::sleep(std::time::Duration::from_secs(10));
session = connect( thread::sleep(std::time::Duration::from_secs(10));
imap_store.host.clone(), session = connect(&imap_store).unwrap();
imap_store.user.clone(),
imap_store.password().clone(),
)
.unwrap();
continue; continue;
} }