Skip to content
This repository has been archived by the owner on Oct 2, 2022. It is now read-only.

Commit

Permalink
Merge pull request #10 from dsone/patch-12.0
Browse files Browse the repository at this point in the history
Patch 13.0
  • Loading branch information
dsone authored Feb 14, 2021
2 parents b40465e + 8bec078 commit ac4e6a5
Show file tree
Hide file tree
Showing 5 changed files with 196 additions and 106 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## 0.13.0 - Bugfix and refactoring Release
* New notifications here and there instead of silent `console.error`s that no one sees
* Scanning project folder on startup is only done if enabled in settings and if global project folder setting is non-empty, instead of failing silently
* Enabling project folder starts scanning immediately only if the global project folder is non-empty as well (ie. was set beforehand), otherwise you need to trigger scanning after setting the project folders via `CTRL+ALT+D` manually
* Fixed an issue where a project folder like `C:/AtomProjects/superproject/subproject` itself wasn't included in the scanning, only its subfolders, contradicting the description in the README
* [https://github.com/dsone/remote-ssh/issues/9](#9) Fixed issue with SSH keys inside PUTTY, when `pass` inside .ftpconfig was rightfully not set
* [https://github.com/dsone/remote-ssh/issues/9](#4) Similar to #9 but for non-Win32 platforms when password isn't set

## 0.12.0
* Skipped - because `apm publish minor` doesn't increase what is available on atom.io but what is saved inside package.json :)

## 0.11.0 - Feature Release
* Rearranged options, forcing a specific order in settings view
* Added new (opt-in) command to scan ftpconfig files and a search function, available via CTRL+ALT+S and CTRL+ALT+D
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ A present non-empty `session` takes precedence over any other option in the conf

## Remote SSH specific options in .ftpconfig
There is support for two new entries available inside .ftpconfig:
`rs_name`: String, an alias for the project. Should be unique for quick searches.
`rs_name`: String, an alias title for the project. Should be unique for quick searches. If not set `host` is used as fallback if possible.
`rs_tags`: Array of strings, tags for a project. Encouraged to be shared by multiple projects.

These two options are entirely optional to use. They only take effect when you check the setting `Scan for ftpconfig files` (opt-in) and set the appropriate project folders all your Atom projects reside in.
Expand Down
230 changes: 144 additions & 86 deletions lib/remote-ssh.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ const RemoteSSH = {
}));

this.scanning = false;
this.scanProjectFolder()
.then((items) => this.resolveScanning(items))
.catch((e) => this.rejectScanning(e))
if (typeof(this.settings.globalProjectFolder) !== 'undefined' && this.settings.globalProjectFolder.length > 0) {
this.scanProjectFolder()
.then((items) => this.resolveScanning(items))
.catch((e) => this.rejectScanning(e))
}
},

