Ausgabe der neuen DB Einträge
This commit is contained in:
parent
bad48e1627
commit
cfbbb9ee3d
2399 changed files with 843193 additions and 43 deletions
949
venv/lib/python3.9/site-packages/twisted/conch/scripts/cftp.py
Normal file
949
venv/lib/python3.9/site-packages/twisted/conch/scripts/cftp.py
Normal file
|
|
@ -0,0 +1,949 @@
|
|||
# -*- test-case-name: twisted.conch.test.test_cftp -*-
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Implementation module for the I{cftp} command.
|
||||
"""
|
||||
from __future__ import division, print_function
|
||||
import os, sys, getpass, struct, tty, fcntl, stat
|
||||
import fnmatch, pwd, glob
|
||||
|
||||
from twisted.conch.client import connect, default, options
|
||||
from twisted.conch.ssh import connection, common
|
||||
from twisted.conch.ssh import channel, filetransfer
|
||||
from twisted.protocols import basic
|
||||
from twisted.python.compat import _PY3, unicode
|
||||
from twisted.internet import reactor, stdio, defer, utils
|
||||
from twisted.python import log, usage, failure
|
||||
from twisted.python.filepath import FilePath
|
||||
|
||||
class ClientOptions(options.ConchOptions):
|
||||
|
||||
synopsis = """Usage: cftp [options] [user@]host
|
||||
cftp [options] [user@]host[:dir[/]]
|
||||
cftp [options] [user@]host[:file [localfile]]
|
||||
"""
|
||||
longdesc = ("cftp is a client for logging into a remote machine and "
|
||||
"executing commands to send and receive file information")
|
||||
|
||||
optParameters = [
|
||||
['buffersize', 'B', 32768, 'Size of the buffer to use for sending/receiving.'],
|
||||
['batchfile', 'b', None, 'File to read commands from, or \'-\' for stdin.'],
|
||||
['requests', 'R', 5, 'Number of requests to make before waiting for a reply.'],
|
||||
['subsystem', 's', 'sftp', 'Subsystem/server program to connect to.']]
|
||||
|
||||
compData = usage.Completions(
|
||||
descriptions={
|
||||
"buffersize": "Size of send/receive buffer (default: 32768)"},
|
||||
extraActions=[usage.CompleteUserAtHost(),
|
||||
usage.CompleteFiles(descr="local file")])
|
||||
|
||||
def parseArgs(self, host, localPath=None):
|
||||
self['remotePath'] = ''
|
||||
if ':' in host:
|
||||
host, self['remotePath'] = host.split(':', 1)
|
||||
self['remotePath'].rstrip('/')
|
||||
self['host'] = host
|
||||
self['localPath'] = localPath
|
||||
|
||||
def run():
|
||||
# import hotshot
|
||||
# prof = hotshot.Profile('cftp.prof')
|
||||
# prof.start()
|
||||
args = sys.argv[1:]
|
||||
if '-l' in args: # cvs is an idiot
|
||||
i = args.index('-l')
|
||||
args = args[i:i+2]+args
|
||||
del args[i+2:i+4]
|
||||
options = ClientOptions()
|
||||
try:
|
||||
options.parseOptions(args)
|
||||
except usage.UsageError as u:
|
||||
print('ERROR: %s' % u)
|
||||
sys.exit(1)
|
||||
if options['log']:
|
||||
realout = sys.stdout
|
||||
log.startLogging(sys.stderr)
|
||||
sys.stdout = realout
|
||||
else:
|
||||
log.discardLogs()
|
||||
doConnect(options)
|
||||
reactor.run()
|
||||
# prof.stop()
|
||||
# prof.close()
|
||||
|
||||
def handleError():
|
||||
global exitStatus
|
||||
exitStatus = 2
|
||||
try:
|
||||
reactor.stop()
|
||||
except: pass
|
||||
log.err(failure.Failure())
|
||||
raise
|
||||
|
||||
def doConnect(options):
|
||||
# log.deferr = handleError # HACK
|
||||
if '@' in options['host']:
|
||||
options['user'], options['host'] = options['host'].split('@',1)
|
||||
host = options['host']
|
||||
if not options['user']:
|
||||
options['user'] = getpass.getuser()
|
||||
if not options['port']:
|
||||
options['port'] = 22
|
||||
else:
|
||||
options['port'] = int(options['port'])
|
||||
host = options['host']
|
||||
port = options['port']
|
||||
conn = SSHConnection()
|
||||
conn.options = options
|
||||
vhk = default.verifyHostKey
|
||||
uao = default.SSHUserAuthClient(options['user'], options, conn)
|
||||
connect.connect(host, port, options, vhk, uao).addErrback(_ebExit)
|
||||
|
||||
def _ebExit(f):
|
||||
#global exitStatus
|
||||
if hasattr(f.value, 'value'):
|
||||
s = f.value.value
|
||||
else:
|
||||
s = str(f)
|
||||
print(s)
|
||||
#exitStatus = "conch: exiting with error %s" % f
|
||||
try:
|
||||
reactor.stop()
|
||||
except: pass
|
||||
|
||||
def _ignore(*args): pass
|
||||
|
||||
class FileWrapper:
|
||||
|
||||
def __init__(self, f):
|
||||
self.f = f
|
||||
self.total = 0.0
|
||||
f.seek(0, 2) # seek to the end
|
||||
self.size = f.tell()
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.f, attr)
|
||||
|
||||
class StdioClient(basic.LineReceiver):
|
||||
|
||||
_pwd = pwd
|
||||
|
||||
ps = 'cftp> '
|
||||
delimiter = b'\n'
|
||||
|
||||
reactor = reactor
|
||||
|
||||
def __init__(self, client, f = None):
|
||||
self.client = client
|
||||
self.currentDirectory = ''
|
||||
self.file = f
|
||||
self.useProgressBar = (not f and 1) or 0
|
||||
|
||||
def connectionMade(self):
|
||||
self.client.realPath('').addCallback(self._cbSetCurDir)
|
||||
|
||||
def _cbSetCurDir(self, path):
|
||||
self.currentDirectory = path
|
||||
self._newLine()
|
||||
|
||||
def _writeToTransport(self, msg):
|
||||
if isinstance(msg, unicode):
|
||||
msg = msg.encode("utf-8")
|
||||
return self.transport.write(msg)
|
||||
|
||||
def lineReceived(self, line):
|
||||
if self.client.transport.localClosed:
|
||||
return
|
||||
if _PY3 and isinstance(line, bytes):
|
||||
line = line.decode("utf-8")
|
||||
log.msg('got line %s' % line)
|
||||
line = line.lstrip()
|
||||
if not line:
|
||||
self._newLine()
|
||||
return
|
||||
if self.file and line.startswith('-'):
|
||||
self.ignoreErrors = 1
|
||||
line = line[1:]
|
||||
else:
|
||||
self.ignoreErrors = 0
|
||||
d = self._dispatchCommand(line)
|
||||
if d is not None:
|
||||
d.addCallback(self._cbCommand)
|
||||
d.addErrback(self._ebCommand)
|
||||
|
||||
|
||||
def _dispatchCommand(self, line):
|
||||
if ' ' in line:
|
||||
command, rest = line.split(' ', 1)
|
||||
rest = rest.lstrip()
|
||||
else:
|
||||
command, rest = line, ''
|
||||
if command.startswith('!'): # command
|
||||
f = self.cmd_EXEC
|
||||
rest = (command[1:] + ' ' + rest).strip()
|
||||
else:
|
||||
command = command.upper()
|
||||
log.msg('looking up cmd %s' % command)
|
||||
f = getattr(self, 'cmd_%s' % command, None)
|
||||
if f is not None:
|
||||
return defer.maybeDeferred(f, rest)
|
||||
else:
|
||||
errMsg = "No command called `%s'" % (command)
|
||||
self._ebCommand(failure.Failure(NotImplementedError(errMsg)))
|
||||
self._newLine()
|
||||
|
||||
def _printFailure(self, f):
|
||||
log.msg(f)
|
||||
e = f.trap(NotImplementedError, filetransfer.SFTPError, OSError, IOError)
|
||||
if e == NotImplementedError:
|
||||
self._writeToTransport(self.cmd_HELP(''))
|
||||
elif e == filetransfer.SFTPError:
|
||||
errMsg = "remote error %i: %s\n" % (f.value.code, f.value.message)
|
||||
self._writeToTransport(errMsg)
|
||||
elif e in (OSError, IOError):
|
||||
errMsg = "local error %i: %s\n" % (f.value.errno, f.value.strerror)
|
||||
self._writeToTransport(errMsg)
|
||||
|
||||
def _newLine(self):
|
||||
if self.client.transport.localClosed:
|
||||
return
|
||||
self._writeToTransport(self.ps)
|
||||
self.ignoreErrors = 0
|
||||
if self.file:
|
||||
l = self.file.readline()
|
||||
if not l:
|
||||
self.client.transport.loseConnection()
|
||||
else:
|
||||
self._writeToTransport(l)
|
||||
self.lineReceived(l.strip())
|
||||
|
||||
def _cbCommand(self, result):
|
||||
if result is not None:
|
||||
if isinstance(result, unicode):
|
||||
result = result.encode("utf-8")
|
||||
self._writeToTransport(result)
|
||||
if not result.endswith(b'\n'):
|
||||
self._writeToTransport(b'\n')
|
||||
self._newLine()
|
||||
|
||||
def _ebCommand(self, f):
|
||||
self._printFailure(f)
|
||||
if self.file and not self.ignoreErrors:
|
||||
self.client.transport.loseConnection()
|
||||
self._newLine()
|
||||
|
||||
def cmd_CD(self, path):
|
||||
path, rest = self._getFilename(path)
|
||||
if not path.endswith('/'):
|
||||
path += '/'
|
||||
newPath = path and os.path.join(self.currentDirectory, path) or ''
|
||||
d = self.client.openDirectory(newPath)
|
||||
d.addCallback(self._cbCd)
|
||||
d.addErrback(self._ebCommand)
|
||||
return d
|
||||
|
||||
def _cbCd(self, directory):
|
||||
directory.close()
|
||||
d = self.client.realPath(directory.name)
|
||||
d.addCallback(self._cbCurDir)
|
||||
return d
|
||||
|
||||
def _cbCurDir(self, path):
|
||||
self.currentDirectory = path
|
||||
|
||||
def cmd_CHGRP(self, rest):
|
||||
grp, rest = rest.split(None, 1)
|
||||
path, rest = self._getFilename(rest)
|
||||
grp = int(grp)
|
||||
d = self.client.getAttrs(path)
|
||||
d.addCallback(self._cbSetUsrGrp, path, grp=grp)
|
||||
return d
|
||||
|
||||
def cmd_CHMOD(self, rest):
|
||||
mod, rest = rest.split(None, 1)
|
||||
path, rest = self._getFilename(rest)
|
||||
mod = int(mod, 8)
|
||||
d = self.client.setAttrs(path, {'permissions':mod})
|
||||
d.addCallback(_ignore)
|
||||
return d
|
||||
|
||||
def cmd_CHOWN(self, rest):
|
||||
usr, rest = rest.split(None, 1)
|
||||
path, rest = self._getFilename(rest)
|
||||
usr = int(usr)
|
||||
d = self.client.getAttrs(path)
|
||||
d.addCallback(self._cbSetUsrGrp, path, usr=usr)
|
||||
return d
|
||||
|
||||
def _cbSetUsrGrp(self, attrs, path, usr=None, grp=None):
|
||||
new = {}
|
||||
new['uid'] = (usr is not None) and usr or attrs['uid']
|
||||
new['gid'] = (grp is not None) and grp or attrs['gid']
|
||||
d = self.client.setAttrs(path, new)
|
||||
d.addCallback(_ignore)
|
||||
return d
|
||||
|
||||
def cmd_GET(self, rest):
|
||||
remote, rest = self._getFilename(rest)
|
||||
if '*' in remote or '?' in remote: # wildcard
|
||||
if rest:
|
||||
local, rest = self._getFilename(rest)
|
||||
if not os.path.isdir(local):
|
||||
return "Wildcard get with non-directory target."
|
||||
else:
|
||||
local = b''
|
||||
d = self._remoteGlob(remote)
|
||||
d.addCallback(self._cbGetMultiple, local)
|
||||
return d
|
||||
if rest:
|
||||
local, rest = self._getFilename(rest)
|
||||
else:
|
||||
local = os.path.split(remote)[1]
|
||||
log.msg((remote, local))
|
||||
lf = open(local, 'wb', 0)
|
||||
path = FilePath(self.currentDirectory).child(remote)
|
||||
d = self.client.openFile(path.path, filetransfer.FXF_READ, {})
|
||||
d.addCallback(self._cbGetOpenFile, lf)
|
||||
d.addErrback(self._ebCloseLf, lf)
|
||||
return d
|
||||
|
||||
def _cbGetMultiple(self, files, local):
|
||||
#if self._useProgressBar: # one at a time
|
||||
# XXX this can be optimized for times w/o progress bar
|
||||
return self._cbGetMultipleNext(None, files, local)
|
||||
|
||||
def _cbGetMultipleNext(self, res, files, local):
|
||||
if isinstance(res, failure.Failure):
|
||||
self._printFailure(res)
|
||||
elif res:
|
||||
self._writeToTransport(res)
|
||||
if not res.endswith('\n'):
|
||||
self._writeToTransport('\n')
|
||||
if not files:
|
||||
return
|
||||
f = files.pop(0)[0]
|
||||
lf = open(os.path.join(local, os.path.split(f)[1]), 'wb', 0)
|
||||
path = FilePath(self.currentDirectory).child(f)
|
||||
d = self.client.openFile(path.path, filetransfer.FXF_READ, {})
|
||||
d.addCallback(self._cbGetOpenFile, lf)
|
||||
d.addErrback(self._ebCloseLf, lf)
|
||||
d.addBoth(self._cbGetMultipleNext, files, local)
|
||||
return d
|
||||
|
||||
def _ebCloseLf(self, f, lf):
|
||||
lf.close()
|
||||
return f
|
||||
|
||||
def _cbGetOpenFile(self, rf, lf):
|
||||
return rf.getAttrs().addCallback(self._cbGetFileSize, rf, lf)
|
||||
|
||||
def _cbGetFileSize(self, attrs, rf, lf):
|
||||
if not stat.S_ISREG(attrs['permissions']):
|
||||
rf.close()
|
||||
lf.close()
|
||||
return "Can't get non-regular file: %s" % rf.name
|
||||
rf.size = attrs['size']
|
||||
bufferSize = self.client.transport.conn.options['buffersize']
|
||||
numRequests = self.client.transport.conn.options['requests']
|
||||
rf.total = 0.0
|
||||
dList = []
|
||||
chunks = []
|
||||
startTime = self.reactor.seconds()
|
||||
for i in range(numRequests):
|
||||
d = self._cbGetRead('', rf, lf, chunks, 0, bufferSize, startTime)
|
||||
dList.append(d)
|
||||
dl = defer.DeferredList(dList, fireOnOneErrback=1)
|
||||
dl.addCallback(self._cbGetDone, rf, lf)
|
||||
return dl
|
||||
|
||||
def _getNextChunk(self, chunks):
|
||||
end = 0
|
||||
for chunk in chunks:
|
||||
if end == 'eof':
|
||||
return # nothing more to get
|
||||
if end != chunk[0]:
|
||||
i = chunks.index(chunk)
|
||||
chunks.insert(i, (end, chunk[0]))
|
||||
return (end, chunk[0] - end)
|
||||
end = chunk[1]
|
||||
bufSize = int(self.client.transport.conn.options['buffersize'])
|
||||
chunks.append((end, end + bufSize))
|
||||
return (end, bufSize)
|
||||
|
||||
def _cbGetRead(self, data, rf, lf, chunks, start, size, startTime):
|
||||
if data and isinstance(data, failure.Failure):
|
||||
log.msg('get read err: %s' % data)
|
||||
reason = data
|
||||
reason.trap(EOFError)
|
||||
i = chunks.index((start, start + size))
|
||||
del chunks[i]
|
||||
chunks.insert(i, (start, 'eof'))
|
||||
elif data:
|
||||
log.msg('get read data: %i' % len(data))
|
||||
lf.seek(start)
|
||||
lf.write(data)
|
||||
if len(data) != size:
|
||||
log.msg('got less than we asked for: %i < %i' %
|
||||
(len(data), size))
|
||||
i = chunks.index((start, start + size))
|
||||
del chunks[i]
|
||||
chunks.insert(i, (start, start + len(data)))
|
||||
rf.total += len(data)
|
||||
if self.useProgressBar:
|
||||
self._printProgressBar(rf, startTime)
|
||||
chunk = self._getNextChunk(chunks)
|
||||
if not chunk:
|
||||
return
|
||||
else:
|
||||
start, length = chunk
|
||||
log.msg('asking for %i -> %i' % (start, start+length))
|
||||
d = rf.readChunk(start, length)
|
||||
d.addBoth(self._cbGetRead, rf, lf, chunks, start, length, startTime)
|
||||
return d
|
||||
|
||||
def _cbGetDone(self, ignored, rf, lf):
|
||||
log.msg('get done')
|
||||
rf.close()
|
||||
lf.close()
|
||||
if self.useProgressBar:
|
||||
self._writeToTransport('\n')
|
||||
return "Transferred %s to %s" % (rf.name, lf.name)
|
||||
|
||||
|
||||
def cmd_PUT(self, rest):
|
||||
"""
|
||||
Do an upload request for a single local file or a globing expression.
|
||||
|
||||
@param rest: Requested command line for the PUT command.
|
||||
@type rest: L{str}
|
||||
|
||||
@return: A deferred which fires with L{None} when transfer is done.
|
||||
@rtype: L{defer.Deferred}
|
||||
"""
|
||||
local, rest = self._getFilename(rest)
|
||||
|
||||
# FIXME: https://twistedmatrix.com/trac/ticket/7241
|
||||
# Use a better check for globbing expression.
|
||||
if '*' in local or '?' in local:
|
||||
if rest:
|
||||
remote, rest = self._getFilename(rest)
|
||||
remote = os.path.join(self.currentDirectory, remote)
|
||||
else:
|
||||
remote = ''
|
||||
|
||||
files = glob.glob(local)
|
||||
return self._putMultipleFiles(files, remote)
|
||||
|
||||
else:
|
||||
if rest:
|
||||
remote, rest = self._getFilename(rest)
|
||||
else:
|
||||
remote = os.path.split(local)[1]
|
||||
return self._putSingleFile(local, remote)
|
||||
|
||||
|
||||
def _putSingleFile(self, local, remote):
|
||||
"""
|
||||
Perform an upload for a single file.
|
||||
|
||||
@param local: Path to local file.
|
||||
@type local: L{str}.
|
||||
|
||||
@param remote: Remote path for the request relative to current working
|
||||
directory.
|
||||
@type remote: L{str}
|
||||
|
||||
@return: A deferred which fires when transfer is done.
|
||||
"""
|
||||
return self._cbPutMultipleNext(None, [local], remote, single=True)
|
||||
|
||||
|
||||
def _putMultipleFiles(self, files, remote):
|
||||
"""
|
||||
Perform an upload for a list of local files.
|
||||
|
||||
@param files: List of local files.
|
||||
@type files: C{list} of L{str}.
|
||||
|
||||
@param remote: Remote path for the request relative to current working
|
||||
directory.
|
||||
@type remote: L{str}
|
||||
|
||||
@return: A deferred which fires when transfer is done.
|
||||
"""
|
||||
return self._cbPutMultipleNext(None, files, remote)
|
||||
|
||||
|
||||
def _cbPutMultipleNext(
|
||||
self, previousResult, files, remotePath, single=False):
|
||||
"""
|
||||
Perform an upload for the next file in the list of local files.
|
||||
|
||||
@param previousResult: Result form previous file form the list.
|
||||
@type previousResult: L{str}
|
||||
|
||||
@param files: List of local files.
|
||||
@type files: C{list} of L{str}
|
||||
|
||||
@param remotePath: Remote path for the request relative to current
|
||||
working directory.
|
||||
@type remotePath: L{str}
|
||||
|
||||
@param single: A flag which signals if this is a transfer for a single
|
||||
file in which case we use the exact remote path
|
||||
@type single: L{bool}
|
||||
|
||||
@return: A deferred which fires when transfer is done.
|
||||
"""
|
||||
if isinstance(previousResult, failure.Failure):
|
||||
self._printFailure(previousResult)
|
||||
elif previousResult:
|
||||
if isinstance(previousResult, unicode):
|
||||
previousResult = previousResult.encode("utf-8")
|
||||
self._writeToTransport(previousResult)
|
||||
if not previousResult.endswith(b'\n'):
|
||||
self._writeToTransport(b'\n')
|
||||
|
||||
currentFile = None
|
||||
while files and not currentFile:
|
||||
try:
|
||||
currentFile = files.pop(0)
|
||||
localStream = open(currentFile, 'rb')
|
||||
except:
|
||||
self._printFailure(failure.Failure())
|
||||
currentFile = None
|
||||
|
||||
# No more files to transfer.
|
||||
if not currentFile:
|
||||
return None
|
||||
|
||||
if single:
|
||||
remote = remotePath
|
||||
else:
|
||||
name = os.path.split(currentFile)[1]
|
||||
remote = os.path.join(remotePath, name)
|
||||
log.msg((name, remote, remotePath))
|
||||
|
||||
d = self._putRemoteFile(localStream, remote)
|
||||
d.addBoth(self._cbPutMultipleNext, files, remotePath)
|
||||
return d
|
||||
|
||||
|
||||
def _putRemoteFile(self, localStream, remotePath):
|
||||
"""
|
||||
Do an upload request.
|
||||
|
||||
@param localStream: Local stream from where data is read.
|
||||
@type localStream: File like object.
|
||||
|
||||
@param remotePath: Remote path for the request relative to current working directory.
|
||||
@type remotePath: L{str}
|
||||
|
||||
@return: A deferred which fires when transfer is done.
|
||||
"""
|
||||
remote = os.path.join(self.currentDirectory, remotePath)
|
||||
flags = (
|
||||
filetransfer.FXF_WRITE |
|
||||
filetransfer.FXF_CREAT |
|
||||
filetransfer.FXF_TRUNC
|
||||
)
|
||||
d = self.client.openFile(remote, flags, {})
|
||||
d.addCallback(self._cbPutOpenFile, localStream)
|
||||
d.addErrback(self._ebCloseLf, localStream)
|
||||
return d
|
||||
|
||||
|
||||
def _cbPutOpenFile(self, rf, lf):
|
||||
numRequests = self.client.transport.conn.options['requests']
|
||||
if self.useProgressBar:
|
||||
lf = FileWrapper(lf)
|
||||
dList = []
|
||||
chunks = []
|
||||
startTime = self.reactor.seconds()
|
||||
for i in range(numRequests):
|
||||
d = self._cbPutWrite(None, rf, lf, chunks, startTime)
|
||||
if d:
|
||||
dList.append(d)
|
||||
dl = defer.DeferredList(dList, fireOnOneErrback=1)
|
||||
dl.addCallback(self._cbPutDone, rf, lf)
|
||||
return dl
|
||||
|
||||
def _cbPutWrite(self, ignored, rf, lf, chunks, startTime):
|
||||
chunk = self._getNextChunk(chunks)
|
||||
start, size = chunk
|
||||
lf.seek(start)
|
||||
data = lf.read(size)
|
||||
if self.useProgressBar:
|
||||
lf.total += len(data)
|
||||
self._printProgressBar(lf, startTime)
|
||||
if data:
|
||||
d = rf.writeChunk(start, data)
|
||||
d.addCallback(self._cbPutWrite, rf, lf, chunks, startTime)
|
||||
return d
|
||||
else:
|
||||
return
|
||||
|
||||
def _cbPutDone(self, ignored, rf, lf):
|
||||
lf.close()
|
||||
rf.close()
|
||||
if self.useProgressBar:
|
||||
self._writeToTransport('\n')
|
||||
return 'Transferred %s to %s' % (lf.name, rf.name)
|
||||
|
||||
def cmd_LCD(self, path):
|
||||
os.chdir(path)
|
||||
|
||||
def cmd_LN(self, rest):
|
||||
linkpath, rest = self._getFilename(rest)
|
||||
targetpath, rest = self._getFilename(rest)
|
||||
linkpath, targetpath = map(
|
||||
lambda x: os.path.join(self.currentDirectory, x),
|
||||
(linkpath, targetpath))
|
||||
return self.client.makeLink(linkpath, targetpath).addCallback(_ignore)
|
||||
|
||||
def cmd_LS(self, rest):
|
||||
# possible lines:
|
||||
# ls current directory
|
||||
# ls name_of_file that file
|
||||
# ls name_of_directory that directory
|
||||
# ls some_glob_string current directory, globbed for that string
|
||||
options = []
|
||||
rest = rest.split()
|
||||
while rest and rest[0] and rest[0][0] == '-':
|
||||
opts = rest.pop(0)[1:]
|
||||
for o in opts:
|
||||
if o == 'l':
|
||||
options.append('verbose')
|
||||
elif o == 'a':
|
||||
options.append('all')
|
||||
rest = ' '.join(rest)
|
||||
path, rest = self._getFilename(rest)
|
||||
if not path:
|
||||
fullPath = self.currentDirectory + '/'
|
||||
else:
|
||||
fullPath = os.path.join(self.currentDirectory, path)
|
||||
d = self._remoteGlob(fullPath)
|
||||
d.addCallback(self._cbDisplayFiles, options)
|
||||
return d
|
||||
|
||||
def _cbDisplayFiles(self, files, options):
|
||||
files.sort()
|
||||
if 'all' not in options:
|
||||
files = [f for f in files if not f[0].startswith(b'.')]
|
||||
if 'verbose' in options:
|
||||
lines = [f[1] for f in files]
|
||||
else:
|
||||
lines = [f[0] for f in files]
|
||||
if not lines:
|
||||
return None
|
||||
else:
|
||||
return b'\n'.join(lines)
|
||||
|
||||
def cmd_MKDIR(self, path):
|
||||
path, rest = self._getFilename(path)
|
||||
path = os.path.join(self.currentDirectory, path)
|
||||
return self.client.makeDirectory(path, {}).addCallback(_ignore)
|
||||
|
||||
def cmd_RMDIR(self, path):
|
||||
path, rest = self._getFilename(path)
|
||||
path = os.path.join(self.currentDirectory, path)
|
||||
return self.client.removeDirectory(path).addCallback(_ignore)
|
||||
|
||||
def cmd_LMKDIR(self, path):
|
||||
os.system("mkdir %s" % path)
|
||||
|
||||
def cmd_RM(self, path):
|
||||
path, rest = self._getFilename(path)
|
||||
path = os.path.join(self.currentDirectory, path)
|
||||
return self.client.removeFile(path).addCallback(_ignore)
|
||||
|
||||
def cmd_LLS(self, rest):
|
||||
os.system("ls %s" % rest)
|
||||
|
||||
def cmd_RENAME(self, rest):
|
||||
oldpath, rest = self._getFilename(rest)
|
||||
newpath, rest = self._getFilename(rest)
|
||||
oldpath, newpath = map (
|
||||
lambda x: os.path.join(self.currentDirectory, x),
|
||||
(oldpath, newpath))
|
||||
return self.client.renameFile(oldpath, newpath).addCallback(_ignore)
|
||||
|
||||
def cmd_EXIT(self, ignored):
|
||||
self.client.transport.loseConnection()
|
||||
|
||||
cmd_QUIT = cmd_EXIT
|
||||
|
||||
def cmd_VERSION(self, ignored):
|
||||
version = "SFTP version %i" % self.client.version
|
||||
if isinstance(version, unicode):
|
||||
version = version.encode("utf-8")
|
||||
return version
|
||||
|
||||
def cmd_HELP(self, ignored):
|
||||
return """Available commands:
|
||||
cd path Change remote directory to 'path'.
|
||||
chgrp gid path Change gid of 'path' to 'gid'.
|
||||
chmod mode path Change mode of 'path' to 'mode'.
|
||||
chown uid path Change uid of 'path' to 'uid'.
|
||||
exit Disconnect from the server.
|
||||
get remote-path [local-path] Get remote file.
|
||||
help Get a list of available commands.
|
||||
lcd path Change local directory to 'path'.
|
||||
lls [ls-options] [path] Display local directory listing.
|
||||
lmkdir path Create local directory.
|
||||
ln linkpath targetpath Symlink remote file.
|
||||
lpwd Print the local working directory.
|
||||
ls [-l] [path] Display remote directory listing.
|
||||
mkdir path Create remote directory.
|
||||
progress Toggle progress bar.
|
||||
put local-path [remote-path] Put local file.
|
||||
pwd Print the remote working directory.
|
||||
quit Disconnect from the server.
|
||||
rename oldpath newpath Rename remote file.
|
||||
rmdir path Remove remote directory.
|
||||
rm path Remove remote file.
|
||||
version Print the SFTP version.
|
||||
? Synonym for 'help'.
|
||||
"""
|
||||
|
||||
def cmd_PWD(self, ignored):
|
||||
return self.currentDirectory
|
||||
|
||||
def cmd_LPWD(self, ignored):
|
||||
return os.getcwd()
|
||||
|
||||
def cmd_PROGRESS(self, ignored):
|
||||
self.useProgressBar = not self.useProgressBar
|
||||
return "%ssing progess bar." % (self.useProgressBar and "U" or "Not u")
|
||||
|
||||
def cmd_EXEC(self, rest):
|
||||
"""
|
||||
Run C{rest} using the user's shell (or /bin/sh if they do not have
|
||||
one).
|
||||
"""
|
||||
shell = self._pwd.getpwnam(getpass.getuser())[6]
|
||||
if not shell:
|
||||
shell = '/bin/sh'
|
||||
if rest:
|
||||
cmds = ['-c', rest]
|
||||
return utils.getProcessOutput(shell, cmds, errortoo=1)
|
||||
else:
|
||||
os.system(shell)
|
||||
|
||||
# accessory functions
|
||||
|
||||
def _remoteGlob(self, fullPath):
|
||||
log.msg('looking up %s' % fullPath)
|
||||
head, tail = os.path.split(fullPath)
|
||||
if '*' in tail or '?' in tail:
|
||||
glob = 1
|
||||
else:
|
||||
glob = 0
|
||||
if tail and not glob: # could be file or directory
|
||||
# try directory first
|
||||
d = self.client.openDirectory(fullPath)
|
||||
d.addCallback(self._cbOpenList, '')
|
||||
d.addErrback(self._ebNotADirectory, head, tail)
|
||||
else:
|
||||
d = self.client.openDirectory(head)
|
||||
d.addCallback(self._cbOpenList, tail)
|
||||
return d
|
||||
|
||||
def _cbOpenList(self, directory, glob):
|
||||
files = []
|
||||
d = directory.read()
|
||||
d.addBoth(self._cbReadFile, files, directory, glob)
|
||||
return d
|
||||
|
||||
def _ebNotADirectory(self, reason, path, glob):
|
||||
d = self.client.openDirectory(path)
|
||||
d.addCallback(self._cbOpenList, glob)
|
||||
return d
|
||||
|
||||
def _cbReadFile(self, files, l, directory, glob):
|
||||
if not isinstance(files, failure.Failure):
|
||||
if glob:
|
||||
if _PY3:
|
||||
glob = glob.encode("utf-8")
|
||||
l.extend([f for f in files if fnmatch.fnmatch(f[0], glob)])
|
||||
else:
|
||||
l.extend(files)
|
||||
d = directory.read()
|
||||
d.addBoth(self._cbReadFile, l, directory, glob)
|
||||
return d
|
||||
else:
|
||||
reason = files
|
||||
reason.trap(EOFError)
|
||||
directory.close()
|
||||
return l
|
||||
|
||||
def _abbrevSize(self, size):
|
||||
# from http://mail.python.org/pipermail/python-list/1999-December/018395.html
|
||||
_abbrevs = [
|
||||
(1<<50, 'PB'),
|
||||
(1<<40, 'TB'),
|
||||
(1<<30, 'GB'),
|
||||
(1<<20, 'MB'),
|
||||
(1<<10, 'kB'),
|
||||
(1, 'B')
|
||||
]
|
||||
|
||||
for factor, suffix in _abbrevs:
|
||||
if size > factor:
|
||||
break
|
||||
return '%.1f' % (size/factor) + suffix
|
||||
|
||||
def _abbrevTime(self, t):
|
||||
if t > 3600: # 1 hour
|
||||
hours = int(t / 3600)
|
||||
t -= (3600 * hours)
|
||||
mins = int(t / 60)
|
||||
t -= (60 * mins)
|
||||
return "%i:%02i:%02i" % (hours, mins, t)
|
||||
else:
|
||||
mins = int(t/60)
|
||||
t -= (60 * mins)
|
||||
return "%02i:%02i" % (mins, t)
|
||||
|
||||
|
||||
def _printProgressBar(self, f, startTime):
|
||||
"""
|
||||
Update a console progress bar on this L{StdioClient}'s transport, based
|
||||
on the difference between the start time of the operation and the
|
||||
current time according to the reactor, and appropriate to the size of
|
||||
the console window.
|
||||
|
||||
@param f: a wrapper around the file which is being written or read
|
||||
@type f: L{FileWrapper}
|
||||
|
||||
@param startTime: The time at which the operation being tracked began.
|
||||
@type startTime: L{float}
|
||||
"""
|
||||
diff = self.reactor.seconds() - startTime
|
||||
total = f.total
|
||||
try:
|
||||
winSize = struct.unpack('4H',
|
||||
fcntl.ioctl(0, tty.TIOCGWINSZ, '12345679'))
|
||||
except IOError:
|
||||
winSize = [None, 80]
|
||||
if diff == 0.0:
|
||||
speed = 0.0
|
||||
else:
|
||||
speed = total / diff
|
||||
if speed:
|
||||
timeLeft = (f.size - total) / speed
|
||||
else:
|
||||
timeLeft = 0
|
||||
front = f.name
|
||||
if f.size:
|
||||
percentage = (total / f.size) * 100
|
||||
else:
|
||||
percentage = 100
|
||||
back = '%3i%% %s %sps %s ' % (percentage,
|
||||
self._abbrevSize(total),
|
||||
self._abbrevSize(speed),
|
||||
self._abbrevTime(timeLeft))
|
||||
spaces = (winSize[1] - (len(front) + len(back) + 1)) * ' '
|
||||
command = '\r%s%s%s' % (front, spaces, back)
|
||||
self._writeToTransport(command)
|
||||
|
||||
|
||||
def _getFilename(self, line):
|
||||
"""
|
||||
Parse line received as command line input and return first filename
|
||||
together with the remaining line.
|
||||
|
||||
@param line: Arguments received from command line input.
|
||||
@type line: L{str}
|
||||
|
||||
@return: Tupple with filename and rest. Return empty values when no path was not found.
|
||||
@rtype: C{tupple}
|
||||
"""
|
||||
line = line.strip()
|
||||
if not line:
|
||||
return '', ''
|
||||
if line[0] in '\'"':
|
||||
ret = []
|
||||
line = list(line)
|
||||
try:
|
||||
for i in range(1,len(line)):
|
||||
c = line[i]
|
||||
if c == line[0]:
|
||||
return ''.join(ret), ''.join(line[i+1:]).lstrip()
|
||||
elif c == '\\': # quoted character
|
||||
del line[i]
|
||||
if line[i] not in '\'"\\':
|
||||
raise IndexError("bad quote: \\%s" % (line[i],))
|
||||
ret.append(line[i])
|
||||
else:
|
||||
ret.append(line[i])
|
||||
except IndexError:
|
||||
raise IndexError("unterminated quote")
|
||||
ret = line.split(None, 1)
|
||||
if len(ret) == 1:
|
||||
return ret[0], ''
|
||||
else:
|
||||
return ret[0], ret[1]
|
||||
|
||||
setattr(StdioClient, 'cmd_?', StdioClient.cmd_HELP)
|
||||
|
||||
class SSHConnection(connection.SSHConnection):
|
||||
def serviceStarted(self):
|
||||
self.openChannel(SSHSession())
|
||||
|
||||
class SSHSession(channel.SSHChannel):
|
||||
|
||||
name = b'session'
|
||||
|
||||
def channelOpen(self, foo):
|
||||
log.msg('session %s open' % self.id)
|
||||
if self.conn.options['subsystem'].startswith('/'):
|
||||
request = 'exec'
|
||||
else:
|
||||
request = 'subsystem'
|
||||
d = self.conn.sendRequest(self, request, \
|
||||
common.NS(self.conn.options['subsystem']), wantReply=1)
|
||||
d.addCallback(self._cbSubsystem)
|
||||
d.addErrback(_ebExit)
|
||||
|
||||
def _cbSubsystem(self, result):
|
||||
self.client = filetransfer.FileTransferClient()
|
||||
self.client.makeConnection(self)
|
||||
self.dataReceived = self.client.dataReceived
|
||||
f = None
|
||||
if self.conn.options['batchfile']:
|
||||
fn = self.conn.options['batchfile']
|
||||
if fn != '-':
|
||||
f = open(fn)
|
||||
self.stdio = stdio.StandardIO(StdioClient(self.client, f))
|
||||
|
||||
def extReceived(self, t, data):
|
||||
if t==connection.EXTENDED_DATA_STDERR:
|
||||
log.msg('got %s stderr data' % len(data))
|
||||
sys.stderr.write(data)
|
||||
sys.stderr.flush()
|
||||
|
||||
def eofReceived(self):
|
||||
log.msg('got eof')
|
||||
self.stdio.loseWriteConnection()
|
||||
|
||||
def closeReceived(self):
|
||||
log.msg('remote side closed %s' % self)
|
||||
self.conn.sendClose(self)
|
||||
|
||||
def closed(self):
|
||||
try:
|
||||
reactor.stop()
|
||||
except:
|
||||
pass
|
||||
|
||||
def stopWriting(self):
|
||||
self.stdio.pauseProducing()
|
||||
|
||||
def startWriting(self):
|
||||
self.stdio.resumeProducing()
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
Loading…
Add table
Add a link
Reference in a new issue