From 57c674fef443f3bb849aed4921e35b3438de363f Mon Sep 17 00:00:00 2001 From: Matthias Behr Date: Sat, 5 Oct 2024 17:07:36 +0200 Subject: [PATCH] feat: offer file picker if sharkd not found Offer "open file dialog..." to select sharkd path if its not found. Included an option for Mac to check automatically from .app to .app/Contents/MacOS/... --- src/extension.ts | 99 +++++++++++------------- src/websharkView.ts | 179 ++++++++++++++++++++++++++++---------------- 2 files changed, 158 insertions(+), 120 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 6f5a5c0..d0d3ad8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -14,6 +14,7 @@ import { } from './websharkView'; import { TreeViewNode, TreeViewProvider } from './treeViewProvider'; import { filterPcap, extractDlt, removeTecmp } from './filterPcap'; +import { fileExistsOrPick } from './utils'; const extensionId = 'mbehr1.vsc-webshark'; let reporter: TelemetryReporter; @@ -80,63 +81,51 @@ export function activate(context: vscode.ExtensionContext) { // register our command to open pcap files in webshark view: context.subscriptions.push( vscode.commands.registerCommand('webshark.openFile', async () => { - let _sharkdPath = vscode.workspace.getConfiguration().get('vsc-webshark.sharkdFullPath'); - // check if _sharkdPath exists - if (!fileExists(_sharkdPath)) { - vscode.window - .showErrorMessage( - `sharkdFullPath setting not pointing to a file. Please check setting. Currently used: '${_sharkdPath}'`, - { modal: true }, - 'open settings' - ) - .then((value) => { - switch (value) { - case 'open settings': - vscode.commands.executeCommand('workbench.action.openSettings', 'vsc-webshark.sharkdFullPath'); - break; - } - }); - } else { - return vscode.window - .showOpenDialog({ - canSelectFiles: true, - canSelectFolders: false, - canSelectMany: false, - filters: { - 'pcap files': ['pcap', 'cap', 'pcapng'].flatMap((ext) => [ext, ext + '.gz', ext + '.zst', ext + '.lz4']), - }, - openLabel: 'Select pcap file to open...', - }) - .then(async (uris: vscode.Uri[] | undefined) => { - if (uris) { - uris.forEach((uri) => { - console.log(`webshark.openFile got URI=${uri.toString()}`); - const sharkd = new SharkdProcess(_sharkdPath); - sharkd.ready().then((ready) => { - if (ready) { - context.subscriptions.push( - new WebsharkView(undefined, context, treeDataProvider, _onDidChangeSelectedTime, uri, sharkd, activeViews, (r) => { - const idx = activeViews.indexOf(r); - console.log(` openFile dispose called( r idx = ${idx}) activeViews=${activeViews.length}`); - if (idx >= 0) { - activeViews.splice(idx, 1); - } - }) - ); - if (reporter) { - reporter.sendTelemetryEvent('open file', undefined, { err: 0 }); - } - } else { - vscode.window.showErrorMessage(`sharkd connection not ready! Please check setting. Currently used: '${_sharkdPath}'`); - if (reporter) { - reporter.sendTelemetryEvent('open file', undefined, { err: -1 }); + fileExistsOrPick('vsc-webshark.sharkdFullPath', 'sharkd') + .then((_sharkdPath) => { + return vscode.window + .showOpenDialog({ + canSelectFiles: true, + canSelectFolders: false, + canSelectMany: false, + filters: { + 'pcap files': ['pcap', 'cap', 'pcapng'].flatMap((ext) => [ext, ext + '.gz', ext + '.zst', ext + '.lz4']), + }, + openLabel: 'Select pcap file to open...', + }) + .then(async (uris: vscode.Uri[] | undefined) => { + if (uris) { + uris.forEach((uri) => { + console.log(`webshark.openFile got URI=${uri.toString()}`); + const sharkd = new SharkdProcess(_sharkdPath); + sharkd.ready().then((ready) => { + if (ready) { + context.subscriptions.push( + new WebsharkView(undefined, context, treeDataProvider, _onDidChangeSelectedTime, uri, sharkd, activeViews, (r) => { + const idx = activeViews.indexOf(r); + console.log(` openFile dispose called( r idx = ${idx}) activeViews=${activeViews.length}`); + if (idx >= 0) { + activeViews.splice(idx, 1); + } + }) + ); + if (reporter) { + reporter.sendTelemetryEvent('open file', undefined, { err: 0 }); + } + } else { + vscode.window.showErrorMessage(`sharkd connection not ready! Please check setting. Currently used: '${_sharkdPath}'`); + if (reporter) { + reporter.sendTelemetryEvent('open file', undefined, { err: -1 }); + } } - } + }); }); - }); - } - }); - } + } + }); + }) + .catch((err) => { + throw Error(`sharkdPath not valid: ${err}`); + }); }) ); diff --git a/src/websharkView.ts b/src/websharkView.ts index ca89e56..90e7e8a 100644 --- a/src/websharkView.ts +++ b/src/websharkView.ts @@ -8,6 +8,7 @@ import * as path from 'path'; import { spawn, ChildProcess } from 'child_process'; import TelemetryReporter from '@vscode/extension-telemetry'; import { TreeViewProvider, TreeViewNode } from './treeViewProvider'; +import { fileExistsOrPick } from './utils'; let _nextSharkdId = 1; const platformWin32: boolean = process.platform === 'win32'; @@ -190,78 +191,126 @@ export class SharkdProcess implements vscode.Disposable { } export class WebsharkViewSerializer implements vscode.WebviewPanelSerializer { - constructor(private reporter: TelemetryReporter, private treeViewProvider: TreeViewProvider, private _onDidChangeSelectedTime: vscode.EventEmitter, private sharkdPath: string, private context: vscode.ExtensionContext, private activeViews: WebsharkView[], private callOnDispose: (r: WebsharkView) => any) { - - } - async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, state: any) { - console.log(`WebsharkView deserializeWebviewPanel called. state='${JSON.stringify(state)}'`); - try { - if ('uri' in state) { - const uri: vscode.Uri = vscode.Uri.parse(state.uri, true); - console.log(`creating WebsharkView for uri=${uri.toString()}`); - - const sharkd = new SharkdProcess(this.sharkdPath); - sharkd.ready().then((ready) => { - if (ready) { - this.context.subscriptions.push(new WebsharkView(webviewPanel, this.context, this.treeViewProvider, this._onDidChangeSelectedTime, uri, sharkd, this.activeViews, this.callOnDispose)); - if (this.reporter) { this.reporter.sendTelemetryEvent("open file", undefined, { 'err': 0 }); } - } else { - vscode.window.showErrorMessage(`sharkd connection not ready! Please check setting. Currently used: '${this.sharkdPath}'`); - if (this.reporter) { this.reporter.sendTelemetryEvent("open file", undefined, { 'err': -1 }); } - } - }); - - } else { console.warn(`deserializeWebviewPanel but no uri within state='${JSON.stringify(state)}'`); } - } catch (err) { - console.warn(`deserializeWebviewPanel got err=${err} with state='${JSON.stringify(state)}'`); - } + constructor( + private reporter: TelemetryReporter, + private treeViewProvider: TreeViewProvider, + private _onDidChangeSelectedTime: vscode.EventEmitter, + private sharkdPath: string, + private context: vscode.ExtensionContext, + private activeViews: WebsharkView[], + private callOnDispose: (r: WebsharkView) => any + ) {} + async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, state: any) { + console.log(`WebsharkView deserializeWebviewPanel called. state='${JSON.stringify(state)}'`); + try { + if ('uri' in state) { + const uri: vscode.Uri = vscode.Uri.parse(state.uri, true); + console.log(`creating WebsharkView for uri=${uri.toString()}`); + + const sharkd = new SharkdProcess(this.sharkdPath); + sharkd.ready().then((ready) => { + if (ready) { + this.context.subscriptions.push( + new WebsharkView( + webviewPanel, + this.context, + this.treeViewProvider, + this._onDidChangeSelectedTime, + uri, + sharkd, + this.activeViews, + this.callOnDispose + ) + ); + if (this.reporter) { + this.reporter.sendTelemetryEvent('open file', undefined, { err: 0 }); + } + } else { + vscode.window.showErrorMessage(`sharkd connection not ready! Please check setting. Currently used: '${this.sharkdPath}'`); + if (this.reporter) { + this.reporter.sendTelemetryEvent('open file', undefined, { err: -1 }); + } + } + }); + } else { + console.warn(`deserializeWebviewPanel but no uri within state='${JSON.stringify(state)}'`); + } + } catch (err) { + console.warn(`deserializeWebviewPanel got err=${err} with state='${JSON.stringify(state)}'`); } + } } class WebsharkViewCustomDocument implements vscode.CustomDocument { - constructor(public uri: vscode.Uri) { - console.log(`WebsharkViewCustomDocument(uri=${this.uri.toString()}) called`); - } - dispose() { - console.log(`WebsharkViewCustomDocument dispose(uri=${this.uri.toString()}) called`); - } + constructor(public uri: vscode.Uri) { + console.log(`WebsharkViewCustomDocument(uri=${this.uri.toString()}) called`); + } + dispose() { + console.log(`WebsharkViewCustomDocument dispose(uri=${this.uri.toString()}) called`); + } } export class WebsharkViewReadonlyEditorProvider implements vscode.CustomReadonlyEditorProvider { - constructor(private reporter: TelemetryReporter, private treeViewProvider: TreeViewProvider, private _onDidChangeSelectedTime: vscode.EventEmitter, private context: vscode.ExtensionContext, private activeViews: WebsharkView[], private callOnDispose: (r: WebsharkView) => any) { - } - openCustomDocument(uri: vscode.Uri, openContext: vscode.CustomDocumentOpenContext, token: vscode.CancellationToken): Thenable | WebsharkViewCustomDocument { - console.log(`WebsharkViewReadonlyEditorProvider openCustomDocument(uri=${uri.toString()}, openContext=${JSON.stringify(openContext)}) called`); - // we dont support backupId yet (necessary for readonly at all?) - return new WebsharkViewCustomDocument(uri); - } - resolveCustomEditor(document: WebsharkViewCustomDocument, webviewPanel: vscode.WebviewPanel, token: vscode.CancellationToken): Thenable | void { - console.log(`WebsharkViewReadonlyEditorProvider resolveCustomEditor(document.uri=${document.uri.toString()}, webviewPanel:${webviewPanel}) called`); - const _wiresharkProfile = (vscode.workspace.getConfiguration().get("vsc-webshark.wiresharkProfile")); - const _sharkdPath = (vscode.workspace.getConfiguration().get("vsc-webshark.sharkdFullPath")); - if (!fileExists(_sharkdPath)) { - vscode.window.showErrorMessage(`sharkdFullPath setting not pointing to a file. Please check setting. Currently used: '${_sharkdPath}'`, - { modal: true }, 'open settings').then((value) => { - switch (value) { - case 'open settings': - vscode.commands.executeCommand('workbench.action.openSettings', "vsc-webshark.sharkdFullPath"); - break; - } - }); - throw Error(`sharkdPath not pointing to a file`); - } else { - const sharkd = new SharkdProcess(_sharkdPath, _wiresharkProfile); - sharkd.ready().then((ready) => { - if (ready) { - this.context.subscriptions.push(new WebsharkView(webviewPanel, this.context, this.treeViewProvider, this._onDidChangeSelectedTime, document.uri, sharkd, this.activeViews, this.callOnDispose)); - if (this.reporter) { this.reporter.sendTelemetryEvent("open file resolve custom editor", { fn: 'resolveCustomEditor' }, { 'err': 0 }); } - } else { - vscode.window.showErrorMessage(`sharkd connection not ready! Please check setting. Currently used: '${_sharkdPath}'`); - if (this.reporter) { this.reporter.sendTelemetryEvent("open file resolve custom editor", { fn: 'resolveCustomEditor' }, { 'err': -1 }); } - } - }); - } - } + constructor( + private reporter: TelemetryReporter, + private treeViewProvider: TreeViewProvider, + private _onDidChangeSelectedTime: vscode.EventEmitter, + private context: vscode.ExtensionContext, + private activeViews: WebsharkView[], + private callOnDispose: (r: WebsharkView) => any + ) {} + openCustomDocument( + uri: vscode.Uri, + openContext: vscode.CustomDocumentOpenContext, + token: vscode.CancellationToken + ): Thenable | WebsharkViewCustomDocument { + console.log( + `WebsharkViewReadonlyEditorProvider openCustomDocument(uri=${uri.toString()}, openContext=${JSON.stringify(openContext)}) called` + ); + // we dont support backupId yet (necessary for readonly at all?) + return new WebsharkViewCustomDocument(uri); + } + resolveCustomEditor( + document: WebsharkViewCustomDocument, + webviewPanel: vscode.WebviewPanel, + token: vscode.CancellationToken + ): Thenable | void { + console.log( + `WebsharkViewReadonlyEditorProvider resolveCustomEditor(document.uri=${document.uri.toString()}, webviewPanel:${webviewPanel}) called` + ); + const _wiresharkProfile = vscode.workspace.getConfiguration().get('vsc-webshark.wiresharkProfile'); + fileExistsOrPick('vsc-webshark.sharkdFullPath', 'sharkd') + .then((filePath) => { + const sharkd = new SharkdProcess(filePath, _wiresharkProfile); + sharkd.ready().then((ready) => { + if (ready) { + this.context.subscriptions.push( + new WebsharkView( + webviewPanel, + this.context, + this.treeViewProvider, + this._onDidChangeSelectedTime, + document.uri, + sharkd, + this.activeViews, + this.callOnDispose + ) + ); + if (this.reporter) { + this.reporter.sendTelemetryEvent('open file resolve custom editor', { fn: 'resolveCustomEditor' }, { err: 0 }); + } + } else { + vscode.window.showErrorMessage(`sharkd connection not ready! Please check setting. Currently used: '${filePath}'`); + if (this.reporter) { + this.reporter.sendTelemetryEvent('open file resolve custom editor', { fn: 'resolveCustomEditor' }, { err: -1 }); + } + } + }); + }) + .catch((err) => { + throw Error(`sharkdPath not valid: ${err}`); + }); + } } interface ResponseData {