Overview
| Comment: | simplify |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
42ac4c7a9a14e744479784146292ffe9 |
| User & Date: | arcade on 2026-10-28 17:22:21.130 |
| Other Links: | manifest | tags |
Context
|
2026-10-28
| ||
| 17:33 | case insensitive check and some tests leaf check-in: e3cad77b2b user: arcade tags: trunk | |
| 17:22 | simplify check-in: 42ac4c7a9a user: arcade tags: trunk | |
| 17:13 | bumps, global permissions for workflow, docs check-in: ec55d3e69e user: arcade tags: trunk | |
Changes
Modified src/mail.rs
from [c3fdca9cf0]
to [014486d6f8].
| ︙ | ︙ | |||
183 184 185 186 187 188 189 |
reply.push(format!("<u><i>Date:</i></u> <code>{date}</code>"));
}
reply.push("</blockquote><pre>".into());
let reply = reply.join("\n");
let text_parts = mail.text_body().len();
let html_parts = mail.html_body().len();
| < < < | | < < | < < < < < < < < | < < < < | < < < < < | < < < < | < < < < | | > | | | | | | | | | | | < < < < < < | | 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
reply.push(format!("<u><i>Date:</i></u> <code>{date}</code>"));
}
reply.push("</blockquote><pre>".into());
let reply = reply.join("\n");
let text_parts = mail.text_body().len();
let html_parts = mail.html_body().len();
if html_parts != text_parts {
self.tg.debug(&format!("Hm, we have {html_parts} HTML parts and {text_parts} text parts.")).await?;
}
let mut text_num = 0;
// Display the first text part as the body if it fits.
let mut body = String::new();
if text_parts > 0 {
let text = mail.body_text(0)
.context("Failed to extract text from message")?
.replace("\r\n", "\n");
let text = validate(&text).stack()?;
// 6:
// - (headers)
// - (mail text)
// - 6: </pre>
if text.len() < 4096 - ( reply.len() + 6 ) {
body = text.into_owned();
text_num = 1;
}
};
let msg = format!("{}{}</pre>", reply, body);
// Send remaining text-body parts followed by attachments.
let files_to_send: Vec<_> = mail.text_body().skip(text_num)
.chain(mail.attachments()).collect();
for chat in rcpt {
if !files_to_send.is_empty() {
let mut files = vec![];
for chunk in &files_to_send {
let data: Vec<u8> = chunk.decoded().to_vec();
let mut filename: Option<String> = None;
for header in chunk.headers().iter()
.filter(|header| header.raw_name() == "Content-Type")
{
match header.value() {
mail_parser::HeaderValue::ContentType(contenttype) => {
if let Some(fname) = contenttype.attribute("name") {
filename = Some(fname.to_owned());
}
},
_ => {
self.tg.debug("Attachment has bad ContentType header.").await?;
},
};
};
files.push(Attachment {
data: Cursor::new(data),
name: filename.unwrap_or_else(|| "Attachment.txt".into()),
});
}
self.tg.sendgroup(chat, files, &msg).await?;
} else {
self.tg.send(chat, &msg).await?;
}
}
|
| ︙ | ︙ |
Modified src/main.rs
from [a66af013e1]
to [44d6752f1f].
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
use std::{
os::unix::fs::PermissionsExt,
path::Path,
};
use clap::Parser;
| | < < | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
use std::{
os::unix::fs::PermissionsExt,
path::Path,
};
use clap::Parser;
use smol::fs::metadata;
use stacked_errors::{
Result,
StackableErr,
bail,
};
use smtp2tg::mail::MailServer;
|
| ︙ | ︙ | |||
47 48 49 50 51 52 53 |
/// Also returns an error if the system hostname cannot be read or is not
/// valid UTF-8, or if mail server initialisation fails.
///
/// # Panics
/// Panics if configuring or serving the SMTP server fails.
async fn main (ex: &LocalExecutor<'_>) -> Result<()> {
let args = Args::parse();
| > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
/// Also returns an error if the system hostname cannot be read or is not
/// valid UTF-8, or if mail server initialisation fails.
///
/// # Panics
/// Panics if configuring or serving the SMTP server fails.
async fn main (ex: &LocalExecutor<'_>) -> Result<()> {
let args = Args::parse();
let settings = {
// Load configuration defaults and overrides after checking file permissions.
let config_file = Path::new(&args.config);
if !config_file.try_exists().stack()? {
bail!("Configuration file not found: {config_file:?}\n\
Hint: Ensure the file exists and the path is correct.");
};
{
let meta = metadata(config_file).await.stack()?;
if (!0o100600 & meta.permissions().mode()) > 0 {
bail!("Configuration file permissions are insecure {config_file:?}\n\
Current permissions: {:o}\n\
Required: 0600 (owner read/write only).\n\
Fix with: chmod 600 {config_file:?}",
meta.permissions().mode());
} }
config::Config::builder()
.set_default("api_gateway", "https://api.telegram.org").stack()?
.set_default("fields", vec!["date", "from", "subject"]).stack()?
.set_default("hostname", "smtp.2.tg").stack()?
.set_default("listen_on", "0.0.0.0:1025").stack()?
.set_default("domains", vec!["localhost",
hostname::get().context("Failed to get current hostname")?
.to_str().context("Can't convert hostname to string, bad UTF-8?")?]).stack()?
.add_source(config::File::from(config_file))
.build()
.with_context(|| format!(
"Failed to parse configuration file: {config_file:?}\n\
Check syntax against smtp2tg.toml.example.\n\
Common issues: missing quotes, trailing commas in inline tables, or invalid types."
))?
};
let listen_on = settings.get_string("listen_on").stack()?;
let server_name = settings.get_string("hostname").stack()?;
let core = MailServer::new(settings)?;
let mut server = mailin_embedded::Server::new(core);
// TODO: remove unwraps when mailin-embedded bumps with better error handling
|
| ︙ | ︙ |