diff options
Diffstat (limited to 'services/docker/mail-server/relay/dovecot.ts')
-rw-r--r-- | services/docker/mail-server/relay/dovecot.ts | 83 |
1 files changed, 83 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..0528968 --- /dev/null +++ b/services/docker/mail-server/relay/dovecot.ts @@ -0,0 +1,83 @@ +import { getConfig } from "./config.ts"; +import { getLogger } from "./logger.ts"; +import { + Mail, + MailDeliverContext, + MailDeliverer, + MailDeliverReceiptResult, +} from "./mail.ts"; + +export class DovecotMailDeliverer extends MailDeliverer { + readonly name = "dovecot"; + readonly ldaBin = "dovecot-lda"; + + protected override async doDeliver( + mail: Mail, + context: MailDeliverContext, + ): Promise<void> { + const { ldaBin } = this; + const utf8Stream = mail.toUtf8Bytes(); + const receipts = mail.simpleParseReceipts({ + domain: getConfig().getValue("mailDomain"), + }); + + if (receipts.length === 0) { + throw new Error("No receipts detected from mail headers."); + } + + getLogger().log(`Deliver to (from headers) ${receipts.join(", ")}.`); + + for (const receipt of receipts) { + getLogger().log(`Call ${ldaBin} for ${receipt}...`); + + const result: MailDeliverReceiptResult = { + kind: "done", + message: `${ldaBin} exited with success.`, + }; + + try { + const ldaCommand = new Deno.Command(ldaBin, { + args: ["-d", receipt], + stdin: "piped", + stdout: "piped", + stderr: "piped", + }); + + const ldaProcess = ldaCommand.spawn(); + getLogger().logProgramOutput(ldaProcess, ldaBin); + + const stdinWriter = ldaProcess.stdin.getWriter(); + await stdinWriter.ready; + await stdinWriter.write(utf8Stream); + await stdinWriter.ready; + const status = await ldaProcess.status; + + if (!status.success) { + result.kind = "fail"; + result.message = `${ldaBin} 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."; + result.cause = e; + } + context.result.set(receipt, result); + } + } +} |