From a7e3c24a2702b252ac87b65c4ed758dc67bea6c2 Mon Sep 17 00:00:00 2001 From: grossmj Date: Mon, 26 Feb 2024 17:36:11 +0800 Subject: [PATCH 1/6] Development on 2.2.47.dev1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 127fbdd4..bbab9fd8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gns3-web-ui", - "version": "2.2.46", + "version": "2.2.47.dev1", "author": { "name": "GNS3 Technology Inc.", "email": "developers@gns3.com" From e9487c5ada42820ddeaa96375b3d9a476bd027dc Mon Sep 17 00:00:00 2001 From: grossmj Date: Tue, 14 May 2024 15:18:59 +0700 Subject: [PATCH 2/6] Create protocol handler service --- src/app/app.module.ts | 2 + ...console-device-action-browser.component.ts | 39 ++------------- .../log-console/log-console.component.ts | 8 ++-- .../project-map/project-map.component.ts | 12 +++-- src/app/services/packet-capture.service.ts | 11 +++-- src/app/services/protocol-handler.service.ts | 48 +++++++++++++++++++ 6 files changed, 73 insertions(+), 47 deletions(-) create mode 100644 src/app/services/protocol-handler.service.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 1769c4db..9b895320 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -238,6 +238,7 @@ import { NodeService } from './services/node.service'; import { NodeConsoleService } from './services/nodeConsole.service'; import { NotificationService } from './services/notification.service'; import { PacketCaptureService } from './services/packet-capture.service'; +import { ProtocolHandlerService } from './services/protocol-handler.service'; import { PlatformService } from './services/platform.service'; import { ProjectService } from './services/project.service'; import { QemuConfigurationService } from './services/qemu-configuration.service'; @@ -538,6 +539,7 @@ import { RotationValidator } from './validators/rotation-validator'; ComputeService, TracengService, PacketCaptureService, + ProtocolHandlerService, NotificationService, Gns3vmService, ThemeService, diff --git a/src/app/components/project-map/context-menu/actions/console-device-action-browser/console-device-action-browser.component.ts b/src/app/components/project-map/context-menu/actions/console-device-action-browser/console-device-action-browser.component.ts index 57f5df01..c521be41 100644 --- a/src/app/components/project-map/context-menu/actions/console-device-action-browser/console-device-action-browser.component.ts +++ b/src/app/components/project-map/context-menu/actions/console-device-action-browser/console-device-action-browser.component.ts @@ -4,6 +4,8 @@ import { Node } from '../../../../../cartography/models/node'; import { Server } from '../../../../../models/server'; import { NodeService } from '../../../../../services/node.service'; import { ToasterService } from '../../../../../services/toaster.service'; +import { ProtocolHandlerService } from '../../../../../services/protocol-handler.service'; + import * as ipaddr from 'ipaddr.js'; @Component({ @@ -14,7 +16,7 @@ export class ConsoleDeviceActionBrowserComponent { @Input() server: Server; @Input() node: Node; - constructor(private toasterService: ToasterService, private nodeService: NodeService, private deviceService: DeviceDetectorService) {} + constructor(private toasterService: ToasterService, private nodeService: NodeService, private protocolHandlerService: ProtocolHandlerService) {} openConsole() { this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => { @@ -23,31 +25,6 @@ export class ConsoleDeviceActionBrowserComponent { }); } - createHiddenIframe(target: Element, uri: string) { - const iframe = document.createElement("iframe"); - iframe.src = uri; - iframe.id = "hiddenIframe"; - iframe.style.display = "none"; - target.appendChild(iframe); - return iframe; - } - - openUriUsingFirefox(uri: string) { - var iframe = (document.querySelector("#hiddenIframe") as HTMLIFrameElement); - - if (!iframe) { - iframe = this.createHiddenIframe(document.body, "about:blank"); - } - - try { - iframe.contentWindow.location.href = uri; - } catch (e) { - if (e.name === "NS_ERROR_UNKNOWN_PROTOCOL") { - this.toasterService.error('Protocol handler does not exist'); - } - } - } - startConsole() { if (this.node.status !== 'started') { this.toasterService.error('This node must be started before a console can be opened'); @@ -60,8 +37,6 @@ export class ConsoleDeviceActionBrowserComponent { this.node.console_host = this.server.host; } - const device = this.deviceService.getDeviceInfo(); - try { var uri; var host = this.node.console_host; @@ -81,13 +56,7 @@ export class ConsoleDeviceActionBrowserComponent { this.toasterService.error('Supported console types are: telnet, vnc, spice and spice+agent.'); } - if (device.browser === "Firefox") { - // Use a hidden iframe otherwise Firefox will disconnect - // from the GNS3 controller websocket if we use location.assign() - this.openUriUsingFirefox(uri); - } else { - location.assign(uri); - } + this.protocolHandlerService.open(uri); } catch (e) { this.toasterService.error(e); diff --git a/src/app/components/project-map/log-console/log-console.component.ts b/src/app/components/project-map/log-console/log-console.component.ts index 59412210..1c3ed625 100644 --- a/src/app/components/project-map/log-console/log-console.component.ts +++ b/src/app/components/project-map/log-console/log-console.component.ts @@ -23,6 +23,7 @@ import { Server } from '../../../models/server'; import { HttpServer } from '../../../services/http-server.service'; import { NodeService } from '../../../services/node.service'; import { NodeConsoleService } from '../../../services/nodeConsole.service'; +import { ProtocolHandlerService } from '../../../services/protocol-handler.service'; import { ThemeService } from '../../../services/theme.service'; import { version } from '../../../version'; import { LogEventsDataSource } from './log-events-datasource'; @@ -70,6 +71,7 @@ export class LogConsoleComponent implements OnInit, AfterViewInit, OnDestroy { private projectWebServiceHandler: ProjectWebServiceHandler, private nodeService: NodeService, private nodesDataSource: NodesDataSource, + private protocolHandlerService: ProtocolHandlerService, private logEventsDataSource: LogEventsDataSource, private httpService: HttpServer, private themeService: ThemeService, @@ -230,15 +232,15 @@ export class LogConsoleComponent implements OnInit, AfterViewInit, OnDestroy { host = `[${host}]`; } if (node.console_type === 'telnet') { - location.assign( + this.protocolHandlerService.open( `gns3+telnet://${host}:${node.console}?name=${node.name}&project_id=${node.project_id}&node_id=${node.node_id}` ); } else if (node.console_type === 'vnc') { - location.assign( + this.protocolHandlerService.open( `gns3+vnc://${host}:${node.console}?name=${node.name}&project_id=${node.project_id}&node_id=${node.node_id}` ); } else if (node.console_type.startsWith('spice')) { - location.assign( + this.protocolHandlerService.open( `gns3+spice://${host}:${node.console}?name=${node.name}&project_id=${node.project_id}&node_id=${node.node_id}` ); } else if (node.console_type.startsWith('http')) { diff --git a/src/app/components/project-map/project-map.component.ts b/src/app/components/project-map/project-map.component.ts index 3f2ce336..3dfd32c3 100644 --- a/src/app/components/project-map/project-map.component.ts +++ b/src/app/components/project-map/project-map.component.ts @@ -67,6 +67,7 @@ import { SymbolService } from '../../services/symbol.service'; import { ThemeService } from '../../services/theme.service'; import { ToasterService } from '../../services/toaster.service'; import { ToolsService } from '../../services/tools.service'; +import { ProtocolHandlerService } from '../../services/protocol-handler.service'; import { AddBlankProjectDialogComponent } from '../projects/add-blank-project-dialog/add-blank-project-dialog.component'; import { ConfirmationBottomSheetComponent } from '../projects/confirmation-bottomsheet/confirmation-bottomsheet.component'; import { EditProjectDialogComponent } from '../projects/edit-project-dialog/edit-project-dialog.component'; @@ -173,8 +174,9 @@ export class ProjectMapComponent implements OnInit, OnDestroy { private title: Title, private nodeConsoleService: NodeConsoleService, private symbolService: SymbolService, + private protocolHandlerService: ProtocolHandlerService, private cd: ChangeDetectorRef, - private cfr: ComponentFactoryResolver, + private cfr: ComponentFactoryResolver, private injector: Injector ) {} @@ -229,7 +231,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy { this.instance.instance.ngOnDestroy(); this.instance.destroy(); } - } + } } addSubscriptions() { @@ -975,7 +977,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy { ) { this.toasterService.error('Project with running nodes cannot be exported.'); } else { - location.assign(this.projectService.getExportPath(this.server, this.project)); + this.protocolHandlerService.open(this.projectService.getExportPath(this.server, this.project)); } } @@ -990,8 +992,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy { fileReader.onloadend = () => { let image = fileReader.result; - let svg = `\n\n\n`; this.drawingService .add(this.server, this.project.project_id, -(imageToUpload.width / 2), -(imageToUpload.height / 2), svg) diff --git a/src/app/services/packet-capture.service.ts b/src/app/services/packet-capture.service.ts index 642a3d72..d3ebb26e 100644 --- a/src/app/services/packet-capture.service.ts +++ b/src/app/services/packet-capture.service.ts @@ -2,14 +2,17 @@ import { Injectable } from '@angular/core'; import { Link } from '../models/link'; import { Project } from '../models/project'; import { Server } from '../models/server'; +import { ProtocolHandlerService } from './protocol-handler.service'; @Injectable() export class PacketCaptureService { - constructor() {} + + constructor(private protocolHandlerService: ProtocolHandlerService) {} startCapture(server: Server, project: Project, link: Link, name: string) { - location.assign( - `gns3+pcap://${server.host}:${server.port}?protocol=${server.protocol.slice(0, -1)}&project_id=${project.project_id}&link_id=${link.link_id}&project=${project.name}&name=${name}` - ); + + const uri = `gns3+pcap://${server.host}:${server.port}?protocol=${server.protocol.slice(0, -1)}&project_id=${project.project_id}&link_id=${link.link_id}&project=${project.name}&name=${name}`; + this.protocolHandlerService.open(uri); + } } diff --git a/src/app/services/protocol-handler.service.ts b/src/app/services/protocol-handler.service.ts new file mode 100644 index 00000000..70ca8384 --- /dev/null +++ b/src/app/services/protocol-handler.service.ts @@ -0,0 +1,48 @@ +import { Injectable } from '@angular/core'; +import { DeviceDetectorService } from 'ngx-device-detector'; +import { ToasterService } from './toaster.service'; + +@Injectable() +export class ProtocolHandlerService { + + constructor(private toasterService: ToasterService, private deviceService: DeviceDetectorService) {} + + createHiddenIframe(target: Element, uri: string) { + const iframe = document.createElement("iframe"); + iframe.src = uri; + iframe.id = "hiddenIframe"; + iframe.style.display = "none"; + target.appendChild(iframe); + return iframe; + } + + openUriUsingFirefox(uri: string) { + var iframe = (document.querySelector("#hiddenIframe") as HTMLIFrameElement); + + if (!iframe) { + iframe = this.createHiddenIframe(document.body, "about:blank"); + } + + try { + iframe.contentWindow.location.href = uri; + } catch (e) { + if (e.name === "NS_ERROR_UNKNOWN_PROTOCOL") { + this.toasterService.error('Protocol handler does not exist'); + } + } + } + + open(uri: string) { + + const device = this.deviceService.getDeviceInfo(); + + console.log("Launching external protocol handler with " + device.browser + ": " + uri) + if (device.browser === "Firefox") { + // Use a hidden iframe otherwise Firefox will disconnect + // from the GNS3 controller websocket if we use location.assign() + this.openUriUsingFirefox(uri); + } else { + location.assign(uri); + } + } +} From de058e175b0d16024b7c85cfc587b7d78892c90a Mon Sep 17 00:00:00 2001 From: grossmj Date: Tue, 14 May 2024 17:15:29 +0700 Subject: [PATCH 3/6] Fix tests --- src/app/app.module.ts | 2 +- .../project-map/log-console/log-console.component.spec.ts | 4 ++++ .../start-capture/start-capture.component.spec.ts | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 9b895320..dcc6ac46 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -215,6 +215,7 @@ import { DefaultLayoutComponent } from './layouts/default-layout/default-layout. import { MATERIAL_IMPORTS } from './material.imports'; import { ServerResolve } from './resolvers/server-resolve'; import { ApplianceService } from './services/appliances.service'; +import { ProtocolHandlerService } from './services/protocol-handler.service'; import { BuiltInTemplatesConfigurationService } from './services/built-in-templates-configuration.service'; import { BuiltInTemplatesService } from './services/built-in-templates.service'; import { ComputeService } from './services/compute.service'; @@ -238,7 +239,6 @@ import { NodeService } from './services/node.service'; import { NodeConsoleService } from './services/nodeConsole.service'; import { NotificationService } from './services/notification.service'; import { PacketCaptureService } from './services/packet-capture.service'; -import { ProtocolHandlerService } from './services/protocol-handler.service'; import { PlatformService } from './services/platform.service'; import { ProjectService } from './services/project.service'; import { QemuConfigurationService } from './services/qemu-configuration.service'; diff --git a/src/app/components/project-map/log-console/log-console.component.spec.ts b/src/app/components/project-map/log-console/log-console.component.spec.ts index 1d7886c3..bc6cc8b5 100644 --- a/src/app/components/project-map/log-console/log-console.component.spec.ts +++ b/src/app/components/project-map/log-console/log-console.component.spec.ts @@ -6,6 +6,7 @@ import { MatMenuModule } from '@angular/material/menu'; import { BrowserModule } from '@angular/platform-browser'; import { RouterTestingModule } from '@angular/router/testing'; import { ToasterService } from '../../../services/toaster.service'; +import { ProtocolHandlerService } from '../../../services/protocol-handler.service'; import { of } from 'rxjs'; import { NodesDataSource } from '../../../cartography/datasources/nodes-datasource'; import { ProjectWebServiceHandler, WebServiceMessage } from '../../../handlers/project-web-service-handler'; @@ -38,6 +39,7 @@ describe('LogConsoleComponent', () => { let nodeConsoleService: NodeConsoleService; let mapSettingsService: MapSettingsService; let toasterService: ToasterService; + let protocolHandlerService: ProtocolHandlerService; let httpServer = new HttpServer({} as HttpClient, {} as ServerErrorHandler); @@ -52,6 +54,7 @@ describe('LogConsoleComponent', () => { { provide: HttpServer, useValue: httpServer }, NodeConsoleService, ToasterService, + ProtocolHandlerService, MapSettingsService ], declarations: [LogConsoleComponent], @@ -59,6 +62,7 @@ describe('LogConsoleComponent', () => { }).compileComponents(); toasterService = TestBed.inject(ToasterService); + protocolHandlerService = TestBed.inject(ProtocolHandlerService); mapSettingsService = TestBed.inject(MapSettingsService); nodeConsoleService = TestBed.inject(NodeConsoleService); })); diff --git a/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.spec.ts b/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.spec.ts index ebb8a644..73571515 100644 --- a/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.spec.ts +++ b/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.spec.ts @@ -17,6 +17,7 @@ import { ToasterService } from '../../../../services/toaster.service'; import { MockedToasterService } from '../../../../services/toaster.service.spec'; import { MockedLinkService, MockedNodesDataSource } from '../../project-map.component.spec'; import { StartCaptureDialogComponent } from './start-capture.component'; +import { ProtocolHandlerService } from '../../../../services/protocol-handler.service'; describe('StartCaptureDialogComponent', () => { let component: StartCaptureDialogComponent; @@ -25,6 +26,8 @@ describe('StartCaptureDialogComponent', () => { let mockedToasterService = new MockedToasterService(); let mockedLinkService = new MockedLinkService(); let mockedNodesDataSource = new MockedNodesDataSource(); + let protocolHandlerService: ProtocolHandlerService; + let dialogRef = { close: jasmine.createSpy('close'), }; @@ -49,10 +52,13 @@ describe('StartCaptureDialogComponent', () => { { provide: LinkService, useValue: mockedLinkService }, { provide: NodesDataSource, useValue: mockedNodesDataSource }, { provide: PacketCaptureService }, + ProtocolHandlerService, ], declarations: [StartCaptureDialogComponent], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); + + protocolHandlerService = TestBed.inject(ProtocolHandlerService); })); beforeEach(() => { From 42599dafb5f8f9d74da5d4f122dd2d0b34f7e867 Mon Sep 17 00:00:00 2001 From: grossmj Date: Tue, 14 May 2024 20:07:30 +0700 Subject: [PATCH 4/6] Auxiliary console support --- src/app/cartography/models/node.ts | 1 + .../console-device-action-browser.component.html | 8 ++++++++ .../console-device-action-browser.component.ts | 15 +++++++++++---- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/app/cartography/models/node.ts b/src/app/cartography/models/node.ts index 8bef9b6a..c422e428 100644 --- a/src/app/cartography/models/node.ts +++ b/src/app/cartography/models/node.ts @@ -14,6 +14,7 @@ export class Properties { headless: boolean; linked_clone: boolean; on_close: string; + aux: number; ram: number; nvram: number; usage: string; diff --git a/src/app/components/project-map/context-menu/actions/console-device-action-browser/console-device-action-browser.component.html b/src/app/components/project-map/context-menu/actions/console-device-action-browser/console-device-action-browser.component.html index 15f3f924..6faeccf9 100644 --- a/src/app/components/project-map/context-menu/actions/console-device-action-browser/console-device-action-browser.component.html +++ b/src/app/components/project-map/context-menu/actions/console-device-action-browser/console-device-action-browser.component.html @@ -2,3 +2,11 @@ web_asset Console + diff --git a/src/app/components/project-map/context-menu/actions/console-device-action-browser/console-device-action-browser.component.ts b/src/app/components/project-map/context-menu/actions/console-device-action-browser/console-device-action-browser.component.ts index c521be41..7f517e3e 100644 --- a/src/app/components/project-map/context-menu/actions/console-device-action-browser/console-device-action-browser.component.ts +++ b/src/app/components/project-map/context-menu/actions/console-device-action-browser/console-device-action-browser.component.ts @@ -18,14 +18,14 @@ export class ConsoleDeviceActionBrowserComponent { constructor(private toasterService: ToasterService, private nodeService: NodeService, private protocolHandlerService: ProtocolHandlerService) {} - openConsole() { + openConsole(auxiliary: boolean = false) { this.nodeService.getNode(this.server, this.node).subscribe((node: Node) => { this.node = node; - this.startConsole(); + this.startConsole(auxiliary); }); } - startConsole() { + startConsole(auxiliary: boolean) { if (this.node.status !== 'started') { this.toasterService.error('This node must be started before a console can be opened'); } else { @@ -44,7 +44,14 @@ export class ConsoleDeviceActionBrowserComponent { host = `[${host}]`; } if (this.node.console_type === 'telnet') { - uri = `gns3+telnet://${host}:${this.node.console}?name=${this.node.name}&project_id=${this.node.project_id}&node_id=${this.node.node_id}`; + + var console_port; + if (auxiliary === true) { + console_port = this.node.properties.aux; + } else { + console_port = this.node.console; + } + uri = `gns3+telnet://${host}:${console_port}?name=${this.node.name}&project_id=${this.node.project_id}&node_id=${this.node.node_id}`; } else if (this.node.console_type === 'vnc') { uri = `gns3+vnc://${host}:${this.node.console}?name=${this.node.name}&project_id=${this.node.project_id}&node_id=${this.node.node_id}`; } else if (this.node.console_type.startsWith('spice')) { From a9781943d509367c4a2dd726a8248efa94d21ef8 Mon Sep 17 00:00:00 2001 From: grossmj Date: Tue, 14 May 2024 22:11:38 +0700 Subject: [PATCH 5/6] Check auxiliary console port is defined --- .../console-device-action-browser.component.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/app/components/project-map/context-menu/actions/console-device-action-browser/console-device-action-browser.component.ts b/src/app/components/project-map/context-menu/actions/console-device-action-browser/console-device-action-browser.component.ts index 7f517e3e..1f01294a 100644 --- a/src/app/components/project-map/context-menu/actions/console-device-action-browser/console-device-action-browser.component.ts +++ b/src/app/components/project-map/context-menu/actions/console-device-action-browser/console-device-action-browser.component.ts @@ -48,6 +48,10 @@ export class ConsoleDeviceActionBrowserComponent { var console_port; if (auxiliary === true) { console_port = this.node.properties.aux; + if (console_port === undefined) { + this.toasterService.error('Auxiliary console port is not set.'); + return; + } } else { console_port = this.node.console; } @@ -61,6 +65,7 @@ export class ConsoleDeviceActionBrowserComponent { return window.open(uri); // open an http console directly in a new window/tab } else { this.toasterService.error('Supported console types are: telnet, vnc, spice and spice+agent.'); + return; } this.protocolHandlerService.open(uri); From 5b41f9789a60b62b028a419136371e10c68756b7 Mon Sep 17 00:00:00 2001 From: grossmj Date: Wed, 15 May 2024 11:51:18 +0700 Subject: [PATCH 6/6] Release v2.2.47 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bbab9fd8..5258c01a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gns3-web-ui", - "version": "2.2.47.dev1", + "version": "2.2.47", "author": { "name": "GNS3 Technology Inc.", "email": "developers@gns3.com"