diff --git a/src/config.ts b/src/config.ts index fdb7444..b6bafe2 100644 --- a/src/config.ts +++ b/src/config.ts @@ -342,6 +342,30 @@ vscode.workspace.onDidChangeConfiguration(async (e) => { }); loadConfigs(); +function parseFlagList(list: string[] | undefined, origin: string): Record { + if (list === undefined) return {}; + if (!Array.isArray(list)) throw new Error(`Expected string array for flags, but got: ${list}`); + const scope: Record = {}; + for (const flag of list) { + let name: string = flag; + let value: FlagValue = null; + const eq = flag.indexOf('='); + if (eq !== -1) { + name = flag.substring(0, eq); + value = flag.substring(eq + 1); + } else if (flag.startsWith('+')) { + name = flag.substring(1); + value = true; + } else if (flag.startsWith('-')) { + name = flag.substring(1); + value = false; + } + name = name.toLocaleLowerCase(); + if (name in scope) continue; + scope[name] = [value, origin]; + } + return scope; +} export type FlagValue = string | boolean | null; export type FlagCombo = [value: FlagValue, origin: string]; @@ -351,41 +375,16 @@ function calculateFlags(): Record { const flags: Record = {}; const config = vscode.workspace.getConfiguration('sshfs').inspect('flags'); if (!config) throw new Error(`Could not inspect "sshfs.flags" config field`); - function parseList(list: string[] | undefined, origin: string) { - if (list === undefined) return; - if (!Array.isArray(list)) throw new Error(`Expected string array for flags, but got: ${list}`); - const scope: Record = {}; - for (const flag of list) { - let name: string = flag; - let value: FlagValue = null; - const eq = flag.indexOf('='); - if (eq !== -1) { - name = flag.substring(0, eq); - value = flag.substring(eq + 1); - } else if (flag.startsWith('+')) { - name = flag.substring(1); - value = true; - } else if (flag.startsWith('-')) { - name = flag.substring(1); - value = false; - } - name = name.toLocaleLowerCase(); - if (name in scope) continue; - scope[name] = [value, origin]; - } - // Override if necessary (since workspace settings come after global settings) - // Per "location", we still ignore duplicate flag names - Object.assign(flags, scope); - } - parseList(DEFAULT_FLAGS, 'Built-in Default'); - parseList(config.defaultValue, 'Default Settings'); + const applyList = (list: string[] | undefined, origin: string) => Object.assign(flags, parseFlagList(list, origin)); + applyList(DEFAULT_FLAGS, 'Built-in Default'); + applyList(config.defaultValue, 'Default Settings'); // Electron v11 crashes for DiffieHellman GroupExchange, although it's fixed in 11.3.0 if ((process.versions as { electron?: string }).electron?.match(/^11\.(0|1|2)\./)) { - parseList(['+DF-GE'], 'Fix for issue #239') + applyList(['+DF-GE'], 'Fix for issue #239') } - parseList(config.globalValue, 'Global Settings'); - parseList(config.workspaceValue, 'Workspace Settings'); - parseList(config.workspaceFolderValue, 'WorkspaceFolder Settings'); + applyList(config.globalValue, 'Global Settings'); + applyList(config.workspaceValue, 'Workspace Settings'); + applyList(config.workspaceFolderValue, 'WorkspaceFolder Settings'); Logging.info(`Calculated config flags: ${JSON.stringify(flags)}`); return cachedFlags = flags; } @@ -395,8 +394,17 @@ vscode.workspace.onDidChangeConfiguration(event => { }); calculateFlags(); -/** Returns a cached version. Gets updated by ConfigurationChangeEvent events */ -export function getFlags(): Record { return cachedFlags; } +/** + * Returns (a copy of the) global flags. Gets updated by ConfigurationChangeEvent events. + * In case `flags` is given, flags specified in this array will override global ones in the returned result. + * @param flags An optional array of flags to check before the global ones + */ +export function getFlags(flags?: string[]): Record { + return { + ...cachedFlags, + ...parseFlagList(flags, 'Override'), + }; +} /** * Checks the `sshfs.flags` config (overridable by e.g. workspace settings). @@ -411,9 +419,10 @@ export function getFlags(): Record { return cachedFlags; } * For `undefined`, an actual `undefined` is returned. For all other cases, a FlagCombo * is returned, e.g. "NAME" returns `[null, "someOrigin"]` and `"+F"` returns `[true, "someOrigin"]` * @param target The name of the flag to look for + * @param flags An optional array of flags to check before the global ones */ -export function getFlag(target: string): FlagCombo | undefined { - return calculateFlags()[target.toLowerCase()]; +export function getFlag(target: string, flags?: string[]): FlagCombo | undefined { + return getFlags(flags)[target.toLowerCase()]; } /** @@ -428,10 +437,11 @@ export function getFlag(target: string): FlagCombo | undefined { * - All other strings result in an error * @param target The name of the flag to look for * @param defaultValue The value to return when no flag with the given name is present + * @param flags An optional array of flags to check before the global ones * @returns The matching FlagCombo or `[missingValue, 'missing']` instead */ -export function getFlagBoolean(target: string, missingValue: boolean): FlagCombo { - const combo = getFlag(target); +export function getFlagBoolean(target: string, missingValue: boolean, flags?: string[]): FlagCombo { + const combo = getFlag(target, flags); if (!combo) return [missingValue, 'missing']; const [value, reason] = combo; if (value == null) return [true, reason]; diff --git a/src/connect.ts b/src/connect.ts index 4c586aa..39ea369 100644 --- a/src/connect.ts +++ b/src/connect.ts @@ -253,13 +253,13 @@ export async function createSSH(config: FileSystemConfig, sock?: NodeJS.Readable }); try { const finalConfig: ConnectConfig = { ...config, sock, ...DEFAULT_CONFIG }; - if (config.debug || getFlag('DEBUG_SSH2') !== undefined) { + if (config.debug || getFlag('DEBUG_SSH2', config.flags) !== undefined) { const scope = Logging.scope(`ssh2(${config.name})`); finalConfig.debug = (msg: string) => scope.debug(msg); } // Unless the flag 'DF-GE' is specified, disable DiffieHellman groupex algorithms (issue #239) // Note: If the config already specifies a custom `algorithms.key`, ignore it (trust the user?) - const [flagV, flagR] = getFlagBoolean('DF-GE', false); + const [flagV, flagR] = getFlagBoolean('DF-GE', false, config.flags); if (flagV) { logging.info(`Flag "DF-GE" enabled due to '${flagR}', disabling DiffieHellman kex groupex algorithms`); let kex: string[] = require('ssh2-streams/lib/constants').ALGORITHMS.KEX; diff --git a/src/fileSystemConfig.ts b/src/fileSystemConfig.ts index b444964..54dccbe 100644 --- a/src/fileSystemConfig.ts +++ b/src/fileSystemConfig.ts @@ -104,6 +104,8 @@ export interface FileSystemConfig extends ConnectConfig { 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; + /** List of special flags to enable/disable certain fixes/features. Flags are usually used for issues or beta testing. Flags can disappear/change anytime! */ + flags?: string[]; /** 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) */