feat(core): the e-mail server after Java, measured against Java
SMTP on port 25 stores mails for the domain's accounts as Java reads them anew and tells a local sender of unknown recipients; POP3 on port 110 hands them out. The mailboxes are in the snapshot and go back into the project. EmailRun plays four stories over the network; MailCases feeds Java's workers without one (26 cases, de and en). 18 cases name Java bugs that the core does not take. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,15 +5,17 @@
|
||||
// Only the cables count: a host's SSID connects nothing at run time, as in
|
||||
// Java (Swift joins a wireless host to the switch of its SSID; not taken).
|
||||
// A DNS server runs only when it is switched on (Java: DNSServer.aktiv); an
|
||||
// echo or web server is there either way, switched on or off (it can be
|
||||
// echo, web or e-mail server is there either way, switched on or off (it can be
|
||||
// switched at run time). The programs read the nodes' files (a web server its
|
||||
// pages).
|
||||
//
|
||||
// Back the other way: what the simulation changes that Java keeps in the
|
||||
// project (its objects are the project's), so a project saved after it holds
|
||||
// it. That is a router's or home router's firewall, set on its page konfig.
|
||||
// it. That is a router's or home router's firewall, set on its page konfig,
|
||||
// and an e-mail server's mailboxes (Java writes them into
|
||||
// /mailserver/konten.txt at once).
|
||||
|
||||
import { portSnapshot, topologySnapshot, type DnsServerConfiguration, type EchoServerSnapshot, type Id, type WebServerSnapshot, type TopologySnapshot } from '@mdg-filius/core';
|
||||
import { portSnapshot, topologySnapshot, type DnsServerConfiguration, type EchoServerSnapshot, type EmailServerSnapshot, type Id, type WebServerSnapshot, type TopologySnapshot } from '@mdg-filius/core';
|
||||
import type { ImportedProject } from './importProject.js';
|
||||
|
||||
export function projectSnapshot(project: ImportedProject): TopologySnapshot {
|
||||
@@ -29,6 +31,11 @@ export function projectSnapshot(project: ImportedProject): TopologySnapshot {
|
||||
for (const [nodeId, configuration] of project.webServerConfigurations) {
|
||||
webServers.set(nodeId, { ...configuration, isActive: project.activePrograms.get(nodeId)?.has('webServer') === true });
|
||||
}
|
||||
const emailServers = new Map<Id, EmailServerSnapshot>();
|
||||
for (const [nodeId, configuration] of project.emailServerConfigurations) {
|
||||
if (project.installedPrograms.get(nodeId)?.has('emailServer') !== true) continue;
|
||||
emailServers.set(nodeId, { domain: configuration.domain, accounts: configuration.accounts, isActive: project.activePrograms.get(nodeId)?.has('emailServer') === true });
|
||||
}
|
||||
const webBrowsers = new Set([...project.installedPrograms].filter(([, programs]) => programs.has('webBrowser')).map(([nodeId]) => nodeId));
|
||||
return topologySnapshot({
|
||||
nodes: project.graph.nodes.map((node) => ({
|
||||
@@ -52,11 +59,22 @@ export function projectSnapshot(project: ImportedProject): TopologySnapshot {
|
||||
simpleClientsByNodeId: project.simpleClientConfigurations,
|
||||
webServersByNodeId: webServers,
|
||||
webBrowserNodeIds: webBrowsers,
|
||||
emailServersByNodeId: emailServers,
|
||||
fileSystemsByNodeId: project.virtualFileSystems,
|
||||
});
|
||||
}
|
||||
|
||||
/** The project with what the simulation changed that Java keeps: the firewalls, as the run's snapshot holds them. */
|
||||
/**
|
||||
* The project with what the simulation changed that Java keeps, as the run's
|
||||
* snapshot holds it: the firewalls, and the e-mail servers' mailboxes.
|
||||
*/
|
||||
export function projectWithSimulationChanges(project: ImportedProject, snapshot: TopologySnapshot): ImportedProject {
|
||||
return { ...project, firewallConfigurationsByNodeId: new Map(snapshot.firewallConfigurationsByNodeId) };
|
||||
const emailServers = new Map(project.emailServerConfigurations);
|
||||
for (const [nodeId, server] of snapshot.emailServersByNodeId) {
|
||||
const configuration = emailServers.get(nodeId);
|
||||
if (configuration === undefined) continue;
|
||||
const messages = server.accounts.reduce((count, account) => count + account.mailbox.length, 0);
|
||||
emailServers.set(nodeId, { ...configuration, accounts: server.accounts, nextMessageID: messages + 1 });
|
||||
}
|
||||
return { ...project, firewallConfigurationsByNodeId: new Map(snapshot.firewallConfigurationsByNodeId), emailServerConfigurations: emailServers };
|
||||
}
|
||||
|
||||
@@ -801,6 +801,41 @@ describe('importProject rules', () => {
|
||||
expect(reopened.firewallConfigurationsByNodeId.get(reopenedRouter)).toEqual(expected);
|
||||
});
|
||||
|
||||
it("keeps a mail the e-mail server took during the run in the project saved after it (projectWithSimulationChanges)", () => {
|
||||
const project = openProjectArchive(new Uint8Array(readFileSync(join(corpusRoot, 'java-examples/email_komplett.fls'))), counter());
|
||||
const node = (name: string) => project.graph.nodes.find((each) => each.displayName === name)!.id;
|
||||
const server = node('Mail-Server (filius.de)');
|
||||
const sender = node('Rechner 1');
|
||||
const engine = new NetworkRuntimeEngine(14);
|
||||
const snapshot = projectSnapshot(project);
|
||||
expect(snapshot.emailServersByNodeId.get(server)).toEqual({ domain: 'filius.de', accounts: project.emailServerConfigurations.get(server)!.accounts, isActive: true });
|
||||
const switchedOff = { ...project, activePrograms: new Map([...project.activePrograms].map(([id, programs]) => [id, new Set([...programs].filter((each) => each !== 'emailServer'))])) };
|
||||
expect(projectSnapshot(switchedOff).emailServersByNodeId.get(server)?.isActive).toBe(false);
|
||||
engine.handle({
|
||||
type: 'start',
|
||||
snapshot: { ...snapshot, simpleClientsByNodeId: new Map([[sender, { targetAddress: '141.99.50.232', targetPort: 25 }]]) },
|
||||
seed: 14,
|
||||
initialTimeMilliseconds: 0,
|
||||
});
|
||||
const client = engine.simpleClient(sender)!;
|
||||
client.connect('141.99.50.232', 25);
|
||||
const commands = ['HELO 141.99.50.181', 'MAIL FROM:<rechner1@filius.de>', 'RCPT TO:<rechner2@filius.de>', 'DATA', 'From: <rechner1@filius.de>\nTo: <rechner2@filius.de>\nSubject: Hallo\n\nGespeichert?\n.\n', 'QUIT'];
|
||||
for (const [index, command] of commands.entries()) {
|
||||
engine.handle({ type: 'advance', toMilliseconds: 5_000 + index * 2_000 });
|
||||
client.send(command);
|
||||
}
|
||||
engine.handle({ type: 'advance', toMilliseconds: 20_000 });
|
||||
// The project itself is as it was opened, until the run's changes go back into it.
|
||||
expect(project.emailServerConfigurations.get(server)!.accounts[1]!.mailbox).toEqual([]);
|
||||
|
||||
const saved = saveProjectArchive(projectWithSimulationChanges(project, engine.state.topologySnapshot)).archive;
|
||||
const reopened = openProjectArchive(saved, counter());
|
||||
const reopenedServer = reopened.graph.nodes.find((each) => each.displayName === 'Mail-Server (filius.de)')!.id;
|
||||
const [first, second] = reopened.emailServerConfigurations.get(reopenedServer)!.accounts;
|
||||
expect(first!.mailbox).toEqual([]);
|
||||
expect(second!.mailbox).toMatchObject([{ from: { name: null, mailAddress: 'rechner1@filius.de' }, to: [{ name: null, mailAddress: 'rechner2@filius.de' }], subject: 'Hallo', body: 'Gespeichert?\n' }]);
|
||||
});
|
||||
|
||||
it("keeps a node's IP forwarding where it differs from its kind's: a host that forwards, a router that does not", () => {
|
||||
const system = (ipForwardingEnabled: boolean) => ({ manualRoutes: [], ipForwardingEnabled });
|
||||
const project = importProject(
|
||||
|
||||
Reference in New Issue
Block a user