aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuqian Yang <crupest@crupest.life>2025-04-30 00:20:23 +0800
committerYuqian Yang <crupest@crupest.life>2025-05-05 21:03:36 +0800
commitc1b189e0344be1b472e4863984a2c3a1faba0865 (patch)
tree9b6af0eb106c270e2e6ce05d82579038722ab6d3
parent690f9f843324c359a46bcb8b51b5563e2bec800a (diff)
downloadcrupest-c1b189e0344be1b472e4863984a2c3a1faba0865.tar.gz
crupest-c1b189e0344be1b472e4863984a2c3a1faba0865.tar.bz2
crupest-c1b189e0344be1b472e4863984a2c3a1faba0865.zip
HALF WORK!: 2025-5-5
-rw-r--r--services/docker/mail-server/aws-sendmail/aws/deliver.ts2
-rw-r--r--services/docker/mail-server/aws-sendmail/dovecot.ts2
-rw-r--r--services/docker/mail-server/aws-sendmail/mail.ts82
3 files changed, 64 insertions, 22 deletions
diff --git a/services/docker/mail-server/aws-sendmail/aws/deliver.ts b/services/docker/mail-server/aws-sendmail/aws/deliver.ts
index bdc9584..da100f5 100644
--- a/services/docker/mail-server/aws-sendmail/aws/deliver.ts
+++ b/services/docker/mail-server/aws-sendmail/aws/deliver.ts
@@ -20,7 +20,7 @@ export class AwsMailDeliverer extends MailDeliverer {
try {
const sendCommand = new SendEmailCommand({
Content: {
- Raw: { Data: mail.encodeUtf8() },
+ Raw: { Data: mail.toUtf8Bytes() },
},
});
diff --git a/services/docker/mail-server/aws-sendmail/dovecot.ts b/services/docker/mail-server/aws-sendmail/dovecot.ts
index 520f93b..9097cd0 100644
--- a/services/docker/mail-server/aws-sendmail/dovecot.ts
+++ b/services/docker/mail-server/aws-sendmail/dovecot.ts
@@ -13,7 +13,7 @@ export class DovecotMailDeliverer extends MailDeliverer {
let status;
try {
- const utf8Stream = mail.encodeUtf8();
+ const utf8Stream = mail.toUtf8Bytes();
const ldaCommand = new Deno.Command(ldaBin, {
stdin: "piped",
diff --git a/services/docker/mail-server/aws-sendmail/mail.ts b/services/docker/mail-server/aws-sendmail/mail.ts
index de7942f..82902d2 100644
--- a/services/docker/mail-server/aws-sendmail/mail.ts
+++ b/services/docker/mail-server/aws-sendmail/mail.ts
@@ -17,16 +17,39 @@ export type MailDeliverState =
| MailDeliverStateDelivered
| MailDeliverStateError;
+class MailParseError extends Error {
+ constructor(
+ message: string,
+ public readonly mail: Mail,
+ options?: ErrorOptions,
+ ) {
+ super(message, options);
+ }
+}
+
class MailDeliverError extends Error {
constructor(
message: string,
- options: ErrorOptions,
public readonly mail: Mail,
+ options?: ErrorOptions,
) {
super(message, options);
}
}
+const eolNames = new Map([
+ ["\n", "\\n"],
+ ["\r", "\\r"],
+ ["\n\r", "\\n\\r"],
+]);
+
+interface ParsedMail {
+ headerStr: string;
+ bodyStr: string;
+ sepStr: string;
+ eol: string;
+}
+
export class Mail {
messageId: string | null = null;
awsMessageId: string | null = null;
@@ -45,44 +68,63 @@ export class Mail {
): never {
const error = new MailDeliverError(
`Failed to deliver mail to ${deliverer.destination}: ${reason}`,
- { cause },
this,
+ { cause },
);
this.deliverState = { "kind": "error", deliverer, error };
throw error;
}
- encodeUtf8(): Uint8Array {
+ toUtf8Bytes(): Uint8Array {
const utf8Encoder = new TextEncoder();
- // TODO: A problem here is if mail is VERY long, this will block for a long time.
- // Maybe some task queue can be used.
return utf8Encoder.encode(this.raw);
}
- getRawBase64(): string {
+ toBase64(): string {
return encodeBase64(this.raw);
}
- appendHeaders(
- rawMail: string,
- headers: [key: string, value: string][],
- ): string {
- const separatorMatch = rawMail.match(/(\r\n|\n)(\r\n|\n)/);
- if (separatorMatch == null) {
- throw new Error(
+ simpleParse(): ParsedMail {
+ const twoEolMatch = this.raw.match(/(\r\n|\n|\r)(\r\n|\n|\r)/);
+ // "\r\n" is a false positive.
+ if (twoEolMatch == null || twoEolMatch[0] === "\r\n") {
+ throw new MailParseError(
"No header/body separator (2 successive EOLs) found. Cannot append headers.",
+ this,
+ );
+ }
+
+ const [eol, sepStr] = [twoEolMatch[1], twoEolMatch[2]];
+
+ if (eol !== sepStr) {
+ getLogger().warn(
+ `Different EOLs (${eolNames.get(eol)} \
+ and ${eolNames.get(sepStr)}) found in mail.`,
);
}
- if (separatorMatch[1] !== separatorMatch[2]) {
- getLogger().warn("Different EOLs (\\r\\n and \\n) found in mail!");
+ return {
+ headerStr: this.raw.slice(0, twoEolMatch.index! + eol.length),
+ bodyStr: this.raw.slice(twoEolMatch.index! + eol.length + sepStr.length),
+ sepStr,
+ eol,
+ };
+ }
+
+ simpleParseHeaders(): [key: string, value: string][] {
+ const { headerStr } = this.simpleParse();
+ const rawLines = headerStr.split(/\r\n|\n|\r/);
+ const headerLines = [];
+ for (const l of rawLines) {
+
}
+ }
- const headerStr = headers.map(([k, v]) => `${k}: ${v}${separatorMatch[1]}`)
- .join("");
- const endOfHeadersIndex = separatorMatch.index! + separatorMatch[1].length;
- return rawMail.slice(0, endOfHeadersIndex) + headerStr +
- rawMail.slice(endOfHeadersIndex);
+ appendHeaders(headers: [key: string, value: string][]) {
+ const { headerStr, bodyStr, sepStr, eol } = this.simpleParse();
+ const newHeaderStr = headerStr +
+ headers.map(([k, v]) => `${k}: ${v}${eol}`).join("");
+ this.raw = newHeaderStr + sepStr + bodyStr;
}
}