feat(fls): write the e-mail programs of a .fls, loaded by Java as the original
topologyWriter.ts writes the e-mail server's domain and bean accounts with their mailboxes, and the e-mail client's three folders and address book. The client's account is no bean (FILIUS 2.14 has no kontoListe property): it lives in /konten.txt and is written with the files. A Java error is not copied: older files keep recipients as strings, which Java puts into its typed lists unchecked, and its e-mail window fails on them. The writer writes the AddressEntry Java's own AddressEntry(String) makes of each; a null recipient is left out. The Java oracle now reports that reading next to each such string, and our addressEntry matches it on all 9 in the corpus. The test names the departure (RECIPIENTS_AS_STRINGS) and checks that it touches man-in-the-middle.fls alone. Checked by Java against Java: every corpus file, read and written again, loads with the same e-mail programs as the original, up to that departure. The synthetic topology adds an e-mail server and client with an account, a mailbox, three folders, an address book and recipients of every form. 15 mutation probes, each loaded by Java, were all caught. The writer now writes everything the reader reads of Java's view. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,8 +19,15 @@
|
||||
// the web server, the peer-to-peer application and the e-mail server make
|
||||
// their directories only where the files lack them, and the DNS server reads
|
||||
// /dns/hosts when it is installed. Each is put under its key and given the
|
||||
// system software, as installApp does. E-mail accounts, messages and address
|
||||
// books are a later layer.
|
||||
// system software, as installApp does.
|
||||
//
|
||||
// E-mail: the server's domain and bean accounts with their mailboxes, the
|
||||
// client's three folders and address book. The client's account is no bean
|
||||
// (FILIUS 2.14 has no kontoListe property): it lives in /konten.txt, a file.
|
||||
// Java's errors are not copied: a recipient an older file keeps as a string
|
||||
// is written as the AddressEntry Java's own AddressEntry(String) makes of it
|
||||
// — Java puts the string into the typed list unchecked, and its e-mail
|
||||
// window fails on it; a null recipient is left out.
|
||||
//
|
||||
// Checked by Java: every corpus file, read and written again, loads in Java
|
||||
// FILIUS to the same topology and system software (topologyWriter.test.ts,
|
||||
@@ -38,14 +45,20 @@ import {
|
||||
type BeanValue,
|
||||
short,
|
||||
} from './javaBeansWriter.js';
|
||||
import { addressEntry, type JavaAddressEntry, type JavaContact, type JavaEmail, type JavaEmailAccount, type JavaRecipient } from './javaEmail.js';
|
||||
import type { FlsApplication, FlsFileTree, FlsFirewall, FlsSystemSoftware } from './systemSoftware.js';
|
||||
import type { FlsDocumentationItem, FlsInterface, FlsLink, FlsNode, FlsNodeClass } from './topology.js';
|
||||
|
||||
/** The version line of the files this writer makes: the format of FILIUS 2.14. */
|
||||
export const WRITTEN_VERSION_LINE = 'Filius version: 2.14.0 (mdg-filius)';
|
||||
|
||||
/** An installed application; e-mail accounts, messages and address books are a later layer. */
|
||||
export type ApplicationToWrite = Omit<FlsApplication, 'source'>;
|
||||
/**
|
||||
* An installed application with its settings. The e-mail accounts Java reads
|
||||
* from files (/mailserver/konten.txt, /konten.txt) are written with the
|
||||
* files; an older file's kontoListe, which FILIUS 2.14 does not read, is not
|
||||
* written.
|
||||
*/
|
||||
export type ApplicationToWrite = Omit<FlsApplication, 'source' | 'fileAccounts' | 'fileAccount' | 'legacyAccounts'>;
|
||||
|
||||
/** The system software's settings, files and applications. */
|
||||
export type SystemToWrite = Omit<FlsSystemSoftware, 'applications'> & {
|
||||
@@ -282,9 +295,91 @@ function applicationStatements(application: ApplicationToWrite, system: BeanRefe
|
||||
statements.push({ set: 'maxTeilnehmerZahl', value: int(application.maxPeers) });
|
||||
}
|
||||
if (application.firewall !== undefined) statements.push(...firewallStatements(application.firewall));
|
||||
if (application.mailDomain !== undefined) statements.push({ set: 'mailDomain', value: application.mailDomain });
|
||||
if (application.beanAccounts !== undefined) {
|
||||
statements.push({ set: 'listeBenutzerkonten', value: listOf(application.beanAccounts.map(emailAccount)) });
|
||||
}
|
||||
if (application.received !== undefined) statements.push({ set: 'empfangeneNachrichten', value: emails(application.received) });
|
||||
if (application.sent !== undefined) statements.push({ set: 'gesendeteNachrichten', value: emails(application.sent) });
|
||||
if (application.drafts !== undefined) statements.push({ set: 'erstellteNachrichten', value: emails(application.drafts) });
|
||||
if (application.addressBook !== undefined) {
|
||||
statements.push({
|
||||
set: 'adressbuch',
|
||||
value: object(
|
||||
'java.util.Vector',
|
||||
application.addressBook.map((entry) => ({ call: 'add', args: [contact(entry)] })),
|
||||
),
|
||||
});
|
||||
}
|
||||
return statements;
|
||||
}
|
||||
|
||||
const EMAIL_PACKAGE = 'filius.software.email';
|
||||
|
||||
function emailAccount(account: JavaEmailAccount): BeanObject {
|
||||
return object(`${EMAIL_PACKAGE}.EmailKonto`, [
|
||||
{ set: 'benutzername', value: account.username },
|
||||
{ set: 'passwort', value: account.password },
|
||||
{ set: 'nachname', value: account.lastName },
|
||||
{ set: 'vorname', value: account.firstName },
|
||||
{ set: 'emailAdresse', value: account.emailAddress },
|
||||
{ set: 'pop3server', value: account.pop3Server },
|
||||
{ set: 'pop3port', value: account.pop3Port },
|
||||
{ set: 'smtpserver', value: account.smtpServer },
|
||||
{ set: 'smtpport', value: account.smtpPort },
|
||||
{ set: 'nachrichten', value: emails(account.messages) },
|
||||
]);
|
||||
}
|
||||
|
||||
function emails(messages: readonly JavaEmail[]): BeanObject {
|
||||
return listOf(
|
||||
messages.map((message) =>
|
||||
object(`${EMAIL_PACKAGE}.Email`, [
|
||||
{ set: 'absender', value: address(message.sender) },
|
||||
{ set: 'empfaenger', value: recipients(message.to) },
|
||||
{ set: 'cc', value: recipients(message.cc) },
|
||||
{ set: 'bcc', value: recipients(message.bcc) },
|
||||
{ set: 'betreff', value: message.subject },
|
||||
{ set: 'text', value: message.text },
|
||||
{ set: 'dateReceived', value: message.dateReceived },
|
||||
{ set: 'neu', value: message.isNew },
|
||||
{ set: 'delete', value: message.isDeleted },
|
||||
{ set: 'versendet', value: message.isSent },
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/** A list of AddressEntry: a string read as Java's AddressEntry(String) reads it, null left out. */
|
||||
function recipients(entries: readonly JavaRecipient[]): BeanObject {
|
||||
return object(
|
||||
'java.util.ArrayList',
|
||||
entries.flatMap((entry) =>
|
||||
entry === null ? [] : [{ call: 'add', args: [address(typeof entry === 'string' ? addressEntry(entry) : entry)] }],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function address(entry: JavaAddressEntry): BeanObject {
|
||||
const statements: BeanStatement[] = [];
|
||||
if (entry.name !== null) statements.push({ set: 'name', value: entry.name });
|
||||
if (entry.mailAddress !== null) statements.push({ set: 'mailAddress', value: entry.mailAddress });
|
||||
return object(`${EMAIL_PACKAGE}.AddressEntry`, statements);
|
||||
}
|
||||
|
||||
function contact(entry: JavaContact): BeanObject {
|
||||
return object(`${EMAIL_PACKAGE}.Kontakt`, [
|
||||
{ set: 'name', value: entry.name },
|
||||
{ set: 'vorname', value: entry.firstName },
|
||||
{ set: 'strasse', value: entry.street },
|
||||
{ set: 'hausnr', value: int(entry.houseNumber) },
|
||||
{ set: 'plz', value: int(entry.postalCode) },
|
||||
{ set: 'wohnort', value: entry.city },
|
||||
{ set: 'email', value: entry.email },
|
||||
{ set: 'telefon', value: entry.phone },
|
||||
]);
|
||||
}
|
||||
|
||||
/** A LinkedList of String arrays: routes, static NAT. */
|
||||
function rows(table: readonly (readonly (string | null)[])[]): BeanObject {
|
||||
return listOf(table.map((row) => ({ kind: 'array', componentClass: 'java.lang.String', items: [...row] })));
|
||||
|
||||
Reference in New Issue
Block a user