resolveScanning(items) {
Expand All @@ -58,8 +60,8 @@ const RemoteSSH = {
console.timeEnd('Remote SSH - Project scanning');
},
rejectScanning(error) {
this.showNotification('Remote SSH: Scanning projects', { detail: error }, 'error');
this.scanning = false;
console.error(error);
console.timeEnd('Remote SSH - Project scanning');
},

Expand All @@ -78,7 +80,7 @@ const RemoteSSH = {

// Let's load and map available config, to force sorting they have a leading \d_
let _sets = atom.config.get('Remote-SSH');
Object.keys(_sets).map( (e) => {
Object.keys(_sets).map(e => {
_this.settings[ e.replace(/^\d+?_/, '') ] = _sets[e];
});

Expand Down Expand Up @@ -106,7 +108,7 @@ const RemoteSSH = {
_this.delayTimer = undefined;
atom.config.observe('Remote-SSH.30_scanConfig', function(_b) {
_this.settings.scanConfig = _b;
if (_b && !_this.scanning) {
if (_b && !_this.scanning && typeof(_this.settings.globalProjectFolder) !== 'undefined' && _this.settings.globalProjectFolder.length > 0) {
_this.scanProjectFolder()
.then((items) => _this.resolveScanning(items))
.catch((e) => _this.rejectScanning(e));
Expand Down Expand Up @@ -140,16 +142,18 @@ const RemoteSSH = {
if (this.scanning) { return reject('Scanning already in progress'); }
else if (typeof(this.settings.globalProjectFolder) === 'undefined' || this.settings.globalProjectFolder.length === 0) {
this.scanning = false;
return reject('Scanning aborted, project folder invalid');
return reject('Scanning aborted, project folder not set in settings');
}

this.scanning = true;
let _si = []; // temp var for items
let startPaths = this.settings.globalProjectFolder.split(',');
startPaths.map((filePath) => {
startPaths.map(filePath => {
try {
filePath = filePath.trim();
if (fs.existsSync(filePath)) {
var files = fs.readdirSync(filePath);
files.unshift('./'); // to include "C:/AtomProjects/superproject/subproject" itself in the lookup, too
for (let i = 0 ; i < files.length; ++i) {
var filename = Path.resolve(filePath, files[i]);
var stat = fs.lstatSync(filename);
Expand All @@ -166,7 +170,7 @@ const RemoteSSH = {
}

let item = {
projectName: json.rs_name,
projectName: json.rs_name || json.host,
folderName: files[i],
host: json.host || undefined,
tags: [],
Expand Down Expand Up @@ -203,81 +207,133 @@ const RemoteSSH = {
this.searchModal.destroy();
},

/**
* Displays a notification on screen.
*
* @param string title The title of the notification to display
* @param json options Options literal
* @param string notifyType The type of notification, optional, default is `info`, supports `error|fatalError|info|success|warning`
* @return void
*/
showNotification(title, options = {}, notifyType = 'info') {
try {
let type = `add${ notifyType.charAt(0).toUpperCase() }${ notifyType.slice(1) }`;
if (typeof(atom.notifications[type]) !== 'undefined') {
atom.notifications[type](title, options);
} else {
atom.notifications.addWarning(title, { dismissible: true, ...options });
}
} catch (e) {
console.error(e);
atom.notifications.addWarning(title, { dismissible: true });
}
},

/**
* Shorthand function for getting values from JSON literals with fallacks for undefined values.
*
* @param json json The json to look for a key in to return
* @param string withKey A key inside json to return
* @param string withDefaultValue Optional default value to return if withKey is undefined, if not set undefined is used
* @param bool whileEmptyStringAllowed Optional setting to allow empty strings, default is allowed (true)
* @return mixed Any kind of datatype inside json
*/
getValueFrom(json, withKey, withDefaultValue = undefined, whileEmptyStringAllowed = true) {
let type = typeof(json[withKey]);
if (type !== 'undefined') {
if (type === 'string') {
if (type.length === 0 && !whileEmptyStringAllowed) {
return withDefaultValue;
}
}
return json[withKey];
}
return withDefaultValue;
},

startSSH(file, name) {
let filePath = this.getFilePath((file || './.ftpconfig'));
if (filePath != null) {
try {
if (existsSync(filePath)) {
readFile(filePath, 'utf8', (err, res) => {
if (err) { return err; }
if (existsSync(filePath)) {
readFile(filePath, 'utf8', (err, res) => {
if (err) { return err; }

const data = stripJsonComments(res);
let json;
try {
json = JSON.parse(data);
} catch (e) {
this.showNotification('Remote SSH: Error loading .ftpconfig', { detail: e }, 'error');
return;
}

let notificationTitle = "Remote SSH: Starting SSH client";
let notificationDetail = "";
if (name) {
notificationDetail = name;
} else {
notificationDetail = this.getValueFrom(
json, 'rs_name',
this.getValueFrom(
json, 'host', 'Undefined host', false
),
false
);
}

const data = stripJsonComments(res);
let json;
let jsonUser = this.getValueFrom(json, 'user');
let jsonHost = this.getValueFrom(json, 'host');
if (!jsonUser || !jsonHost) {
this.showNotification(notificationTitle, { detail: 'User or hostname not set, cannot start' }, 'error');
return;
}
let jsonPassword = this.getValueFrom(json, 'pass', '');

this.showNotification(notificationTitle, { detail: notificationDetail });

if (!process.platform == "win32" || this.settings.useSSH) {
try {
json = JSON.parse(data);
spawn('ssh', [ `${ jsonUser }@${ jsonHost }` ], { detached: true, shell: true });
atom.clipboard.write(jsonPassword);
} catch (e) {
atom.notifications.addError('Remote SSH: Error loading .ftpconfig',
{ dismissible: true, detail: e });
return;
this.showNotification(notificationTitle, { detail: e }, 'error');
}

let n_str = "Remote SSH: Starting SSH client";
let detail = "";
if (name) {
detail = name;
} else {
if (typeof(json.rs_name) === 'string' && json.rs_name.length > 0) {
detail = json.rs_name;
} else {
detail = json.host;
}
}
if (process.platform == "win32") {
if (this.settings.useSSH) {
atom.notifications.addInfo(n_str, { dismissible: true, detail: detail });
spawn('ssh', [ json.user + '@' + json.host ], { detached: true, shell: true });
atom.clipboard.write(json.pass);
return;
}
let proc = [
'putty',
'-ssh',
'-2'
];
if (typeof(json.session) !== "undefined" && json.session.length > 0) {
proc.push("-load");
proc.push('"' + json.session + '"');

if (this.settings.copyPassword) {
atom.clipboard.write(json.pass);
} else {
proc.push("-pw");
proc.push('"' + json.pass + '"');
}
} else {
if (this.settings.copyPassword) {
atom.clipboard.write(json.pass);
} else {
proc.push("-pw");
proc.push('"' + json.pass + '"');
}
proc.push("-l");
proc.push('"' + json.user + '"');
proc.push("-P");
proc.push('"' + json.port + '"');
proc.push(json.host);
}
atom.notifications.addInfo(n_str, { dismissible: true, detail: detail });
exec(proc.join(' '));
return;
}

let proc = [
'putty',
'-ssh',
'-2'
];
if (typeof(json.pass) !== 'undefined') {
if (this.settings.copyPassword) {
atom.clipboard.write(jsonPassword);
} else {
atom.notifications.addInfo(n_str, { dismissible: true, detail: detail });
spawn('ssh', [ json.user + '@' + json.host ], { detached: true, shell: true });
atom.clipboard.write(json.pass);
proc.push(`-pw "${ jsonPassword }"`);
}
});
}
} catch (e) {
atom.notifications.addWarning("Remote SSH: Couldn't connect", { dismissible: true, detail: e });
}

let jsonSession = this.getValueFrom(json, 'session', undefined, false);
// Using saved session inside Putty - might include SSH key usage
if (jsonSession) {
proc.push(`-load "${ jsonSession }"`);
// manually building putty connection
} else {
let jsonPort = this.getValueFrom(json, 'port', 22);

proc.push(`-l "${ jsonUser }"`);
proc.push(`-P "${ jsonPort }"`);
proc.push(jsonHost);
}

try {
exec(proc.join(' '));
} catch (e) {
this.showNotification(notificationTitle, { detail: e }, 'error');
}
});
}
}
},
Expand Down Expand Up @@ -331,12 +387,12 @@ const RemoteSSH = {
if (!err) {
atom.workspace.open(configPath);
} else {
atom.notifications.addError("Remote SSH: Writing config file failed", { dismissible: true });
this.showNotification('Remote SSH: Writing config file failed', {}, 'error');
}
});
}
} catch (e) {
atom.notifications.addWarning("Remote SSH: Couldn't write config file", { dismissible: true, detail: e });
this.showNotification("Remote SSH: Couldn't write config file", { detail: e }, 'warning');
}
},

Expand All @@ -347,8 +403,9 @@ const RemoteSSH = {
if (projectPath === false) { return false; }
return Path.resolve(projectPath, relativePath);
} catch (e) {
atom.notifications.addError("Remote SSH: getFilePath failed", { dismissible: true, detail: e });
return "";
this.showNotification('Remote SSH: getFilePath failed', { detail: e }, 'error');

return null;
}
},

Expand All @@ -364,12 +421,12 @@ const RemoteSSH = {
self.projectPath = projectPath;
return projectPath;
}
atom.notifications.addError('Remote SSH: Could not load project path', {
dismissible: true,
});

this.showNotification("Remote SSH: Could not load project path", {}, 'error');
return false;
} catch (e) {
atom.notifications.addError("Remote SSH: Couldn't get project Path", { dismissible: true, detail: e });
this.showNotification("Remote SSH: Couldn't get project Path", { detail: e }, 'error');

return false;
}
},
Expand All @@ -388,12 +445,12 @@ const RemoteSSH = {
if (forceHide || this.searchModal.isVisible()) {
this.searchModal.hide();
} else {

if (!this.settings.scanConfig) {
atom.notifications.addInfo('Remote SSH: To use this feature you need to enable the project folder scan setting', { dismissible: true });
this.showNotification('Remote SSH', { detail: 'To use this feature you need to enable the project folder scan setting' }, 'info');
return;
} else if (this.settings.globalProjectFolder.length === 0) {
atom.notifications.addInfo('Remote SSH: You need to set your global project folder to use this', { dismissible: true });
}
if (this.settings.globalProjectFolder.length === 0) {
this.showNotification('Remote SSH', { detail: 'You need to set your global project folder to use this' }, 'info');
return;
}

Expand All @@ -402,6 +459,7 @@ const RemoteSSH = {
// delay focus of input or opening the modal with ctrl + shift + p will propagate enter to the input
setTimeout(() => this.searchView.selectList.focusFilterEditor(), 100);
}

return true;
},
};
Expand Down
Loading

0 comments on commit ac4e6a5

Please sign in to comment.