From 262ec9c9dfda6e549996898d0cf31abe82a46c40 Mon Sep 17 00:00:00 2001 From: Kelvin Schoofs Date: Fri, 19 Mar 2021 18:13:59 +0100 Subject: [PATCH] Add taskCommand to ConfigEditor --- webview/src/ConfigEditor/fields.tsx | 15 ++++++++--- webview/src/types/fileSystemConfig.ts | 36 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/webview/src/ConfigEditor/fields.tsx b/webview/src/ConfigEditor/fields.tsx index 2d1688f..f395fb2 100644 --- a/webview/src/ConfigEditor/fields.tsx +++ b/webview/src/ConfigEditor/fields.tsx @@ -34,7 +34,7 @@ export function merge(config: FileSystemConfig, onChange: FSCChanged<'merge'>): } export function label(config: FileSystemConfig, onChange: FSCChanged<'label'>): React.ReactElement { - const callback = (value?: string) => onChange('label', value); + const callback = (newValue?: string) => onChange('label', newValue); const description = 'Label to display in some UI places (e.g. popups)'; return } @@ -97,7 +97,7 @@ export function password(config: FileSystemConfig, onChange: FSCChanged<'passwor } export function privateKeyPath(config: FileSystemConfig, onChange: FSCChanged<'privateKeyPath'>): React.ReactElement { - const callback = (value?: string) => onChange('privateKeyPath', value); + const callback = (newValue?: string) => onChange('privateKeyPath', newValue); const description = 'A path to a private key. Supports environment variables, e.g. `$USERPROFILE/.ssh/myKey.ppk` or `$HOME/.ssh/myKey`'; return } @@ -133,9 +133,18 @@ export function terminalCommand(config: FileSystemConfig, onChange: FSCChanged<' return } +export function taskCommand(config: FileSystemConfig, onChange: FSCChanged<'taskCommand'>): React.ReactElement { + const callback = (newValue?: string) => onChange('taskCommand', newValue); + const description = 'The command(s) to run when a `ssh-shell` gets run. Defaults to the placeholder `$COMMAND`. Internally the command `cd ...` is run first'; + const values = ['$COMMAND']; + let value = config.taskCommand; + if (Array.isArray(value)) value = value.join('; '); + return +} + export type FieldFactory = (config: FileSystemConfig, onChange: FSCChanged, onChangeMultiple: FSCChangedMultiple) => React.ReactElement | null; export const FIELDS: FieldFactory[] = [ name, merge, label, group, putty, host, port, root, agent, username, password, privateKeyPath, passphrase, - sftpCommand, sftpSudo, terminalCommand, + sftpCommand, sftpSudo, terminalCommand, taskCommand, PROXY_FIELD]; diff --git a/webview/src/types/fileSystemConfig.ts b/webview/src/types/fileSystemConfig.ts index 4ea92d6..7ade3a9 100644 --- a/webview/src/types/fileSystemConfig.ts +++ b/webview/src/types/fileSystemConfig.ts @@ -98,8 +98,12 @@ export interface FileSystemConfig extends ConnectConfig { sftpSudo?: string | boolean; /** The command(s) to run when a new SSH terminal gets created. Defaults to `$SHELL`. Internally the command `cd ...` is run first */ terminalCommand?: string | string[]; + /** The command(s) to run when a `ssh-shell` gets run. Defaults to the placeholder `$COMMAND`. Internally the command `cd ...` is run first */ + taskCommand?: string | string[]; /** The filemode to assign to created files */ newFileMode?: number | string; + /** Whether this config was created from an instant connection string. Enables fuzzy matching for e.g. PuTTY, config-by-host, ... */ + instantConnection?: boolean; /** Internal property saying where this config comes from. Undefined if this config is merged or something */ _location?: ConfigLocation; /** Internal property keeping track of where this config comes from (including merges) */ @@ -113,3 +117,35 @@ export function invalidConfigName(name: string) { if (name.match(/^[\w_\\/.@\-+]+$/)) return null; return `A SSH FS name can only exists of lowercase alphanumeric characters, slashes and any of these: _.+-@`; } + +/** + * https://regexr.com/5m3gl (mostly based on https://tools.ietf.org/html/draft-ietf-secsh-scp-sftp-ssh-uri-04) + * Supports several formats, the first one being the "full" format, with others being partial: + * - `user;abc=def,a-b=1-5@server.example.com:22/some/file.ext` + * - `user@server.example.com/directory` + * - `server:22/directory` + * - `user@server` + * - `server` + * - `@server/path` - Unlike OpenSSH, we allow a @ (and connection parameters) without a username + * + * The resulting FileSystemConfig will have as name basically the input, but without the path. If there is no + * username given, the name will start with `@`, as to differentiate between connection strings and config names. + */ +const CONNECTION_REGEX = /^((?\w+)?(;[\w-]+=[\w\d-]+(,[\w\d-]+=[\w\d-]+)*)?@)?(?[^@\\/:,=]+)(:(?\d+))?(?\/.*)?$/; + +export function parseConnectionString(input: string): [config: FileSystemConfig, path?: string] | string { + input = input.trim(); + const match = input.match(CONNECTION_REGEX); + if (!match) return 'Invalid format, expected something like "user@example.com:22/some/path"'; + const { user, host, path } = match.groups!; + const portStr = match.groups!.port; + const port = portStr ? Number.parseInt(portStr) : undefined; + if (portStr && (!port || port < 1 || port > 65535)) return `The string '${port}' is not a valid port number`; + const name = `${user || ''}@${host}${port ? `:${port}` : ''}${path || ''}`; + return [{ + name, host, port, + instantConnection: true, + username: user || '$USERNAME', + _locations: [], + }, path]; +}