Author:
Hash:
Timestamp:
+218 -22 +/-7 browse
Kevin Schoon [me@kevinschoon.com]
ccd72d39abe9822cc8fb4ccde261c25007ed5691
Thu, 05 Sep 2024 21:52:26 +0000 (1.2 years ago)
| 1 | diff --git a/Cargo.lock b/Cargo.lock |
| 2 | index 14f2435..25f2088 100644 |
| 3 | --- a/Cargo.lock |
| 4 | +++ b/Cargo.lock |
| 5 | @@ -949,7 +949,9 @@ dependencies = [ |
| 6 | "clap", |
| 7 | "futures", |
| 8 | "maitred", |
| 9 | + "serde", |
| 10 | "tokio", |
| 11 | + "toml", |
| 12 | "tracing", |
| 13 | "tracing-subscriber", |
| 14 | ] |
| 15 | @@ -1306,18 +1308,18 @@ dependencies = [ |
| 16 | |
| 17 | [[package]] |
| 18 | name = "serde" |
| 19 | - version = "1.0.204" |
| 20 | + version = "1.0.209" |
| 21 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 22 | - checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" |
| 23 | + checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" |
| 24 | dependencies = [ |
| 25 | "serde_derive", |
| 26 | ] |
| 27 | |
| 28 | [[package]] |
| 29 | name = "serde_derive" |
| 30 | - version = "1.0.204" |
| 31 | + version = "1.0.209" |
| 32 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 33 | - checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" |
| 34 | + checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" |
| 35 | dependencies = [ |
| 36 | "proc-macro2", |
| 37 | "quote", |
| 38 | @@ -1337,6 +1339,15 @@ dependencies = [ |
| 39 | ] |
| 40 | |
| 41 | [[package]] |
| 42 | + name = "serde_spanned" |
| 43 | + version = "0.6.7" |
| 44 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
| 45 | + checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" |
| 46 | + dependencies = [ |
| 47 | + "serde", |
| 48 | + ] |
| 49 | + |
| 50 | + [[package]] |
| 51 | name = "sha1" |
| 52 | version = "0.10.6" |
| 53 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 54 | @@ -1584,6 +1595,40 @@ dependencies = [ |
| 55 | ] |
| 56 | |
| 57 | [[package]] |
| 58 | + name = "toml" |
| 59 | + version = "0.8.19" |
| 60 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
| 61 | + checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" |
| 62 | + dependencies = [ |
| 63 | + "serde", |
| 64 | + "serde_spanned", |
| 65 | + "toml_datetime", |
| 66 | + "toml_edit", |
| 67 | + ] |
| 68 | + |
| 69 | + [[package]] |
| 70 | + name = "toml_datetime" |
| 71 | + version = "0.6.8" |
| 72 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
| 73 | + checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" |
| 74 | + dependencies = [ |
| 75 | + "serde", |
| 76 | + ] |
| 77 | + |
| 78 | + [[package]] |
| 79 | + name = "toml_edit" |
| 80 | + version = "0.22.20" |
| 81 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
| 82 | + checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" |
| 83 | + dependencies = [ |
| 84 | + "indexmap", |
| 85 | + "serde", |
| 86 | + "serde_spanned", |
| 87 | + "toml_datetime", |
| 88 | + "winnow", |
| 89 | + ] |
| 90 | + |
| 91 | + [[package]] |
| 92 | name = "tracing" |
| 93 | version = "0.1.40" |
| 94 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 95 | @@ -1954,6 +1999,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" |
| 96 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" |
| 97 | |
| 98 | [[package]] |
| 99 | + name = "winnow" |
| 100 | + version = "0.6.18" |
| 101 | + source = "registry+https://github.com/rust-lang/crates.io-index" |
| 102 | + checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" |
| 103 | + dependencies = [ |
| 104 | + "memchr", |
| 105 | + ] |
| 106 | + |
| 107 | + [[package]] |
| 108 | name = "winreg" |
| 109 | version = "0.50.0" |
| 110 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 111 | diff --git a/cmd/maitred-debug/Cargo.toml b/cmd/maitred-debug/Cargo.toml |
| 112 | index 9792cd0..973dda8 100644 |
| 113 | --- a/cmd/maitred-debug/Cargo.toml |
| 114 | +++ b/cmd/maitred-debug/Cargo.toml |
| 115 | @@ -8,7 +8,9 @@ clap = { version = "4.5.16", features = ["derive"] } |
| 116 | futures = "0.3.30" |
| 117 | |
| 118 | maitred = {path = "../../maitred"} |
| 119 | + serde = "1.0.209" |
| 120 | tokio = { version = "1.39.2", features = ["full"] } |
| 121 | + toml = "0.8.19" |
| 122 | tracing = { version = "0.1.40", features = ["log"] } |
| 123 | tracing-subscriber = "0.3.18" |
| 124 | |
| 125 | diff --git a/cmd/maitred-debug/src/main.rs b/cmd/maitred-debug/src/main.rs |
| 126 | index 5a2d19b..e8e4382 100644 |
| 127 | --- a/cmd/maitred-debug/src/main.rs |
| 128 | +++ b/cmd/maitred-debug/src/main.rs |
| 129 | @@ -1,15 +1,13 @@ |
| 130 | - use std::{ |
| 131 | - path::{Path, PathBuf}, |
| 132 | - sync::Arc, |
| 133 | - }; |
| 134 | + use std::fs::read_to_string; |
| 135 | + use std::path::{Path, PathBuf}; |
| 136 | |
| 137 | use clap::Parser; |
| 138 | - use tokio::sync::{Mutex, RwLock}; |
| 139 | + use toml::from_str; |
| 140 | use tracing::Level; |
| 141 | |
| 142 | use maitred::{ |
| 143 | - mail_parser::Message, Delivery, DeliveryError, DeliveryFunc, Envelope, Error, Maildir, |
| 144 | - MilterFunc, PlainAuthFunc, Server, SessionOptions, |
| 145 | + mail_parser::Message, Delivery, DeliveryError, DeliveryFunc, Envelope, Maildir, MilterFunc, |
| 146 | + PlainAuthFunc, Server, SessionOptions, |
| 147 | }; |
| 148 | |
| 149 | async fn print_message(envelope: &Envelope) -> Result<(), DeliveryError> { |
| 150 | @@ -20,8 +18,27 @@ async fn print_message(envelope: &Envelope) -> Result<(), DeliveryError> { |
| 151 | Ok(()) |
| 152 | } |
| 153 | |
| 154 | + #[derive(Clone, serde::Deserialize)] |
| 155 | + struct Account { |
| 156 | + pub address: String, |
| 157 | + } |
| 158 | + |
| 159 | + #[derive(serde::Deserialize)] |
| 160 | + struct Config { |
| 161 | + pub maildir: String, |
| 162 | + pub accounts: Vec<Account>, |
| 163 | + } |
| 164 | + |
| 165 | + const LONG_ABOUT: &str = r#" |
| 166 | + Maitred SMTP Demo Server |
| 167 | + |
| 168 | + NOTE: This tool only exists for illustrative purposes and should not be |
| 169 | + considered stable or suitable for a production environment. |
| 170 | + "#; |
| 171 | + |
| 172 | + /// Maitred SMTP Demo Server |
| 173 | #[derive(Parser, Debug)] |
| 174 | - #[clap(author, version, about, long_about = None)] |
| 175 | + #[clap(author, version, about, long_about = LONG_ABOUT)] |
| 176 | struct Args { |
| 177 | /// Enable DKIM verification of incoming e-mail. |
| 178 | #[clap(long, default_value_t = false)] |
| 179 | @@ -30,24 +47,29 @@ struct Args { |
| 180 | #[clap(long, default_value_t = false)] |
| 181 | spf: bool, |
| 182 | /// Addresses from which to accept e-mail for |
| 183 | - #[clap(long)] |
| 184 | - addresses: Vec<String>, |
| 185 | - /// Path to a directory to store e-mails in the maildir format |
| 186 | - #[clap(long, default_value_t = String::from("mail"))] |
| 187 | - maildir: String, |
| 188 | + #[clap(long, default_value = "maitred.toml")] |
| 189 | + config: String, |
| 190 | } |
| 191 | |
| 192 | #[tokio::main] |
| 193 | - async fn main() -> Result<(), Error> { |
| 194 | + async fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 195 | let args = Args::parse(); |
| 196 | + let config_str = read_to_string(Path::new(&args.config))?; |
| 197 | + let config: Config = from_str(&config_str)?; |
| 198 | // Create a subscriber that logs events to the console |
| 199 | tracing_subscriber::fmt() |
| 200 | .compact() |
| 201 | .with_line_number(true) |
| 202 | .with_max_level(Level::DEBUG) |
| 203 | .init(); |
| 204 | - let maildir_path = PathBuf::from(&args.maildir); |
| 205 | - let addresses = args.addresses.clone(); |
| 206 | + let maildir_path = PathBuf::from(&config.maildir); |
| 207 | + let accounts = config.accounts.clone(); |
| 208 | + let addresses: Vec<String> = accounts |
| 209 | + .iter() |
| 210 | + .map(|account| account.address.clone()) |
| 211 | + .collect(); |
| 212 | + // initialize maildirs before starting |
| 213 | + let _ = Maildir::new(maildir_path.as_path(), &addresses)?; |
| 214 | // Set the subscriber as the default subscriber |
| 215 | let mut mail_server = Server::default() |
| 216 | .address("127.0.0.1:2525") |
| 217 | @@ -61,7 +83,7 @@ async fn main() -> Result<(), Error> { |
| 218 | let cloned = envelope.clone(); |
| 219 | async move { |
| 220 | print_message(&cloned).await?; |
| 221 | - let maildir = Maildir::new(maildir_path.as_path(), addresses.as_slice())?; |
| 222 | + let maildir = Maildir::new(maildir_path.as_path(), &addresses)?; |
| 223 | maildir.deliver(&cloned).await |
| 224 | } |
| 225 | })) |
| 226 | diff --git a/demo/user-1.neomuttrc b/demo/user-1.neomuttrc |
| 227 | new file mode 100644 |
| 228 | index 0000000..9974613 |
| 229 | --- /dev/null |
| 230 | +++ b/demo/user-1.neomuttrc |
| 231 | @@ -0,0 +1,51 @@ |
| 232 | + # vim: set filetype=neomuttrc : |
| 233 | + |
| 234 | + set mbox_type=Maildir |
| 235 | + set folder="mail/demo-1@example.org" |
| 236 | + set spoolfile=+ |
| 237 | + |
| 238 | + set realname = 'Demo User 1' |
| 239 | + set from = demo-1@example.org |
| 240 | + |
| 241 | + set smtp_url=smtp://demo-1@127.0.0.1:2525 |
| 242 | + set ssl_starttls = no |
| 243 | + set ssl_force_tls = false |
| 244 | + |
| 245 | + set sidebar_visible |
| 246 | + set sidebar_format = "%B%<F? [%F]>%* %<N?%N/>%S" |
| 247 | + # set mail_check_stats |
| 248 | + |
| 249 | + color normal default default # Text is "Text" |
| 250 | + color index color2 default ~N # New Messages are Green |
| 251 | + color index color1 default ~F # Flagged messages are Red |
| 252 | + color index color13 default ~T # Tagged Messages are Red |
| 253 | + color index color1 default ~D # Messages to delete are Red |
| 254 | + color attachment color5 default # Attachments are Pink |
| 255 | + color signature color8 default # Signatures are Surface 2 |
| 256 | + color search color4 default # Highlighted results are Blue |
| 257 | + |
| 258 | + color indicator default color8 # currently highlighted message Surface 2=Background Text=Foreground |
| 259 | + color error color1 default # error messages are Red |
| 260 | + color status color15 default # status line "Subtext 0" |
| 261 | + color tree color15 default # thread tree arrows Subtext 0 |
| 262 | + color tilde color15 default # blank line padding Subtext 0 |
| 263 | + |
| 264 | + color hdrdefault color13 default # default headers Pink |
| 265 | + color header color13 default "^From:" |
| 266 | + color header color13 default "^Subject:" |
| 267 | + |
| 268 | + color quoted color15 default # Subtext 0 |
| 269 | + color quoted1 color7 default # Subtext 1 |
| 270 | + color quoted2 color8 default # Surface 2 |
| 271 | + color quoted3 color0 default # Surface 1 |
| 272 | + color quoted4 color0 default |
| 273 | + color quoted5 color0 default |
| 274 | + |
| 275 | + color body color2 default [\-\.+_a-zA-Z0-9]+@[\-\.a-zA-Z0-9]+ # email addresses Green |
| 276 | + color body color2 default (https?|ftp)://[\-\.,/%~_:?&=\#a-zA-Z0-9]+ # URLs Green |
| 277 | + color body color4 default (^|[[:space:]])\\*[^[:space:]]+\\*([[:space:]]|$) # *bold* text Blue |
| 278 | + color body color4 default (^|[[:space:]])_[^[:space:]]+_([[:space:]]|$) # _underlined_ text Blue |
| 279 | + color body color4 default (^|[[:space:]])/[^[:space:]]+/([[:space:]]|$) # /italic/ text Blue |
| 280 | + |
| 281 | + color sidebar_flagged color1 default # Mailboxes with flagged mails are Red |
| 282 | + color sidebar_new color10 default # Mailboxes with new mail are Green |
| 283 | diff --git a/demo/user-2.neomuttrc b/demo/user-2.neomuttrc |
| 284 | new file mode 100644 |
| 285 | index 0000000..fda07f0 |
| 286 | --- /dev/null |
| 287 | +++ b/demo/user-2.neomuttrc |
| 288 | @@ -0,0 +1,51 @@ |
| 289 | + # vim: set filetype=neomuttrc : |
| 290 | + |
| 291 | + set mbox_type=Maildir |
| 292 | + set folder="mail/demo-2@example.org" |
| 293 | + set spoolfile=+ |
| 294 | + |
| 295 | + set realname = 'kevin' |
| 296 | + set from = kevin@ayllu-dev.local |
| 297 | + |
| 298 | + set smtp_url=smtp://kevin@127.0.0.1:2525 |
| 299 | + set ssl_starttls = no |
| 300 | + set ssl_force_tls = false |
| 301 | + |
| 302 | + set sidebar_visible |
| 303 | + set sidebar_format = "%B%<F? [%F]>%* %<N?%N/>%S" |
| 304 | + # set mail_check_stats |
| 305 | + |
| 306 | + color normal default default # Text is "Text" |
| 307 | + color index color2 default ~N # New Messages are Green |
| 308 | + color index color1 default ~F # Flagged messages are Red |
| 309 | + color index color13 default ~T # Tagged Messages are Red |
| 310 | + color index color1 default ~D # Messages to delete are Red |
| 311 | + color attachment color5 default # Attachments are Pink |
| 312 | + color signature color8 default # Signatures are Surface 2 |
| 313 | + color search color4 default # Highlighted results are Blue |
| 314 | + |
| 315 | + color indicator default color8 # currently highlighted message Surface 2=Background Text=Foreground |
| 316 | + color error color1 default # error messages are Red |
| 317 | + color status color15 default # status line "Subtext 0" |
| 318 | + color tree color15 default # thread tree arrows Subtext 0 |
| 319 | + color tilde color15 default # blank line padding Subtext 0 |
| 320 | + |
| 321 | + color hdrdefault color13 default # default headers Pink |
| 322 | + color header color13 default "^From:" |
| 323 | + color header color13 default "^Subject:" |
| 324 | + |
| 325 | + color quoted color15 default # Subtext 0 |
| 326 | + color quoted1 color7 default # Subtext 1 |
| 327 | + color quoted2 color8 default # Surface 2 |
| 328 | + color quoted3 color0 default # Surface 1 |
| 329 | + color quoted4 color0 default |
| 330 | + color quoted5 color0 default |
| 331 | + |
| 332 | + color body color2 default [\-\.+_a-zA-Z0-9]+@[\-\.a-zA-Z0-9]+ # email addresses Green |
| 333 | + color body color2 default (https?|ftp)://[\-\.,/%~_:?&=\#a-zA-Z0-9]+ # URLs Green |
| 334 | + color body color4 default (^|[[:space:]])\\*[^[:space:]]+\\*([[:space:]]|$) # *bold* text Blue |
| 335 | + color body color4 default (^|[[:space:]])_[^[:space:]]+_([[:space:]]|$) # _underlined_ text Blue |
| 336 | + color body color4 default (^|[[:space:]])/[^[:space:]]+/([[:space:]]|$) # /italic/ text Blue |
| 337 | + |
| 338 | + color sidebar_flagged color1 default # Mailboxes with flagged mails are Red |
| 339 | + color sidebar_new color10 default # Mailboxes with new mail are Green |
| 340 | diff --git a/maitred.toml b/maitred.toml |
| 341 | new file mode 100644 |
| 342 | index 0000000..0fcf4e1 |
| 343 | --- /dev/null |
| 344 | +++ b/maitred.toml |
| 345 | @@ -0,0 +1,9 @@ |
| 346 | + # Path of the directory to deliver mail in the "maildir" format to |
| 347 | + maildir = "mail" |
| 348 | + |
| 349 | + # List of user accounts and their hard coded username / passwords |
| 350 | + [[accounts]] |
| 351 | + address = "demo-1@example.org" |
| 352 | + |
| 353 | + [[accounts]] |
| 354 | + address = "demo-2@example.org" |
| 355 | diff --git a/scripts/swaks_test.sh b/scripts/swaks_test.sh |
| 356 | index e73450b..78c933b 100755 |
| 357 | --- a/scripts/swaks_test.sh |
| 358 | +++ b/scripts/swaks_test.sh |
| 359 | @@ -3,4 +3,11 @@ |
| 360 | # Uses swaks: https://www.jetmore.org/john/code/swaks/ to do some basic SMTP |
| 361 | # verification. Make sure you install the tool first! |
| 362 | |
| 363 | - printf "Subject: Hello\nWorld\n" | swaks --to hello@example.com --server localhost:2525 --pipeline --data - |
| 364 | + make_email() { |
| 365 | + printf "From: aaaa@example.org\n" |
| 366 | + printf "To: hello@example.org\n" |
| 367 | + printf "Subject: Hello\n" |
| 368 | + printf "World\n" |
| 369 | + } |
| 370 | + |
| 371 | + make_email | swaks --to hello@example.org --server localhost:2525 --pipeline --data - |