Check-in [ec55d3e69e]
Logged in as anonymous
Overview
Comment:bumps, global permissions for workflow, docs
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: ec55d3e69ea86301053f33d9024093d5319ed80407cc1594b9f03cc5161f22f2
User & Date: arcade on 2026-10-28 17:13:38.992
Other Links: manifest | tags
Context
2026-10-28
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
2026-10-25
19:48
bump, new mail-parser, force LocalExecutor check-in: 1984831c3f user: arcade tags: trunk
Changes
1
2
3
4
5
6



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
name: rust-ci
on:
  push:
    branches: [ master, release ]
  pull_request:




# sccache enable for rust/C builds
env:
  SCCACHE_GHA_ENABLED: "true"
  RUSTC_WRAPPER: "sccache"

jobs:
  rust-ci-run:
    name: Run rust-clippy analyzing and tests
    runs-on: ubuntu-latest
    if: github.event_name == 'push' || (github.head_ref != 'master' && github.head_ref != 'release')
    permissions:
      contents: read
    steps:
      # SETUP
      - uses: actions/checkout@v7
        with:
          persist-credentials: false
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2






>
>
>










<
<







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19


20
21
22
23
24
25
26
name: rust-ci
on:
  push:
    branches: [ master, release ]
  pull_request:

permissions:
  contents: read

# sccache enable for rust/C builds
env:
  SCCACHE_GHA_ENABLED: "true"
  RUSTC_WRAPPER: "sccache"

jobs:
  rust-ci-run:
    name: Run rust-clippy analyzing and tests
    runs-on: ubuntu-latest
    if: github.event_name == 'push' || (github.head_ref != 'master' && github.head_ref != 'release')


    steps:
      # SETUP
      - uses: actions/checkout@v7
        with:
          persist-credentials: false
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
︙︙
︙︙
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
name = "autocfg"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"

[[package]]
name = "aws-lc-rs"
version = "1.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faac5829c2b74c28f830747e7818ccfb684261b5f48a1118b1e2a13d36dfab13"
dependencies = [
 "aws-lc-sys",
 "zeroize",
]

[[package]]
name = "aws-lc-sys"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1622d8446a2d4b2ce0c7eefc73dd43a99779028d5ee5c2dd8073a658ba8a2bc"
dependencies = [
 "cc",
 "cmake",
 "dunce",
 "fs_extra",
 "pkg-config",
]







|

|







|

|







225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
name = "autocfg"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"

[[package]]
name = "aws-lc-rs"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "048006790404103c488e6c5616b3e7e702a71292aadda08e8f7698a9e0d2eb53"
dependencies = [
 "aws-lc-sys",
 "zeroize",
]

[[package]]
name = "aws-lc-sys"
version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bfbe32ac36b3c428696ce315464181bc4029d689fd58e3f2a4e58c252242f52"
dependencies = [
 "cc",
 "cmake",
 "dunce",
 "fs_extra",
 "pkg-config",
]
︙︙
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"

[[package]]
name = "openssl-probe"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"

[[package]]
name = "owo-colors"
version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c45bb4a6ae1280ec0803b1ef9d3455eb50f01efbbe1447ab020f1d54fba9d8"








|

|







1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"

[[package]]
name = "openssl-probe"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e036b411754132fd6b74e94b74e9a13c5cf606fa93d1885c35860d3060820b1"

[[package]]
name = "owo-colors"
version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c45bb4a6ae1280ec0803b1ef9d3455eb50f01efbbe1447ab020f1d54fba9d8"

︙︙
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
 "semver",
]

[[package]]
name = "rustix"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d"
dependencies = [
 "bitflags",
 "errno",
 "libc",
 "linux-raw-sys",
 "windows-sys 0.61.2",
]







|

|







1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
 "semver",
]

[[package]]
name = "rustix"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5e76742e5294153d4dde1207dbec5bdd81eef6fabdc0d2118babadabfd858aa"
dependencies = [
 "bitflags",
 "errno",
 "libc",
 "linux-raw-sys",
 "windows-sys 0.61.2",
]
︙︙
︙︙
65
66
67
68
69
70
71
72

