From 0105d6baf2a68f19bd9f54de30acae9b7e26f227 Mon Sep 17 00:00:00 2001 From: Cyril Damas Date: Mon, 12 Feb 2018 20:20:45 +0000 Subject: [PATCH] Add ssh url scheme handling (#2516) --- app/config/config-default.js | 3 ++ app/index.js | 25 +++++++++++++++ app/package.json | 1 + app/yarn.lock | 62 +++++++++++++++++++++++++++++++++++- lib/actions/ui.js | 26 +++++++++++++++ lib/constants/ui.js | 1 + lib/index.js | 4 +++ package.json | 5 +++ yarn.lock | 28 +++++++++++++++- 9 files changed, 153 insertions(+), 2 deletions(-) diff --git a/app/config/config-default.js b/app/config/config-default.js index 62779606..6f335336 100644 --- a/app/config/config-default.js +++ b/app/config/config-default.js @@ -107,6 +107,9 @@ module.exports = { // if `true` (without backticks and without quotes), selected text will automatically be copied to the clipboard copyOnSelect: false, + // if `true` (without backticks and without quotes), hyper will be set as the default protocol client for SSH + defaultSSHApp: true, + // if `true` (without backticks and without quotes), on right click selected text will be copied or pasted if no // selection is present (`true` by default on Windows and disables the context menu feature) // quickEdit: true, diff --git a/app/index.js b/app/index.js index 68f23a2c..63ce56db 100644 --- a/app/index.js +++ b/app/index.js @@ -180,6 +180,17 @@ app.on('ready', () => // expose to plugins app.createWindow = createWindow; + // check if should be set/removed as default ssh protocol client + if (config.getConfig().defaultSSHApp && !app.isDefaultProtocolClient('ssh')) { + //eslint-disable-next-line no-console + console.log('Setting Hyper as default client for ssh:// protocol'); + app.setAsDefaultProtocolClient('ssh'); + } else if (!config.getConfig().defaultSSHApp && app.isDefaultProtocolClient('ssh')) { + //eslint-disable-next-line no-console + console.log('Removing Hyper from default client for ssh:// protocl'); + app.removeAsDefaultProtocolClient('ssh'); + } + // mac only. when the dock icon is clicked // and we don't have any active windows open, // we open one @@ -233,6 +244,20 @@ app.on('open-file', (event, path) => { } }); +app.on('open-url', (event, sshUrl) => { + const lastWindow = app.getLastFocusedWindow(); + const callback = win => win.rpc.emit('open ssh', sshUrl); + if (lastWindow) { + callback(lastWindow); + } else if (!lastWindow && {}.hasOwnProperty.call(app, 'createWindow')) { + app.createWindow(callback); + } else { + // If createWindow doesn't exist yet ('ready' event was not fired), + // sets his callback to an app.windowCallback property. + app.windowCallback = callback; + } +}); + function installDevExtensions(isDev_) { if (!isDev_) { return Promise.resolve(); diff --git a/app/package.json b/app/package.json index 2d05fe39..90e7cad7 100644 --- a/app/package.json +++ b/app/package.json @@ -24,6 +24,7 @@ "ms": "2.1.1", "node-fetch": "1.7.3", "node-pty": "0.7.4", + "parse-url": "3.0.2", "queue": "4.4.2", "semver": "5.4.1", "shell-env": "0.3.0", diff --git a/app/yarn.lock b/app/yarn.lock index cffdf0ff..11259d51 100644 --- a/app/yarn.lock +++ b/app/yarn.lock @@ -177,6 +177,16 @@ is-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" +is-plain-obj@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + +is-ssh@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.0.tgz#ebea1169a2614da392a63740366c3ce049d8dff6" + dependencies: + protocols "^1.1.0" + is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -244,13 +254,22 @@ node-pty@0.7.4: dependencies: nan "^2.6.2" +normalize-url@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" + dependencies: + object-assign "^4.0.1" + prepend-http "^1.0.0" + query-string "^4.1.0" + sort-keys "^1.0.0" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" dependencies: path-key "^2.0.0" -object-assign@^4.0.1: +object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -268,6 +287,22 @@ p-locate@^2.0.0: dependencies: p-limit "^1.1.0" +parse-path@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-3.0.2.tgz#4686980f0b262ee2dbb9a64eef739c91edc85245" + dependencies: + is-ssh "^1.3.0" + protocols "^1.4.0" + +parse-url@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-3.0.2.tgz#602787a7063a795d72b8673197505e72f60610be" + dependencies: + is-ssh "^1.3.0" + normalize-url "^1.9.1" + parse-path "^3.0.1" + protocols "^1.4.0" + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -296,10 +331,25 @@ pkg-up@^2.0.0: dependencies: find-up "^2.1.0" +prepend-http@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + +protocols@^1.1.0, protocols@^1.4.0: + version "1.4.6" + resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.6.tgz#f8bb263ea1b5fd7a7604d26b8be39bd77678bf8a" + pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" +query-string@^4.1.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" + dependencies: + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + queue@4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/queue/-/queue-4.4.2.tgz#5a9733d9a8b8bd1b36e934bc9c55ab89b28e29c7" @@ -332,6 +382,16 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + dependencies: + is-plain-obj "^1.0.0" + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + strip-ansi@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" diff --git a/lib/actions/ui.js b/lib/actions/ui.js index 118fc2e3..67f6a4e4 100644 --- a/lib/actions/ui.js +++ b/lib/actions/ui.js @@ -20,11 +20,13 @@ import { UI_WINDOW_GEOMETRY_CHANGED, UI_WINDOW_MOVE, UI_OPEN_FILE, + UI_OPEN_SSH_URL, UI_CONTEXTMENU_OPEN, UI_COMMAND_EXEC } from '../constants/ui'; import {setActiveGroup} from './term-groups'; +import parseUrl from 'parse-url'; const {stat} = window.require('fs'); @@ -279,6 +281,30 @@ export function openFile(path) { }; } +export function openSSH(url) { + return dispatch => { + dispatch({ + type: UI_OPEN_SSH_URL, + effect() { + let parsedUrl = parseUrl(url, true); + let command = parsedUrl.protocol + ' ' + (parsedUrl.user || '') + '@' + parsedUrl.resource; + + if (parsedUrl.port) command += ' -p ' + parsedUrl.port; + + command += '\n'; + + rpc.once('session add', ({uid}) => { + rpc.once('session data', () => { + dispatch(sendSessionData(uid, command)); + }); + }); + + dispatch(requestSession()); + } + }); + }; +} + export function execCommand(command, fn, e) { return dispatch => dispatch({ diff --git a/lib/constants/ui.js b/lib/constants/ui.js index 47a4a1cb..b2d4d29a 100644 --- a/lib/constants/ui.js +++ b/lib/constants/ui.js @@ -14,6 +14,7 @@ export const UI_WINDOW_MAXIMIZE = 'UI_WINDOW_MAXIMIZE'; export const UI_WINDOW_UNMAXIMIZE = 'UI_WINDOW_UNMAXIMIZE'; export const UI_WINDOW_GEOMETRY_CHANGED = 'UI_WINDOW_GEOMETRY_CHANGED'; export const UI_OPEN_FILE = 'UI_OPEN_FILE'; +export const UI_OPEN_SSH_URL = 'UI_OPEN_SSH_URL'; export const UI_OPEN_HAMBURGER_MENU = 'UI_OPEN_HAMBURGER_MENU'; export const UI_WINDOW_MINIMIZE = 'UI_WINDOW_MINIMIZE'; export const UI_WINDOW_CLOSE = 'UI_WINDOW_CLOSE'; diff --git a/lib/index.js b/lib/index.js index 5dcd4df5..034523c8 100644 --- a/lib/index.js +++ b/lib/index.js @@ -151,6 +151,10 @@ rpc.on('open file', ({path}) => { store_.dispatch(uiActions.openFile(path)); }); +rpc.on('open ssh', url => { + store_.dispatch(uiActions.openSSH(url)); +}); + rpc.on('update available', ({releaseName, releaseNotes, releaseUrl, canInstall}) => { store_.dispatch(updaterActions.updateAvailable(releaseName, releaseNotes, releaseUrl, canInstall)); }); diff --git a/package.json b/package.json index a13b4448..00c18e19 100644 --- a/package.json +++ b/package.json @@ -154,6 +154,10 @@ }, "rpm": { "afterInstall": "./build/linux/after-install.tpl" + }, + "protocols": { + "name": "ssh URL", + "schemes": ["ssh"] } }, "license": "MIT", @@ -175,6 +179,7 @@ "npm-name": "3.1.0", "opn": "5.1.0", "ora": "1.3.0", + "parse-url": "3.0.2", "php-escape-shell": "1.0.0", "pify": "3.0.0", "react": "16.2.0", diff --git a/yarn.lock b/yarn.lock index 8f031f3b..dcf0f93d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3629,6 +3629,12 @@ is-retry-allowed@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" +is-ssh@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.0.tgz#ebea1169a2614da392a63740366c3ce049d8dff6" + dependencies: + protocols "^1.1.0" + is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -4352,7 +4358,7 @@ normalize-range@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" -normalize-url@^1.4.0: +normalize-url@^1.4.0, normalize-url@^1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" dependencies: @@ -4637,6 +4643,22 @@ parse-ms@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-1.0.1.tgz#56346d4749d78f23430ca0c713850aef91aa361d" +parse-path@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-3.0.2.tgz#4686980f0b262ee2dbb9a64eef739c91edc85245" + dependencies: + is-ssh "^1.3.0" + protocols "^1.4.0" + +parse-url@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-3.0.2.tgz#602787a7063a795d72b8673197505e72f60610be" + dependencies: + is-ssh "^1.3.0" + normalize-url "^1.9.1" + parse-path "^3.0.1" + protocols "^1.4.0" + path-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" @@ -5099,6 +5121,10 @@ prop-types@^15.5.10, prop-types@^15.6.0: loose-envify "^1.3.1" object-assign "^4.1.1" +protocols@^1.1.0, protocols@^1.4.0: + version "1.4.6" + resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.6.tgz#f8bb263ea1b5fd7a7604d26b8be39bd77678bf8a" + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"