Lines of src/lib.rs from check-in 3c858ed7c4 that are changed by the sequence of edits moving toward check-in 713c1e7fb2:
3c858ed7c4 2026-07-31 1: mod mail; 2: mod telegram; 3: pub mod utils; 4: 5: use crate::mail::MailServer; 6: 7: use just_getopt::{ 8: OptFlags, 9: OptSpecs, 10: OptValue, 11: }; 12: use smol::{ 13: fs::metadata, 14: }; 15: use stacked_errors::{ 16: Result, 17: StackableErr, 18: bail, 3c858ed7c4 2026-07-31 19: }; 3c858ed7c4 2026-07-31 20: 3c858ed7c4 2026-07-31 21: use std::{ 3c858ed7c4 2026-07-31 22: io::Cursor, 3c858ed7c4 2026-07-31 23: os::unix::fs::PermissionsExt, 3c858ed7c4 2026-07-31 24: path::Path, 25: }; 26: 27: /// Actual main function running async with Error propagation support 28: pub async fn async_main () -> Result<()> { 29: let specs = OptSpecs::new() 30: .option("help", "h", OptValue::None) 31: .option("help", "help", OptValue::None) 32: .option("config", "c", OptValue::Required) 33: .option("config", "config", OptValue::Required) 34: .flag(OptFlags::OptionsEverywhere); 35: let mut args = std::env::args(); 36: args.next(); 37: let parsed = specs.getopt(args); 38: for u in &parsed.unknown { 39: println!("Unknown option: {u}"); 40: } 41: if !(parsed.unknown.is_empty()) || parsed.options_first("help").is_some() { 42: println!("SMTP2TG v{}, (C) 2024 - 2026\n\n\ 43: \t-h|--help\tDisplay this help\n\ 44: \t-c|--config …\tSet configuration file location.", 45: env!("CARGO_PKG_VERSION")); 46: return Ok(()); 47: }; 48: let config_file = Path::new(if let Some(path) = parsed.options_value_last("config") { 49: &path[..] 50: } else { 51: "smtp2tg.toml" 52: }); 53: if !config_file.exists() { 54: bail!("can't read configuration from {config_file:?}"); 55: }; 56: { 57: let meta = metadata(config_file).await.stack()?; 58: if (!0o100600 & meta.permissions().mode()) > 0 { 59: bail!("other users can read or write config file {config_file:?}\n\ 60: File permissions: {:o}", meta.permissions().mode()); 61: } 62: } 63: let settings: config::Config = config::Config::builder() 64: .set_default("api_gateway", "https://api.telegram.org").stack()? 65: .set_default("fields", vec!["date", "from", "subject"]).stack()? 66: .set_default("hostname", "smtp.2.tg").stack()? 67: .set_default("listen_on", "0.0.0.0:1025").stack()? 68: .set_default("unknown", "relay").stack()? 3c858ed7c4 2026-07-31 69: .set_default("domains", vec!["localhost", hostname::get().stack()?.to_str().expect("Failed to get current hostname")]).stack()? 70: .add_source(config::File::from(config_file)) 71: .build() 72: .with_context(|| format!("[{config_file:?}] there was an error reading config\n\ 73: \tplease consult \"smtp2tg.toml.example\" for details"))?; 74: 75: let listen_on = settings.get_string("listen_on").stack()?; 76: let server_name = settings.get_string("hostname").stack()?; 77: let core = MailServer::new(settings)?; 78: let mut server = mailin_embedded::Server::new(core); 79: 80: server.with_name(server_name) 81: .with_ssl(mailin_embedded::SslConfig::None).unwrap() 82: .with_addr(listen_on).unwrap(); 83: server.serve().unwrap(); 84: 85: Ok(()) 86: }