From 06a4004f4c433c23108f8993c47ca3917f00f3f3 Mon Sep 17 00:00:00 2001 From: worksofliam Date: Tue, 7 May 2024 09:36:37 -0500 Subject: [PATCH 1/5] Seperate URI handler and sandbox Signed-off-by: worksofliam --- src/extension.ts | 7 +-- src/sandbox.ts | 110 +++-------------------------------------------- src/uri/index.ts | 108 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 108 deletions(-) create mode 100644 src/uri/index.ts diff --git a/src/extension.ts b/src/extension.ts index 28e514e6d..78cd7b5d3 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -19,7 +19,6 @@ import { Deployment } from "./api/local/deployment"; import { IFSFS } from "./filesystems/ifsFs"; import { LocalActionCompletionItemProvider } from "./languages/actions/completion"; import { updateLocale } from "./locale"; -import * as Sandbox from "./sandbox"; import { initialise } from "./testing"; import { CodeForIBMi, ConnectionData } from "./typings"; import { initializeConnectionBrowser } from "./views/ConnectionBrowser"; @@ -30,6 +29,8 @@ import { HelpView } from "./views/helpView"; import { initializeIFSBrowser } from "./views/ifsBrowser"; import { initializeObjectBrowser } from "./views/objectBrowser"; import { SettingsUI } from "./webviews/settings"; +import { registerUriHandler } from "./uri"; +import { handleSandboxStartup } from "./sandbox"; export async function activate(context: ExtensionContext): Promise { // Use the console to output diagnostic information (console.log) and errors (console.error) @@ -101,8 +102,8 @@ export async function activate(context: ExtensionContext): Promise Deployment.initialize(context); checkLastConnections(); - Sandbox.handleStartup(); - Sandbox.registerUriHandler(context); + handleSandboxStartup(); + registerUriHandler(context); console.log(`Developer environment: ${process.env.DEV}`); if (process.env.DEV) { diff --git a/src/sandbox.ts b/src/sandbox.ts index e7f9cc091..93923cf4e 100644 --- a/src/sandbox.ts +++ b/src/sandbox.ts @@ -1,112 +1,12 @@ import { env } from "process"; -import querystring from "querystring"; -import { commands, ExtensionContext, Uri, window } from "vscode"; -import { ConnectionConfiguration, GlobalConfiguration } from "./api/Configuration"; +import { commands, window } from "vscode"; +import { ConnectionConfiguration } from "./api/Configuration"; import { Tools } from "./api/Tools"; import { instance } from "./instantiate"; import { t } from "./locale"; import { ConnectionData } from "./typings"; -export async function registerUriHandler(context: ExtensionContext) { - context.subscriptions.push( - window.registerUriHandler({ - async handleUri(uri: Uri) { - console.log(uri); - - const connection = instance.getConnection(); - - switch (uri.path) { - case `/connect`: - if (connection === undefined) { - const queryData = querystring.parse(uri.query); - - const save = queryData.save === `true`; - const server = String(queryData.server); - let user: string | string[] | undefined = queryData.user; - let pass: string | string[] | undefined = queryData.pass; - - if (server) { - if (!user) { - user = await window.showInputBox({ - title: t(`sandbox.input.user.title`), - prompt: t(`sandbox.input.user.prompt`, server) - }); - } - - if (pass) { - pass = Buffer.from(String(pass), `base64`).toString(); - } else { - pass = await window.showInputBox({ - password: true, - title: t(`sandbox.input.password.title`), - prompt: t(`sandbox.input.password.prompt`, String(user), server) - }); - } - - if (user && pass) { - const serverParts = String(server).split(`:`); - const host = serverParts[0]; - const port = serverParts.length === 2 ? Number(serverParts[1]) : 22; - - const connectionData: ConnectionData = { - host, - name: `${user}-${host}`, - username: String(user), - password: String(pass), - port - }; - - const connectionResult = await commands.executeCommand(`code-for-ibmi.connectDirect`, connectionData); - - if (connectionResult) { - await initialSetup(connectionData.username); - - if (save) { - let existingConnections: ConnectionData[] | undefined = GlobalConfiguration.get(`connections`); - - if (existingConnections) { - const existingConnection = existingConnections.find(item => item.name === host); - - if (!existingConnection) { - // New connection! - existingConnections.push({ - ...connectionData, - password: undefined, // Removes the password from the object - }); - - await context.secrets.store(`${host}_password`, pass); - await GlobalConfiguration.set(`connections`, existingConnections); - } - } - } - - } else { - window.showInformationMessage(t(`sandbox.failedToConnect.title`), { - modal: true, - detail: t(`sandbox.failedToConnect`, server, user) - }); - } - - } else { - window.showErrorMessage(t(`sandbox.noPassword`, server)); - } - } - } else { - window.showInformationMessage(t(`sandbox.failedToConnect.title`), { - modal: true, - detail: t(`sandbox.alreadyConnected`) - }); - } - - break; - } - - } - }) - ); -} - -export async function handleStartup() { +export async function handleSandboxStartup() { let server: string | undefined = env.SANDBOX_SERVER; let username: string | undefined = env.SANDBOX_USER; @@ -166,7 +66,7 @@ export async function handleStartup() { const connectionResult = await commands.executeCommand(`code-for-ibmi.connectDirect`, connectionData); if (connectionResult) { - await initialSetup(connectionData.username); + await initialSandboxSetup(connectionData.username); } else { window.showInformationMessage(t(`sandbox.noconnection.modal.title`), { @@ -177,7 +77,7 @@ export async function handleStartup() { } } -async function initialSetup(username: string) { +export async function initialSandboxSetup(username: string) { const config = instance.getConfig(); if (config) { const libraryList = config.libraryList; diff --git a/src/uri/index.ts b/src/uri/index.ts new file mode 100644 index 000000000..c90067a10 --- /dev/null +++ b/src/uri/index.ts @@ -0,0 +1,108 @@ +import { env } from "process"; +import querystring from "querystring"; +import { commands, ExtensionContext, Uri, window } from "vscode"; +import { ConnectionConfiguration, GlobalConfiguration } from "../api/Configuration"; +import { Tools } from "../api/Tools"; +import { instance } from "../instantiate"; +import { t } from "../locale"; +import { ConnectionData } from "../typings"; +import { initialSandboxSetup } from "../sandbox"; + +export async function registerUriHandler(context: ExtensionContext) { + context.subscriptions.push( + window.registerUriHandler({ + async handleUri(uri: Uri) { + console.log(uri); + + const connection = instance.getConnection(); + + switch (uri.path) { + case `/connect`: + if (connection === undefined) { + const queryData = querystring.parse(uri.query); + + const save = queryData.save === `true`; + const server = String(queryData.server); + let user: string | string[] | undefined = queryData.user; + let pass: string | string[] | undefined = queryData.pass; + + if (server) { + if (!user) { + user = await window.showInputBox({ + title: t(`sandbox.input.user.title`), + prompt: t(`sandbox.input.user.prompt`, server) + }); + } + + if (pass) { + pass = Buffer.from(String(pass), `base64`).toString(); + } else { + pass = await window.showInputBox({ + password: true, + title: t(`sandbox.input.password.title`), + prompt: t(`sandbox.input.password.prompt`, String(user), server) + }); + } + + if (user && pass) { + const serverParts = String(server).split(`:`); + const host = serverParts[0]; + const port = serverParts.length === 2 ? Number(serverParts[1]) : 22; + + const connectionData: ConnectionData = { + host, + name: `${user}-${host}`, + username: String(user), + password: String(pass), + port + }; + + const connectionResult = await commands.executeCommand(`code-for-ibmi.connectDirect`, connectionData); + + if (connectionResult) { + await initialSandboxSetup(connectionData.username); + + if (save) { + let existingConnections: ConnectionData[] | undefined = GlobalConfiguration.get(`connections`); + + if (existingConnections) { + const existingConnection = existingConnections.find(item => item.name === host); + + if (!existingConnection) { + // New connection! + existingConnections.push({ + ...connectionData, + password: undefined, // Removes the password from the object + }); + + await context.secrets.store(`${host}_password`, pass); + await GlobalConfiguration.set(`connections`, existingConnections); + } + } + } + + } else { + window.showInformationMessage(t(`sandbox.failedToConnect.title`), { + modal: true, + detail: t(`sandbox.failedToConnect`, server, user) + }); + } + + } else { + window.showErrorMessage(t(`sandbox.noPassword`, server)); + } + } + } else { + window.showInformationMessage(t(`sandbox.failedToConnect.title`), { + modal: true, + detail: t(`sandbox.alreadyConnected`) + }); + } + + break; + } + + } + }) + ); +} \ No newline at end of file From 6ba2544371682d08d32ff1b8c624b31af78acad0 Mon Sep 17 00:00:00 2001 From: worksofliam Date: Tue, 7 May 2024 09:55:05 -0500 Subject: [PATCH 2/5] Add new URI handler to open a file Signed-off-by: worksofliam --- src/uri/index.ts | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/uri/index.ts b/src/uri/index.ts index c90067a10..aa9787b73 100644 --- a/src/uri/index.ts +++ b/src/uri/index.ts @@ -1,8 +1,6 @@ -import { env } from "process"; import querystring from "querystring"; import { commands, ExtensionContext, Uri, window } from "vscode"; -import { ConnectionConfiguration, GlobalConfiguration } from "../api/Configuration"; -import { Tools } from "../api/Tools"; +import { GlobalConfiguration } from "../api/Configuration"; import { instance } from "../instantiate"; import { t } from "../locale"; import { ConnectionData } from "../typings"; @@ -12,15 +10,35 @@ export async function registerUriHandler(context: ExtensionContext) { context.subscriptions.push( window.registerUriHandler({ async handleUri(uri: Uri) { - console.log(uri); + const queryData = querystring.parse(uri.query); const connection = instance.getConnection(); switch (uri.path) { + case '/open': + if (connection) { + if (queryData.path) { + if (queryData.host) { + const host = Array.isArray(queryData.host) ? queryData.host[0] : queryData.host; + if (host !== connection.currentHost) { + // TODO: host does not match requested host, do you still want to open? + return; + } + } + + const paths = Array.isArray(queryData.path) ? queryData.path : [queryData.path]; + for (const path of paths) { + commands.executeCommand(`code-for-ibmi.openEditable`, path); + } + } else { + // TODO: error message: missing path + } + } else { + // TODO: error message: no connection + } + break; case `/connect`: if (connection === undefined) { - const queryData = querystring.parse(uri.query); - const save = queryData.save === `true`; const server = String(queryData.server); let user: string | string[] | undefined = queryData.user; From 9c266cd51207bb4eeec05f588c9b127c48f22455 Mon Sep 17 00:00:00 2001 From: worksofliam Date: Mon, 3 Jun 2024 03:39:36 -0400 Subject: [PATCH 3/5] Add warning messages Signed-off-by: worksofliam --- src/locale/ids/en.json | 4 ++++ src/uri/index.ts | 15 +++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/locale/ids/en.json b/src/locale/ids/en.json index fb993f31f..af4d1f2d0 100644 --- a/src/locale/ids/en.json +++ b/src/locale/ids/en.json @@ -365,6 +365,10 @@ "sandbox.noPassword": "Connection to {0} ended as no password was provided.", "save": "Save", "service.certificate.exists": "Remote service certificate exists", + "uriOpen.openError": "Error opening file", + "uriOpen.hostMismatch": "A file on another host is trying to be opened. Do you want to open on the current connection instead?", + "uriOpen.missingPath": "The provided URI is missing the path attribute.", + "uriOpen.noConnection": "You must have an active connection to open a file.", "shortcut": "shortcut", "size": "Size", "skip": "Skip", diff --git a/src/uri/index.ts b/src/uri/index.ts index aa9787b73..34a79218f 100644 --- a/src/uri/index.ts +++ b/src/uri/index.ts @@ -21,8 +21,14 @@ export async function registerUriHandler(context: ExtensionContext) { if (queryData.host) { const host = Array.isArray(queryData.host) ? queryData.host[0] : queryData.host; if (host !== connection.currentHost) { - // TODO: host does not match requested host, do you still want to open? - return; + const chosen = await window.showInformationMessage(t(`uriOpen.openError`), { + detail: t(`uriOpen.hostMismatch`), + modal: true + }, `Open`); + + if (chosen !== `Open`) { + return; + } } } @@ -31,12 +37,13 @@ export async function registerUriHandler(context: ExtensionContext) { commands.executeCommand(`code-for-ibmi.openEditable`, path); } } else { - // TODO: error message: missing path + window.showWarningMessage(t(`uriOpen.missingPath`)); } } else { - // TODO: error message: no connection + window.showWarningMessage(t(`uriOpen.noConnection`)); } break; + case `/connect`: if (connection === undefined) { const save = queryData.save === `true`; From 7a6a00bd4ae268a49c80030bd1f2c5252ac0714b Mon Sep 17 00:00:00 2001 From: worksofliam Date: Mon, 3 Jun 2024 03:42:44 -0400 Subject: [PATCH 4/5] Build commit Signed-off-by: worksofliam From 61b209e2bc3cb6ea2081b96ca7f54abbbd04c3db Mon Sep 17 00:00:00 2001 From: worksofliam Date: Wed, 11 Sep 2024 09:56:21 -0400 Subject: [PATCH 5/5] Ability to connect to system from open Signed-off-by: worksofliam --- src/api/Configuration.ts | 12 +++++++-- src/uri/index.ts | 56 +++++++++++++++++++++++++++------------- 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/src/api/Configuration.ts b/src/api/Configuration.ts index 4dffbf84c..07e78b3c7 100644 --- a/src/api/Configuration.ts +++ b/src/api/Configuration.ts @@ -37,9 +37,17 @@ export interface StoredConnection { const getPasswordKey = (connectionName:string) => `${connectionName}_password`; export namespace ConnectionManager { - export function getByName(name: string): StoredConnection | undefined { + export function getByHost(host: string, caseInsensitive = false): StoredConnection | undefined { const connections = getAll(); - const index = connections.findIndex(conn => conn.name === name); + const index = connections.findIndex(conn => caseInsensitive ? conn.host.toLowerCase() === host.toLowerCase() : conn.host === host); + if (index !== -1) { + return { index, data: connections[index] }; + } + } + + export function getByName(name: string, caseInsensitive = false): StoredConnection | undefined { + const connections = getAll(); + const index = connections.findIndex(conn => caseInsensitive ? conn.name.toLowerCase() === name.toLowerCase() : conn.name === name); if (index !== -1) { return { index, data: connections[index] }; } diff --git a/src/uri/index.ts b/src/uri/index.ts index 34a79218f..da27589d7 100644 --- a/src/uri/index.ts +++ b/src/uri/index.ts @@ -1,6 +1,6 @@ import querystring from "querystring"; import { commands, ExtensionContext, Uri, window } from "vscode"; -import { GlobalConfiguration } from "../api/Configuration"; +import { ConnectionManager, GlobalConfiguration } from "../api/Configuration"; import { instance } from "../instantiate"; import { t } from "../locale"; import { ConnectionData } from "../typings"; @@ -16,10 +16,10 @@ export async function registerUriHandler(context: ExtensionContext) { switch (uri.path) { case '/open': - if (connection) { - if (queryData.path) { - if (queryData.host) { - const host = Array.isArray(queryData.host) ? queryData.host[0] : queryData.host; + if (queryData.path) { + if (queryData.host) { + const host = Array.isArray(queryData.host) ? queryData.host[0] : queryData.host; + if (connection) { if (host !== connection.currentHost) { const chosen = await window.showInformationMessage(t(`uriOpen.openError`), { detail: t(`uriOpen.hostMismatch`), @@ -30,6 +30,32 @@ export async function registerUriHandler(context: ExtensionContext) { return; } } + } else { + const connection = ConnectionManager.getByHost(host, true) || ConnectionManager.getByName(host, true); + if (connection) { + let password = await ConnectionManager.getStoredPassword(context, connection.data.name); + + if (!password) { + password = await window.showInputBox({ + password: true, + title: t(`sandbox.input.password.title`), + prompt: t(`sandbox.input.password.prompt`, connection.data.username, connection.data.host) + }); + } + + const connected = await commands.executeCommand(`code-for-ibmi.connectDirect`, { + ...connection.data, + password + }); + + if (!connected) { + window.showWarningMessage(t(`uriOpen.noConnection`)); + return; + }; + } else { + window.showWarningMessage(t(`uriOpen.noConnection`)); + return; + } } const paths = Array.isArray(queryData.path) ? queryData.path : [queryData.path]; @@ -88,21 +114,15 @@ export async function registerUriHandler(context: ExtensionContext) { await initialSandboxSetup(connectionData.username); if (save) { - let existingConnections: ConnectionData[] | undefined = GlobalConfiguration.get(`connections`); - - if (existingConnections) { - const existingConnection = existingConnections.find(item => item.name === host); + const existingConnection = ConnectionManager.getByHost(host); - if (!existingConnection) { - // New connection! - existingConnections.push({ - ...connectionData, - password: undefined, // Removes the password from the object - }); + if (!existingConnection) { + await ConnectionManager.storeNew({ + ...connectionData, + password: undefined, // Removes the password from the object + }); - await context.secrets.store(`${host}_password`, pass); - await GlobalConfiguration.set(`connections`, existingConnections); - } + await ConnectionManager.setStoredPassword(context, host, pass); } }