Skip to content

Commit

Permalink
Merge pull request #5 from urban-1/master
Browse files Browse the repository at this point in the history
Multiple Fixes - working on 1.12.7
  • Loading branch information
duxet authored Feb 26, 2017
2 parents 6acc2eb + 9f6400d commit cf8858d
Show file tree
Hide file tree
Showing 12 changed files with 288 additions and 52 deletions.
2 changes: 2 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@
- [x] When deserializing localFiles, check to see if the file is deleted and if so remove it.
- [x] Display more information in "Show downloaded files"
- [x] Store when files are downloaded
- [ ] Handle SFTP "expected" errors like PERMISSION_DENIED when navigating
- [ ] Add a "Remote File Modified" warning based on timestamp
8 changes: 6 additions & 2 deletions lib/main.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ _ = require 'underscore-plus'
# Import needed to register deserializer
RemoteEditEditor = require './model/remote-edit-editor'



# Deferred requirements
OpenFilesView = null
HostView = null
Expand Down Expand Up @@ -99,6 +101,8 @@ module.exports =
atom.commands.add('atom-workspace', 'remote-edit:create-file', => @createFilesView().createFile())
atom.commands.add('atom-workspace', 'remote-edit:rename-folder-file', => @createFilesView().renameFolderFile())
atom.commands.add('atom-workspace', 'remote-edit:remove-folder-file', => @createFilesView().deleteFolderFile())
atom.commands.add('atom-workspace', 'remote-edit:cut-folder-file', => @createFilesView().copycutFolderFile(true))
atom.commands.add('atom-workspace', 'remote-edit:paste-folder-file', => @createFilesView().pasteFolderFile())

deactivate: ->
@ipdw?.destroy()
Expand Down Expand Up @@ -134,7 +138,7 @@ module.exports =
@filesView

initializeIpdwIfNecessary: ->
if atom.config.get 'remote-edit.notifications'
if atom.config.get 'remote-edit2.notifications'
stop = false
for editor in atom.workspace.getTextEditors() when !stop
if editor instanceof RemoteEditEditor
Expand All @@ -145,7 +149,7 @@ module.exports =
if @ipdw is undefined
InterProcessDataWatcher ?= require './model/inter-process-data-watcher'
fs = require 'fs-plus'
@ipdw = new InterProcessDataWatcher(fs.absolute(atom.config.get('remote-edit.defaultSerializePath')))
@ipdw = new InterProcessDataWatcher(fs.absolute(atom.config.get('remote-edit2.defaultSerializePath')))
else
@ipdw

Expand Down
7 changes: 4 additions & 3 deletions lib/model/ftp-host.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ module.exports =
####################
# Overridden methods
getConnectionString: (connectionOptions) ->
if atom.config.get('remote-edit.storePasswordsUsingKeytar') and (keytar?)
if atom.config.get('remote-edit2.storePasswordsUsingKeytar') and (keytar?)
keytarPassword = keytar.getPassword(@getServiceNamePassword(), @getServiceAccount())
_.extend({host: @hostname, port: @port, user: @username, password: keytarPassword}, connectionOptions)
else
Expand Down Expand Up @@ -110,7 +110,7 @@ module.exports =
async.filter(objects, ((item, callback) -> callback(item?)), ((result) -> callback(null, result)))
(objects, callback) ->
objects.push(new RemoteFile((path + "/.."), false, true, false, null, null, null))
if atom.config.get 'remote-edit.showHiddenFiles'
if atom.config.get 'remote-edit2.showHiddenFiles'
callback(null, objects)
else
async.filter(objects, ((item, callback) -> item.isHidden(callback)), ((result) -> callback(null, result)))
Expand Down Expand Up @@ -165,14 +165,15 @@ module.exports =
@port
localFiles: localFile.serialize() for localFile in @localFiles
@usePassword
@password
password: new Buffer(@password).toString("base64")
@lastOpenDirectory
}

deserializeParams: (params) ->
tmpArray = []
tmpArray.push(LocalFile.deserialize(localFile, host: this)) for localFile in params.localFiles
params.localFiles = tmpArray
params.password = new Buffer(params.password, "base64").toString("utf8")
params

