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;    }  }  | 
