Annotation For src/main.rs
Logged in as anonymous

Origin for each line in src/main.rs from check-in 37a0139d49:

e81897ec87 2024-05-24    1: use anyhow::{
e81897ec87 2024-05-24    2: 	anyhow,
e81897ec87 2024-05-24    3: 	Result,
e81897ec87 2024-05-24    4: };
7620f854a7 2024-05-21    5: use async_std::task;
7620f854a7 2024-05-21    6: use samotop::{
7620f854a7 2024-05-21    7: 	mail::{
7620f854a7 2024-05-21    8: 		Builder,
7620f854a7 2024-05-21    9: 		DebugService,
7620f854a7 2024-05-21   10: 		MailDir,
7620f854a7 2024-05-21   11: 		Name
7620f854a7 2024-05-21   12: 	},
51adce1e7e 2024-05-22   13: 	smtp::{
51adce1e7e 2024-05-22   14: 		SmtpParser,
51adce1e7e 2024-05-22   15: 		Prudence,
51adce1e7e 2024-05-22   16: 	},
7620f854a7 2024-05-21   17: };
f4cad2a5c0 2024-05-26   18: use teloxide::{
f4cad2a5c0 2024-05-26   19: 	Bot,
f4cad2a5c0 2024-05-26   20: 	prelude::{
f4cad2a5c0 2024-05-26   21: 		Requester,
f4cad2a5c0 2024-05-26   22: 		RequesterExt,
f4cad2a5c0 2024-05-26   23: 	},
f4cad2a5c0 2024-05-26   24: 	types::{
f4cad2a5c0 2024-05-26   25: 		ChatId,
f4cad2a5c0 2024-05-26   26: 		ParseMode::MarkdownV2,
f4cad2a5c0 2024-05-26   27: 	},
7620f854a7 2024-05-21   28: };
7620f854a7 2024-05-21   29: 
7620f854a7 2024-05-21   30: use std::{
7620f854a7 2024-05-21   31: 	borrow::Cow,
61238a3618 2024-05-22   32: 	collections::{
61238a3618 2024-05-22   33: 		HashMap,
61238a3618 2024-05-22   34: 		HashSet,
61238a3618 2024-05-22   35: 	},
7620f854a7 2024-05-21   36: 	io::Read,
da7fc7983d 2024-05-23   37: 	os::unix::fs::{
da7fc7983d 2024-05-23   38: 		FileTypeExt,
da7fc7983d 2024-05-23   39: 		PermissionsExt,
da7fc7983d 2024-05-23   40: 	},
7620f854a7 2024-05-21   41: 	path::{
7620f854a7 2024-05-21   42: 		Path,
7620f854a7 2024-05-21   43: 		PathBuf
7620f854a7 2024-05-21   44: 	},
7620f854a7 2024-05-21   45: 	time::Duration,
7620f854a7 2024-05-21   46: 	vec::Vec,
7620f854a7 2024-05-21   47: };
7620f854a7 2024-05-21   48: 
61238a3618 2024-05-22   49: fn address_into_iter<'a>(addr: &'a mail_parser::Address<'a, >) -> impl Iterator<Item = Cow<'a, str>> {
61238a3618 2024-05-22   50: 	addr.clone().into_list().into_iter().map(|a| a.address.unwrap())
61238a3618 2024-05-22   51: }
7620f854a7 2024-05-21   52: 
e81897ec87 2024-05-24   53: async fn relay_mails(maildir: &Path, core: &TelegramTransport) -> Result<()> {
7620f854a7 2024-05-21   54: 	let new_dir = maildir.join("new");
7620f854a7 2024-05-21   55: 
7620f854a7 2024-05-21   56: 	std::fs::create_dir_all(&new_dir)?;
7620f854a7 2024-05-21   57: 
7620f854a7 2024-05-21   58: 	let files = std::fs::read_dir(new_dir)?;
7620f854a7 2024-05-21   59: 	for file in files {
61238a3618 2024-05-22   60: 		let file = file?;
f4cad2a5c0 2024-05-26   61: 		let mut buf: String = Default::default();
f4cad2a5c0 2024-05-26   62: 		std::fs::File::open(file.path())?.read_to_string(&mut buf)?;
e81897ec87 2024-05-24   63: 
f4cad2a5c0 2024-05-26   64: 		let mail = mail_parser::MessageParser::new().parse(&buf)
f4cad2a5c0 2024-05-26   65: 			.ok_or(anyhow!("Failed to parse mail `{:?}`.", file))?;
e81897ec87 2024-05-24   66: 
e81897ec87 2024-05-24   67: 		// Fetching address lists from fields we know
e81897ec87 2024-05-24   68: 		let mut to = HashSet::new();
e81897ec87 2024-05-24   69: 		if let Some(addr) = mail.to() {
e81897ec87 2024-05-24   70: 			let _ = address_into_iter(addr).map(|x| to.insert(x));
e81897ec87 2024-05-24   71: 		};
e81897ec87 2024-05-24   72: 		if let Some(addr) = mail.header("X-Samotop-To") {
e81897ec87 2024-05-24   73: 			match addr {
e81897ec87 2024-05-24   74: 				mail_parser::HeaderValue::Address(addr) => {
e81897ec87 2024-05-24   75: 					let _ = address_into_iter(addr).map(|x| to.insert(x));
e81897ec87 2024-05-24   76: 				},
e81897ec87 2024-05-24   77: 				mail_parser::HeaderValue::Text(text) => {
e81897ec87 2024-05-24   78: 					to.insert(text.clone());
e81897ec87 2024-05-24   79: 				},
e81897ec87 2024-05-24   80: 				_ => {}
e81897ec87 2024-05-24   81: 			}
e81897ec87 2024-05-24   82: 		};
e81897ec87 2024-05-24   83: 
e81897ec87 2024-05-24   84: 		// Adding all known addresses to recipient list, for anyone else adding default
e81897ec87 2024-05-24   85: 		// Also if list is empty also adding default
f4cad2a5c0 2024-05-26   86: 		let mut rcpt: HashSet<&ChatId> = HashSet::new();
e81897ec87 2024-05-24   87: 		for item in to {
e81897ec87 2024-05-24   88: 			let item = item.into_owned();
e81897ec87 2024-05-24   89: 			match core.recipients.get(&item) {
e81897ec87 2024-05-24   90: 				Some(addr) => rcpt.insert(addr),
e81897ec87 2024-05-24   91: 				None => {
e81897ec87 2024-05-24   92: 					core.debug(format!("Recipient [{}] not found.", &item)).await?;
e81897ec87 2024-05-24   93: 					rcpt.insert(core.recipients.get("_")
e81897ec87 2024-05-24   94: 						.ok_or(anyhow!("Missing default address in recipient table."))?)
e81897ec87 2024-05-24   95: 				}
e81897ec87 2024-05-24   96: 			};
e81897ec87 2024-05-24   97: 		};
e81897ec87 2024-05-24   98: 		if rcpt.is_empty() {
e81897ec87 2024-05-24   99: 			core.debug("No recipient or envelope address.").await?;
e81897ec87 2024-05-24  100: 			rcpt.insert(core.recipients.get("_")
e81897ec87 2024-05-24  101: 				.ok_or(anyhow!("Missing default address in recipient table."))?);
e81897ec87 2024-05-24  102: 		};
e81897ec87 2024-05-24  103: 
e81897ec87 2024-05-24  104: 		// prepating message header
f4cad2a5c0 2024-05-26  105: 		let mut reply: Vec<Cow<'_, str>> = vec![];
e81897ec87 2024-05-24  106: 		if let Some(subject) = mail.subject() {
e81897ec87 2024-05-24  107: 			reply.push(format!("**Subject:** `{}`", subject).into());
e81897ec87 2024-05-24  108: 		} else if let Some(thread) = mail.thread_name() {
e81897ec87 2024-05-24  109: 			reply.push(format!("**Thread:** `{}`", thread).into());
e81897ec87 2024-05-24  110: 		}
e81897ec87 2024-05-24  111: 		if let Some(from) = mail.from() {
e81897ec87 2024-05-24  112: 			reply.push(format!("**From:** `{:?}`", address_into_iter(from).collect::<Vec<_>>().join(", ")).into());
e81897ec87 2024-05-24  113: 		}
e81897ec87 2024-05-24  114: 		if let Some(sender) = mail.sender() {
e81897ec87 2024-05-24  115: 			reply.push(format!("**Sender:** `{:?}`", address_into_iter(sender).collect::<Vec<_>>().join(", ")).into());
e81897ec87 2024-05-24  116: 		}
e81897ec87 2024-05-24  117: 		reply.push("".into());
e81897ec87 2024-05-24  118: 		let header_size = reply.join("\n").len() + 1;
e81897ec87 2024-05-24  119: 
e81897ec87 2024-05-24  120: 		let html_parts = mail.html_body_count();
e81897ec87 2024-05-24  121: 		let text_parts = mail.text_body_count();
e81897ec87 2024-05-24  122: 		let attachments = mail.attachment_count();
e81897ec87 2024-05-24  123: 		if html_parts != text_parts {
e81897ec87 2024-05-24  124: 			core.debug(format!("Hm, we have {} HTML parts and {} text parts.", html_parts, text_parts)).await?;
e81897ec87 2024-05-24  125: 		}
e81897ec87 2024-05-24  126: 		//let mut html_num = 0;
e81897ec87 2024-05-24  127: 		let mut text_num = 0;
e81897ec87 2024-05-24  128: 		let mut file_num = 0;
e81897ec87 2024-05-24  129: 		// let's display first html or text part as body
e81897ec87 2024-05-24  130: 		let mut body = "".into();
e81897ec87 2024-05-24  131: 		/*
e81897ec87 2024-05-24  132: 		 * actually I don't wanna parse that html stuff
e81897ec87 2024-05-24  133: 		if html_parts > 0 {
e81897ec87 2024-05-24  134: 			let text = mail.body_html(0).unwrap();
e81897ec87 2024-05-24  135: 			if text.len() < 4096 - header_size {
e81897ec87 2024-05-24  136: 				body = text;
e81897ec87 2024-05-24  137: 				html_num = 1;
e81897ec87 2024-05-24  138: 			}
e81897ec87 2024-05-24  139: 		};
e81897ec87 2024-05-24  140: 		*/
e81897ec87 2024-05-24  141: 		if body == "" && text_parts > 0 {
e81897ec87 2024-05-24  142: 			let text = mail.body_text(0)
e81897ec87 2024-05-24  143: 				.ok_or(anyhow!("Failed to extract text from message."))?;
e81897ec87 2024-05-24  144: 			if text.len() < 4096 - header_size {
e81897ec87 2024-05-24  145: 				body = text;
e81897ec87 2024-05-24  146: 				text_num = 1;
e81897ec87 2024-05-24  147: 			}
e81897ec87 2024-05-24  148: 		};
e81897ec87 2024-05-24  149: 		reply.push("```".into());
e81897ec87 2024-05-24  150: 		for line in body.lines() {
e81897ec87 2024-05-24  151: 			reply.push(line.into());
e81897ec87 2024-05-24  152: 		}
e81897ec87 2024-05-24  153: 		reply.push("```".into());
e81897ec87 2024-05-24  154: 
e81897ec87 2024-05-24  155: 		// and let's collect all other attachment parts
e81897ec87 2024-05-24  156: 		let mut files_to_send = vec![];
e81897ec87 2024-05-24  157: 		/*
e81897ec87 2024-05-24  158: 		 * let's just skip html parts for now, they just duplicate text?
e81897ec87 2024-05-24  159: 		while html_num < html_parts {
e81897ec87 2024-05-24  160: 			files_to_send.push(mail.html_part(html_num).unwrap());
e81897ec87 2024-05-24  161: 			html_num += 1;
e81897ec87 2024-05-24  162: 		}
e81897ec87 2024-05-24  163: 		*/
e81897ec87 2024-05-24  164: 		while text_num < text_parts {
e81897ec87 2024-05-24  165: 			files_to_send.push(mail.text_part(text_num)
e81897ec87 2024-05-24  166: 				.ok_or(anyhow!("Failed to get text part from message"))?);
e81897ec87 2024-05-24  167: 			text_num += 1;
e81897ec87 2024-05-24  168: 		}
e81897ec87 2024-05-24  169: 		while file_num < attachments {
e81897ec87 2024-05-24  170: 			files_to_send.push(mail.attachment(file_num)
e81897ec87 2024-05-24  171: 				.ok_or(anyhow!("Failed to get file part from message"))?);
e81897ec87 2024-05-24  172: 			file_num += 1;
e81897ec87 2024-05-24  173: 		}
e81897ec87 2024-05-24  174: 
f4cad2a5c0 2024-05-26  175: 		let msg = reply.join("\n");
e81897ec87 2024-05-24  176: 		for chat in rcpt {
f4cad2a5c0 2024-05-26  177: 			if !files_to_send.is_empty() {
f4cad2a5c0 2024-05-26  178: 				let mut files = vec![];
f4cad2a5c0 2024-05-26  179: 				let mut first_one = true;
f4cad2a5c0 2024-05-26  180: 				for chunk in &files_to_send {
f4cad2a5c0 2024-05-26  181: 					let data = chunk.contents();
f4cad2a5c0 2024-05-26  182: 					let mut filename: Option<String> = None;
f4cad2a5c0 2024-05-26  183: 					for header in chunk.headers() {
f4cad2a5c0 2024-05-26  184: 						if header.name() == "Content-Type" {
f4cad2a5c0 2024-05-26  185: 							match header.value() {
f4cad2a5c0 2024-05-26  186: 								mail_parser::HeaderValue::ContentType(contenttype) => {
f4cad2a5c0 2024-05-26  187: 									if let Some(fname) = contenttype.attribute("name") {
f4cad2a5c0 2024-05-26  188: 										filename = Some(fname.to_owned());
f4cad2a5c0 2024-05-26  189: 									}
f4cad2a5c0 2024-05-26  190: 								},
f4cad2a5c0 2024-05-26  191: 								_ => {
f4cad2a5c0 2024-05-26  192: 									core.debug("Attachment has bad ContentType header.").await?;
f4cad2a5c0 2024-05-26  193: 								},
f4cad2a5c0 2024-05-26  194: 							};
f4cad2a5c0 2024-05-26  195: 						};
f4cad2a5c0 2024-05-26  196: 					};
f4cad2a5c0 2024-05-26  197: 					let filename = if let Some(fname) = filename {
f4cad2a5c0 2024-05-26  198: 						fname
f4cad2a5c0 2024-05-26  199: 					} else {
f4cad2a5c0 2024-05-26  200: 						"Attachment.txt".into()
f4cad2a5c0 2024-05-26  201: 					};
f4cad2a5c0 2024-05-26  202: 					let item = teloxide::types::InputMediaDocument::new(
f4cad2a5c0 2024-05-26  203: 						teloxide::types::InputFile::memory(data.to_vec())
f4cad2a5c0 2024-05-26  204: 						.file_name(filename));
f4cad2a5c0 2024-05-26  205: 					let item = if first_one {
f4cad2a5c0 2024-05-26  206: 						first_one = false;
f4cad2a5c0 2024-05-26  207: 						item.caption(&msg).parse_mode(MarkdownV2)
f4cad2a5c0 2024-05-26  208: 					} else {
f4cad2a5c0 2024-05-26  209: 						item
f4cad2a5c0 2024-05-26  210: 					};
f4cad2a5c0 2024-05-26  211: 					files.push(teloxide::types::InputMedia::Document(item));
f4cad2a5c0 2024-05-26  212: 				}
f4cad2a5c0 2024-05-26  213: 				core.sendgroup(chat, files).await?;
f4cad2a5c0 2024-05-26  214: 			} else {
f4cad2a5c0 2024-05-26  215: 				core.send(chat, &msg).await?;
e81897ec87 2024-05-24  216: 			}
e81897ec87 2024-05-24  217: 		}
7620f854a7 2024-05-21  218: 
7620f854a7 2024-05-21  219: 		std::fs::remove_file(file.path())?;
7620f854a7 2024-05-21  220: 	}
7620f854a7 2024-05-21  221: 	Ok(())
7620f854a7 2024-05-21  222: }
7620f854a7 2024-05-21  223: 
7620f854a7 2024-05-21  224: fn my_prudence() -> Prudence {
7620f854a7 2024-05-21  225: 	Prudence::default().with_read_timeout(Duration::from_secs(60)).with_banner_delay(Duration::from_secs(1))
7620f854a7 2024-05-21  226: }
7620f854a7 2024-05-21  227: 
61238a3618 2024-05-22  228: pub struct TelegramTransport {
f4cad2a5c0 2024-05-26  229: 	tg: teloxide::adaptors::DefaultParseMode<Bot>,
f4cad2a5c0 2024-05-26  230: 	recipients: HashMap<String, ChatId>,
61238a3618 2024-05-22  231: }
61238a3618 2024-05-22  232: 
61238a3618 2024-05-22  233: impl TelegramTransport {
da7fc7983d 2024-05-23  234: 	pub fn new(settings: config::Config) -> TelegramTransport {
f4cad2a5c0 2024-05-26  235: 		let tg = Bot::new(settings.get_string("api_key")
f4cad2a5c0 2024-05-26  236: 			.expect("[smtp2tg.toml] missing \"api_key\" parameter.\n"))
f4cad2a5c0 2024-05-26  237: 			.parse_mode(MarkdownV2);
f4cad2a5c0 2024-05-26  238: 		let recipients: HashMap<String, ChatId> = settings.get_table("recipients")
da7fc7983d 2024-05-23  239: 			.expect("[smtp2tg.toml] missing table \"recipients\".\n")
f4cad2a5c0 2024-05-26  240: 			.into_iter().map(|(a, b)| (a, ChatId (b.into_int()
da7fc7983d 2024-05-23  241: 				.expect("[smtp2tg.toml] \"recipient\" table values should be integers.\n")
da7fc7983d 2024-05-23  242: 				))).collect();
da7fc7983d 2024-05-23  243: 		if !recipients.contains_key("_") {
da7fc7983d 2024-05-23  244: 			eprintln!("[smtp2tg.toml] \"recipient\" table misses \"default_recipient\".\n");
da7fc7983d 2024-05-23  245: 			panic!("no default recipient");
da7fc7983d 2024-05-23  246: 		}
61238a3618 2024-05-22  247: 
61238a3618 2024-05-22  248: 		TelegramTransport {
7620f854a7 2024-05-21  249: 			tg,
7620f854a7 2024-05-21  250: 			recipients,
61238a3618 2024-05-22  251: 		}
61238a3618 2024-05-22  252: 	}
61238a3618 2024-05-22  253: 
f4cad2a5c0 2024-05-26  254: 	pub async fn debug<'b, S>(&self, msg: S) -> Result<teloxide::types::Message>
f4cad2a5c0 2024-05-26  255: 	where S: Into<String> {
f4cad2a5c0 2024-05-26  256: 		task::sleep(Duration::from_secs(5)).await;
f4cad2a5c0 2024-05-26  257: 		Ok(self.tg.send_message(*self.recipients.get("_").unwrap(), msg).await?)
f4cad2a5c0 2024-05-26  258: 	}
f4cad2a5c0 2024-05-26  259: 
f4cad2a5c0 2024-05-26  260: 	pub async fn send<'b, S>(&self, to: &ChatId, msg: S) -> Result<teloxide::types::Message>
f4cad2a5c0 2024-05-26  261: 	where S: Into<String> {
f4cad2a5c0 2024-05-26  262: 		task::sleep(Duration::from_secs(5)).await;
f4cad2a5c0 2024-05-26  263: 		Ok(self.tg.send_message(*to, msg).await?)
f4cad2a5c0 2024-05-26  264: 	}
f4cad2a5c0 2024-05-26  265: 
f4cad2a5c0 2024-05-26  266: 	pub async fn sendgroup<M>(&self, to: &ChatId, media: M) -> Result<Vec<teloxide::types::Message>>
f4cad2a5c0 2024-05-26  267: 	where M: IntoIterator<Item = teloxide::types::InputMedia> {
f4cad2a5c0 2024-05-26  268: 		task::sleep(Duration::from_secs(5)).await;
f4cad2a5c0 2024-05-26  269: 		Ok(self.tg.send_media_group(*to, media).await?)
7620f854a7 2024-05-21  270: 	}
7620f854a7 2024-05-21  271: }
7620f854a7 2024-05-21  272: 
7620f854a7 2024-05-21  273: #[async_std::main]
7620f854a7 2024-05-21  274: async fn main() {
7620f854a7 2024-05-21  275: 	let settings: config::Config = config::Config::builder()
7620f854a7 2024-05-21  276: 		.add_source(config::File::with_name("smtp2tg.toml"))
da7fc7983d 2024-05-23  277: 		.build()
da7fc7983d 2024-05-23  278: 		.expect("[smtp2tg.toml] there was an error reading config\n\
da7fc7983d 2024-05-23  279: 			\tplease consult \"smtp2tg.toml.example\" for details");
7620f854a7 2024-05-21  280: 
da7fc7983d 2024-05-23  281: 	let maildir: PathBuf = settings.get_string("maildir")
da7fc7983d 2024-05-23  282: 		.expect("[smtp2tg.toml] missing \"maildir\" parameter.\n").into();
da7fc7983d 2024-05-23  283: 	let listen_on = settings.get_string("listen_on")
da7fc7983d 2024-05-23  284: 		.expect("[smtp2tg.toml] missing \"listen_on\" parameter.\n");
da7fc7983d 2024-05-23  285: 	let core = TelegramTransport::new(settings);
7620f854a7 2024-05-21  286: 	let sink = Builder + Name::new("smtp2tg") + DebugService +
51adce1e7e 2024-05-22  287: 		my_prudence() + MailDir::new(maildir.clone()).unwrap();
51adce1e7e 2024-05-22  288: 
31aec3c4b0 2024-05-23  289: 	env_logger::init();
31aec3c4b0 2024-05-23  290: 
7620f854a7 2024-05-21  291: 	task::spawn(async move {
7620f854a7 2024-05-21  292: 		loop {
e81897ec87 2024-05-24  293: 			// relay mails
e81897ec87 2024-05-24  294: 			if let Err(err) = relay_mails(&maildir, &core).await {
e81897ec87 2024-05-24  295: 				// in case that fails - inform default recipient
e81897ec87 2024-05-24  296: 				if let Err(err) = core.debug(format!("Sending emails failed:\n{:?}", err)).await {
e81897ec87 2024-05-24  297: 					// in case that also fails - write some logs and bail
e81897ec87 2024-05-24  298: 					eprintln!("Failed to contact Telegram:\n{:?}", err);
e81897ec87 2024-05-24  299: 				};
37a0139d49 2024-05-26  300: 				task::sleep(Duration::from_secs(5 * 60)).await;
e81897ec87 2024-05-24  301: 			};
7620f854a7 2024-05-21  302: 			task::sleep(Duration::from_secs(5)).await;
7620f854a7 2024-05-21  303: 		}
7620f854a7 2024-05-21  304: 	});
7620f854a7 2024-05-21  305: 
7620f854a7 2024-05-21  306: 	match listen_on.as_str() {
51adce1e7e 2024-05-22  307: 		"socket" => {
da7fc7983d 2024-05-23  308: 			let socket_path = "./smtp2tg.sock";
da7fc7983d 2024-05-23  309: 			match std::fs::symlink_metadata(socket_path) {
da7fc7983d 2024-05-23  310: 				Ok(metadata) => {
da7fc7983d 2024-05-23  311: 					if metadata.file_type().is_socket() {
da7fc7983d 2024-05-23  312: 						std::fs::remove_file(socket_path)
da7fc7983d 2024-05-23  313: 							.expect("[smtp2tg] failed to remove old socket.\n");
da7fc7983d 2024-05-23  314: 					} else {
da7fc7983d 2024-05-23  315: 						eprintln!("[smtp2tg] \"./smtp2tg.sock\" we wanted to use is actually not a socket.\n\
da7fc7983d 2024-05-23  316: 							[smtp2tg] please check the file and remove it manually.\n");
da7fc7983d 2024-05-23  317: 						panic!("socket path unavailable");
da7fc7983d 2024-05-23  318: 					}
da7fc7983d 2024-05-23  319: 				},
da7fc7983d 2024-05-23  320: 				Err(err) => {
da7fc7983d 2024-05-23  321: 					match err.kind() {
da7fc7983d 2024-05-23  322: 						std::io::ErrorKind::NotFound => {},
da7fc7983d 2024-05-23  323: 						_ => {
da7fc7983d 2024-05-23  324: 							eprintln!("{:?}", err);
da7fc7983d 2024-05-23  325: 							panic!("unhandled file type error");
da7fc7983d 2024-05-23  326: 						}
da7fc7983d 2024-05-23  327: 					};
da7fc7983d 2024-05-23  328: 				}
da7fc7983d 2024-05-23  329: 			};
da7fc7983d 2024-05-23  330: 
51adce1e7e 2024-05-22  331: 			let sink = sink + samotop::smtp::Lmtp.with(SmtpParser);
da7fc7983d 2024-05-23  332: 			task::spawn(async move {
da7fc7983d 2024-05-23  333: 				// Postpone mode change on the socket. I can't actually change
da7fc7983d 2024-05-23  334: 				// other way, as UnixServer just grabs path, and blocks
da7fc7983d 2024-05-23  335: 				task::sleep(Duration::from_secs(1)).await;
da7fc7983d 2024-05-23  336: 				std::fs::set_permissions(socket_path, std::fs::Permissions::from_mode(0o777)).unwrap();
da7fc7983d 2024-05-23  337: 			});
da7fc7983d 2024-05-23  338: 			samotop::server::UnixServer::on(socket_path)
51adce1e7e 2024-05-22  339: 				.serve(sink.build()).await.unwrap();
51adce1e7e 2024-05-22  340: 		},
51adce1e7e 2024-05-22  341: 		_ => {
51adce1e7e 2024-05-22  342: 			let sink = sink + samotop::smtp::Esmtp.with(SmtpParser);
51adce1e7e 2024-05-22  343: 			samotop::server::TcpServer::on(listen_on)
51adce1e7e 2024-05-22  344: 				.serve(sink.build()).await.unwrap();
51adce1e7e 2024-05-22  345: 		},
61238a3618 2024-05-22  346: 	};
7620f854a7 2024-05-21  347: }