Initial commit
Seto committed
Oct 9, 2025 at 14:13 UTC
d047d95d3fe9913a8374b63437dda2b94988f66f
11 files changed
+1096
.gitignore
new
+1
@@ -0,0 +1 @@
1
+/target
Cargo.lock
new
+763
@@ -0,0 +1,763 @@
1
+# This file is automatically @generated by Cargo.
2
+# It is not intended for manual editing.
3
+version = 4
4
+
5
+[[package]]
6
+name = "addr2line"
7
+version = "0.25.1"
8
+source = "registry+https://github.com/rust-lang/crates.io-index"
9
+checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
10
+dependencies = [
11
+ "gimli",
12
+]
13
+
14
+[[package]]
15
+name = "adler2"
16
+version = "2.0.1"
17
+source = "registry+https://github.com/rust-lang/crates.io-index"
18
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
19
+
20
+[[package]]
21
+name = "aho-corasick"
22
+version = "1.1.3"
23
+source = "registry+https://github.com/rust-lang/crates.io-index"
24
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
25
+dependencies = [
26
+ "memchr",
27
+]
28
+
29
+[[package]]
30
+name = "anyhow"
31
+version = "1.0.100"
32
+source = "registry+https://github.com/rust-lang/crates.io-index"
33
+checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
34
+
35
+[[package]]
36
+name = "backtrace"
37
+version = "0.3.76"
38
+source = "registry+https://github.com/rust-lang/crates.io-index"
39
+checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
40
+dependencies = [
41
+ "addr2line",
42
+ "cfg-if",
43
+ "libc",
44
+ "miniz_oxide",
45
+ "object",
46
+ "rustc-demangle",
47
+ "windows-link",
48
+]
49
+
50
+[[package]]
51
+name = "bitflags"
52
+version = "2.9.4"
53
+source = "registry+https://github.com/rust-lang/crates.io-index"
54
+checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
55
+
56
+[[package]]
57
+name = "bytes"
58
+version = "1.10.1"
59
+source = "registry+https://github.com/rust-lang/crates.io-index"
60
+checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
61
+
62
+[[package]]
63
+name = "cc"
64
+version = "1.2.40"
65
+source = "registry+https://github.com/rust-lang/crates.io-index"
66
+checksum = "e1d05d92f4b1fd76aad469d46cdd858ca761576082cd37df81416691e50199fb"
67
+dependencies = [
68
+ "find-msvc-tools",
69
+ "shlex",
70
+]
71
+
72
+[[package]]
73
+name = "cfg-if"
74
+version = "1.0.3"
75
+source = "registry+https://github.com/rust-lang/crates.io-index"
76
+checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
77
+
78
+[[package]]
79
+name = "core-foundation"
80
+version = "0.9.4"
81
+source = "registry+https://github.com/rust-lang/crates.io-index"
82
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
83
+dependencies = [
84
+ "core-foundation-sys",
85
+ "libc",
86
+]
87
+
88
+[[package]]
89
+name = "core-foundation-sys"
90
+version = "0.8.7"
91
+source = "registry+https://github.com/rust-lang/crates.io-index"
92
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
93
+
94
+[[package]]
95
+name = "errno"
96
+version = "0.3.14"
97
+source = "registry+https://github.com/rust-lang/crates.io-index"
98
+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
99
+dependencies = [
100
+ "libc",
101
+ "windows-sys 0.59.0",
102
+]
103
+
104
+[[package]]
105
+name = "fastrand"
106
+version = "2.3.0"
107
+source = "registry+https://github.com/rust-lang/crates.io-index"
108
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
109
+
110
+[[package]]
111
+name = "find-msvc-tools"
112
+version = "0.1.3"
113
+source = "registry+https://github.com/rust-lang/crates.io-index"
114
+checksum = "0399f9d26e5191ce32c498bebd31e7a3ceabc2745f0ac54af3f335126c3f24b3"
115
+
116
+[[package]]
117
+name = "foreign-types"
118
+version = "0.3.2"
119
+source = "registry+https://github.com/rust-lang/crates.io-index"
120
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
121
+dependencies = [
122
+ "foreign-types-shared",
123
+]
124
+
125
+[[package]]
126
+name = "foreign-types-shared"
127
+version = "0.1.1"
128
+source = "registry+https://github.com/rust-lang/crates.io-index"
129
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
130
+
131
+[[package]]
132
+name = "futures"
133
+version = "0.3.31"
134
+source = "registry+https://github.com/rust-lang/crates.io-index"
135
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
136
+dependencies = [
137
+ "futures-channel",
138
+ "futures-core",
139
+ "futures-executor",
140
+ "futures-io",
141
+ "futures-sink",
142
+ "futures-task",
143
+ "futures-util",
144
+]
145
+
146
+[[package]]
147
+name = "futures-channel"
148
+version = "0.3.31"
149
+source = "registry+https://github.com/rust-lang/crates.io-index"
150
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
151
+dependencies = [
152
+ "futures-core",
153
+ "futures-sink",
154
+]
155
+
156
+[[package]]
157
+name = "futures-core"
158
+version = "0.3.31"
159
+source = "registry+https://github.com/rust-lang/crates.io-index"
160
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
161
+
162
+[[package]]
163
+name = "futures-executor"
164
+version = "0.3.31"
165
+source = "registry+https://github.com/rust-lang/crates.io-index"
166
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
167
+dependencies = [
168
+ "futures-core",
169
+ "futures-task",
170
+ "futures-util",
171
+]
172
+
173
+[[package]]
174
+name = "futures-io"
175
+version = "0.3.31"
176
+source = "registry+https://github.com/rust-lang/crates.io-index"
177
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
178
+
179
+[[package]]
180
+name = "futures-macro"
181
+version = "0.3.31"
182
+source = "registry+https://github.com/rust-lang/crates.io-index"
183
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
184
+dependencies = [
185
+ "proc-macro2",
186
+ "quote",
187
+ "syn",
188
+]
189
+
190
+[[package]]
191
+name = "futures-sink"
192
+version = "0.3.31"
193
+source = "registry+https://github.com/rust-lang/crates.io-index"
194
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
195
+
196
+[[package]]
197
+name = "futures-task"
198
+version = "0.3.31"
199
+source = "registry+https://github.com/rust-lang/crates.io-index"
200
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
201
+
202
+[[package]]
203
+name = "futures-util"
204
+version = "0.3.31"
205
+source = "registry+https://github.com/rust-lang/crates.io-index"
206
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
207
+dependencies = [
208
+ "futures-channel",
209
+ "futures-core",
210
+ "futures-io",
211
+ "futures-macro",
212
+ "futures-sink",
213
+ "futures-task",
214
+ "memchr",
215
+ "pin-project-lite",
216
+ "pin-utils",
217
+ "slab",
218
+]
219
+
220
+[[package]]
221
+name = "getrandom"
222
+version = "0.3.3"
223
+source = "registry+https://github.com/rust-lang/crates.io-index"
224
+checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
225
+dependencies = [
226
+ "cfg-if",
227
+ "libc",
228
+ "r-efi",
229
+ "wasi 0.14.7+wasi-0.2.4",
230
+]
231
+
232
+[[package]]
233
+name = "gimli"
234
+version = "0.32.3"
235
+source = "registry+https://github.com/rust-lang/crates.io-index"
236
+checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
237
+
238
+[[package]]
239
+name = "io-uring"
240
+version = "0.7.10"
241
+source = "registry+https://github.com/rust-lang/crates.io-index"
242
+checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
243
+dependencies = [
244
+ "bitflags",
245
+ "cfg-if",
246
+ "libc",
247
+]
248
+
249
+[[package]]
250
+name = "libc"
251
+version = "0.2.176"
252
+source = "registry+https://github.com/rust-lang/crates.io-index"
253
+checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
254
+
255
+[[package]]
256
+name = "linux-raw-sys"
257
+version = "0.11.0"
258
+source = "registry+https://github.com/rust-lang/crates.io-index"
259
+checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
260
+
261
+[[package]]
262
+name = "log"
263
+version = "0.4.28"
264
+source = "registry+https://github.com/rust-lang/crates.io-index"
265
+checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
266
+
267
+[[package]]
268
+name = "memchr"
269
+version = "2.7.6"
270
+source = "registry+https://github.com/rust-lang/crates.io-index"
271
+checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
272
+
273
+[[package]]
274
+name = "miniz_oxide"
275
+version = "0.8.9"
276
+source = "registry+https://github.com/rust-lang/crates.io-index"
277
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
278
+dependencies = [
279
+ "adler2",
280
+]
281
+
282
+[[package]]
283
+name = "mio"
284
+version = "1.0.4"
285
+source = "registry+https://github.com/rust-lang/crates.io-index"
286
+checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
287
+dependencies = [
288
+ "libc",
289
+ "wasi 0.11.1+wasi-snapshot-preview1",
290
+ "windows-sys 0.59.0",
291
+]
292
+
293
+[[package]]
294
+name = "native-tls"
295
+version = "0.2.14"
296
+source = "registry+https://github.com/rust-lang/crates.io-index"
297
+checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
298
+dependencies = [
299
+ "libc",
300
+ "log",
301
+ "openssl",
302
+ "openssl-probe",
303
+ "openssl-sys",
304
+ "schannel",
305
+ "security-framework",
306
+ "security-framework-sys",
307
+ "tempfile",
308
+]
309
+
310
+[[package]]
311
+name = "object"
312
+version = "0.37.3"
313
+source = "registry+https://github.com/rust-lang/crates.io-index"
314
+checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
315
+dependencies = [
316
+ "memchr",
317
+]
318
+
319
+[[package]]
320
+name = "once_cell"
321
+version = "1.21.3"
322
+source = "registry+https://github.com/rust-lang/crates.io-index"
323
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
324
+
325
+[[package]]
326
+name = "openssl"
327
+version = "0.10.73"
328
+source = "registry+https://github.com/rust-lang/crates.io-index"
329
+checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
330
+dependencies = [
331
+ "bitflags",
332
+ "cfg-if",
333
+ "foreign-types",
334
+ "libc",
335
+ "once_cell",
336
+ "openssl-macros",
337
+ "openssl-sys",
338
+]
339
+
340
+[[package]]
341
+name = "openssl-macros"
342
+version = "0.1.1"
343
+source = "registry+https://github.com/rust-lang/crates.io-index"
344
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
345
+dependencies = [
346
+ "proc-macro2",
347
+ "quote",
348
+ "syn",
349
+]
350
+
351
+[[package]]
352
+name = "openssl-probe"
353
+version = "0.1.6"
354
+source = "registry+https://github.com/rust-lang/crates.io-index"
355
+checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
356
+
357
+[[package]]
358
+name = "openssl-sys"
359
+version = "0.9.109"
360
+source = "registry+https://github.com/rust-lang/crates.io-index"
361
+checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
362
+dependencies = [
363
+ "cc",
364
+ "libc",
365
+ "pkg-config",
366
+ "vcpkg",
367
+]
368
+
369
+[[package]]
370
+name = "pin-project-lite"
371
+version = "0.2.16"
372
+source = "registry+https://github.com/rust-lang/crates.io-index"
373
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
374
+
375
+[[package]]
376
+name = "pin-utils"
377
+version = "0.1.0"
378
+source = "registry+https://github.com/rust-lang/crates.io-index"
379
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
380
+
381
+[[package]]
382
+name = "pkg-config"
383
+version = "0.3.32"
384
+source = "registry+https://github.com/rust-lang/crates.io-index"
385
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
386
+
387
+[[package]]
388
+name = "proc-macro2"
389
+version = "1.0.101"
390
+source = "registry+https://github.com/rust-lang/crates.io-index"
391
+checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
392
+dependencies = [
393
+ "unicode-ident",
394
+]
395
+
396
+[[package]]
397
+name = "quote"
398
+version = "1.0.41"
399
+source = "registry+https://github.com/rust-lang/crates.io-index"
400
+checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
401
+dependencies = [
402
+ "proc-macro2",
403
+]
404
+
405
+[[package]]
406
+name = "r-efi"
407
+version = "5.3.0"
408
+source = "registry+https://github.com/rust-lang/crates.io-index"
409
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
410
+
411
+[[package]]
412
+name = "regex"
413
+version = "1.11.3"
414
+source = "registry+https://github.com/rust-lang/crates.io-index"
415
+checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c"
416
+dependencies = [
417
+ "aho-corasick",
418
+ "memchr",
419
+ "regex-automata",
420
+ "regex-syntax",
421
+]
422
+
423
+[[package]]
424
+name = "regex-automata"
425
+version = "0.4.11"
426
+source = "registry+https://github.com/rust-lang/crates.io-index"
427
+checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad"
428
+dependencies = [
429
+ "aho-corasick",
430
+ "memchr",
431
+ "regex-syntax",
432
+]
433
+
434
+[[package]]
435
+name = "regex-syntax"
436
+version = "0.8.6"
437
+source = "registry+https://github.com/rust-lang/crates.io-index"
438
+checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
439
+
440
+[[package]]
441
+name = "rustc-demangle"
442
+version = "0.1.26"
443
+source = "registry+https://github.com/rust-lang/crates.io-index"
444
+checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
445
+
446
+[[package]]
447
+name = "rustix"
448
+version = "1.1.2"
449
+source = "registry+https://github.com/rust-lang/crates.io-index"
450
+checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
451
+dependencies = [
452
+ "bitflags",
453
+ "errno",
454
+ "libc",
455
+ "linux-raw-sys",
456
+ "windows-sys 0.59.0",
457
+]
458
+
459
+[[package]]
460
+name = "schannel"
461
+version = "0.1.28"
462
+source = "registry+https://github.com/rust-lang/crates.io-index"
463
+checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1"
464
+dependencies = [
465
+ "windows-sys 0.61.2",
466
+]
467
+
468
+[[package]]
469
+name = "security-framework"
470
+version = "2.11.1"
471
+source = "registry+https://github.com/rust-lang/crates.io-index"
472
+checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
473
+dependencies = [
474
+ "bitflags",
475
+ "core-foundation",
476
+ "core-foundation-sys",
477
+ "libc",
478
+ "security-framework-sys",
479
+]
480
+
481
+[[package]]
482
+name = "security-framework-sys"
483
+version = "2.15.0"
484
+source = "registry+https://github.com/rust-lang/crates.io-index"
485
+checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0"
486
+dependencies = [
487
+ "core-foundation-sys",
488
+ "libc",
489
+]
490
+
491
+[[package]]
492
+name = "shlex"
493
+version = "1.3.0"
494
+source = "registry+https://github.com/rust-lang/crates.io-index"
495
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
496
+
497
+[[package]]
498
+name = "slab"
499
+version = "0.4.11"
500
+source = "registry+https://github.com/rust-lang/crates.io-index"
501
+checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
502
+
503
+[[package]]
504
+name = "smbcloud-mail"
505
+version = "0.1.0"
506
+dependencies = [
507
+ "anyhow",
508
+ "futures",
509
+ "native-tls",
510
+ "openssl",
511
+ "regex",
512
+ "tokio",
513
+ "tokio-native-tls",
514
+ "tokio-util",
515
+ "tracing",
516
+]
517
+
518
+[[package]]
519
+name = "socket2"
520
+version = "0.6.0"
521
+source = "registry+https://github.com/rust-lang/crates.io-index"
522
+checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
523
+dependencies = [
524
+ "libc",
525
+ "windows-sys 0.59.0",
526
+]
527
+
528
+[[package]]
529
+name = "syn"
530
+version = "2.0.106"
531
+source = "registry+https://github.com/rust-lang/crates.io-index"
532
+checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
533
+dependencies = [
534
+ "proc-macro2",
535
+ "quote",
536
+ "unicode-ident",
537
+]
538
+
539
+[[package]]
540
+name = "tempfile"
541
+version = "3.23.0"
542
+source = "registry+https://github.com/rust-lang/crates.io-index"
543
+checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
544
+dependencies = [
545
+ "fastrand",
546
+ "getrandom",
547
+ "once_cell",
548
+ "rustix",
549
+ "windows-sys 0.59.0",
550
+]
551
+
552
+[[package]]
553
+name = "tokio"
554
+version = "1.47.1"
555
+source = "registry+https://github.com/rust-lang/crates.io-index"
556
+checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
557
+dependencies = [
558
+ "backtrace",
559
+ "bytes",
560
+ "io-uring",
561
+ "libc",
562
+ "mio",
563
+ "pin-project-lite",
564
+ "slab",
565
+ "socket2",
566
+ "tokio-macros",
567
+ "windows-sys 0.59.0",
568
+]
569
+
570
+[[package]]
571
+name = "tokio-macros"
572
+version = "2.5.0"
573
+source = "registry+https://github.com/rust-lang/crates.io-index"
574
+checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
575
+dependencies = [
576
+ "proc-macro2",
577
+ "quote",
578
+ "syn",
579
+]
580
+
581
+[[package]]
582
+name = "tokio-native-tls"
583
+version = "0.3.1"
584
+source = "registry+https://github.com/rust-lang/crates.io-index"
585
+checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
586
+dependencies = [
587
+ "native-tls",
588
+ "tokio",
589
+]
590
+
591
+[[package]]
592
+name = "tokio-util"
593
+version = "0.7.16"
594
+source = "registry+https://github.com/rust-lang/crates.io-index"
595
+checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
596
+dependencies = [
597
+ "bytes",
598
+ "futures-core",
599
+ "futures-sink",
600
+ "pin-project-lite",
601
+ "tokio",
602
+]
603
+
604
+[[package]]
605
+name = "tracing"
606
+version = "0.1.41"
607
+source = "registry+https://github.com/rust-lang/crates.io-index"
608
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
609
+dependencies = [
610
+ "pin-project-lite",
611
+ "tracing-attributes",
612
+ "tracing-core",
613
+]
614
+
615
+[[package]]
616
+name = "tracing-attributes"
617
+version = "0.1.30"
618
+source = "registry+https://github.com/rust-lang/crates.io-index"
619
+checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
620
+dependencies = [
621
+ "proc-macro2",
622
+ "quote",
623
+ "syn",
624
+]
625
+
626
+[[package]]
627
+name = "tracing-core"
628
+version = "0.1.34"
629
+source = "registry+https://github.com/rust-lang/crates.io-index"
630
+checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
631
+dependencies = [
632
+ "once_cell",
633
+]
634
+
635
+[[package]]
636
+name = "unicode-ident"
637
+version = "1.0.19"
638
+source = "registry+https://github.com/rust-lang/crates.io-index"
639
+checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
640
+
641
+[[package]]
642
+name = "vcpkg"
643
+version = "0.2.15"
644
+source = "registry+https://github.com/rust-lang/crates.io-index"
645
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
646
+
647
+[[package]]
648
+name = "wasi"
649
+version = "0.11.1+wasi-snapshot-preview1"
650
+source = "registry+https://github.com/rust-lang/crates.io-index"
651
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
652
+
653
+[[package]]
654
+name = "wasi"
655
+version = "0.14.7+wasi-0.2.4"
656
+source = "registry+https://github.com/rust-lang/crates.io-index"
657
+checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c"
658
+dependencies = [
659
+ "wasip2",
660
+]
661
+
662
+[[package]]
663
+name = "wasip2"
664
+version = "1.0.1+wasi-0.2.4"
665
+source = "registry+https://github.com/rust-lang/crates.io-index"
666
+checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
667
+dependencies = [
668
+ "wit-bindgen",
669
+]
670
+
671
+[[package]]
672
+name = "windows-link"
673
+version = "0.2.1"
674
+source = "registry+https://github.com/rust-lang/crates.io-index"
675
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
676
+
677
+[[package]]
678
+name = "windows-sys"
679
+version = "0.59.0"
680
+source = "registry+https://github.com/rust-lang/crates.io-index"
681
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
682
+dependencies = [
683
+ "windows-targets",
684
+]
685
+
686
+[[package]]
687
+name = "windows-sys"
688
+version = "0.61.2"
689
+source = "registry+https://github.com/rust-lang/crates.io-index"
690
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
691
+dependencies = [
692
+ "windows-link",
693
+]
694
+
695
+[[package]]
696
+name = "windows-targets"
697
+version = "0.52.6"
698
+source = "registry+https://github.com/rust-lang/crates.io-index"
699
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
700
+dependencies = [
701
+ "windows_aarch64_gnullvm",
702
+ "windows_aarch64_msvc",
703
+ "windows_i686_gnu",
704
+ "windows_i686_gnullvm",
705
+ "windows_i686_msvc",
706
+ "windows_x86_64_gnu",
707
+ "windows_x86_64_gnullvm",
708
+ "windows_x86_64_msvc",
709
+]
710
+
711
+[[package]]
712
+name = "windows_aarch64_gnullvm"
713
+version = "0.52.6"
714
+source = "registry+https://github.com/rust-lang/crates.io-index"
715
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
716
+
717
+[[package]]
718
+name = "windows_aarch64_msvc"
719
+version = "0.52.6"
720
+source = "registry+https://github.com/rust-lang/crates.io-index"
721
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
722
+
723
+[[package]]
724
+name = "windows_i686_gnu"
725
+version = "0.52.6"
726
+source = "registry+https://github.com/rust-lang/crates.io-index"
727
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
728
+
729
+[[package]]
730
+name = "windows_i686_gnullvm"
731
+version = "0.52.6"
732
+source = "registry+https://github.com/rust-lang/crates.io-index"
733
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
734
+
735
+[[package]]
736
+name = "windows_i686_msvc"
737
+version = "0.52.6"
738
+source = "registry+https://github.com/rust-lang/crates.io-index"
739
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
740
+
741
+[[package]]
742
+name = "windows_x86_64_gnu"
743
+version = "0.52.6"
744
+source = "registry+https://github.com/rust-lang/crates.io-index"
745
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
746
+
747
+[[package]]
748
+name = "windows_x86_64_gnullvm"
749
+version = "0.52.6"
750
+source = "registry+https://github.com/rust-lang/crates.io-index"
751
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
752
+
753
+[[package]]
754
+name = "windows_x86_64_msvc"
755
+version = "0.52.6"
756
+source = "registry+https://github.com/rust-lang/crates.io-index"
757
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
758
+
759
+[[package]]
760
+name = "wit-bindgen"
761
+version = "0.46.0"
762
+source = "registry+https://github.com/rust-lang/crates.io-index"
763
+checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
Cargo.toml
new
+15
@@ -0,0 +1,15 @@
1
+[package]
2
+name = "smbcloud-mail"
3
+version = "0.1.0"
4
+edition = "2024"
5
+
6
+[dependencies]
7
+tokio = { version = "1", features = [ "macros","io-util","net","rt", "rt-multi-thread"] }
8
+tokio-util = { version = "0.7", features = ["codec"] }
9
+tokio-native-tls = "0.3"
10
+native-tls = "0.2"
11
+anyhow = "1"
12
+tracing = "0.1"
13
+regex = "1"
14
+futures = "0.3"
15
+openssl = "0.10"
src/generate_certificate.rs
new
+46
@@ -0,0 +1,46 @@
1
+use openssl::{
2
+ asn1::Asn1Time,
3
+ pkey::PKey,
4
+ rsa::Rsa,
5
+ x509::{
6
+ X509, X509NameBuilder,
7
+ extension::{AuthorityKeyIdentifier, BasicConstraints, SubjectKeyIdentifier},
8
+ },
9
+};
10
+
11
+pub fn generate_certificate() -> anyhow::Result<(String, String)> {
12
+ let cert_result = {
13
+ let rsa = Rsa::generate(4096)?;
14
+ let pkey = PKey::from_rsa(rsa)?;
15
+ let mut name = X509NameBuilder::new()?;
16
+ name.append_entry_by_text("CN", "localhost")?;
17
+ let name = name.build();
18
+ let mut builder = X509::builder()?;
19
+ builder.set_version(2)?;
20
+ builder.set_subject_name(&name)?;
21
+ builder.set_issuer_name(&name)?;
22
+ builder.set_pubkey(&pkey)?;
23
+ let now = Asn1Time::days_from_now(0)?;
24
+ let later = Asn1Time::days_from_now(3650)?;
25
+ builder.set_not_before(now.as_ref())?;
26
+ builder.set_not_after(later.as_ref())?;
27
+ builder.append_extension(BasicConstraints::new().critical().ca().build()?)?;
28
+ builder.append_extension(
29
+ SubjectKeyIdentifier::new().build(&builder.x509v3_context(None, None))?,
30
+ )?;
31
+ builder.append_extension(
32
+ AuthorityKeyIdentifier::new()
33
+ .keyid(true)
34
+ .issuer(true)
35
+ .build(&builder.x509v3_context(None, None))?,
36
+ )?;
37
+ builder.sign(&pkey, openssl::hash::MessageDigest::sha256())?;
38
+ let c = builder.build();
39
+ Ok((c.to_pem()?, pkey.private_key_to_pem_pkcs8()?))
40
+ };
41
+ let (pem_certificate, pem_private_key) = cert_result
42
+ .as_ref()
43
+ .map_err(|e| anyhow::anyhow!("Could not generate self-signed certificates: {}", e))?;
44
+
45
+ Ok((pem_certificate, pem_private_key))
46
+}
src/handle_session.rs
new
+123
@@ -0,0 +1,123 @@
1
+use futures::{SinkExt, StreamExt, stream::iter};
2
+use tokio::net::TcpStream;
3
+use tokio_util::codec::{Framed, LinesCodec, LinesCodecError};
4
+
5
+use crate::send_commands::send_commands;
6
+
7
+enum SmtpState {
8
+ Command,
9
+ Data,
10
+ Quit,
11
+}
12
+
13
+pub async fn handle_session(mut stream: TcpStream) -> anyhow::Result<()> {
14
+ let RE_SMTP_MAIL = regex::Regex::new(r"(?i)from: ?<(.+)>").unwrap();
15
+ let RE_SMTP_RCPT = regex::Regex::new(r"(?i)to: ?<(.+)>").unwrap();
16
+ let mut message = String::new();
17
+ let mut state = SmtpState::Command;
18
+ let mut mailfrom: Option<String> = None;
19
+ let mut rcpts: Vec<String> = Vec::new();
20
+ let mut framed = Framed::new(stream, LinesCodec::new());
21
+ while let Some(line_str) = framed.next().await {
22
+ let line = line_str?;
23
+ match state {
24
+ SmtpState::Command => {
25
+ let space_pos = line.find(" ").unwrap_or(line.len());
26
+ let (command, arg) = line.split_at(space_pos);
27
+ let arg = arg.trim();
28
+ match &*command.trim().to_uppercase() {
29
+ "HELO" | "EHLO" => {
30
+ send_commands(&mut framed, vec!["250 Hello".to_string()]).await?;
31
+ }
32
+ "MAIL" => {
33
+ // Handle MAIL FROM command
34
+ if let Some(address) = RE_SMTP_MAIL.captures(arg).and_then(|cap| cap.get(1))
35
+ {
36
+ mailfrom = Some(address.as_str().to_string());
37
+ send_commands(&mut framed, vec!["250 OK".to_string()]).await?;
38
+ } else {
39
+ send_commands(
40
+ &mut framed,
41
+ vec!["501 Syntax: MAIL From: <address>".to_string()],
42
+ )
43
+ .await?;
44
+ }
45
+ }
46
+ "RCPT" => {
47
+ // Handle RCPT TO command
48
+ if mailfrom.is_none() {
49
+ send_commands(
50
+ &mut framed,
51
+ vec!["503 Error: Send MAIL first".to_string()],
52
+ )
53
+ .await?;
54
+ } else {
55
+ if let Some(address) =
56
+ RE_SMTP_RCPT.captures(arg).and_then(|cap| cap.get(1))
57
+ {
58
+ rcpts.push(address.as_str().to_string());
59
+ send_commands(&mut framed, vec!["250 OK".to_string()]).await?;
60
+ } else {
61
+ send_commands(
62
+ &mut framed,
63
+ vec!["501 Syntax: RCPT TO: <address>".to_string()],
64
+ )
65
+ .await?;
66
+ }
67
+ }
68
+ }
69
+ "DATA" => {
70
+ if rcpts.is_empty() {
71
+ send_commands(&mut framed, vec!["503 Error: MAIL FROM and RCPT TO must be set before sending DATA".to_string()]).await?;
72
+ } else {
73
+ state = SmtpState::Data;
74
+ send_commands(
75
+ &mut framed,
76
+ vec!["354 End data with <CR><LF>.<CR><LF>".to_string()],
77
+ )
78
+ .await?;
79
+ }
80
+ }
81
+ "NOOP" => {
82
+ send_commands(&mut framed, vec!["250 OK".to_string()]).await?;
83
+ }
84
+ "RSET" => {
85
+ mailfrom = None;
86
+ rcpts = Vec::new();
87
+ message = String::new();
88
+ send_commands(&mut framed, vec!["250 OK".to_string()]).await?;
89
+ }
90
+ "QUIT" => {
91
+ send_commands(&mut framed, vec!["221 Bye".to_string()]).await?;
92
+ state = SmtpState::Quit;
93
+ }
94
+ _ => {
95
+ send_commands(&mut framed, vec!["500 Unknown command".to_string()]).await?;
96
+ }
97
+ }
98
+ }
99
+ SmtpState::Data => {
100
+ if line.trim() == "." {
101
+ // The end of the email content has been received
102
+ send_commands(&mut framed, vec!["250 OK".to_string()]).await?;
103
+ // reset the state and variables for the next email
104
+ mailfrom = None;
105
+ rcpts = Vec::new();
106
+ message = String::new();
107
+ state = SmtpState::Command;
108
+ // we can now handle the email:
109
+ //handle_email(mailfrom, rcpts, message);
110
+ panic!()
111
+ } else {
112
+ // Add the received line to the email content
113
+ message.push_str(&line);
114
+ message.push_str("\n");
115
+ }
116
+ }
117
+ SmtpState::Quit => {
118
+ break;
119
+ }
120
+ }
121
+ }
122
+ Ok(())
123
+}
src/handle_starttls.rs
new
+24
@@ -0,0 +1,24 @@
1
+use native_tls::{Identity, TlsAcceptor};
2
+use tokio::net::TcpStream;
3
+use tokio_native_tls::{TlsAcceptor as TokioTlsAcceptor, TlsStream};
4
+
5
+use crate::{generate_certificate::generate_certificate, handle_session::handle_session};
6
+
7
+async fn handle_starttls(stream: &mut TcpStream) -> anyhow::Result<()> {
8
+ // ideally the certificate should only be loaded from here and not generated each time
9
+ let (pem_certificate, pem_private_key) = generate_certificate()?;
10
+ let identity = Identity::from_pkcs8(&pem_certificate, &pem_private_key)?;
11
+ let tls_acceptor = TlsAcceptor::builder(identity).build()?;
12
+ let tls_acceptor = TokioTlsAcceptor::from(tls_acceptor);
13
+
14
+ match tls_acceptor.accept(stream).await {
15
+ Ok(stream) => {
16
+ // we can now handle the normal SMTP session
17
+ handle_session(stream).await?;
18
+ }
19
+ Err(e) => {
20
+ tracing::error!("Error establishing SMTP TLS connection: {:?}", e);
21
+ }
22
+ };
23
+ Ok(())
24
+}
src/handle_unsecured_session.rs
new
+58
@@ -0,0 +1,58 @@
1
+use tokio::{
2
+ io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter},
3
+ net::TcpStream,
4
+};
5
+
6
+use crate::handle_starttls;
7
+
8
+async fn handle_unsecured_session(
9
+ reader: &mut BufReader<TcpStream>,
10
+ writer: &mut BufWriter<TcpStream>,
11
+) -> anyhow::Result<()> {
12
+ let mut is_tls = false;
13
+ let mut line = String::new();
14
+ while reader.read_line(&mut line).await? != 0 {
15
+ let space_pos = line.find(" ").unwrap_or(line.len());
16
+ let (command, _) = line.split_at(space_pos);
17
+
18
+ match command.trim().to_uppercase().as_ref() {
19
+ "EHLO" | "HELO" => {
20
+ writer.write_all(b"250-windmill Hello\r\n").await?;
21
+ writer.write_all(b"250-STARTTLS\r\n").await?;
22
+ writer.write_all(b"250 What you've got?\r\n").await?;
23
+ writer.flush().await?;
24
+ }
25
+ "STARTTLS" => {
26
+ writer.write_all(b"220 GO ON\r\n").await?;
27
+ writer.flush().await?;
28
+ is_tls = true;
29
+ break;
30
+ }
31
+ "QUIT" => {
32
+ writer.write_all(b"221 Have a nice day!\r\n").await?;
33
+ writer.flush().await?;
34
+ break;
35
+ }
36
+ "NOOP" => {
37
+ writer.write_all(b"250 OK\r\n").await?;
38
+ writer.flush().await?;
39
+ }
40
+ "MAIL" | "RCPT" | "DATA" | "RSET" => {
41
+ writer
42
+ .write_all(b"530 Must issue a STARTTLS command first\r\n")
43
+ .await?;
44
+ writer.flush().await?;
45
+ }
46
+ _ => {
47
+ writer.write_all(b"500 Unknown command\r\n").await?;
48
+ writer.flush().await?;
49
+ }
50
+ }
51
+
52
+ line.clear();
53
+ }
54
+
55
+ if is_tls {
56
+ handle_starttls(stream).await?
57
+ }
58
+}
src/lib.rs
new
+6
@@ -0,0 +1,6 @@
1
+pub mod generate_certificate;
2
+pub mod handle_session;
3
+pub mod handle_starttls;
4
+pub mod handle_unsecured_session;
5
+pub mod listen;
6
+pub mod send_commands;
src/listen.rs
new
+39
@@ -0,0 +1,39 @@
1
+use std::net::SocketAddr;
2
+use tokio::io::{AsyncWriteExt, BufReader, BufWriter};
3
+use tokio::net::{TcpListener, TcpStream};
4
+
5
+//use crate::handle_session::handle_session;
6
+
7
+pub async fn listen() -> anyhow::Result<()> {
8
+ let addr = SocketAddr::from(([127, 0, 0, 1], 2525));
9
+ let listener = TcpListener::bind(addr).await?;
10
+
11
+ tracing::info!("SMTP server listening on {}", addr);
12
+
13
+ loop {
14
+ match listener.accept().await {
15
+ Ok((mut stream, _)) => {
16
+ tokio::spawn(async move {
17
+ if let Err(err) = handle_connection(&mut stream).await {
18
+ tracing::error!("Error handling SMTP connection: {:?}", err);
19
+ };
20
+ });
21
+ }
22
+ Err(err) => {
23
+ tracing::error!("Error establishing SMTP connection: {:?}", err);
24
+ }
25
+ }
26
+ }
27
+}
28
+
29
+async fn handle_connection(stream: &mut TcpStream) -> anyhow::Result<()> {
30
+ let (reader, writer) = stream.split();
31
+ let mut reader = BufReader::new(reader);
32
+ let mut writer = BufWriter::new(writer);
33
+ writer.write_all(b"220 My SMTP server\r\n").await?;
34
+ writer.flush().await?;
35
+
36
+ // handle session
37
+ // handle_session(stream).await
38
+ Ok(())
39
+}
src/main.rs
new
+7
@@ -0,0 +1,7 @@
1
+use anyhow::Result;
2
+use smbcloud_mail::listen::listen;
3
+
4
+#[tokio::main]
5
+async fn main() -> Result<()> {
6
+ listen().await
7
+}
src/send_commands.rs
new
+14
@@ -0,0 +1,14 @@
1
+use futures::{SinkExt, StreamExt, stream::iter};
2
+use tokio::net::TcpStream;
3
+use tokio_native_tls::TlsStream;
4
+use tokio_util::codec::{Framed, LinesCodec};
5
+
6
+pub async fn send_commands(
7
+ framed: &mut Framed<TcpStream, LinesCodec>,
8
+ commands: Vec<String>,
9
+) -> anyhow::Result<()> {
10
+ // only need to add \r because the codec only adds \n
11
+ let messages = iter(commands.into_iter().map(|x| format!("{}\r", x)));
12
+ framed.send_all(&mut messages.map(Ok)).await?;
13
+ Ok(())
14
+}