createFolder: (folderpath, callback) ->
Expand Down
4 changes: 2 additions & 2 deletions lib/model/host.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ module.exports =
constructor: (@alias = null, @hostname, @directory = "/", @username = osenv.user(), @port, @localFiles = [], @usePassword, @lastOpenDirectory) ->
@emitter = new Emitter
@searchKey = @hostname
atom.config.observe "remote-edit.filterHostsUsing", (settings) =>
atom.config.observe 'remote-edit2.filterHostsUsing', (settings) =>
@searchKey = @getSearchKey(settings) ? @searchKey

if atom.config.get 'remote-edit.clearFileList'
if atom.config.get 'remote-edit2.clearFileList'
_.each(@localFiles, (val) =>
@removeLocalFile(val)
)
Expand Down
4 changes: 2 additions & 2 deletions lib/model/inter-process-data.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ module.exports =
for host in @hostList
@addSubscriptionToHost(host)

if atom.config.get 'remote-edit.notifications'
if atom.config.get 'remote-edit2.notifications'
RemoteEditEditor ?= require '../model/remote-edit-editor'

@disposables.add atom.workspace.observeTextEditors((editor) =>
Expand Down Expand Up @@ -69,7 +69,7 @@ module.exports =
@hostList = _.reject(@hostList, ((val) -> val == host))
@emitter.emit 'did-change'

if atom.config.get 'remote-edit.notifications'
if atom.config.get 'remote-edit2.notifications'
@disposables.add host.onInfo (info) => atom.notifications.add(info.type, info.message)

addNewHost: (host) ->
Expand Down
2 changes: 1 addition & 1 deletion lib/model/remote-edit-editor.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ module.exports =
@initiateUpload()

initiateUpload: ->
if atom.config.get 'remote-edit.uploadOnSave'
if atom.config.get 'remote-edit2.uploadOnSave'
@upload()
else
Dialog ?= require '../view/dialog'
Expand Down
149 changes: 139 additions & 10 deletions lib/model/sftp-host.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ module.exports =
protocol: "sftp"

constructor: (@alias = null, @hostname, @directory, @username, @port = "22", @localFiles = [], @usePassword = false, @useAgent = true, @usePrivateKey = false, @password, @passphrase, @privateKeyPath, @lastOpenDirectory) ->
# Default to /home/<username> which is the most common case...
if @directory == ""
@directory = "/home/#{@username}"

super( @alias, @hostname, @directory, @username, @port, @localFiles, @usePassword, @lastOpenDirectory)

getConnectionStringUsingAgent: ->
Expand All @@ -38,8 +42,8 @@ module.exports =
username: @username,
}

if atom.config.get('remote-edit.agentToUse') != 'Default'
_.extend(connectionString, {agent: atom.config.get('remote-edit.agentToUse')})
if atom.config.get('remote-edit2.agentToUse') != 'Default'
_.extend(connectionString, {agent: atom.config.get('remote-edit2.agentToUse')})
else if process.platform == "win32"
_.extend(connectionString, {agent: 'pageant'})
else
Expand All @@ -48,15 +52,15 @@ module.exports =
connectionString

getConnectionStringUsingKey: ->
if atom.config.get('remote-edit.storePasswordsUsingKeytar') and (keytar?)
if atom.config.get('remote-edit2.storePasswordsUsingKeytar') and (keytar?)
keytarPassphrase = keytar.getPassword(@getServiceNamePassphrase(), @getServiceAccount())
{host: @hostname, port: @port, username: @username, privateKey: @getPrivateKey(@privateKeyPath), passphrase: keytarPassphrase}
else
{host: @hostname, port: @port, username: @username, privateKey: @getPrivateKey(@privateKeyPath), passphrase: @passphrase}


