Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create protocol handler service #1498

Merged
merged 2 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -538,6 +539,7 @@ import { RotationValidator } from './validators/rotation-validator';
ComputeService,
TracengService,
PacketCaptureService,
ProtocolHandlerService,
NotificationService,
Gns3vmService,
ThemeService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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) => {
Expand All @@ -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');
Expand All @@ -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;
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);

Expand All @@ -52,13 +54,15 @@ describe('LogConsoleComponent', () => {
{ provide: HttpServer, useValue: httpServer },
NodeConsoleService,
ToasterService,
ProtocolHandlerService,
MapSettingsService
],
declarations: [LogConsoleComponent],
schemas: [NO_ERRORS_SCHEMA],
}).compileComponents();

toasterService = TestBed.inject(ToasterService);
protocolHandlerService = TestBed.inject(ProtocolHandlerService);
mapSettingsService = TestBed.inject(MapSettingsService);
nodeConsoleService = TestBed.inject(NodeConsoleService);
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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')) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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'),
};
Expand All @@ -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(() => {
Expand Down
12 changes: 7 additions & 5 deletions src/app/components/project-map/project-map.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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
) {}

Expand Down Expand Up @@ -229,7 +231,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
this.instance.instance.ngOnDestroy();
this.instance.destroy();
}
}
}
}

addSubscriptions() {
Expand Down Expand Up @@ -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));
}
}

Expand All @@ -990,8 +992,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy {

fileReader.onloadend = () => {
let image = fileReader.result;
let svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"
height=\"${imageToUpload.height}\" width=\"${imageToUpload.width}\">\n<image height=\"${imageToUpload.height}\" width=\"${imageToUpload.width}\"
let svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"
height=\"${imageToUpload.height}\" width=\"${imageToUpload.width}\">\n<image height=\"${imageToUpload.height}\" width=\"${imageToUpload.width}\"
xlink:href=\"${image}\"/>\n</svg>`;
this.drawingService
.add(this.server, this.project.project_id, -(imageToUpload.width / 2), -(imageToUpload.height / 2), svg)
Expand Down
11 changes: 7 additions & 4 deletions src/app/services/packet-capture.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

}
}
48 changes: 48 additions & 0 deletions src/app/services/protocol-handler.service.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Loading