diff --git a/CHANGES.md b/CHANGES.md index 38d13af..acaea68 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -51,8 +51,8 @@ Swift-Pfade relativ zu `filius-ipad/ios/FiliusPad/TopologyEditor/` (Stand | `test/javaBeansWriter.test.ts`, `test/beans/` | — | neu: 11 Dateien des Schreibers, von Javas `XMLDecoder` gelesen und beschrieben; Erwartungen von Hand | | `src/topologyWriter.ts` | Java `SzenarioVerwaltung.netzwerkSpeichern`, Konstruktoren von `NetzwerkInterface`, `Port`, `GUIKabelItem` und der Systemsoftware | neu nach Java: `konfiguration.xml` aus Javas Sicht — Geräte, Schnittstellen (vom Konstruktor angelegte über Getter, weitere per `add`), Ports über Getter-Verweise, Kabel samt Zeichen-Enden (`kabelpanel`), Doku-Elemente; Systemsoftware über ihre Setter: SSID, Lernzeit, RIP, manuelle Routen, DHCP-Client und -Server, statische NAT, Modem, Router-Firewall (über das `konfig`-Plug-in des Webservers, wie XMLEncoder sie erreicht); Dateisysteme (an die Wurzel des Konstruktors angehängt wie bei XMLEncoder, beim Router hinter dessen Vorlagen); installierte Programme nach den Dateien (Webserver, Tauschbörse und E-Mail-Server legen ihre Ordner nur an, wo sie fehlen; der DNS-Server liest `/dns/hosts` beim Installieren), je unter ihrem Schlüssel mit der Systemsoftware wie `installApp`, samt Einstellungen und persönlicher Firewall; E-Mail: Domain und Bean-Konten des Servers samt Postfächern, Ordner und Adressbuch des Clients (sein Konto steht in `/konten.txt`, einer Datei); Javas Fehler nicht übernommen: ein Empfänger, den eine ältere Datei als Zeichenkette hält, wird als der `AddressEntry` geschrieben, den Javas `AddressEntry(String)` daraus macht (Java legt die Zeichenkette ungeprüft in die typisierte Liste, sein E-Mail-Fenster scheitert daran), ein `null`-Empfänger fällt weg; Kopf „Filius version: 2.14.0 (mdg-filius)“ | | `test/topologyWriter.test.ts`, `test/written.java.json` | — | neu: alle 23 Korpus-Dateien gelesen und neu geschrieben, von Java geladen — Topologie, Systemsoftware, Dateisysteme und Programme samt E-Mail wie beim Original (bis auf die benannte Abweichung bei Empfängern als Zeichenkette, nur in `man-in-the-middle.fls`) (Java gegen Java); dazu eine künstliche Topologie mit dem, was der Korpus nicht hat (u. a. eingeschaltete Router-Firewall mit Regeln jedes Protokolls, alle Firewall-Schalter abseits der Vorgabe, Modem mit eigenem Port und Ziel, verschachtelter Dateibaum mit umbenannter Wurzel, namenlosem Ordner, Datei ohne Typ und Inhalt mit CR/CRLF und Zeichen jenseits der BMP, Heimrouter mit eigener Wurzel, jedes Filius-Programm mit Einstellungen abseits der Vorgabe samt persönlicher Firewall, von Java angelegte Ordner `/peer2peer` und `/mailserver`, E-Mail-Server und -Client mit Konto, Postfach, drei Ordnern, Adressbuch und Empfängern aller Formen) | -| `src/exportProject.ts` | Gegenstück zu `importProject.ts`; Java `JHostKonfiguration.updateWifiConnection`, `InternetKnotenBetriebssystem.getStandardGateway`/`setStandardGateway` | neu: Editor-Modell → Javas Sicht für den Schreiber, Schritt X1 Topologie — Geräte, Schnittstellen (MAC wie in der Simulation: aus der Datei oder aus der Port-ID abgeleitet), Kabel samt Funk (Karte drahtlos und Switch, wie Java verbindet), Gateway und DNS an allen Schnittstellen wie Javas eigene Setter, Doku-Elemente; X2 Systemsoftware — SSIDs, Lernzeit, Routen, RIP, DHCP-Client und -Server, Portweiterleitung, Firewall (Namen des Kerns als Javas Zahlen), Modems aus den Paaren des Modells (Port aus dem Paarnamen, der erste eines Paars dient, Ziel `localhost`) | -| `test/exportProject.test.ts`, `test/exported.java.json` | — | neu: alle 23 Korpus-Dateien importiert, exportiert, geschrieben und von Java geladen — Topologie wie beim Original bis auf benannte Abweichungen (abgeleiteter MAC und abgeleitete Switch-SSID statt gewürfelter, Modem-Rollen nach Reihenfolge im Paar, Doku in Zeichenreihenfolge mit Text, Schrift und Mindestgröße des Modells); dazu ein künstliches Modell, das Java ebenfalls lädt | +| `src/exportProject.ts` | Gegenstück zu `importProject.ts`; Java `JHostKonfiguration.updateWifiConnection`, `InternetKnotenBetriebssystem.getStandardGateway`/`setStandardGateway` | neu: Editor-Modell → Javas Sicht für den Schreiber, Schritt X1 Topologie — Geräte, Schnittstellen (MAC wie in der Simulation: aus der Datei oder aus der Port-ID abgeleitet), Kabel samt Funk (Karte drahtlos und Switch, wie Java verbindet), Gateway und DNS an allen Schnittstellen wie Javas eigene Setter, Doku-Elemente; X2 Systemsoftware — SSIDs, Lernzeit, Routen, RIP, DHCP-Client und -Server, Portweiterleitung, Firewall (Namen des Kerns als Javas Zahlen), Modems aus den Paaren des Modells (Port aus dem Paarnamen, der erste eines Paars dient, Ziel `localhost`); X3 Dateien von Rechner und Notebook — Typ aus der Inhaltsart (Text nach Endung, sonst `text`; Bild und Binärdaten nach Medientyp), Bilder und Binärdaten in Base64 wie Filius' `Base64.encodeBytes` (Umbruch nach 76 Zeichen, an 19 Längen mit Java gemessen) | +| `test/exportProject.test.ts`, `test/exported.java.json` | — | neu: alle 23 Korpus-Dateien importiert, exportiert, geschrieben und von Java geladen — Topologie wie beim Original bis auf benannte Abweichungen (abgeleiteter MAC und abgeleitete Switch-SSID statt gewürfelter, Modem-Rollen nach Reihenfolge im Paar, Doku in Zeichenreihenfolge mit Text, Schrift und Mindestgröße des Modells, Dateien in der Ordnung und mit dem Typ des Modells, keine eigenen Dateien von Routern); dazu ein künstliches Modell, das Java ebenfalls lädt | | `src/importEmail.ts` | `Persistence/TopologyProjectStore.swift` (`registerJavaEmailConfigurations`, `assignedJavaEmailMessages`) | übertragen, Eingabe ist Javas Sicht: Server-Konten aus `/mailserver/konten.txt`, ohne Datei aus der Bean-Liste (Java verlöre sie); Client-Konto aus `/konten.txt`, ohne Datei aus der älteren `kontoListe` (Filius 2.14 liest sie nicht mehr); Konten und Nachrichten bleiben, wo Swift verwirft, was seine Prüfung nicht besteht; Vor- und Nachname getrennt; Empfangszeit nur aus der Datei (Swift nummeriert); gegen Swifts Modell geprüft | | `test/importEmail.test.ts`, `test/email-*.swift.json` | `State/TopologyEmail.swift` (`decodeJavaServer`, `decodeJavaClient`) als Vergleich | neu: dieselben 43 Kontodateien mit Swifts Leser; wie Swift bis auf benannte Fälle, in denen Swift prüft und die ganze Datei verwirft, wo Java liest | | `src/javaFileSystem.ts`, `src/javaString.ts` | Java `Dateisystem.holeDatei`, `String.trim`/`split`/`equalsIgnoreCase` | neu nach Java; die Groß-/Kleinschreibung gegenüber ASCII am JDK über die ganze BMP gemessen | diff --git a/packages/fls/src/exportProject.ts b/packages/fls/src/exportProject.ts index 54dd4ef..cc39e84 100644 --- a/packages/fls/src/exportProject.ts +++ b/packages/fls/src/exportProject.ts @@ -15,7 +15,13 @@ // model has no setting for is what Java's constructors make. A remote link // pair becomes modems on the port its identifier names (12345 when it names // none): the first of a pair in node order serves, the others connect to -// localhost — the model's pairs live in one project. System software, files and programs +// localhost — the model's pairs live in one project. +// +// The files of a PC or notebook: the model's directories and files under +// Java's root, each file a Datei of the type Java's importer reads it by — +// text by its extension (else "text"), an image or binary data by its media +// type, in Base64 as FILIUS's Base64.encodeBytes writes it (javaBase64). A router or home +// router keeps the files its constructor makes; the model holds none for it. System software, files and programs // follow in later steps. // // What the model keeps is written as it keeps it, and so differs from the @@ -31,12 +37,16 @@ // - a documentation item always has a text and a font, bold or plain: where // Java has none, the empty text and Dialog 12 the model gives it; // - a switch's SSID the file lacked (Java rolls one) is the one the model -// derives from the node's id. +// derives from the node's id; +// - files in the model's order (directories first, then by name) and of the +// type their kind says, not the one the file named; a router's own files +// (a web server's www.conf) are not in the model. // // Checked by Java: every corpus file, imported, exported and written, loads in // Java FILIUS to the topology of the original up to those departures. import { + base64Encode, defaultSwitchConfiguration, dhcpServerConfiguration, effectiveMacAddress, @@ -60,8 +70,9 @@ import { type NodeKind, type TopologyGraph, type TopologyNode, + type VirtualFileSystem, } from '@mdg-filius/core'; -import type { FlsDhcpServer, FlsFirewall } from './systemSoftware.js'; +import type { FlsDhcpServer, FlsFileTree, FlsFirewall } from './systemSoftware.js'; import type { FlsDocumentationItem, FlsInterface, FlsLink, FlsNodeClass } from './topology.js'; import type { NodeToWrite, SystemToWrite, TopologyToWrite } from './topologyWriter.js'; @@ -79,6 +90,7 @@ export interface ProjectToExport { readonly dhcpServerConfigurations: ReadonlyMap; readonly portForwardingRowsByNodeId: ReadonlyMap; readonly remoteLinkConfigurations: ReadonlyMap; + readonly virtualFileSystems: ReadonlyMap; readonly documentationItems: readonly DocumentationItem[]; } @@ -206,7 +218,12 @@ function exportSystem(node: TopologyNode, project: ProjectToExport, servingModem case 'pc': case 'notebook': { const ssid = project.hostWirelessConfigurations.get(id)?.ssid ?? ''; - return { ...system, ssid: ssid === '' ? null : ssid }; + const files = project.virtualFileSystems.get(id); + return { + ...system, + ssid: ssid === '' ? null : ssid, + fileSystem: { kind: 'directory', name: 'root', children: files === undefined ? [] : fileTrees(files, '/') }, + }; } case 'router': return { @@ -228,6 +245,50 @@ function exportSystem(node: TopologyNode, project: ProjectToExport, servingModem } } +/** The text types Java's importer reads as text, named by a file's extension. */ +const TEXT_EXTENSIONS = new Set(['txt', 'html', 'htm', 'css', 'xml', 'csv', 'conf', 'log']); + +function fileTrees(files: VirtualFileSystem, directory: string): FlsFileTree[] { + return files.entries(directory).map((entry): FlsFileTree => { + const name = entry.path.slice(entry.path.lastIndexOf('/') + 1); + const file = (type: string | null, content: string): FlsFileTree => ({ kind: 'file', name, type, content, children: [] }); + switch (entry.content.kind) { + case 'directory': + return { kind: 'directory', name, children: fileTrees(files, entry.path) }; + case 'text': { + const dot = name.lastIndexOf('.'); + const extension = dot < 0 ? '' : name.slice(dot + 1).toLowerCase(); + return file(TEXT_EXTENSIONS.has(extension) ? extension : 'text', entry.content.text); + } + case 'image': + return file(subtype(entry.content.mediaType), javaBase64(entry.content.data)); + case 'binary': + return file(entry.content.mediaType === undefined ? '' : subtype(entry.content.mediaType), javaBase64(entry.content.data)); + } + }); +} + +/** + * FILIUS's Base64.encodeBytes (iHarder Base64 2.x): a line break after every + * 76 characters — also after the last full line — then the padded rest. + */ +export function javaBase64(bytes: Uint8Array): string { + const full = bytes.length - (bytes.length % 3); + let text = ''; + for (let offset = 0; offset < full; offset += 57) { + const end = Math.min(offset + 57, full); + text += base64Encode(bytes.subarray(offset, end)); + if (end - offset === 57) text += '\n'; + } + return text + base64Encode(bytes.subarray(full)); +} + +/** "image/png" → "png", "image/jpeg" → "jpg", as Java names them. */ +function subtype(mediaType: string): string { + const name = mediaType.slice(mediaType.indexOf('/') + 1); + return name === 'jpeg' ? 'jpg' : name; +} + function exportDhcpServer(server: DhcpServerConfiguration): FlsDhcpServer { return { isActive: server.isActive, diff --git a/packages/fls/test/base64.java.json b/packages/fls/test/base64.java.json new file mode 100644 index 0000000..9e1b198 --- /dev/null +++ b/packages/fls/test/base64.java.json @@ -0,0 +1,78 @@ +[ + [ + 0, + "" + ], + [ + 1, + "Cw==" + ], + [ + 2, + "CzA=" + ], + [ + 3, + "CzBV" + ], + [ + 55, + "CzBVep/E6Q4zWH2ix+wRNluApcrvFDleg6jN8hc8YYar0PUaP2SJrtP4HUJnjLHW+yBFao+02Q==" + ], + [ + 56, + "CzBVep/E6Q4zWH2ix+wRNluApcrvFDleg6jN8hc8YYar0PUaP2SJrtP4HUJnjLHW+yBFao+02f4=" + ], + [ + 57, + "CzBVep/E6Q4zWH2ix+wRNluApcrvFDleg6jN8hc8YYar0PUaP2SJrtP4HUJnjLHW+yBFao+02f4j\n" + ], + [ + 58, + "CzBVep/E6Q4zWH2ix+wRNluApcrvFDleg6jN8hc8YYar0PUaP2SJrtP4HUJnjLHW+yBFao+02f4j\nSA==" + ], + [ + 59, + "CzBVep/E6Q4zWH2ix+wRNluApcrvFDleg6jN8hc8YYar0PUaP2SJrtP4HUJnjLHW+yBFao+02f4j\nSG0=" + ], + [ + 113, + "CzBVep/E6Q4zWH2ix+wRNluApcrvFDleg6jN8hc8YYar0PUaP2SJrtP4HUJnjLHW+yBFao+02f4j\nSG2St9wBJktwlbrfBClOc5i94gcsUXabwOUKL1R5nsPoDTJXfKHG6xA1Wn+kye4TOF2Cp8zxFjs=" + ], + [ + 114, + "CzBVep/E6Q4zWH2ix+wRNluApcrvFDleg6jN8hc8YYar0PUaP2SJrtP4HUJnjLHW+yBFao+02f4j\nSG2St9wBJktwlbrfBClOc5i94gcsUXabwOUKL1R5nsPoDTJXfKHG6xA1Wn+kye4TOF2Cp8zxFjtg\n" + ], + [ + 115, + "CzBVep/E6Q4zWH2ix+wRNluApcrvFDleg6jN8hc8YYar0PUaP2SJrtP4HUJnjLHW+yBFao+02f4j\nSG2St9wBJktwlbrfBClOc5i94gcsUXabwOUKL1R5nsPoDTJXfKHG6xA1Wn+kye4TOF2Cp8zxFjtg\nhQ==" + ], + [ + 116, + "CzBVep/E6Q4zWH2ix+wRNluApcrvFDleg6jN8hc8YYar0PUaP2SJrtP4HUJnjLHW+yBFao+02f4j\nSG2St9wBJktwlbrfBClOc5i94gcsUXabwOUKL1R5nsPoDTJXfKHG6xA1Wn+kye4TOF2Cp8zxFjtg\nhao=" + ], + [ + 171, + "CzBVep/E6Q4zWH2ix+wRNluApcrvFDleg6jN8hc8YYar0PUaP2SJrtP4HUJnjLHW+yBFao+02f4j\nSG2St9wBJktwlbrfBClOc5i94gcsUXabwOUKL1R5nsPoDTJXfKHG6xA1Wn+kye4TOF2Cp8zxFjtg\nharP9Bk+Y4it0vccQWaLsNX6H0RpjrPY/SJHbJG22wAlSm+Uud4DKE1yl7zhBitQdZq/5AkuU3id\n" + ], + [ + 172, + "CzBVep/E6Q4zWH2ix+wRNluApcrvFDleg6jN8hc8YYar0PUaP2SJrtP4HUJnjLHW+yBFao+02f4j\nSG2St9wBJktwlbrfBClOc5i94gcsUXabwOUKL1R5nsPoDTJXfKHG6xA1Wn+kye4TOF2Cp8zxFjtg\nharP9Bk+Y4it0vccQWaLsNX6H0RpjrPY/SJHbJG22wAlSm+Uud4DKE1yl7zhBitQdZq/5AkuU3id\nwg==" + ], + [ + 2000, + "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\nYoes0fY=" + ], + [ + 2001, + "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\nYoes0fYb" + ], + [ + 2002, + "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\nYoes0fYbQA==" + ], + [ + 2052, + "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\n" + ] +] diff --git a/packages/fls/test/exportProject.test.ts b/packages/fls/test/exportProject.test.ts index 50da906..10995f1 100644 --- a/packages/fls/test/exportProject.test.ts +++ b/packages/fls/test/exportProject.test.ts @@ -20,12 +20,14 @@ import { stableGeneratedMacAddress, type Id, type TopologyNode, + VirtualFileSystem, } from '@mdg-filius/core'; import { describe, expect, it } from 'vitest'; import { decodeJavaBeans, exportProject, importProject, + javaBase64, readFlsArchive, readTopology, writeFlsArchive, @@ -66,10 +68,12 @@ const original = JSON.parse(readFileSync(join(corpusRoot, 'corpus.java.json'), ' const exportedPath = join(here, 'exported.java.json'); const exported = JSON.parse(readFileSync(exportedPath, 'utf8')) as Record; -/** The system software without its files and applications, later steps. */ +/** The system software without its applications, a later step; files in any order. */ function systemOf(system: Record) { - const { fileSystem: _files, applications: _applications, ...settings } = system; - return settings; + const { applications: _applications, ...settings } = system; + return settings.fileSystem === undefined || settings.fileSystem === null + ? settings + : { ...settings, fileSystem: sortedTree(settings.fileSystem as JavaTree) }; } /** The topology of Java's view: the label's position, not its size. */ @@ -102,7 +106,15 @@ function topologyOf(view: JavaFile) { * the model derives from the node's id; * - modemRoles: the model keeps a pair of modems, not which one serves: the * first of a pair in node order serves, the others connect. Both ways - * work within one project, where the client connects to localhost. + * work within one project, where the client connects to localhost; + * - routerFiles: the model holds no files for a router or home router; what + * the file adds to the ones its constructor makes (a web server's + * www.conf) is not written; + * - fileTypes: a file's type follows from its kind in the model (fileType + * below), not the one the file named — Java names them unevenly itself + * (txt, text/txt, html, none); + * - fileOrder: directories first, then by name (compared regardless of + * order here). */ const DEPARTURES = { generatedMac(view: JavaFile, macs: (string | undefined)[][]): JavaFile { @@ -130,6 +142,25 @@ const DEPARTURES = { return { ...node.system, modem: { ...(node.system.modem as object), mode: serves ? 1 : 2 } }; }); }, + routerFiles(view: JavaFile, added: number[]): JavaFile { + return mapSystems(view, (node, index) => { + if ((node.class !== 'Vermittlungsrechner' && node.class !== 'Gateway') || added[index] === 0) return node.system; + const tree = node.system.fileSystem as JavaTree; + return { ...node.system, fileSystem: { ...tree, children: tree.children.slice(0, tree.children.length - added[index]!) } }; + }); + }, + fileTypes(view: JavaFile): JavaFile { + const retype = (tree: JavaTree): JavaTree => ({ + ...tree, + ...('file' in tree ? { type: fileType(tree.file as string, tree.type as string | null) } : {}), + children: tree.children.map(retype), + }); + return mapSystems(view, (node) => + node.system.fileSystem === undefined || node.system.fileSystem === null + ? node.system + : { ...node.system, fileSystem: retype(node.system.fileSystem as JavaTree) }, + ); + }, documentation(view: JavaFile): JavaFile { const items = (view.documentation as Record[]).map((item) => { const [x, y, width, height] = item.bounds as number[]; @@ -144,6 +175,45 @@ const DEPARTURES = { }, }; +type JavaTree = { file?: string; type?: string | null; children: JavaTree[] } & Record; + +const IMAGE_TYPES = new Set(['png', 'jpg', 'jpeg', 'gif', 'bmp', 'image']); +const TEXT_TYPES = new Set(['', 'text', 'txt', 'text/txt', 'html', 'htm', 'css', 'xml', 'csv', 'conf', 'log']); +const TEXT_EXTENSIONS = new Set(['txt', 'html', 'htm', 'css', 'xml', 'csv', 'conf', 'log']); + +/** + * The type of a file in the model's kind: an image (by its type or + * extension) by its media type, "jpg" for JPEG; text by a known extension, + * else "text"; other data by its type. + */ +function fileType(name: string, type: string | null): string { + const declared = (type ?? '').trim().toLowerCase(); + const dot = name.lastIndexOf('.'); + const extension = dot < 0 ? '' : name.slice(dot + 1).toLowerCase(); + if (IMAGE_TYPES.has(declared) || IMAGE_TYPES.has(extension)) { + const subtype = declared === 'image' || declared === '' ? (extension === '' ? 'png' : extension) : declared; + return subtype === 'jpeg' ? 'jpg' : subtype; + } + if (TEXT_TYPES.has(declared)) return TEXT_EXTENSIONS.has(extension) ? extension : 'text'; + return declared; +} + +/** A file tree with every directory's children in an order that does not depend on the list's. */ +function sortedTree(tree: JavaTree): JavaTree { + const children = tree.children.map(sortedTree); + return { ...tree, children: children.map((child) => JSON.stringify(child)).sort().map((text) => JSON.parse(text) as JavaTree) }; +} + +/** Every node's file tree as text, sorted or as it is. */ +function fileTrees(view: JavaFile, sorted: boolean): string { + return JSON.stringify( + view.nodes.map((node) => { + const tree = node.system.fileSystem as JavaTree | null | undefined; + return tree === undefined || tree === null ? null : sorted ? sortedTree(tree) : tree; + }), + ); +} + function mapSystems(view: JavaFile, system: (node: JavaNode, index: number) => Record): JavaFile { return { ...view, nodes: view.nodes.map((node, index) => ({ ...node, system: system(node, index) })) }; } @@ -163,6 +233,9 @@ const departed = { documentation: new Set(), switchSsid: new Set(), modemRoles: new Set(), + routerFiles: new Set(), + fileTypes: new Set(), + fileOrder: new Set(), }; describe('the editor model exported, as Java loads it', () => { @@ -192,10 +265,14 @@ describe('the editor model exported, as Java loads it', () => { const source = original[name]!; const nodeIds = project.graph.nodes.map((node) => node.id); const pairs = nodeIds.map((id) => project.remoteLinkConfigurations.get(id)?.pairIdentifier); + // What each router's file adds to its constructor's files. + const added = topology.nodes.map((node) => (node.system.templateFiles === true ? (node.system.fileSystem?.children.length ?? 0) : 0)); const steps: [keyof typeof departed, (view: JavaFile) => JavaFile][] = [ ['generatedMac', (view) => DEPARTURES.generatedMac(view, macs)], ['switchSsid', (view) => DEPARTURES.switchSsid(view, nodeIds)], ['modemRoles', (view) => DEPARTURES.modemRoles(view, pairs)], + ['routerFiles', (view) => DEPARTURES.routerFiles(view, added)], + ['fileTypes', (view) => DEPARTURES.fileTypes(view)], ['documentation', (view) => DEPARTURES.documentation(view)], ]; let expected = source; @@ -208,6 +285,9 @@ describe('the editor model exported, as Java loads it', () => { } expected = next; } + if (fileTrees(expected, true) === fileTrees(java!, true) && fileTrees(expected, false) !== fileTrees(java!, false)) { + departed.fileOrder.add(name); + } expect(topologyOf({ ...java!, documentation: sortedDocumentation(java!.documentation) })).toEqual(topologyOf(expected)); }); } @@ -234,12 +314,49 @@ function emptyProject(): ProjectToExport { dhcpServerConfigurations: new Map(), portForwardingRowsByNodeId: new Map(), remoteLinkConfigurations: new Map(), + virtualFileSystems: new Map(), documentationItems: [], }; } const port = (id: string) => ({ id, label: id, isOccupied: false }); +/** Bytes of a length: (index * 37 + 11) mod 256. */ +function bytesOf(length: number): Uint8Array { + return Uint8Array.from({ length }, (_, index) => (index * 37 + 11) % 256); +} + +/** + * Nested directories, text with a known extension and without one, a PNG and + * a JPEG, binary data with and without a media type, 57 bytes — one line of + * Base64 exactly. + */ +function syntheticFiles(): VirtualFileSystem { + const files = new VirtualFileSystem(); + files.createDirectory('/docs/deep', true); + files.writeTextFile('/docs/readme.txt', 'Zeile 1\r\nZeile 2'); + files.writeTextFile('/docs/deep/notes', 'no extension'); + files.writeTextFile('/index.HTML', '

upper case extension

'); + files.writeImageFile('/bild.png', bytesOf(57), 'image/png'); + files.writeImageFile('/foto.jpg', bytesOf(100), 'image/jpeg'); + files.writeBinaryFile('/daten.bin', bytesOf(58), 'application/zip'); + files.writeBinaryFile('/roh', bytesOf(3)); + return files; +} + +/** The files a router's or home router's constructor makes, as Java loaded them from the corpus. */ +function templateOf(javaClass: string): unknown[] { + const node = Object.values(original) + .flatMap((file) => file.nodes) + .find((each) => each.class === javaClass); + return (node!.system.fileSystem as JavaTree).children.slice(0, 1); +} + +/** A file as the oracle describes it: its content's UTF-16 length and the SHA-256 of its UTF-8 bytes. */ +function javaFile(name: string, type: string | null, content: string) { + return { file: name, type, length: content.length, sha256: createHash('sha256').update(content, 'utf8').digest('hex'), children: [] }; +} + /** * What the corpus lacks: a label with IP and MAC, a position between * pixels, a switch with five ports, a wireless notebook, a router with three @@ -319,6 +436,7 @@ const SYNTHETIC: ProjectToExport = { ], ]), portForwardingRowsByNodeId: new Map([['gw', [{ protocolValue: 'TCP', publicPortValue: '8080', lanIPAddress: '192.168.1.10', lanPortValue: '80' }]]]), + virtualFileSystems: new Map([['pc', syntheticFiles()]]), remoteLinkConfigurations: new Map([ ['m1', { pairIdentifier: 'java-modem-port-4242', latencyMilliseconds: 20, isEnabled: true }], ['m2', { pairIdentifier: 'java-modem-port-4242', latencyMilliseconds: 20, isEnabled: true }], @@ -390,6 +508,20 @@ describe('a model beyond the corpus, as Java loads it', () => { manualRoutes: [['10.5.0.0', '255.255.0.0', '10.0.0.254', '10.0.0.1']], dhcpClient: true, dhcpServer: JAVA_DHCP_DEFAULTS, + fileSystem: sortedTree({ + directory: 'root', + children: [ + { + directory: 'docs', + children: [{ directory: 'deep', children: [javaFile('notes', 'text', 'no extension')] }, javaFile('readme.txt', 'txt', 'Zeile 1\r\nZeile 2')], + }, + javaFile('bild.png', 'png', javaBase64(bytesOf(57))), + javaFile('daten.bin', 'zip', javaBase64(bytesOf(58))), + javaFile('foto.jpg', 'jpg', javaBase64(bytesOf(100))), + javaFile('index.HTML', 'html', '

upper case extension

'), + javaFile('roh', '', javaBase64(bytesOf(3))), + ], + }), }, }, { @@ -408,7 +540,7 @@ describe('a model beyond the corpus, as Java loads it', () => { useIpAsName: false, useMacAsName: true, ports: [card('10.0.0.2', '255.255.255.0', stableGeneratedMacAddress('n0'), '', '', true)], - system: { ssid: 'net', manualRoutes: [], dhcpClient: false, dhcpServer: JAVA_DHCP_DEFAULTS }, + system: { ssid: 'net', manualRoutes: [], dhcpClient: false, dhcpServer: JAVA_DHCP_DEFAULTS, fileSystem: { directory: 'root', children: [] } }, }, { class: 'Vermittlungsrechner', @@ -423,6 +555,7 @@ describe('a model beyond the corpus, as Java loads it', () => { ], system: { ripEnabled: true, + fileSystem: sortedTree({ directory: 'root', children: templateOf('Vermittlungsrechner') as JavaTree[] }), manualRoutes: [['10.6.0.0', '255.255.0.0', '10.1.0.2', '10.1.0.1']], dhcpClient: false, dhcpServer: JAVA_DHCP_DEFAULTS, @@ -452,6 +585,7 @@ describe('a model beyond the corpus, as Java loads it', () => { card('192.168.1.1', '255.255.255.0', stableGeneratedMacAddress('g1'), '', ''), ], system: { + fileSystem: sortedTree({ directory: 'root', children: templateOf('Gateway') as JavaTree[] }), manualRoutes: [], dhcpClient: true, dhcpServer: { @@ -498,6 +632,15 @@ const JAVA_DHCP_DEFAULTS = { // ── Rules the corpus does not exercise ───────────────────────────────────── +describe('javaBase64', () => { + // FILIUS's Base64.encodeBytes around its line length (tools/java-oracle Base64Cases). + const cases = JSON.parse(readFileSync(join(here, 'base64.java.json'), 'utf8')) as [number, string][]; + it.each(cases)('encodes %i bytes as Java does', (length, java) => { + const bytes = Uint8Array.from({ length }, (_, index) => (index * 37 + 11) % 256); + expect(javaBase64(bytes)).toBe(java); + }); +}); + describe('exportProject rules', () => { const node = (id: string, kind: TopologyNode['kind'], ports: string[], extra: Partial = {}): TopologyNode => ({ id, diff --git a/packages/fls/test/exported.java.json b/packages/fls/test/exported.java.json index 37ae5ee..ca1bf7b 100644 --- a/packages/fls/test/exported.java.json +++ b/packages/fls/test/exported.java.json @@ -229,7 +229,27 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 538, + "sha256": "1966c377d3651754cfc513da8f425ec0e943e6cc5e8f7112aa06102e21eb0775", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -271,7 +291,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 36, + "sha256": "c4f62d9c112a0def5f67e6b803e8a55886a46712b52bfb73941e12f2f8a63c59", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -728,7 +761,7 @@ ] } ], - "sha256": "f0defe45fbcc22b7a6ab73d3961907737ac5b528c362855bb236befa83e5507b" + "sha256": "de90771cb42c28259bb07b52b5b87fc26e8a084c0a8ee2a34ed515fc372f0b9d" }, "ipad-fixtures/dns_server.fls": { "success": true, @@ -801,7 +834,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 84, + "sha256": "84d02c9a311e5cc7d1c45f3da3f30331903e8f8fc6c45e0d8a2500d393a6adb9", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -843,7 +889,31 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 557, + "sha256": "7d56103add4688141350c5901c1c9bb2c9495a054531b71a32cc9f96aed4ef45", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + }, + { + "directory": "www.conf", + "children": [] + } + ] }, "applications": [], "dhcpClient": false, @@ -927,7 +997,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 47, + "sha256": "d6fcb4528251a65a261ddefad36602c40aa55574cdb88d711a8955d999f2473a", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -1010,7 +1093,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 68, + "sha256": "2f916dbcb3ff8d29840ee37fe341fd8d323960526e0c891128963217d4039e53", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -1052,7 +1148,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 34, + "sha256": "fdba09cf276c8c66cf4676b2c5b741075268911b5ebac2c384c187a6f7137282", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -1190,7 +1299,7 @@ } ], "documentation": [], - "sha256": "6b40b826ebbd20964e1e4f4c19c3be105d5357704c6966aa875241cabaeec759" + "sha256": "c653639f118af6851ce1bbfe90da6d131a69cd0ddbcce942376528c6f8972231" }, "ipad-fixtures/zwei_rechnernetze_komplett.fls": { "success": true, @@ -1304,7 +1413,51 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 33, + "sha256": "3d801783d135a0e42a61ecbba025a318f92c79945e0ca7ae6ea54698b6f5e917", + "children": [] + } + ] + }, + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 557, + "sha256": "156dfa798344ce7f75e3f40ab416c5e2c994c68a110cec5c8d686c31a403f10b", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + }, + { + "directory": "www.conf", + "children": [ + { + "file": "vhosts", + "type": "text", + "length": 52, + "sha256": "528219400a878871604b1ed891f8926fcf142fbaa329722d1b6fcec159c54241", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -1388,7 +1541,39 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 557, + "sha256": "7d56103add4688141350c5901c1c9bb2c9495a054531b71a32cc9f96aed4ef45", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + }, + { + "directory": "www.conf", + "children": [ + { + "file": "vhosts", + "type": "text", + "length": 50, + "sha256": "49583d26538d3b0a66f4332e97ffeaeee1b91c4199dc44deaaa5f1e40ffc6089", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -1567,7 +1752,7 @@ } ], "documentation": [], - "sha256": "2ed2c7a1240329c741000aa6eec7f8ed721e5448efc957cdc8ab04dd94898f69" + "sha256": "fd7a250c308e2ade4b7aed196c145b4d06742394834bbea966d922002ecafb3e" }, "java-examples/Internet_Complete_with_e-Mail_Webserver_Intranet_Portforwarding_Firewall_DHCP_EN.fls": { "success": true, @@ -1599,7 +1784,15 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 84, + "sha256": "011c91d5e632db92ddd42ddf351d2741a99be3410f76b6528fbeca029ce90ab5", + "children": [] + } + ] }, "applications": [], "dhcpClient": true, @@ -1682,7 +1875,15 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 88, + "sha256": "f4692cfecba61e6a4f0c6db1009cf76756d0a0239b1d2ca4ab5bd95693c3d63f", + "children": [] + } + ] }, "applications": [], "dhcpClient": true, @@ -2238,7 +2439,27 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 214, + "sha256": "8c8286bc4ce20a063fd6bcea621c7e576ec1b55b172c3f33d226be0d34cb32a4", + "children": [] + }, + { + "file": "restored-1770036475063", + "type": "text", + "length": 215, + "sha256": "024f81af0105fc629070ef4c3de21426c0e37cac4a415c1cb3ad01edf726e8ab", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -2280,7 +2501,27 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 500, + "sha256": "94fc9cc5ea871634a045e3e2c01838796d4623ec4488ea23cff50e949c2929b4", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -2322,7 +2563,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "mailserver", + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 90, + "sha256": "378f8265131d36031047d7918f0e5931c746c52bea39758143f7a42ed16cc379", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -2364,7 +2618,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "mailserver", + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 46, + "sha256": "d860c7be31f9fa05c689eba6f5b527371e3c81de5e54aa93719a09decd44fb93", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -2406,7 +2673,39 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 33, + "sha256": "5f8235d016bcc8024b554ec91df22ec0bebab7f799fc6009bbd34073224c87d4", + "children": [] + } + ] + }, + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 502, + "sha256": "742fcdde965a05cb80624374067cab0560a9196d224d97d5e7ab9bae6c507127", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -2526,7 +2825,15 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 78, + "sha256": "37acca898f0c5587824be7fe2ea7ca4771976a2a16b90dc262531126d4970437", + "children": [] + } + ] }, "applications": [], "dhcpClient": true, @@ -2568,7 +2875,15 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 81, + "sha256": "cd35dc5dacfe0681c4ca63b0930d64586328f35313bfd207fe90c9647361de63", + "children": [] + } + ] }, "applications": [], "dhcpClient": true, @@ -3786,7 +4101,7 @@ ] } ], - "sha256": "b6c00e3e81dc32d03c349622e8559993575a00bf5d699f39e19f955963edcbc6" + "sha256": "55c56cb7e5ce455128c180515c6b12a93c2f57256b841521856bb127aa4d5ba0" }, "java-examples/Internet_Komplett_mit_eMail_Webserver_Intranet_Portforwarding_Firewall_DHCP_DE.fls": { "success": true, @@ -3818,7 +4133,15 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 81, + "sha256": "cd35dc5dacfe0681c4ca63b0930d64586328f35313bfd207fe90c9647361de63", + "children": [] + } + ] }, "applications": [], "dhcpClient": true, @@ -3901,7 +4224,15 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 85, + "sha256": "584f97b2108104e758f2896f914a8c02f91bedc28dc4d762714596c116c853c5", + "children": [] + } + ] }, "applications": [], "dhcpClient": true, @@ -4457,7 +4788,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 210, + "sha256": "609f4ac4d06ac54a04b080458b83ed7ee2f523807ffa2bf9a64849117c921368", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -4499,7 +4843,27 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 500, + "sha256": "94fc9cc5ea871634a045e3e2c01838796d4623ec4488ea23cff50e949c2929b4", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -4541,7 +4905,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "mailserver", + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 88, + "sha256": "d5eef2ffc8970f093433912ff9ef74e0b0d166310d9ed56f5f4f8ff3ed7c97e6", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -4583,7 +4960,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "mailserver", + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 45, + "sha256": "63cba5c91ef0bc92aec8c3c5b800397a59f4959dde5a818dde9abd8ab37946b6", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -4625,7 +5015,39 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 33, + "sha256": "5f8235d016bcc8024b554ec91df22ec0bebab7f799fc6009bbd34073224c87d4", + "children": [] + } + ] + }, + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 518, + "sha256": "534e5a8f60e38149d47c863b4b7c143ade68a31744a44e02ed1e770f9e7a7b3f", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -4745,7 +5167,15 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 75, + "sha256": "6d8585e1f74b612e758505a156694dfb412b842bdc28e8ed3841199a3970682c", + "children": [] + } + ] }, "applications": [], "dhcpClient": true, @@ -4787,7 +5217,15 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 81, + "sha256": "cd35dc5dacfe0681c4ca63b0930d64586328f35313bfd207fe90c9647361de63", + "children": [] + } + ] }, "applications": [], "dhcpClient": true, @@ -5963,7 +6401,7 @@ ] } ], - "sha256": "4aa181ace877932ab88fbd0a7715f59c21ac00a84aa12ec18d1556b6d91b0e27" + "sha256": "9bf1d655d2c53bb7887271c97b65518c03461d2237575ccc855d0660770eadcd" }, "java-examples/Public_and_Private_Networks_2_EN.fls": { "success": true, @@ -6195,7 +6633,27 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 538, + "sha256": "1966c377d3651754cfc513da8f425ec0e943e6cc5e8f7112aa06102e21eb0775", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -6237,7 +6695,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 36, + "sha256": "c4f62d9c112a0def5f67e6b803e8a55886a46712b52bfb73941e12f2f8a63c59", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -6694,7 +7165,7 @@ ] } ], - "sha256": "f0defe45fbcc22b7a6ab73d3961907737ac5b528c362855bb236befa83e5507b" + "sha256": "de90771cb42c28259bb07b52b5b87fc26e8a084c0a8ee2a34ed515fc372f0b9d" }, "java-examples/anwendung_komplett.fls": { "success": true, @@ -6921,7 +7392,7 @@ } ], "documentation": [], - "sha256": "c4d7f545e981678988135cf988f87863d385ecc281a607cf5aab97b62f0f6b08" + "sha256": "1d22ad715862ffc950c9323b7372eaeb241455753995be089e8541dd23b72f6b" }, "java-examples/dhcp_test_scenario.fls": { "success": true, @@ -7341,7 +7812,7 @@ } ], "documentation": [], - "sha256": "863f5d67e356ba363db33d31ec818fa3de17a022812644551bf0c9838d90247d" + "sha256": "76d8317e6d352e8686663957f0bbc74f1945b0c419a80ac94992e40590e75e8c" }, "java-examples/dns_server.fls": { "success": true, @@ -7414,7 +7885,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 84, + "sha256": "84d02c9a311e5cc7d1c45f3da3f30331903e8f8fc6c45e0d8a2500d393a6adb9", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -7456,7 +7940,31 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 557, + "sha256": "7d56103add4688141350c5901c1c9bb2c9495a054531b71a32cc9f96aed4ef45", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + }, + { + "directory": "www.conf", + "children": [] + } + ] }, "applications": [], "dhcpClient": false, @@ -7540,7 +8048,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 47, + "sha256": "d6fcb4528251a65a261ddefad36602c40aa55574cdb88d711a8955d999f2473a", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -7623,7 +8144,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 68, + "sha256": "2f916dbcb3ff8d29840ee37fe341fd8d323960526e0c891128963217d4039e53", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -7665,7 +8199,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 34, + "sha256": "fdba09cf276c8c66cf4676b2c5b741075268911b5ebac2c384c187a6f7137282", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -7803,7 +8350,7 @@ } ], "documentation": [], - "sha256": "6b40b826ebbd20964e1e4f4c19c3be105d5357704c6966aa875241cabaeec759" + "sha256": "c653639f118af6851ce1bbfe90da6d131a69cd0ddbcce942376528c6f8972231" }, "java-examples/einfaches_rechnernetz_komplett.fls": { "success": true, @@ -8003,7 +8550,65 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 64, + "sha256": "94c7a19c36a5e11af62ffba031840ce9c238231f18f1e999f1a908b3956540aa", + "children": [] + } + ] + }, + { + "directory": "webserver", + "children": [ + { + "file": "800px-Loewe_3.JPG", + "type": "jpg", + "length": 117116, + "sha256": "04d6353a6e750e8af80058fc299ebaaf85a14d740eaa7c19b5db8b42e9000f9d", + "children": [] + }, + { + "file": "index.html", + "type": "html", + "length": 615, + "sha256": "c7682b3a9758588021d73ec7a01769c1a3056b666a252822ecaea18514b6e784", + "children": [] + }, + { + "file": "meine-seite.html", + "type": "html", + "length": 121, + "sha256": "3f9f9a107b72e97f97671f00232db9ab60e96859d6be66553faf329b64136427", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + }, + { + "directory": "www.conf", + "children": [ + { + "file": "vhosts", + "type": "text", + "length": 52, + "sha256": "528219400a878871604b1ed891f8926fcf142fbaa329722d1b6fcec159c54241", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -8500,7 +9105,7 @@ } ], "documentation": [], - "sha256": "b6c7806c4394e91a338416bb8d9547d9f7f5cba0d77edbcf99d0963b00a9cabe" + "sha256": "cdba1260edf112085e1975c8ad76342fbcb59e72bbc3b6bc7efa7bc97235d706" }, "java-examples/email_komplett.fls": { "success": true, @@ -8532,7 +9137,27 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 557, + "sha256": "7d56103add4688141350c5901c1c9bb2c9495a054531b71a32cc9f96aed4ef45", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -8574,7 +9199,15 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 82, + "sha256": "db07ea20f140dc28e0c08d4379c3745c527012935d14b006c481c4e66db101b9", + "children": [] + } + ] }, "applications": [], "dhcpClient": false, @@ -8616,7 +9249,15 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 82, + "sha256": "46fda62f5183c745e1ea1fc627511761a3532c6240a70230b8d2b7c022fad134", + "children": [] + } + ] }, "applications": [], "dhcpClient": false, @@ -8658,7 +9299,15 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 82, + "sha256": "a1d89620fc770742f30003283d9db30a2993b0f22eb53eed5bfb9fd38bb5514b", + "children": [] + } + ] }, "applications": [], "dhcpClient": false, @@ -8700,7 +9349,15 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 82, + "sha256": "0884e1ff30882ed87c758e949b05df36a3c38f8af9a2461d449517f143eeb92b", + "children": [] + } + ] }, "applications": [], "dhcpClient": false, @@ -8783,7 +9440,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 173, + "sha256": "597ad9f41447233f8bb178a0ade919345ac79dbb599d748abea2564b496193eb", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -8825,7 +9495,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "mailserver", + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 92, + "sha256": "0899cce8e063b7287c0b73b011adf1e02bfab11e834765b3e6d43ebf3a37d478", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -8867,7 +9550,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "mailserver", + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 92, + "sha256": "bb3604ef16c2182b5097e628624b62e95c4872b45ffecba550e983cef5b066ab", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -9022,7 +9718,7 @@ } ], "documentation": [], - "sha256": "cb3e3989a69306a93a28f1fcae310fe3717c59103695a91a76bd5175d05c5461" + "sha256": "b6a77cdbca831776ac8093fd7873f3cbe60e8c1a72384688420b44375a68aeb0" }, "java-examples/internetwork_komplett.fls": { "success": true, @@ -9423,7 +10119,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 33, + "sha256": "6d4d6e43368e520d27734832db0ba83ba078865398d0ee6c57b3345a733b66ba", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -9507,7 +10216,27 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 557, + "sha256": "7d56103add4688141350c5901c1c9bb2c9495a054531b71a32cc9f96aed4ef45", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -9591,7 +10320,27 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 557, + "sha256": "7d56103add4688141350c5901c1c9bb2c9495a054531b71a32cc9f96aed4ef45", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -9956,7 +10705,7 @@ } ], "documentation": [], - "sha256": "4fad67697c52751855d232ce12ac5b7d33f3b36a300a9563d66d0deb1499a66e" + "sha256": "8731f9355d9bf2679cf8e393b680e8092be2ef44d85d6787bcb910b864697463" }, "java-examples/man-in-the-middle.fls": { "success": true, @@ -9988,7 +10737,15 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 71, + "sha256": "2bc178b534af284e8e997b2306c83630e4eca07bf5ffcd110e6de9725c103a4f", + "children": [] + } + ] }, "applications": [], "dhcpClient": false, @@ -10030,7 +10787,39 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 144, + "sha256": "15be56da82ced21860c3c859f7d615a2e0e2393ddba8823fb603958ee875cd4e", + "children": [] + } + ] + }, + { + "directory": "mailserver", + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "children": [] + } + ] + }, + { + "file": "konten.txt", + "type": "txt", + "length": 82, + "sha256": "46fda62f5183c745e1ea1fc627511761a3532c6240a70230b8d2b7c022fad134", + "children": [] + } + ] }, "applications": [], "dhcpClient": false, @@ -10072,7 +10861,15 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 63, + "sha256": "e8c73d7f647fad92ef790fe273b4685b376c3ebfab403e66e2982b077eabe1bc", + "children": [] + } + ] }, "applications": [], "dhcpClient": false, @@ -10155,7 +10952,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 180, + "sha256": "0978bc12de5a52832ee7b7df9d5aa2bbdcd2e6b506f8a5ea58173dc148b0daf0", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -10197,7 +11007,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "mailserver", + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 216, + "sha256": "96e965caacdef402e4041d55f337e1113f7d59b500658ec2b74ae20a9feca59c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -10239,7 +11062,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "mailserver", + "children": [ + { + "file": "konten.txt", + "type": "txt", + "length": 40, + "sha256": "336d0c1e9a13c917c64d91f072e56891c7685562d7cefcb9d8c548e13bb11b41", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -10424,7 +11260,7 @@ ] } ], - "sha256": "d56bb01b5b7d7125e3c5897b11aede16497e1401e780c4952a8f74f1e2aaae4e" + "sha256": "665f9ae5ec9573f1922248adcfebf663f26f16e25759317dcfb2d629bd157bdc" }, "java-examples/modem_beispiel.fls": { "success": true, @@ -10456,7 +11292,31 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 504, + "sha256": "28c812abf90ecf38cb27490ba231b3ae2b9c9a5311744038a7c248d4ebdbd145", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + }, + { + "directory": "www.conf", + "children": [] + } + ] }, "applications": [], "dhcpClient": false, @@ -10593,7 +11453,7 @@ } ], "documentation": [], - "sha256": "eae83c9db80a8289f425c2026bfab2dd3e417fb571a56bdf144a6a1c61c32f4f" + "sha256": "12e37a892c32b55eab37ab683dd18ac0fa6d5361c2a009cc26a4b83b1dca58a4" }, "java-examples/multicast_server.fls": { "success": true, @@ -10667,7 +11527,19 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "peer2peer", + "children": [] + }, + { + "file": "test.txt", + "type": "txt", + "length": 5, + "sha256": "6382c29c5fdcdc94705afc46f1d9b37c21e1663042ae0a3778ade3f5556a9254", + "children": [] + } + ] }, "applications": [], "dhcpClient": false, @@ -10820,7 +11692,7 @@ } ], "documentation": [], - "sha256": "2eee88e78a1a50308a6f1a63485e6927c7bd81bf0d96c481fddb1e91ac1c67d3" + "sha256": "dcc87330dead893eaf9801e2faf517d569e763977ea210dfe634811cbf67102b" }, "java-examples/nat.fls": { "success": true, @@ -10927,7 +11799,27 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 538, + "sha256": "1966c377d3651754cfc513da8f425ec0e943e6cc5e8f7112aa06102e21eb0775", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -11080,7 +11972,7 @@ } ], "documentation": [], - "sha256": "4ead19012d1bb245f5649fca807561ba8974544e720e6cb486c4b1fd9c6c82ea" + "sha256": "f3cbdf99f552c567275cd08e86c73049eeadce7e21aca0f9709e31cac09be21f" }, "java-examples/nat_with_mapped_port.fls": { "success": true, @@ -11440,7 +12332,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 69, + "sha256": "39238be537759ecfb24fc46723a27fe5e7136996792df18c6daa647929017529", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -11524,7 +12429,27 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 557, + "sha256": "7d56103add4688141350c5901c1c9bb2c9495a054531b71a32cc9f96aed4ef45", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -11566,7 +12491,27 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 213, + "sha256": "36a1dbc59a58840d2308fbecefa3646902a143938c36892628bd12d2e5645928", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -11608,7 +12553,27 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 393, + "sha256": "a914b401e99f75d1d9becb3e290e92f0a77b6d7bff44b5f2f4039bafc18d974b", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -12336,7 +13301,7 @@ ] } ], - "sha256": "4d36314f4f69837268d5b0efc27f0730345fbbffc85bb977721583992c3991ba" + "sha256": "bd7e9369123196c4f9db9dc1e44d6ffb02ca85b0c0d5afab4b3b599d1ee95cea" }, "java-examples/private_and_public_network.fls": { "success": true, @@ -12451,7 +13416,27 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 557, + "sha256": "7d56103add4688141350c5901c1c9bb2c9495a054531b71a32cc9f96aed4ef45", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": true, @@ -12609,7 +13594,39 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 34, + "sha256": "83b965bcc5eef638772b9cb7e0db056797e9a1201da798dd100151a0413a0043", + "children": [] + } + ] + }, + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 429, + "sha256": "225419200943fbe708a6680b66570b3fe609cbbd4e05f1c2faa0d96d9e98b980", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -12861,7 +13878,7 @@ ] } ], - "sha256": "bd7f5a5f96982f8db3d7730f459407b0638ab9d148803e46b10a3fee706e30d7" + "sha256": "e1389dd78c357dda1a60f59d226517574f5a12057da1b45e0d6424cbb3a812ea" }, "java-examples/private_und_public_networks_mit_provider_DE.fls": { "success": true, @@ -12976,7 +13993,27 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 557, + "sha256": "7d56103add4688141350c5901c1c9bb2c9495a054531b71a32cc9f96aed4ef45", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": true, @@ -13134,7 +14171,39 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 31, + "sha256": "91576d35c0857efcc36c4af0ed40a90cd231f3a905ee4c411ed929f0ed2e0bae", + "children": [] + } + ] + }, + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 445, + "sha256": "7436e0ed47fdaaefc06b8ee991cae129e09231007a302f8af83eb9ee14182b3f", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -13259,7 +14328,27 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 540, + "sha256": "c31760d4ecb049671d80666845f315f35f44bcb94ecc8905cdad5976aa95047c", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -13301,7 +14390,20 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 36, + "sha256": "2ada78d7cdaf174e4657b0ab6fe37b912287577c07d65ec43db3ffc4768d1f4b", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -13767,7 +14869,7 @@ ] } ], - "sha256": "bdb684d8f4653604e6a40d7783b7235743a340605c55ef0a886967e40fc52cde" + "sha256": "815faa519722a0c93350aac97466a3dedd2727778039d0bb2425051cc22ca0a7" }, "java-examples/webserver.fls": { "success": true, @@ -13799,7 +14901,45 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "Ahornblatt.jpg", + "type": "jpg", + "length": 33069, + "sha256": "bf38aff1cafd1bf38df6cf47c25448b3c25ee0a9ffa35f28e98f327902c111ac", + "children": [] + }, + { + "file": "beispiel.html", + "type": "html", + "length": 149, + "sha256": "8ba3a312c296ee02d5dfcb69a931d95582ac5b5d67884d25230fc27f1e16208a", + "children": [] + }, + { + "file": "index.html", + "type": "html", + "length": 443, + "sha256": "61e19fe97d2681ae0ec7c8a53ccda98577a0716b92af0c7f5e8d9bbe2bedffcd", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + }, + { + "directory": "www.conf", + "children": [] + } + ] }, "applications": [], "dhcpClient": false, @@ -13841,7 +14981,15 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "file": "test", + "type": "text", + "length": 5, + "sha256": "6382c29c5fdcdc94705afc46f1d9b37c21e1663042ae0a3778ade3f5556a9254", + "children": [] + } + ] }, "applications": [], "dhcpClient": false, @@ -13935,7 +15083,7 @@ } ], "documentation": [], - "sha256": "ba5908aa925435ca6c7bb68939a33c4a0ce702ace4a96a11adb579406f1ae4b7" + "sha256": "8f2103782c99f8718953dc56c73af72fde9024654395d9c378cb322431a3c1f9" }, "java-examples/zwei_rechnernetze_komplett.fls": { "success": true, @@ -14049,7 +15197,51 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 33, + "sha256": "3d801783d135a0e42a61ecbba025a318f92c79945e0ca7ae6ea54698b6f5e917", + "children": [] + } + ] + }, + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 557, + "sha256": "156dfa798344ce7f75e3f40ab416c5e2c994c68a110cec5c8d686c31a403f10b", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + }, + { + "directory": "www.conf", + "children": [ + { + "file": "vhosts", + "type": "text", + "length": 52, + "sha256": "528219400a878871604b1ed891f8926fcf142fbaa329722d1b6fcec159c54241", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -14133,7 +15325,39 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "webserver", + "children": [ + { + "file": "index.html", + "type": "html", + "length": 557, + "sha256": "7d56103add4688141350c5901c1c9bb2c9495a054531b71a32cc9f96aed4ef45", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + }, + { + "directory": "www.conf", + "children": [ + { + "file": "vhosts", + "type": "text", + "length": 50, + "sha256": "49583d26538d3b0a66f4332e97ffeaeee1b91c4199dc44deaaa5f1e40ffc6089", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -14312,7 +15536,7 @@ } ], "documentation": [], - "sha256": "2ed2c7a1240329c741000aa6eec7f8ed721e5448efc957cdc8ab04dd94898f69" + "sha256": "fd7a250c308e2ade4b7aed196c145b4d06742394834bbea966d922002ecafb3e" }, "java-tests/einfaches_rechnernetz_komplett.fls": { "success": true, @@ -14512,7 +15736,65 @@ "manualRoutes": [], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "dns", + "children": [ + { + "file": "hosts", + "type": "text", + "length": 64, + "sha256": "94c7a19c36a5e11af62ffba031840ce9c238231f18f1e999f1a908b3956540aa", + "children": [] + } + ] + }, + { + "directory": "webserver", + "children": [ + { + "file": "800px-Loewe_3.JPG", + "type": "jpg", + "length": 117116, + "sha256": "04d6353a6e750e8af80058fc299ebaaf85a14d740eaa7c19b5db8b42e9000f9d", + "children": [] + }, + { + "file": "index.html", + "type": "html", + "length": 615, + "sha256": "c7682b3a9758588021d73ec7a01769c1a3056b666a252822ecaea18514b6e784", + "children": [] + }, + { + "file": "meine-seite.html", + "type": "html", + "length": 121, + "sha256": "3f9f9a107b72e97f97671f00232db9ab60e96859d6be66553faf329b64136427", + "children": [] + }, + { + "file": "splashscreen-mini.png", + "type": "png", + "length": 8397, + "sha256": "c876b828d7258ddfce4aa01ecaa69d4685d238bccf6a94842972165bd1a0107c", + "children": [] + } + ] + }, + { + "directory": "www.conf", + "children": [ + { + "file": "vhosts", + "type": "text", + "length": 52, + "sha256": "528219400a878871604b1ed891f8926fcf142fbaa329722d1b6fcec159c54241", + "children": [] + } + ] + } + ] }, "applications": [], "dhcpClient": false, @@ -15009,7 +16291,7 @@ } ], "documentation": [], - "sha256": "b6c7806c4394e91a338416bb8d9547d9f7f5cba0d77edbcf99d0963b00a9cabe" + "sha256": "cdba1260edf112085e1975c8ad76342fbcb59e72bbc3b6bc7efa7bc97235d706" }, "java-tests/empty.fls": { "success": true, @@ -15055,7 +16337,67 @@ ], "fileSystem": { "directory": "root", - "children": [] + "children": [ + { + "directory": "docs", + "children": [ + { + "directory": "deep", + "children": [ + { + "file": "notes", + "type": "text", + "length": 12, + "sha256": "5344287ad0cd517b62386e7698af494ea129fbf7fa96a5905ca0011d4b972a40", + "children": [] + } + ] + }, + { + "file": "readme.txt", + "type": "txt", + "length": 16, + "sha256": "926b191ec3a9aaaf0bf36487aa8b728b2ba8fc93fa27ae3f6ba999f19d6a9ef7", + "children": [] + } + ] + }, + { + "file": "bild.png", + "type": "png", + "length": 77, + "sha256": "57af5eced0df44fdb2e7ab8313ebc1488ca3a9dd995a83f19ea04e05b6e95d8e", + "children": [] + }, + { + "file": "daten.bin", + "type": "zip", + "length": 81, + "sha256": "e28d2a92bdde4fd7a0cc04c4451025dc53250493a4796a1b0bd7b5995f2dd22e", + "children": [] + }, + { + "file": "foto.jpg", + "type": "jpg", + "length": 137, + "sha256": "067cf19657ad73978e20da6cdbdddff3f1815736026938b652cbe553c2ee4ab0", + "children": [] + }, + { + "file": "index.HTML", + "type": "html", + "length": 27, + "sha256": "ac4b4251cfd56976b9eeb547912ff7e709d56bdb1d1caa0ade73dff843dff318", + "children": [] + }, + { + "file": "roh", + "type": "", + "length": 4, + "sha256": "09006ade084f4668397a1e9e1d7763c683af7957e3a62e04e039425aaaeff7aa", + "children": [] + } + ] }, "applications": [], "dhcpClient": true, @@ -15506,6 +16848,6 @@ ] } ], - "sha256": "874f2f33f2f5beb5c301fc58f61deacc666fe65fb7289cd52abae84bd7dfb8d4" + "sha256": "16986c17c7720dffb33a07d64a316de8ce9d9fca08bec1f3ebcfe68de8cb0370" } } diff --git a/tools/java-oracle/README.md b/tools/java-oracle/README.md index e700bd2..56cda43 100644 --- a/tools/java-oracle/README.md +++ b/tools/java-oracle/README.md @@ -98,6 +98,15 @@ passt. Je Kabel steht auch `panel`: die Geräte, zwischen denen die Oberfläche es zeichnet (`JCablePanel.ziel1/ziel2`) — im Korpus bei allen 183 Kabeln die Geräte, die es verbindet. +## Wie Java Base64 schreibt + +`Base64Cases` ruft Filius' eigenes `Base64.encodeBytes` (iHarder 2.x) auf +Byte-Folgen von 19 Längen rund um die Zeilenlänge auf +(`packages/fls/test/base64.java.json`). Befund: nach je 76 Zeichen ein +`\n`, auch nach der letzten vollen Zeile, dann der aufgefüllte Rest ohne +Umbruch. So stehen Bilder in den Dateien, auch die, die Javas Konstruktoren +selbst anlegen. + ## Was Java aus unserem XML macht `BeansDump` liest jede `*.xml` unter `packages/fls/test/beans/` mit diff --git a/tools/java-oracle/run.sh b/tools/java-oracle/run.sh index 15fa3cf..874e663 100755 --- a/tools/java-oracle/run.sh +++ b/tools/java-oracle/run.sh @@ -21,7 +21,7 @@ if [ ! -f "$upstream/target/filius.jar" ]; then fi classpath="$upstream/target/filius.jar:$upstream/target/lib/*" mkdir -p "$here/.build" -javac -encoding UTF-8 -d "$here/.build" -cp "$classpath" "$here/src/FlsOracle.java" "$here/src/GetterDefaults.java" "$here/src/IpCheckCases.java" "$here/src/DnsHostsCases.java" "$here/src/EmailFileCases.java" "$here/src/BeansDump.java" +javac -encoding UTF-8 -d "$here/.build" -cp "$classpath" "$here/src/FlsOracle.java" "$here/src/GetterDefaults.java" "$here/src/IpCheckCases.java" "$here/src/DnsHostsCases.java" "$here/src/EmailFileCases.java" "$here/src/BeansDump.java" "$here/src/Base64Cases.java" # FILIUS writes its settings below $HOME; keep them out of the real one. mkdir -p "$here/.build/home" # Removed first: a crashed run must not leave the previous result standing. @@ -67,6 +67,12 @@ rm -f "$ipcheck" HOME="$here/.build/home" java -cp "$classpath:$here/.build" IpCheckCases "$cases" "$ipcheck" 2>&1 | grep -v 'logback\|^$' || true test -s "$ipcheck" +# Base64.encodeBytes around its line length (packages/fls/src/exportProject.ts, javaBase64). +base64="$root/packages/fls/test/base64.java.json" +rm -f "$base64" +HOME="$here/.build/home" java -cp "$classpath:$here/.build" Base64Cases "$base64" 0 1 2 3 55 56 57 58 59 113 114 115 116 171 172 2000 2001 2002 2052 2>&1 | grep -v 'logback\|^$' || true +test -s "$base64" + # DNSServer's own reading of /dns/hosts on hand-picked files (packages/fls/src/dnsHosts.ts). cases="$root/packages/fls/test/dns-hosts.cases.txt" dnshosts="$root/packages/fls/test/dns-hosts.java.json" diff --git a/tools/java-oracle/src/Base64Cases.java b/tools/java-oracle/src/Base64Cases.java new file mode 100644 index 0000000..ff13e5d --- /dev/null +++ b/tools/java-oracle/src/Base64Cases.java @@ -0,0 +1,32 @@ +// Runs FILIUS's own Base64.encodeBytes on byte arrays of the given lengths +// and writes what Java returns — the reference for javaBase64 in +// packages/fls/src/exportProject.ts. The bytes of length n are +// (index * 37 + 11) mod 256, so both sides make them alike. +// +// Usage: java -cp filius.jar:lib/*:classes Base64Cases ... + +import filius.rahmenprogramm.Base64; + +import java.io.FileOutputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class Base64Cases { + + public static void main(String[] args) throws Exception { + List result = new ArrayList<>(); + for (int argument = 1; argument < args.length; argument++) { + int length = Integer.parseInt(args[argument]); + byte[] bytes = new byte[length]; + for (int index = 0; index < length; index++) bytes[index] = (byte) ((index * 37 + 11) % 256); + result.add(Arrays.asList(length, Base64.encodeBytes(bytes))); + } + try (FileOutputStream out = new FileOutputStream(args[0])) { + out.write((FlsOracle.Json.write(result, "") + "\n").getBytes(StandardCharsets.UTF_8)); + } + System.out.println("base64 cases: " + result.size()); + System.exit(0); + } +}