import { SlInput, SlDialog, SlButton, SlTabGroup, SlTabPanel, SlSelect } from '@shoelace-style/shoelace'; import { html, TemplateResult } from 'lit'; import { ComputerNetwork } from '../..'; import { Address } from '../adressing/Address'; import { Ipv4Address } from '../adressing/Ipv4Address'; import { Ipv6Address } from '../adressing/Ipv6Address'; import { MacAddress } from '../adressing/MacAddress'; import { GraphEdge } from '../components/GraphEdge'; import { Data, Packet, Frame } from '../components/logicalNodes/DataNode'; import { Net } from '../components/logicalNodes/Net'; import { Router } from '../components/physicalNodes/Connector'; import { PhysicalNode } from '../components/physicalNodes/PhysicalNode'; import { AlertHelper } from '../utils/AlertHelper'; import { SubnettingController } from './subnetting-controller'; import { msg } from '@lit/localize'; export class DialogFactory { static generateInputsDetailsForNode(network: ComputerNetwork): void { let currentComponentToAdd = network.currentComponentToAdd; if (currentComponentToAdd == '') { return; } let numberOfPortsOrInterfaces: number = (network.renderRoot.querySelector('#ports') as SlInput).valueAsNumber ? (network.renderRoot.querySelector('#ports') as SlInput).valueAsNumber : currentComponentToAdd == 'computer' || currentComponentToAdd == 'mobile' ? 1 : 2; let layer: number = 0; let portNum: number = 0; switch (currentComponentToAdd) { case 'computer': case 'mobile': case 'router': layer = 3; //add IPv4, IPv6 break; case 'access-point': case 'bridge': case 'switch': layer = 2; //add MAC break; case 'hub': case 'repeater': layer = 1; //add connection type break; default: break; } if ( currentComponentToAdd == 'repeater' || currentComponentToAdd == 'bridge' || numberOfPortsOrInterfaces == null || numberOfPortsOrInterfaces == undefined ) { portNum = 2; } else { portNum = numberOfPortsOrInterfaces; } let dialog = new SlDialog(); dialog.label = 'Add details for each port of your ' + currentComponentToAdd; let table = ``; //add the columns table += ``; table += ``; table += layer > 2 ? `` : ``; table += ``; table += layer > 1 ? `` : ''; table += layer > 2 ? `` : ''; table += ``; //add row for each port for (let i = 1; i <= portNum; i++) { table += ``; table += ``; table += layer > 2 ? `` : ``; switch (currentComponentToAdd) { case 'hub': case 'switch': table += ``; break; case 'access-point': table += i == 1 ? `` : ``; break; default: table += ``; break; } table += layer > 1 ? `` : ''; table += layer > 2 ? `` : ''; table += layer > 2 ? `` : ''; table += ``; } table += `
${msg('Port number')}${msg('Interface name')}${msg('Connection type')}${msg('MAC Address')}Ipv4Ipv6
` + i + `${msg('Ethernet')}${msg('Ethernet')}${msg('Wireless')}${msg('Ethernet')} ${msg('Wireless')}
`; dialog.innerHTML = table; //TODO: add event listener vào cái nút add node const saveButton = new SlButton(); saveButton.slot = 'footer'; saveButton.variant = 'primary'; saveButton.innerHTML = 'Save'; saveButton.addEventListener('click', () => dialog.hide()); dialog.appendChild(saveButton); (network.renderRoot.querySelector('#inputDialog') as HTMLElement).innerHTML = ''; (network.renderRoot.querySelector('#inputDialog') as HTMLElement).append(dialog); dialog.show(); } static generateInputsDetailsForEdge( network: ComputerNetwork, edge: any, sourceNode: PhysicalNode, targetNode: PhysicalNode ): void { //filter available ports let availableSourcePorts: number[] = []; let availableTargetPorts: number[] = []; sourceNode.portLinkMapping.forEach((link, port) => { if (link == null || link == undefined || link == '') { availableSourcePorts.push(port); } }); targetNode.portLinkMapping.forEach((link, port) => { if (link == null || link == undefined || link == '') { availableTargetPorts.push(port); } }); let dialog = new SlDialog(); dialog.label = msg('Assigning ports for this connection'); let tabGroup: SlTabGroup = new SlTabGroup(); tabGroup.innerHTML += `` + sourceNode.name + `` + targetNode.name + ``; //init panel with data of ports of source + select port for source let sourcePanel = new SlTabPanel(); sourcePanel.name = 'chooseSourcePort'; let sourceTable: string = ``; sourceTable += ``; sourceNode.portData .entries() .next() .value[1].forEach((_, columnName) => (sourceTable += ``)); sourceTable += ``; sourceNode.portData.forEach((data, port) => { sourceTable += ``; sourceTable += ``; //add index data.forEach((value) => { if (value instanceof Address) { sourceTable += ``; } else { sourceTable += ``; } }); sourceTable += ``; }); sourceTable += `
${msg('Port number')}` + columnName + `
` + port + `` + value.address + `` + value + `
`; sourcePanel.innerHTML += sourceTable; let selectedSourcePort = new SlSelect(); availableSourcePorts.forEach( (port) => (selectedSourcePort.innerHTML += `` + port + ``) ); sourcePanel.innerHTML += msg('Select one from available ports:'); sourcePanel.appendChild(selectedSourcePort); tabGroup.append(sourcePanel); //init panel with data of ports of target + select port for target let targetPanel = new SlTabPanel(); targetPanel.name = 'chooseTargetPort'; let targetTable: string = ``; targetTable += ``; targetNode.portData .entries() .next() .value[1].forEach((_, columnName) => (targetTable += ``)); targetTable += ``; targetNode.portData.forEach((data, index) => { targetTable += ``; targetTable += ``; //add port/interface name data.forEach((value) => { if (value instanceof Address) { targetTable += ``; } else { targetTable += ``; } }); targetTable += ``; }); targetTable += `
${msg('Port number')}` + columnName + `
` + index + `` + value.address + `` + value + `
`; targetPanel.innerHTML += targetTable; let selectedTargetPort = new SlSelect(); availableTargetPorts.forEach( (port) => (selectedTargetPort.innerHTML += `` + port + ``) ); targetPanel.innerHTML += msg('Select one from available ports:'); targetPanel.appendChild(selectedTargetPort); tabGroup.append(targetPanel); dialog.appendChild(tabGroup); const saveButton = new SlButton(); saveButton.slot = 'footer'; saveButton.variant = 'primary'; saveButton.innerHTML = 'Save'; saveButton.addEventListener('click', () => { let inPort: number = +(selectedSourcePort.value as string); let outPort: number = +(selectedTargetPort.value as string); if (Number.isNaN(inPort) || inPort == undefined || inPort == null) { AlertHelper.toastAlert( 'warning', 'exclamation-triangle', '', msg('Please choose port/interface for') + ' ' + sourceNode.name ); return; } if (Number.isNaN(outPort) || outPort == undefined || outPort == null) { AlertHelper.toastAlert( 'warning', 'exclamation-triangle', '', msg('Please choose port/interface for') + ' ' + sourceNode.name ); return; } let newData = GraphEdge.addPorts(edge.data(), inPort, outPort); //add port-link mapping for source+target if (newData != null) { edge.removeClass('unconfigured-edge'); edge.addClass(newData.cssClass); dialog.hide(); } //set new format-display for this connection if no error appears SubnettingController.setUpGateway( network._graph.$('#' + sourceNode.id), network._graph.$('#' + targetNode.id), inPort, network.ipv4Database ); SubnettingController.setUpGateway( network._graph.$('#' + targetNode.id), network._graph.$('#' + sourceNode.id), outPort, network.ipv4Database ); }); dialog.appendChild(saveButton); (network.renderRoot.querySelector('#inputDialog') as HTMLElement).innerHTML = ''; (network.renderRoot.querySelector('#inputDialog') as HTMLElement).append(dialog); dialog.show(); } static handleChangesInDialogForPhysicalNode( id: string, node: any, network: ComputerNetwork, isGateway: boolean, subnet?: Net ) { let physicalNode: PhysicalNode = node.data(); let dialog: SlDialog = new SlDialog(); dialog.label = msg('Details about component') + ' ' + physicalNode.name; dialog.innerHTML += ``; let table: string = `
${msg('Details of the ports')}
`; table += ``; physicalNode.portData .entries() .next() .value[1].forEach((_, columnName) => (table += ``)); table += ``; physicalNode.portData.forEach((data, index) => { table += ``; table += ``; //add index data.forEach((value, key) => { if (value instanceof Address) { table += ``; } else if (key == 'Connection Type') { //show only table += ``; } else { table += ``; } }); table += ``; }); table += `
Index` + columnName + `
` + index + `` + value + `
`; dialog.innerHTML += table; const saveButton = new SlButton(); saveButton.slot = 'footer'; saveButton.variant = 'primary'; saveButton.innerHTML = 'Save'; saveButton.addEventListener('click', () => { let changed: boolean = false; let newNodeName: string = (network.renderRoot.querySelector('#' + id + '-name') as SlInput).value.trim(); if (newNodeName != physicalNode.name) physicalNode.name = newNodeName; //for each interface-index for (let index = 1; index <= physicalNode.portData.size; index++) { let nameInput = network.renderRoot.querySelector('#' + id + '-' + index + '-' + 'Name') as SlInput; let newName = nameInput.value.trim() != '' ? nameInput.value.trim() : nameInput.placeholder; if (newName != '') { physicalNode.portData.get(index).set('Name', newName); changed = true; } if (physicalNode.layer >= 2) { let macInput = network.renderRoot.querySelector('#' + id + '-' + index + '-' + 'MAC') as SlInput; let newMac = macInput.value.trim() != '' ? macInput.value.trim() : ''; let validatedMac = newMac != '' ? MacAddress.validateAddress(newMac, network.macDatabase) : null; if (validatedMac != null) { MacAddress.removeAddressFromDatabase( physicalNode.portData.get(index).get('MAC'), network.macDatabase ); physicalNode.portData.get(index).set('MAC', validatedMac); MacAddress.addAddressToDatabase(validatedMac, network.macDatabase, physicalNode.id); changed = true; } else if (newMac != '') { AlertHelper.toastAlert( 'warning', 'exclamation-triangle', '', newMac + ' ' + msg('is not a valid MAC Address.') ); } } if (physicalNode.layer >= 3) { let ip4Input = network.renderRoot.querySelector('#' + id + '-' + index + '-' + 'IPv4') as SlInput; let ip6Input = network.renderRoot.querySelector('#' + id + '-' + index + '-' + 'IPv6') as SlInput; let newIpv4 = ip4Input.value.trim() != '' ? ip4Input.value.trim() : ''; let newIpv6 = ip6Input.value.trim() != '' ? ip6Input.value.trim() : ''; let validatedIpv4 = newIpv4 != '' ? Ipv4Address.validateAddress(newIpv4, network.ipv4Database) : null; if (validatedIpv4 != null) { let keepOldIp: boolean = false; //if this physical node is in a network if (subnet != null && subnet != undefined) { switch (Net.mode) { case 'HOST_BASED': if (validatedIpv4 != null && validatedIpv4 != undefined) Net.calculateCIDRGivenNewHost(subnet, validatedIpv4, network.ipv4Database); node.parent().classes(subnet.cssClass); break; case 'NET_BASED': if (validatedIpv4 != null && !validatedIpv4.matchesNetworkCidr(subnet)) { AlertHelper.toastAlert( 'warning', 'exclamation-triangle', msg('Subnet-based mode on:'), msg("Inserted IPv4 doesn't match the subnet mask.") ); keepOldIp = true; } break; default: break; } } //if this physical node is a gateway of some networks if (isGateway) { let affectedNetwork: Net = (physicalNode as Router).portNetMapping.get(index); switch (Net.mode) { case 'HOST_BASED': if (validatedIpv4 != null && validatedIpv4 != undefined) Net.calculateCIDRGivenNewHost( affectedNetwork, validatedIpv4, network.ipv4Database ); network._graph.$('#' + affectedNetwork.id).classes(affectedNetwork.cssClass); break; case 'NET_BASED': if ( affectedNetwork != null && validatedIpv4 != null && !validatedIpv4.matchesNetworkCidr(affectedNetwork) ) { AlertHelper.toastAlert( 'warning', 'exclamation-triangle', msg('Subnet-based mode on:'), msg("Inserted IPv4 for gateway doesn't match the subnet mask or the network is not configured.") ); keepOldIp = true; } break; default: break; } } if (!keepOldIp) { Ipv4Address.removeAddressFromDatabase( physicalNode.portData.get(index).get('IPv4'), network.ipv4Database ); physicalNode.portData.get(index).set('IPv4', validatedIpv4); Ipv4Address.addAddressToDatabase(validatedIpv4, network.ipv4Database, physicalNode.id); changed = true; } } else if (newIpv4 != '') { AlertHelper.toastAlert('warning', 'exclamation-triangle', '', newIpv4 + ' ' + msg('is not valid.')); } let validatedIpv6 = newIpv6 != '' ? Ipv6Address.validateAddress(newIpv6, network.ipv6Database) : null; if (validatedIpv6 != null) { Ipv6Address.removeAddressFromDatabase( physicalNode.portData.get(index).get('IPv6'), network.ipv6Database ); physicalNode.portData.get(index).set('IPv6', validatedIpv6); Ipv6Address.addAddressToDatabase(validatedIpv6, network.ipv6Database, physicalNode.id); changed = true; } else if (newIpv6 != '') { AlertHelper.toastAlert( 'warning', 'exclamation-triangle', '', newIpv6 + ' ' + msg('is not a valid IPv6 Address.') ); } } } if (changed) { AlertHelper.toastAlert('success', 'check2-circle', msg('Your changes have been saved.'), ''); } dialog.hide(); }); dialog.appendChild(saveButton); (network.renderRoot.querySelector('#inputDialog') as HTMLElement).innerHTML = ''; (network.renderRoot.querySelector('#inputDialog') as HTMLElement).append(dialog); dialog.show(); } static handleChangesInDialogForNet(id: string, node: any, network: ComputerNetwork) { let dialog: SlDialog = new SlDialog(); dialog.label = 'Details of this network:'; let subnet: Net = node.data(); dialog.innerHTML += ``; dialog.innerHTML += ``; dialog.innerHTML += ``; //table for gateways let gateways: Map = subnet.gateways; if (gateways.size != 0) { let table: string = ``; //TODO: add id for changes? gateways.forEach((port, gatewayId) => { if (port != null) { let router: Router = network._graph.$('#' + gatewayId).data(); let data = router.portData.get(port); table += ``; table += ``; table += ``; table += ``; table += ``; table += ``; table += ``; table += ``; } }); table += `
${msg('Gateway')}${msg('Interface')}${msg('Connection Type')}MACIPv4IPv6
` + router.name + `` + data.get('Name') + `` + data.get('Connection Type') + `` + data.get('MAC').address + `` + data.get('IPv4').address + `` + data.get('IPv6').address + `
`; dialog.innerHTML += table; } //TODO: add event listener vào cái nút add node const saveButton = new SlButton(); saveButton.slot = 'footer'; saveButton.variant = 'primary'; saveButton.innerHTML = 'Save'; saveButton.addEventListener('click', () => { let idInput = network.renderRoot.querySelector('#change-id-' + id) as SlInput; let bitmaskInput = network.renderRoot.querySelector('#change-bitmask-' + id) as SlInput; let netmaskInput = network.renderRoot.querySelector('#change-mask-' + id) as SlInput; let newId = idInput.value.trim() != '' ? idInput.value.trim() : idInput.placeholder; let newBitmask = bitmaskInput.value.trim() != '' ? bitmaskInput.value.trim() : bitmaskInput.placeholder; let newnetmask = netmaskInput.value.trim() != '' ? netmaskInput.value.trim() : netmaskInput.placeholder; if ( subnet.handleChangesOnNewNetInfo( newId != '' ? newId : null, newnetmask != '' ? newnetmask : null, newBitmask != '' ? +newBitmask : null, network ) ) { node.toggleClass('unconfigured-net', false); } dialog.hide(); }); dialog.appendChild(saveButton); (network.renderRoot.querySelector('#inputDialog') as HTMLElement).innerHTML = ''; (network.renderRoot.querySelector('#inputDialog') as HTMLElement).append(dialog); dialog.show(); } static handleChangeDefaultGateway(subnet: Net, id: string, node: any, network: ComputerNetwork) { let dialog: SlDialog = new SlDialog(); dialog.label = 'Details of available gateways'; let gateways: Map = subnet.gateways; //gateway-node-id, port let select = ``; if (gateways.size != 0) { let table: string = ``; gateways.forEach((port, gatewayId) => { if (port != null) { let router: Router = network._graph.$('#' + gatewayId).data(); let data = router.portData.get(port); table += ``; table += ``; table += ``; table += ``; table += ``; table += ``; table += ``; table += ``; table += ``; select += `` + router.name + ``; } }); table += `
${msg('Gateway')}${msg('Port number')}${msg('Interface')}${msg('Connection Type')}MACIPv4IPv6
` + router.name + `` + port + `` + data.get('Name') + `` + data.get('Connection Type') + `` + data.get('MAC').address + `` + data.get('IPv4').address + `` + data.get('IPv6').address + `
`; dialog.innerHTML += table; select += `
`; dialog.innerHTML += select; } else { dialog.innerHTML += msg('This network has no gateway.'); } if (!node.hasClass('default-gateway-not-found')) { let current: [string, number] = node.data('defaultGateway'); dialog.innerHTML += 'Current default gateway is: ' + current[0] + ', port: ' + current[1]; } const saveButton = new SlButton(); saveButton.slot = 'footer'; saveButton.variant = 'primary'; saveButton.innerHTML = 'Save'; saveButton.addEventListener('click', () => { let newGateway: string = ( network.renderRoot.querySelector('#' + 'new-gateway-' + id) as SlInput ).value.trim(); if (newGateway != '') { node.data('defaultGateway', newGateway.split('/')); let cssClass = node.data('cssClass'); while (cssClass.includes('default-gateway-not-found')) { cssClass.splice(cssClass.indexOf('default-gateway-not-found'), 1); } cssClass.push('gateway-changeable'); node.toggleClass('default-gateway-not-found', false); node.toggleClass('gateway-changeable', true); if (!node.data('cssClass').includes('gateway-changeable')) node.data('cssClass').push('gateway-changeable'); } dialog.hide(); }); dialog.appendChild(saveButton); (network.renderRoot.querySelector('#inputDialog') as HTMLElement).innerHTML = ''; (network.renderRoot.querySelector('#inputDialog') as HTMLElement).append(dialog); dialog.show(); } static showDataHeaders(data: Data, network: ComputerNetwork): void { let dialog = new SlDialog(); dialog.label = data.id; if (data instanceof Packet) { dialog.innerHTML += msg('Mac Address of Sender:') + data.layer2header.macSender + '
'; dialog.innerHTML += msg('IP Address of Sender:') + data.layer3header.ipSender + '
'; dialog.innerHTML += msg('Mac Address of Receiver:') + data.layer2header.macReceiver + '
'; dialog.innerHTML += msg('IP Address of Receiver:') + data.layer3header.ipReceiver; } else if (data instanceof Frame) { dialog.innerHTML += msg('Mac Address of Sender:') + data.layer2header.macSender + '
'; dialog.innerHTML += msg('IP Address of Sender:') + data.layer2header.ipSender + '
'; dialog.innerHTML += msg('Mac Address of Receiver:') + data.layer2header.macReceiver + '
'; dialog.innerHTML += msg('IP Address of Receiver:') + data.layer2header.ipReceiver; } (network.renderRoot.querySelector('#inputDialog') as HTMLElement).innerHTML = ''; (network.renderRoot.querySelector('#inputDialog') as HTMLElement).append(dialog); dialog.show(); } static showHelpText(network: ComputerNetwork): TemplateResult { return html` ${msg('Add/ Configure graph components')} ${msg('CIDR/ Subnetting')} ${msg('Packet travelling simulation')}
${msg('Step')} 1: ${msg('Choose your component')}
${msg('Step')} 2: ${msg('More details with "Add details for ports"')}
${msg('Step')} 3: ${msg('Pick a color also if you want to:')}
${msg('Step')} 4: ${msg('Click add')}
${msg('Step')} 1: ${msg('Choose your component')}
${msg('Step')} 2: ${msg('Configure your network')}
${msg('Step')} 3: ${msg('Pick a color also if you want to:')}
${msg('Step')} 4: ${msg('Click add')}
${msg('Step')} 1: ${msg('Choose edge component')}
${msg('Step')} 2: ${msg('Pick a color also if you want to:')}
${msg('Step')} 3: ${msg('Toggle draw mode')}
${msg('Step')} 4: ${msg('Draw then configure on right click')}
${msg('Step')} 1: ${msg('Activate the "assign gateway" mode:')}
${msg('Step')} 2: ${msg('Drag the gateway on the edge of a network:')}
${msg('Step')} 1: ${msg('Activate the "drag-and-drop" mode:')}
${msg('Step')} 2: ${msg('Drag the component inside the network:')}
${msg('Before dragging a node into network')} 1.1.1.0 /24
${msg('After dragging the node into network')} 1.1.1.0 /24
${msg('New IPv4 will be assigned for conflicting addresses.')}
${msg('During this mode, when users edit addresses of:')}
  • networks: ${msg('conflicting addresses of all related hosts/gateways will be reassigned.')}
  • hosts/gateways/subnetworks: ${msg('new addresses will be validated against the network addresses and will only be accepted when they are valid.')}
${msg('Before dragging host0 into network')} 1.1.1.128 /25
${msg('After dragging host0 into network')} 1.1.1.128 /25
${msg('The existing network will expand to contain the new host.')}
${msg('During this mode, when users edit addresses of:')}
  • ${msg('networks')}: ${msg('new network address range will be checked if they expands the old ones.')}
  • ${msg('hosts/gateways/subnetworks')}: ${msg('the existing network will expand to contain the new addresses.')}
${msg('Step')} 1: ${msg('if you don\'t use a graph from the examples, Init a new simulation session')}
${msg('In this step, for the:')}
  • ${msg('host/router: ARP table and routing table are created')}
  • ${msg('switch/bridge/access point: MAC address table is created')}
${msg('Step')} 2: ${msg('assign a sender/receiver for the packet')}
  • ${msg('Click on Choose sender or Choose receiver')}
  • ${msg('Click on a host on the canvas')}
  • ${msg('Choose an IP')}
${msg('Step')} 3: ${msg('start sending a packet with the assigned sender and receiver in step 2')}
${msg('Repeat steps 2 and 3 as much as desired.
You can also use pause/resume or change focus/speed during or before starting to send a packet.')}
${msg('Last step')}
${msg('Stop session')} ${msg('resets all tables and ends the current simulation session.')}
${msg('Start from step 1 again for a new simulation session.')}
${msg('Step')} 1: ${msg('Init. Ignore this step if you use an example graph, or there is a session running')}.
${msg('Step')} 2.a: ${msg('Click on Add to generate a new row in the table')}
${msg('Step')} 2.b: ${msg('Fill your table according to its type. Except for the tables of the routers, tables of other nodes can be filled automatically.')}
${msg('Step')} 3: ${msg('Remove button removes all checked rows')}
${msg('Step')} 4: ${msg('Save button saves the current table on the UI to the database')}
`; } }