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