diff options
Diffstat (limited to 'services/docker/mail-server/relay/dovecot.ts')
-rw-r--r-- | services/docker/mail-server/relay/dovecot.ts | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/services/docker/mail-server/relay/dovecot.ts b/services/docker/mail-server/relay/dovecot.ts new file mode 100644 index 0000000..bb96e7b --- /dev/null +++ b/services/docker/mail-server/relay/dovecot.ts @@ -0,0 +1,94 @@ +import { basename } from "@std/path"; + +import { getConfigValue } from "./config.ts"; +import logger, { log } from "./logger.ts"; +import { + Mail, + MailDeliverContext, + MailDeliverer, + MailDeliverReceiptResult, + ReceiptsFromHeadersHook, +} from "./mail.ts"; + +export class DovecotMailDeliverer extends MailDeliverer { + readonly name = "dovecot"; + + constructor() { + super(); + this.preHooks.push( + new ReceiptsFromHeadersHook(), + ); + } + + protected override async doDeliver( + mail: Mail, + context: MailDeliverContext, + ): Promise<void> { + const ldaPath = getConfigValue("ldaPath"); + const ldaBinName = basename(ldaPath); + const utf8Stream = mail.toUtf8Bytes(); + + const receipts = [...context.receipts]; + if (receipts.length === 0) { + throw new Error("No receipts found."); + } + + log(`Deliver to ${receipts.join(", ")}.`); + + for (const receipt of receipts) { + log(`Call ${ldaBinName} for ${receipt}...`); + + const result: MailDeliverReceiptResult = { + kind: "done", + message: `${ldaBinName} exited with success.`, + }; + + try { + const ldaCommand = new Deno.Command(ldaPath, { + args: ["-d", receipt], + stdin: "piped", + stdout: "piped", + stderr: "piped", + }); + + const ldaProcess = ldaCommand.spawn(); + using logFiles = await logger.openLogForProgram(ldaBinName); + ldaProcess.stdout.pipeTo(logFiles.stdout.writable); + ldaProcess.stderr.pipeTo(logFiles.stderr.writable); + + const stdinWriter = ldaProcess.stdin.getWriter(); + await stdinWriter.write(utf8Stream); + await stdinWriter.close(); + + const status = await ldaProcess.status; + + if (!status.success) { + result.kind = "fail"; + result.message = + `${ldaBinName} exited with error code ${status.code}`; + + if (status.signal != null) { + result.message += ` (signal ${status.signal})`; + } + + // https://doc.dovecot.org/main/core/man/dovecot-lda.1.html + switch (status.code) { + case 67: + result.message += ", receipt user not known"; + break; + case 75: + result.kind = "retry"; + break; + } + + result.message += "."; + } + } catch (e) { + result.kind = "fail"; + result.message = "An error was thrown when running lda process: " + e; + result.cause = e; + } + context.result.set(receipt, result); + } + } +} |