// The e-mail programs of a host in the editor model (Swift's // registerJavaEmailConfigurations), from Java's view (javaEmail.ts). Java // decides what the programs hold; its errors are not copied: // - the server's accounts are those of /mailserver/konten.txt, which Java // reads at every simulation start; without the file, those of the bean // list, which Java would drop there; // - the client's account is that of /konten.txt; without it, the first of // an older file's kontoListe, which FILIUS 2.14 no longer reads; // - a recipient an older file keeps as a string is read as an address. // Departures from Swift (named in importProject.test.ts): accounts and // messages stay as Java has them, where Swift drops what fails its checks; // first and last name stay apart; a port that is no number is the default // with a warning (Swift drops the account); a message's date is taken only // when the file gives one (Swift numbers a mailbox's messages instead). import { DEFAULT_POP3_PORT, DEFAULT_SMTP_PORT, type EmailAddress, type EmailClientAccount, type EmailClientConfiguration, type EmailContact, type EmailMessage, type EmailServerConfiguration, } from '@mdg-filius/core'; import { addressEntry, type JavaEmail, type JavaEmailAccount, type JavaRecipient } from './javaEmail.js'; import { javaParseInt } from '@mdg-filius/core'; import type { FlsApplication } from './systemSoftware.js'; type Warn = (message: string) => void; export function emailClientConfiguration(application: FlsApplication, warn: Warn): EmailClientConfiguration { const source = application.fileAccount ?? application.legacyAccounts?.[0] ?? null; const numbering = { next: 1 }; const inbox = emailMessages(application.received ?? [], numbering, undefined, warn); const sent = emailMessages(application.sent ?? [], numbering, true, warn); const drafts = emailMessages(application.drafts ?? [], numbering, false, warn); return { account: source === null ? null : clientAccount(source, warn), inbox, sent, drafts, addressBook: (application.addressBook ?? []).map( (contact): EmailContact => ({ name: contact.name ?? '', firstName: contact.firstName ?? '', street: contact.street ?? '', houseNumber: contact.houseNumber, postalCode: contact.postalCode, city: contact.city ?? '', email: contact.email ?? '', phone: contact.phone ?? '', }), ), nextMessageID: numbering.next, }; } export function emailServerConfiguration(application: FlsApplication, warn: Warn): EmailServerConfiguration { const accounts = Array.isArray(application.fileAccounts) ? application.fileAccounts : (application.beanAccounts ?? []); const numbering = { next: 1 }; return { domain: application.mailDomain ?? 'filius.de', pop3Port: DEFAULT_POP3_PORT, accounts: accounts.map((account) => ({ username: account.username ?? '', password: account.password ?? '', firstName: account.firstName, lastName: account.lastName, mailbox: emailMessages(account.messages, numbering, undefined, warn), })), nextMessageID: numbering.next, }; } function clientAccount(account: JavaEmailAccount, warn: Warn): EmailClientAccount { return { pop3Host: account.pop3Server ?? '', pop3Port: port(account.pop3Port, DEFAULT_POP3_PORT, 'POP3', warn), smtpHost: account.smtpServer ?? '', smtpPort: port(account.smtpPort, DEFAULT_SMTP_PORT, 'SMTP', warn), username: account.username ?? '', password: account.password ?? '', firstName: account.firstName, lastName: account.lastName, email: account.emailAddress ?? '', }; } /** A port as Java's client reads it (Integer.parseInt); none or no port is the default. */ function port(text: string | null, fallback: number, protocol: string, warn: Warn): number { if (text === null) return fallback; const value = javaParseInt(text); if (value !== undefined && value >= 0 && value <= 65_535) return value; warn(`e-mail client: ${protocol} port '${text}' is not a port, used ${fallback}`); return fallback; } /** Numbered on from `numbering.next`; `isSent` is the folder's when it has one. */ function emailMessages( messages: readonly JavaEmail[], numbering: { next: number }, isSent: boolean | undefined, warn: Warn, ): EmailMessage[] { return messages.map((message) => { const received = message.dateReceived !== null && /^[0-9]+$/.test(message.dateReceived) ? Number(message.dateReceived) : undefined; return { id: numbering.next++, from: { name: message.sender.name, mailAddress: message.sender.mailAddress }, to: recipients(message.to, warn), cc: recipients(message.cc, warn), bcc: recipients(message.bcc, warn), subject: message.subject ?? '', body: message.text ?? '', ...(received === undefined ? {} : { receivedAtMilliseconds: received }), isNew: message.isNew, isMarkedForDeletion: message.isDeleted, isSent: isSent ?? message.isSent, }; }); } function recipients(entries: readonly JavaRecipient[], warn: Warn): EmailAddress[] { return entries.flatMap((entry) => { if (entry === null) { warn('an e-mail recipient is null, left out'); return []; } const address = typeof entry === 'string' ? addressEntry(entry) : entry; return [{ name: address.name, mailAddress: address.mailAddress }]; }); }