getConnectionStringUsingPassword: ->
if atom.config.get('remote-edit.storePasswordsUsingKeytar') and (keytar?)
if atom.config.get('remote-edit2.storePasswordsUsingKeytar') and (keytar?)
keytarPassword = keytar.getPassword(@getServiceNamePassword(), @getServiceAccount())
{host: @hostname, port: @port, username: @username, password: keytarPassword}
else
Expand Down Expand Up @@ -113,6 +117,7 @@ module.exports =
else
callback(null)
(callback) =>
console.debug "Real Host Connect..."
@connection = new ssh2()
@connection.on 'error', (err) =>
@emitter.emit 'info', {message: "Error occured when connecting to sftp://#{@username}@#{@hostname}:#{@port}", type: 'error'}
Expand All @@ -127,29 +132,35 @@ module.exports =
)

isConnected: ->
@connection? and @connection._state == 'authenticated'
@connection? and @connection._sock and @connection._sock.writable and @connection._sshstream and @connection._sshstream.writable

getFilesMetadata: (path, callback) ->
async.waterfall([
(callback) =>
@connection.sftp(callback)
(sftp, callback) ->
(sftp, callback) =>
# temp store in this so we can close it when we are done...
@tmp_sftp = sftp
sftp.readdir(path, callback)
(files, callback) =>
# ... and now we are done!
@tmp_sftp.end()
async.map(files, ((file, callback) => callback(null, @createRemoteFileFromFile(path, file))), callback)
(objects, callback) ->
objects.push(new RemoteFile((path + "/.."), false, true, false, null, null, null))
if atom.config.get 'remote-edit.showHiddenFiles'
if atom.config.get 'remote-edit2.showHiddenFiles'
callback(null, objects)
else
async.filter(objects, ((item, callback) -> item.isHidden(callback)), ((result) -> callback(null, result)))
], (err, result) =>
if err?
@emitter.emit('info', {message: "Error occured when reading remote directory sftp://#{@username}@#{@hostname}:#{@port}:#{path}", type: 'error'} )
console.error err if err?
console.error err
console.error err.code
callback?(err)
else
callback?(err, (result.sort (a, b) -> return if a.name.toLowerCase() >= b.name.toLowerCase() then 1 else -1))

)

getFile: (localFile, callback) ->
Expand All @@ -162,6 +173,7 @@ module.exports =
], (err, sftp) =>
@emitter.emit('info', {message: "Error when reading remote file sftp://#{@username}@#{@hostname}:#{@port}#{localFile.remoteFile.path}", type: 'error'}) if err?
@emitter.emit('info', {message: "Successfully read remote file sftp://#{@username}@#{@hostname}:#{@port}#{localFile.remoteFile.path}", type: 'success'}) if !err?
sftp?.end()
callback?(err, localFile)
)

Expand All @@ -171,18 +183,23 @@ module.exports =
(callback) =>
@connection.sftp(callback)
(sftp, callback) ->
@tmp_sftp = sftp
sftp.fastPut(localFile.path, localFile.remoteFile.path, callback)
(callback) ->
@tmp_sftp.end()
callback()
], (err) =>
if err?
@emitter.emit('info', {message: "Error occured when writing remote file sftp://#{@username}@#{@hostname}:#{@port}#{localFile.remoteFile.path}", type: 'error'})
console.error err if err?
else
@emitter.emit('info', {message: "Successfully wrote remote file sftp://#{@username}@#{@hostname}:#{@port}#{localFile.remoteFile.path}", type: 'success'})
@close()

callback?(err)
)

