diff options
author | Yuqian Yang <crupest@crupest.life> | 2025-04-30 00:20:23 +0800 |
---|---|---|
committer | Yuqian Yang <crupest@crupest.life> | 2025-05-05 21:03:36 +0800 |
commit | c1b189e0344be1b472e4863984a2c3a1faba0865 (patch) | |
tree | 9b6af0eb106c270e2e6ce05d82579038722ab6d3 | |
parent | 690f9f843324c359a46bcb8b51b5563e2bec800a (diff) | |
download | crupest-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.ts | 2 | ||||
-rw-r--r-- | services/docker/mail-server/aws-sendmail/dovecot.ts | 2 | ||||
-rw-r--r-- | services/docker/mail-server/aws-sendmail/mail.ts | 82 |
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; } } |