diff options
Diffstat (limited to 'services/docker/mail-server/relay/dovecot.ts')
-rw-r--r-- | services/docker/mail-server/relay/dovecot.ts | 106 |
1 files changed, 81 insertions, 25 deletions
diff --git a/services/docker/mail-server/relay/dovecot.ts b/services/docker/mail-server/relay/dovecot.ts index 6f962a3..bb96e7b 100644 --- a/services/docker/mail-server/relay/dovecot.ts +++ b/services/docker/mail-server/relay/dovecot.ts @@ -1,38 +1,94 @@ -import { getLogger } from "./logger.ts"; -import { Mail, MailDeliverer } from "./mail.ts"; +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"; - readonly ldaBin = "dovecot-lda"; - protected override async doDeliver(mail: Mail): Promise<void> { - const { ldaBin } = this; + 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(); - let status; - try { - const utf8Stream = mail.toUtf8Bytes(); + const receipts = [...context.receipts]; + if (receipts.length === 0) { + throw new Error("No receipts found."); + } - const ldaCommand = new Deno.Command(ldaBin, { - stdin: "piped", - stdout: "piped", - stderr: "piped", - }); + log(`Deliver to ${receipts.join(", ")}.`); - const ldaProcess = ldaCommand.spawn(); - getLogger().logProgramOutput(ldaProcess, ldaBin); + for (const receipt of receipts) { + log(`Call ${ldaBinName} for ${receipt}...`); - const stdinWriter = ldaProcess.stdin.getWriter(); - await stdinWriter.ready; - await stdinWriter.write(utf8Stream); - await stdinWriter.ready; + const result: MailDeliverReceiptResult = { + kind: "done", + message: `${ldaBinName} exited with success.`, + }; - status = await ldaProcess.status; - } catch (cause) { - mail.throwDeliverError(this, "external error.", cause); - } + 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; + } - if (!status.success) { - mail.throwDeliverError(this, `${ldaBin} exited with non-zero.`); + 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); } } } |