73
74
75
76
77
78
79
	/// Initializes the mail server: sets up the Telegram API client and
	/// validates all required configuration values.
	///
	/// # Arguments
	/// * `settings` - Parsed application configuration.
	///
	/// # Errors
	/// Returns an error if required configuration values are missing or invalid.

	pub fn new (settings: config::Config) -> Result<MailServer> {
		let api_key = settings.get_string("api_key")
			.context("[smtp2tg.toml] missing \"api_key\" parameter.\n")?;
		let mut recipients = HashMap::new();
		for (name, value) in settings.get_table("recipients")
			.context("[smtp2tg.toml] missing table \"recipients\".\n")?
		{







|
>







65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
	/// Initializes the mail server: sets up the Telegram API client and
	/// validates all required configuration values.
	///
	/// # Arguments
	/// * `settings` - Parsed application configuration.
	///
	/// # Errors
	/// Returns an error if required configuration values are missing or invalid,
	/// Telegram API client creation fails, or the address regex cannot be built.
	pub fn new (settings: config::Config) -> Result<MailServer> {
		let api_key = settings.get_string("api_key")
			.context("[smtp2tg.toml] missing \"api_key\" parameter.\n")?;
		let mut recipients = HashMap::new();
		for (name, value) in settings.get_table("recipients")
			.context("[smtp2tg.toml] missing table \"recipients\".\n")?
		{
︙︙
127
128
129
130
131
132
133







134





135
136
137
138
139
140
141
	pub fn get_id (&self, name: &str) -> Result<&ChatPeerId> {
		if self.address.is_match(name) {
			Ok(self.tg.get(name).unwrap_or(&self.tg.default))
		} else {
			bail!("Email address {name:?} is not from an allowed domain.");
	}	}








	/// Attempt to deliver one message





	async fn relay_mail (&self) -> Result<()> {
		if let Some(headers) = &self.headers {
			let mail = mail_parser::MessageParser::new().parse(&self.data)
				.context("Failed to parse mail.")?;

			// Adding all known addresses to recipient list, for anyone else adding default
			// Also if list is empty also adding default







>
>
>
>
>
>
>
|
>
>
>
>
>







128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
	pub fn get_id (&self, name: &str) -> Result<&ChatPeerId> {
		if self.address.is_match(name) {
			Ok(self.tg.get(name).unwrap_or(&self.tg.default))
		} else {
			bail!("Email address {name:?} is not from an allowed domain.");
	}	}

	/// Forwards the buffered email to each distinct destination chat, using the
	/// default chat for allowed addresses without a configured recipient.
	///
	/// Inlines the first plain-text body if the escaped message is below 4096
	/// bytes. Sends remaining text parts and decoded attachments as documents;
	/// HTML body parts are omitted. Leaves the buffered data and envelope intact.
	///
	/// # Errors
	/// Returns an error for a missing envelope, no recipients, a disallowed
	/// address, mail parsing or part extraction failures, or rejected closing
	/// `pre`/`code` tags in text passed to validation. Propagates media-group
	/// construction and Telegram request errors, including diagnostic sends.
	/// Delivery to earlier chats is not rolled back if a later send fails.
	async fn relay_mail (&self) -> Result<()> {
		if let Some(headers) = &self.headers {
			let mail = mail_parser::MessageParser::new().parse(&self.data)
				.context("Failed to parse mail.")?;

			// Adding all known addresses to recipient list, for anyone else adding default
			// Also if list is empty also adding default
︙︙
︙︙
40
41
42
43
44
45
46


47
48
49
50
51
52
53
///
/// The configuration file must use owner-only permissions. Once configured,
/// the SMTP server runs until it stops or encounters an error.
///
/// # Errors
/// Returns an error if the configuration file is missing, inaccessible,
/// insecure, malformed, or contains invalid required settings.


///
/// # Panics
/// Panics if configuring or serving the SMTP server fails.
	async fn main (ex: &LocalExecutor<'_>) -> Result<()> {
		let args = Args::parse();
		let config_file = Path::new(&args.config);
		if !config_file.try_exists().stack()? {







>
>







40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
///
/// The configuration file must use owner-only permissions. Once configured,
/// the SMTP server runs until it stops or encounters an error.
///
/// # Errors
/// Returns an error if the configuration file is missing, inaccessible,
/// insecure, malformed, or contains invalid required settings.
/// 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 config_file = Path::new(&args.config);
		if !config_file.try_exists().stack()? {
︙︙