aboutsummaryrefslogtreecommitdiff
path: root/services/docker/mail-server/relay/dovecot.ts
diff options
context:
space:
mode:
Diffstat (limited to 'services/docker/mail-server/relay/dovecot.ts')
-rw-r--r--services/docker/mail-server/relay/dovecot.ts106
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);
}
}
}