feat(core): the e-mail server keeps /mailserver/konten.txt up to date at run time, measured against Java
The account file's writer moves from the export (packages/fls) into the core, so that the export and the running server write the same. The server writes the file at its start and whenever its accounts change. EmailRun now records Java's file as it left it and as Java writes the accounts it ended with; Java leaves the notice to a local sender out until its next write. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -74,6 +74,8 @@
|
||||
// Java FILIUS to the topology of the original up to those departures.
|
||||
|
||||
import {
|
||||
accountFileField,
|
||||
serverAccountsFileText,
|
||||
javaBase64,
|
||||
defaultSwitchConfiguration,
|
||||
dhcpServerConfiguration,
|
||||
@@ -292,7 +294,7 @@ function exportSystem(node: TopologyNode, project: ProjectToExport, servingModem
|
||||
const client = project.installedPrograms.get(id)?.has('emailClient') === true ? project.emailClientConfigurations.get(id) : undefined;
|
||||
const server = project.installedPrograms.get(id)?.has('emailServer') === true ? project.emailServerConfigurations.get(id) : undefined;
|
||||
if (client?.account != null) putFile(root, EMAIL_CLIENT_FILE, 'text/txt', clientAccountFile(client, warn));
|
||||
if (server !== undefined) putFile(root, EMAIL_SERVER_FILE, 'txt', serverAccountsFile(server, warn));
|
||||
if (server !== undefined) putFile(root, EMAIL_SERVER_FILE, 'txt', serverAccountsFileText(server.domain, server.accounts, warn));
|
||||
return {
|
||||
...system,
|
||||
ssid: ssid === '' ? null : ssid,
|
||||
@@ -419,11 +421,7 @@ function javaContact(contact: EmailContact): JavaContact {
|
||||
}
|
||||
|
||||
/** A field of an account file: each character it cannot carry becomes a space, with a warning. */
|
||||
function field(value: string, forbidden: RegExp, what: string, warn: Warn): string {
|
||||
if (!forbidden.test(value)) return value;
|
||||
warn(`${what} holds a character Java's account file cannot carry, written there with a space`);
|
||||
return value.replace(new RegExp(forbidden.source, 'g'), ' ');
|
||||
}
|
||||
const field = accountFileField;
|
||||
|
||||
/** EmailKonto.toString: nine fields; a field may hold no ';' and no line break. */
|
||||
function clientAccountFile(client: EmailClientConfiguration, warn: Warn): string {
|
||||
@@ -442,53 +440,6 @@ function clientAccountFile(client: EmailClientConfiguration, warn: Warn): string
|
||||
].join(';');
|
||||
}
|
||||
|
||||
/**
|
||||
* EmailServer.listeBenutzerkontenZuString: per account a line of user,
|
||||
* domain, password, last and first name, then its messages, each "#" and
|
||||
* seven fields joined by "$" — the text with Java's escapes
|
||||
* (replaceSpecialChar, then line breaks as "|"), the others unescaped.
|
||||
*/
|
||||
function serverAccountsFile(server: EmailServerConfiguration, warn: Warn): string {
|
||||
const where = (what: string) => `${EMAIL_SERVER_FILE}: ${what}`;
|
||||
const plain = (value: string, what: string) => field(value, /[;#$\r\n]/, where(what), warn);
|
||||
const address = (entry: EmailAddress, what: string) => {
|
||||
const name = entry.name === null ? null : field(entry.name, /[;#$,<>\r\n]/, where(what), warn);
|
||||
const mail = entry.mailAddress === null ? null : field(entry.mailAddress, /[;#$,<>\r\n]/, where(what), warn);
|
||||
if (name === null) return mail === null ? '' : `<${mail}>`;
|
||||
return mail === null ? name : `${name} <${mail}>`;
|
||||
};
|
||||
const addresses = (entries: readonly EmailAddress[], what: string) => entries.map((entry) => address(entry, what)).join(',');
|
||||
const escapedText = (text: string) =>
|
||||
text.replace(/\$/g, '&&00036&&').replace(/;/g, '&&00059&&').replace(/\|/g, '&&00124&&').replace(/#/g, '&&00035&&').replace(/\n/g, '|');
|
||||
return server.accounts
|
||||
.map((account) => {
|
||||
const messages = account.mailbox
|
||||
.map(
|
||||
(message) =>
|
||||
'#' +
|
||||
[
|
||||
address(message.from, 'a sender'),
|
||||
addresses(message.to, 'a recipient'),
|
||||
addresses(message.cc, 'a recipient'),
|
||||
addresses(message.bcc, 'a recipient'),
|
||||
message.receivedAtMilliseconds === undefined ? '' : String(message.receivedAtMilliseconds),
|
||||
plain(message.subject, 'a subject'),
|
||||
field(escapedText(message.body), /\r$/, where('a text'), warn),
|
||||
].join('$'),
|
||||
)
|
||||
.join('');
|
||||
return [
|
||||
plain(account.username, 'a user name'),
|
||||
server.domain,
|
||||
plain(account.password, 'a password'),
|
||||
plain(account.lastName, 'a last name'),
|
||||
plain(account.firstName, 'a first name'),
|
||||
messages,
|
||||
].join(';') + '\n';
|
||||
})
|
||||
.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* The file at an absolute path of the root's tree, replacing one of the same
|
||||
* name in any case (Java's holeDatei finds it so); directories are made.
|
||||
|
||||
Reference in New Issue
Block a user