serializeParams: ->
tmp = if @password then @password else ""
{
@alias
@hostname
Expand All @@ -193,7 +210,7 @@ module.exports =
@useAgent
@usePrivateKey
@usePassword
@password
password: new Buffer(tmp).toString("base64")
@passphrase
@privateKeyPath
@lastOpenDirectory
Expand All @@ -203,4 +220,116 @@ module.exports =
tmpArray = []
tmpArray.push(LocalFile.deserialize(localFile, host: this)) for localFile in params.localFiles
params.localFiles = tmpArray
params.password = new Buffer(params.password, "base64").toString("utf8")
params

# Create the folder and call the callback. The callback will be called
# for both erroe cases (1st arg) and success (2nd arg is the path)
createFolder: (folderpath, callback) ->
@emitter.emit 'info', {message: "Creating remote directory at sftp://#{@username}@#{@hostname}:#{@port}#{folderpath}", type: 'info'}
async.waterfall([
(callback) =>
@connection.sftp(callback)
(sftp, callback) ->
sftp.mkdir(folderpath, callback)
sftp.end()
callback(null, folderpath)
], (err) =>
if err?
@emitter.emit('info', {message: "Error occured while creating remote directory sftp://#{@username}@#{@hostname}:#{@port}#{folderpath}", type: 'error'})
console.error err if err?
else
@emitter.emit('info', {message: "Successfully created directory sftp://#{@username}@#{@hostname}:#{@port}#{folderpath}", type: 'success'})
callback(err)
)


createFile: (filepath, callback) ->
@emitter.emit 'info', {message: "Creating remote file at sftp://#{@username}@#{@hostname}:#{@port}#{filepath}", type: 'info'}
async.waterfall([
(callback) =>
@connection.sftp(callback)
(sftp, callback) =>
@tmp_sftp = sftp
sftp.exists(filepath, callback)
(callback) =>
@tmp_sftp.writeFile(filepath, "", callback)
(callback) =>
@tmp_sftp.end()
callback()
], (err) =>
if err?
if err == true
@emitter.emit('info', {message: "Fle ftp://#{@username}@#{@hostname}:#{@port}#{filepath} already exists", type: 'error'})
else
@emitter.emit('info', {message: "Error occurred while creating remote file ftp://#{@username}@#{@hostname}:#{@port}#{filepath}", type: 'error'})
console.error err if err?
else
@emitter.emit('info', {message: "Successfully wrote remote file ftp://#{@username}@#{@hostname}:#{@port}#{filepath}", type: 'success'})
callback?(err)
)


deleteFolderFile: (deletepath, isFolder, callback) ->
async.waterfall([
(callback) =>
@connection.sftp(callback)
(sftp, callback) =>
@tmp_sftp = sftp
if isFolder
sftp.rmdir(deletepath, callback)
else
sftp.unlink(deletepath, callback)
(callback) =>
@tmp_sftp.end()
callback(null)
], (err) =>
if err?
@emitter.emit('info', {message: "Error occurred when deleting remote folder/file sftp://#{@username}@#{@hostname}:#{@port}#{deletepath}", type: 'error'})
console.error err if err?
else
@emitter.emit('info', {message: "Successfully deleted remote folder/file sftp://#{@username}@#{@hostname}:#{@port}#{deletepath}", type: 'success'})
callback?(err)
)

# Rename jsut constructs paths and calls moveFolderFile which is more generic
renameFolderFile: (path, oldName, newName, isFolder, callback) =>
if oldName == newName
@emitter.emit('info', {message: "The new name is same as the old", type: 'error'})
return
oldPath = path + "/" + oldName
newPath = path + "/" + newName
@moveFolderFile(oldPath, newPath, isFolder, callback)

moveFolderFile: (oldPath, newPath, isFolder, callback) ->
async.waterfall([
(callback) =>
@connection.sftp(callback)
(sftp, callback) =>
@tmp_sftp = sftp
if isFolder
sftp.readdir(newPath, callback)
else
sftp.exists(newPath, callback)
], (err, result) =>
console.log result
if (isFolder and result != undefined) or (!isFolder and err==true)
@emitter.emit('info', {message: "#{if isFolder then 'Folder' else 'File'} already exists", type: 'error'})
@tmp_sftp.end()
return

async.waterfall([
(callback) =>
@tmp_sftp.rename(oldPath, newPath, callback)
(callback) =>
@tmp_sftp.end()
callback()
], (err) =>
if err?
@emitter.emit('info', {message: "Error occurred when renaming remote folder/file sftp://#{@username}@#{@hostname}:#{@port}#{oldPath}", type: 'error'})
console.error err if err?
else
@emitter.emit('info', {message: "Successfully renamed remote folder/file sftp://#{@username}@#{@hostname}:#{@port}#{oldPath}", type: 'success'})
callback?(err)
)
)
Loading

0 comments on commit cf8858d

Please sign in to comment.