Index: src/mail.rs ================================================================== --- src/mail.rs +++ src/mail.rs @@ -7,11 +7,10 @@ validate, }, }; use std::{ - borrow::Cow, collections::{ HashMap, HashSet, }, io::Error, @@ -160,15 +159,11 @@ && let Some(date) = mail.date() { reply.push(format!("Date:{date}")); } reply.push("
".into());
-			//let header_size = reply.join(" ").len();
-			let mut header_size = 0;
-			for i in reply.iter() {
-				header_size += i.len() + 1;
-			}
+			let reply = reply.join("\n");
 
 			let html_parts = mail.html_body_count();
 			let text_parts = mail.text_body_count();
 			let attachments = mail.attachment_count();
 			if html_parts != text_parts {
@@ -176,11 +171,11 @@
 			}
 			//let mut html_num = 0;
 			let mut text_num = 0;
 			let mut file_num = 0;
 			// let's display first html or text part as body
-			let mut body: Cow<'_, str> = "".into();
+			let mut body: String = "".into();
 			/*
 			 * actually I don't wanna parse that html stuff
 			if html_parts > 0 {
 				let text = mail.body_html(0).stack()?;
 				if text.len() < 4096 - header_size {
@@ -189,22 +184,22 @@
 				}
 			};
 			*/
 			if body.is_empty() && text_parts > 0 {
 				let text = mail.body_text(0)
-					.context("Failed to extract text from message")?;
-				// 7:
+					.context("Failed to extract text from message")?
+					.replace("\r\n", "\n");
+				// 6:
+				// - (headers)
 				// - (mail text)
-				// - 1 trailing newline
 				// - 6: 
- if text.len() < 4096 - ( header_size + 7 ) { + if text.len() < 4096 - ( reply.len() + 7 ) { body = text; text_num = 1; } }; - reply.extend(body.lines().map(|x| x.into())); - reply.push("".into()); + let msg = format!("{}{}", reply, validate(&body).stack()?); // and let's collect all other attachment parts let mut files_to_send = vec![]; /* * let's just skip html parts for now, they just duplicate text? @@ -222,11 +217,10 @@ files_to_send.push(mail.attachment(file_num.try_into().stack()?) .context("Failed to get file part from message.")?); file_num += 1; } - let msg = reply.join("\n"); for chat in rcpt { if !files_to_send.is_empty() { let mut files = vec![]; // let mut first_one = true; for chunk in &files_to_send { Index: src/main.rs ================================================================== --- src/main.rs +++ src/main.rs @@ -3,10 +3,13 @@ //! available in configuration, everything else is sent to default address. mod mail; mod telegram; mod utils; + +#[cfg(test)] +mod tests; use crate::mail::MailServer; use async_compat::Compat; use just_getopt::{ @@ -50,11 +53,11 @@ let parsed = specs.getopt(args); for u in &parsed.unknown { println!("Unknown option: {u}"); } if !(parsed.unknown.is_empty()) || parsed.options_first("help").is_some() { - println!("SMTP2TG v{}, (C) 2024 - 2025\n\n\ + println!("SMTP2TG v{}, (C) 2024 - 2026\n\n\ \t-h|--help\tDisplay this help\n\ \t-c|--config …\tSet configuration file location.", env!("CARGO_PKG_VERSION")); return Ok(()); }; Index: src/telegram.rs ================================================================== --- src/telegram.rs +++ src/telegram.rs @@ -1,6 +1,9 @@ -use crate::utils::Attachment; +use crate::utils::{ + Attachment, + validate, +}; use std::{ collections::HashMap, fmt::Debug, }; @@ -55,11 +58,11 @@ }) } /// Send message to default user, used for debug/log/info purposes pub async fn debug (&self, msg: &str) -> Result { - self.send(&self.default, format!("
{msg}
")).await + self.send(&self.default, format!("
{}
", validate(msg).stack()?)).await } /// Get recipient by address pub fn get (&self, name: &str) -> Result<&ChatPeerId> { self.recipients.get(name) ADDED src/tests.rs Index: src/tests.rs ================================================================== --- /dev/null +++ src/tests.rs @@ -0,0 +1,22 @@ +use crate::utils::validate; + +use stacked_errors::{ + Result, + StackableErr, +}; + +#[test] +fn check_valid () -> Result<()> { + let html = "

Some valid HTML

"; + let res = validate(html).stack()?; + assert_eq!(res, html); + Ok(()) +} + +#[test] +#[should_panic = "Found special tag while closing generic tag"] +fn check_invalid () -> () { + let html = "

Some valid HTML

Link injection!"; + let _ = validate(html).unwrap(); + () +} Index: src/utils.rs ================================================================== --- src/utils.rs +++ src/utils.rs @@ -23,9 +23,8 @@ /// Pass any text here to be validated as HTML, breaks on validation errors pub fn validate (text: &str) -> Result<&str> { let fragment = Html::parse_fragment(text); if !fragment.errors.is_empty() { bail!(fragment.errors.join("\n")); - } else { - Ok(text) } + Ok(text) }