diff options
author | Yuqian Yang <crupest@crupest.life> | 2025-06-05 22:30:51 +0800 |
---|---|---|
committer | Yuqian Yang <crupest@crupest.life> | 2025-06-09 21:48:00 +0800 |
commit | 3bdca0b90cf8bf5dfd6ff1ab482d857abb4acd2d (patch) | |
tree | 42fd1bf1f0119910c09542fbf475c012404658fd /deno/mail-relay/dovecot.ts | |
parent | 543fc733da074751e1750603df6931089efab465 (diff) | |
download | crupest-3bdca0b90cf8bf5dfd6ff1ab482d857abb4acd2d.tar.gz crupest-3bdca0b90cf8bf5dfd6ff1ab482d857abb4acd2d.tar.bz2 crupest-3bdca0b90cf8bf5dfd6ff1ab482d857abb4acd2d.zip |
feat(deno): move deno (mail-server) to top level.
Diffstat (limited to 'deno/mail-relay/dovecot.ts')
-rw-r--r-- | deno/mail-relay/dovecot.ts | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/deno/mail-relay/dovecot.ts b/deno/mail-relay/dovecot.ts new file mode 100644 index 0000000..cb63766 --- /dev/null +++ b/deno/mail-relay/dovecot.ts @@ -0,0 +1,102 @@ +import { basename } from "@std/path"; + +import { Logger } from "@crupest/base/log"; + +import { + Mail, + MailDeliverContext, + MailDeliverer, +} from "./mail.ts"; + +export class DovecotMailDeliverer extends MailDeliverer { + readonly name = "dovecot"; + readonly #ldaPath; + + constructor(logger: Logger, ldaPath: string) { + super(logger); + this.#ldaPath = ldaPath; + } + + protected override async doDeliver( + mail: Mail, + context: MailDeliverContext, + ): Promise<void> { + const ldaPath = this.#ldaPath; + const ldaBinName = basename(ldaPath); + const utf8Stream = mail.toUtf8Bytes(); + + const recipients = [...context.recipients]; + + if (recipients.length === 0) { + context.result.message = + "Failed to deliver to dovecot, no recipients are specified."; + return; + } + + this.logger.info(`Deliver to dovecot users: ${recipients.join(", ")}.`); + + for (const recipient of recipients) { + try { + const commandArgs = ["-d", recipient]; + this.logger.info( + `Run ${ldaBinName} ${commandArgs.join(" ")}...`, + ); + + const ldaCommand = new Deno.Command(ldaPath, { + args: commandArgs, + stdin: "piped", + stdout: "piped", + stderr: "piped", + }); + + const ldaProcess = ldaCommand.spawn(); + using logFiles = await this.logger.createExternalLogStreamsForProgram( + ldaBinName, + ); + ldaProcess.stdout.pipeTo(logFiles.stdout); + ldaProcess.stderr.pipeTo(logFiles.stderr); + + const stdinWriter = ldaProcess.stdin.getWriter(); + await stdinWriter.write(utf8Stream); + await stdinWriter.close(); + + const status = await ldaProcess.status; + + if (status.success) { + context.result.recipients.set(recipient, { + kind: "done", + message: `${ldaBinName} exited with success.`, + }); + } else { + let message = `${ldaBinName} exited with error code ${status.code}`; + + if (status.signal != null) { + message += ` (signal ${status.signal})`; + } + + // https://doc.dovecot.org/main/core/man/dovecot-lda.1.html + switch (status.code) { + case 67: + message += ", recipient user not known"; + break; + case 75: + message += ", temporary error"; + break; + } + + message += "."; + + context.result.recipients.set(recipient, { kind: "fail", message }); + } + } catch (cause) { + context.result.recipients.set(recipient, { + kind: "fail", + message: "An error is thrown when running lda: " + cause, + cause, + }); + } + } + + this.logger.info("Done handling all recipients."); + } +} |