feat(fls): file systems of PCs and notebooks as Java loads them
readSystemSoftware reads a host's file system: Java's tree of DefaultMutableTreeNodes, directories named by their user object, files as Datei (by setters or the three-argument constructor), rooted at arbeitsVerzeichnis (up to 1.10) or root. All 23 corpus files match Java, every file compared by name, type and the SHA-256 of its content. Routers and home routers have files in Java too, but their constructor makes them from templates (the start page of the firewall's web interface); they are not in the file and not claimed here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,8 +11,14 @@
|
||||
// table (port forwarding) and a modem's connection settings.
|
||||
// Java counterparts as well: filius.software.dhcp.DHCPServer, ModemFirmware.
|
||||
//
|
||||
// Checked against Java (corpus.java.json, `system`). File systems and
|
||||
// applications are later layers.
|
||||
// And the file system of a PC or notebook: Java's tree of
|
||||
// DefaultMutableTreeNodes, a directory's user object its name, a file's a
|
||||
// filius.software.system.Datei. Routers and home routers have files too, but
|
||||
// Java's constructor makes them from its own templates (the start page of
|
||||
// the firewall's web interface); they are not in the file.
|
||||
//
|
||||
// Checked against Java (corpus.java.json, `system`). Applications are a
|
||||
// later layer.
|
||||
//
|
||||
// A router's firewall is one object its constructor makes and hangs in two
|
||||
// places: the installed applications and the configuration plug-ins of its
|
||||
@@ -67,6 +73,12 @@ export interface FlsModem {
|
||||
ipAddress: string | null;
|
||||
}
|
||||
|
||||
export type FlsFileTree =
|
||||
| { kind: 'directory'; name: string | null; children: FlsFileTree[] }
|
||||
| { kind: 'file'; name: string | null; type: string | null; content: string | null; children: FlsFileTree[] }
|
||||
/** A node whose user object is neither a name nor a file. */
|
||||
| { kind: 'other'; className: string; children: FlsFileTree[] };
|
||||
|
||||
export interface FlsSystemSoftware {
|
||||
/**
|
||||
* Switch: its SSID, undefined when the file has none (Java rolls one).
|
||||
@@ -89,6 +101,8 @@ export interface FlsSystemSoftware {
|
||||
staticNAT?: (string | null)[][];
|
||||
/** Modem. */
|
||||
modem?: FlsModem;
|
||||
/** Rechner, Notebook: the root directory (named "root" by Java's constructor). */
|
||||
fileSystem?: FlsFileTree;
|
||||
}
|
||||
|
||||
type Warn = (message: string) => void;
|
||||
@@ -110,6 +124,7 @@ export function readSystemSoftware(device: JavaObject, javaClass: FlsNodeClass,
|
||||
case 'Rechner':
|
||||
case 'Notebook':
|
||||
result.ssid = optionalString(value('ssid'), 'ssid', warn) ?? null;
|
||||
result.fileSystem = fileSystem(value('dateisystem'), warn);
|
||||
break;
|
||||
case 'Vermittlungsrechner':
|
||||
result.ripEnabled = boolean(value('ripEnabled'), false, 'ripEnabled', warn);
|
||||
@@ -150,6 +165,53 @@ function modemAddress(value: string | null | undefined): string | null {
|
||||
return value === undefined ? 'localhost' : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dateisystem: its root is `arbeitsVerzeichnis` in files up to 1.10 and
|
||||
* `root` after. The constructor's root is a node named "root"; a node the
|
||||
* file makes with `new DefaultMutableTreeNode()` has no user object.
|
||||
*/
|
||||
function fileSystem(system: JavaValue | undefined, warn: Warn): FlsFileTree {
|
||||
const at = (property: string) => (isJavaObject(system) ? system.properties.get(property) : undefined);
|
||||
const root = at('root') ?? at('arbeitsVerzeichnis');
|
||||
if (root === undefined) return { kind: 'directory', name: 'root', children: [] };
|
||||
return fileTreeNode(root, true, warn);
|
||||
}
|
||||
|
||||
function fileTreeNode(node: JavaValue, constructorRoot: boolean, warn: Warn): FlsFileTree {
|
||||
if (!isJavaObject(node)) {
|
||||
warn(`file system: a node is ${describe(node)}, left empty`);
|
||||
return { kind: 'other', className: 'unknown', children: [] };
|
||||
}
|
||||
for (const call of node.calls) {
|
||||
if (call.method !== 'add') warn(`file system: ${call.method} on a node is not read`);
|
||||
}
|
||||
const children = listItems(node).map((child) => fileTreeNode(child, false, warn));
|
||||
const userObject = node.properties.has('userObject')
|
||||
? node.properties.get('userObject')!
|
||||
: node.fromGetter && constructorRoot
|
||||
? 'root'
|
||||
: (node.args[0] ?? null);
|
||||
if (userObject === null || typeof userObject === 'string') return { kind: 'directory', name: userObject, children };
|
||||
if (isJavaObject(userObject) && userObject.className === 'filius.software.system.Datei') {
|
||||
// new Datei(name, typ, inhalt), then the setters.
|
||||
const [name, type, content] = userObject.args.length === 3 ? userObject.args : [];
|
||||
const text = (property: string, fromConstructor: JavaValue | undefined) => {
|
||||
const value = userObject.properties.get(property) ?? fromConstructor ?? null;
|
||||
if (typeof value === 'string' || value === null) return value;
|
||||
warn(`file system: file ${property} is ${describe(value)}, ignored`);
|
||||
return null;
|
||||
};
|
||||
return {
|
||||
kind: 'file',
|
||||
name: text('name', name),
|
||||
type: text('dateiTyp', type),
|
||||
content: text('dateiInhalt', content),
|
||||
children,
|
||||
};
|
||||
}
|
||||
return { kind: 'other', className: isJavaObject(userObject) ? userObject.className : typeof userObject, children };
|
||||
}
|
||||
|
||||
/** A list of String arrays (routes, static NAT); anything but a string is null. */
|
||||
function rowsOfStrings(list: JavaValue | undefined): (string | null)[][] {
|
||||
return listItems(list).map((row) => listItems(row).map((cell) => (typeof cell === 'string' ? cell : null)));
|
||||
|
||||
Reference in New Issue
Block a user