Skip to content

Commit

Permalink
feat: offer file picker if sharkd not found
Browse files Browse the repository at this point in the history
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/...
  • Loading branch information
mbehr1 committed Oct 5, 2024
1 parent 061f3b0 commit fd1a21a
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 120 deletions.
99 changes: 44 additions & 55 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 = <string>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}`);
});
})
);

Expand Down
61 changes: 61 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* --------------------
* Copyright(C) Matthias Behr, 2024.
*/

import * as vscode from 'vscode';
import { fileExists } from './websharkView';

export function fileExistsOrPick(settingsName: string, binaryName: string): Promise<string> {
const filePath = <string>vscode.workspace.getConfiguration().get(settingsName);
return new Promise((resolve, reject) => {
if (fileExists(filePath)) {
resolve(filePath);
} else {
vscode.window
.showErrorMessage(
`'${settingsName}' setting not pointing to a file. Please change setting or use file picker. Currently used: '${filePath}'`,
{ modal: true },
'use file picker...',
'open settings'
)
.then((value) => {
switch (value) {
case 'open settings':
vscode.commands.executeCommand('workbench.action.openSettings', settingsName);
reject(`file '${filePath}' does not exist`);
return;
case 'use file picker...':
vscode.window
.showOpenDialog({
canSelectFiles: true,
canSelectFolders: false,
canSelectMany: false,
openLabel: `Select ${settingsName}`,
})
.then((uris) => {
if (uris && uris.length > 0) {
// special handling for Mac. If the user selected a folder
// that ends in .app
// check whether
// Contents/MacOS/<binarynam> exists and use that instead
if (uris[0].fsPath.endsWith('.app')) {
const macPath = `${uris[0].fsPath}/Contents/MacOS/${binaryName}`;
if (fileExists(macPath)) {
vscode.workspace.getConfiguration().update(settingsName, macPath, vscode.ConfigurationTarget.Global);
resolve(macPath);
return;
}
}
vscode.workspace.getConfiguration().update(settingsName, uris[0].fsPath, vscode.ConfigurationTarget.Global);
resolve(uris[0].fsPath);
return;
} else {
reject(`no file selected. file '${filePath}' does not exist`);
}
});
return;
}
});
}
});
}
179 changes: 114 additions & 65 deletions src/websharkView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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<SelectedTimeData>, 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<SelectedTimeData>,
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<WebsharkViewCustomDocument> {
constructor(private reporter: TelemetryReporter, private treeViewProvider: TreeViewProvider, private _onDidChangeSelectedTime: vscode.EventEmitter<SelectedTimeData>, private context: vscode.ExtensionContext, private activeViews: WebsharkView[], private callOnDispose: (r: WebsharkView) => any) {
}
openCustomDocument(uri: vscode.Uri, openContext: vscode.CustomDocumentOpenContext, token: vscode.CancellationToken): Thenable<WebsharkViewCustomDocument> | 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> | void {
console.log(`WebsharkViewReadonlyEditorProvider resolveCustomEditor(document.uri=${document.uri.toString()}, webviewPanel:${webviewPanel}) called`);
const _wiresharkProfile = <string>(vscode.workspace.getConfiguration().get("vsc-webshark.wiresharkProfile"));
const _sharkdPath = <string>(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<SelectedTimeData>,
private context: vscode.ExtensionContext,
private activeViews: WebsharkView[],
private callOnDispose: (r: WebsharkView) => any
) {}
openCustomDocument(
uri: vscode.Uri,
openContext: vscode.CustomDocumentOpenContext,
token: vscode.CancellationToken
): Thenable<WebsharkViewCustomDocument> | 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> | void {
console.log(
`WebsharkViewReadonlyEditorProvider resolveCustomEditor(document.uri=${document.uri.toString()}, webviewPanel:${webviewPanel}) called`
);
const _wiresharkProfile = <string>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 {
Expand Down

0 comments on commit fd1a21a

Please sign in to comment.