Merge branch '1.12'
This commit is contained in:
commit
b9cea90581
|
@ -1,39 +0,0 @@
|
|||
from fabric.api import task, sudo, env, local, hosts
|
||||
from fabric.contrib.project import rsync_project
|
||||
from fabric.contrib.console import confirm
|
||||
|
||||
|
||||
@task
|
||||
@hosts("paramiko.org")
|
||||
def upload_docs():
|
||||
target = "/var/www/paramiko.org"
|
||||
staging = "/tmp/paramiko_docs"
|
||||
sudo("mkdir -p %s" % staging)
|
||||
sudo("chown -R %s %s" % (env.user, staging))
|
||||
sudo("rm -rf %s/*" % target)
|
||||
rsync_project(local_dir='docs/', remote_dir=staging, delete=True)
|
||||
sudo("cp -R %s/* %s/" % (staging, target))
|
||||
|
||||
@task
|
||||
def build_docs():
|
||||
local("epydoc --no-private -o docs/ paramiko")
|
||||
|
||||
@task
|
||||
def clean():
|
||||
local("rm -rf build dist docs")
|
||||
local("rm -f MANIFEST *.log demos/*.log")
|
||||
local("rm -f paramiko/*.pyc")
|
||||
local("rm -f test.log")
|
||||
local("rm -rf paramiko.egg-info")
|
||||
|
||||
@task
|
||||
def test():
|
||||
local("python ./test.py")
|
||||
|
||||
@task
|
||||
def release():
|
||||
confirm("Only hit Enter if you remembered to update the version!")
|
||||
confirm("Also, did you remember to tag your release?")
|
||||
build_docs()
|
||||
local("python setup.py sdist register upload")
|
||||
upload_docs()
|
|
@ -16,44 +16,10 @@
|
|||
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
I{Paramiko} (a combination of the esperanto words for "paranoid" and "friend")
|
||||
is a module for python 2.5 or greater that implements the SSH2 protocol for
|
||||
secure (encrypted and authenticated) connections to remote machines. Unlike
|
||||
SSL (aka TLS), the SSH2 protocol does not require hierarchical certificates
|
||||
signed by a powerful central authority. You may know SSH2 as the protocol that
|
||||
replaced C{telnet} and C{rsh} for secure access to remote shells, but the
|
||||
protocol also includes the ability to open arbitrary channels to remote
|
||||
services across an encrypted tunnel. (This is how C{sftp} works, for example.)
|
||||
|
||||
The high-level client API starts with creation of an L{SSHClient} object.
|
||||
For more direct control, pass a socket (or socket-like object) to a
|
||||
L{Transport}, and use L{start_server <Transport.start_server>} or
|
||||
L{start_client <Transport.start_client>} to negoatite
|
||||
with the remote host as either a server or client. As a client, you are
|
||||
responsible for authenticating using a password or private key, and checking
|
||||
the server's host key. I{(Key signature and verification is done by paramiko,
|
||||
but you will need to provide private keys and check that the content of a
|
||||
public key matches what you expected to see.)} As a server, you are
|
||||
responsible for deciding which users, passwords, and keys to allow, and what
|
||||
kind of channels to allow.
|
||||
|
||||
Once you have finished, either side may request flow-controlled L{Channel}s to
|
||||
the other side, which are python objects that act like sockets, but send and
|
||||
receive data over the encrypted session.
|
||||
|
||||
Paramiko is written entirely in python (no C or platform-dependent code) and is
|
||||
released under the GNU Lesser General Public License (LGPL).
|
||||
|
||||
Website: U{https://github.com/paramiko/paramiko/}
|
||||
|
||||
Mailing list: U{paramiko@librelist.com<mailto:paramiko@librelist.com>}
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
if sys.version_info < (2, 5):
|
||||
raise RuntimeError('You need python 2.5+ for this module.')
|
||||
raise RuntimeError('You need Python 2.5+ for this module.')
|
||||
|
||||
|
||||
__author__ = "Jeff Forcier <jeff@bitprophet.org>"
|
||||
|
@ -89,13 +55,6 @@ from hostkeys import HostKeys
|
|||
from config import SSHConfig
|
||||
from proxy import ProxyCommand
|
||||
|
||||
# fix module names for epydoc
|
||||
for c in locals().values():
|
||||
if issubclass(type(c), type) or type(c).__name__ == 'classobj':
|
||||
# classobj for exceptions :/
|
||||
c.__module__ = __name__
|
||||
del c
|
||||
|
||||
from common import AUTH_SUCCESSFUL, AUTH_PARTIALLY_SUCCESSFUL, AUTH_FAILED, \
|
||||
OPEN_SUCCEEDED, OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, OPEN_FAILED_CONNECT_FAILED, \
|
||||
OPEN_FAILED_UNKNOWN_CHANNEL_TYPE, OPEN_FAILED_RESOURCE_SHORTAGE
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
SSH Agent interface for Unix clients.
|
||||
SSH Agent interface
|
||||
"""
|
||||
|
||||
import os
|
||||
|
@ -40,17 +40,8 @@ from paramiko.util import retry_on_signal
|
|||
SSH2_AGENTC_REQUEST_IDENTITIES, SSH2_AGENT_IDENTITIES_ANSWER, \
|
||||
SSH2_AGENTC_SIGN_REQUEST, SSH2_AGENT_SIGN_RESPONSE = range(11, 15)
|
||||
|
||||
class AgentSSH(object):
|
||||
"""
|
||||
Client interface for using private keys from an SSH agent running on the
|
||||
local machine. If an SSH agent is running, this class can be used to
|
||||
connect to it and retreive L{PKey} objects which can be used when
|
||||
attempting to authenticate to remote SSH servers.
|
||||
|
||||
Because the SSH agent protocol uses environment variables and unix-domain
|
||||
sockets, this probably doesn't work on Windows. It does work on most
|
||||
posix platforms though (Linux and MacOS X, for example).
|
||||
"""
|
||||
class AgentSSH(object):
|
||||
def __init__(self):
|
||||
self._conn = None
|
||||
self._keys = ()
|
||||
|
@ -61,8 +52,9 @@ class AgentSSH(object):
|
|||
no SSH agent was running (or it couldn't be contacted), an empty list
|
||||
will be returned.
|
||||
|
||||
@return: a list of keys available on the SSH agent
|
||||
@rtype: tuple of L{AgentKey}
|
||||
:return:
|
||||
a tuple of `.AgentKey` objects representing keys available on the
|
||||
SSH agent
|
||||
"""
|
||||
return self._keys
|
||||
|
||||
|
@ -100,8 +92,11 @@ class AgentSSH(object):
|
|||
result += extra
|
||||
return result
|
||||
|
||||
|
||||
class AgentProxyThread(threading.Thread):
|
||||
""" Class in charge of communication between two chan """
|
||||
"""
|
||||
Class in charge of communication between two channels.
|
||||
"""
|
||||
def __init__(self, agent):
|
||||
threading.Thread.__init__(self, target=self.run)
|
||||
self._agent = agent
|
||||
|
@ -146,6 +141,7 @@ class AgentProxyThread(threading.Thread):
|
|||
self.__inr.close()
|
||||
self._agent._conn.close()
|
||||
|
||||
|
||||
class AgentLocalProxy(AgentProxyThread):
|
||||
"""
|
||||
Class to be used when wanting to ask a local SSH Agent being
|
||||
|
@ -155,8 +151,10 @@ class AgentLocalProxy(AgentProxyThread):
|
|||
AgentProxyThread.__init__(self, agent)
|
||||
|
||||
def get_connection(self):
|
||||
""" Return a pair of socket object and string address
|
||||
May Block !
|
||||
"""
|
||||
Return a pair of socket object and string address.
|
||||
|
||||
May block!
|
||||
"""
|
||||
conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
try:
|
||||
|
@ -168,6 +166,7 @@ class AgentLocalProxy(AgentProxyThread):
|
|||
raise
|
||||
return None
|
||||
|
||||
|
||||
class AgentRemoteProxy(AgentProxyThread):
|
||||
"""
|
||||
Class to be used when wanting to ask a remote SSH Agent
|
||||
|
@ -177,22 +176,20 @@ class AgentRemoteProxy(AgentProxyThread):
|
|||
self.__chan = chan
|
||||
|
||||
def get_connection(self):
|
||||
"""
|
||||
Class to be used when wanting to ask a local SSH Agent being
|
||||
asked from a remote fake agent (so use a unix socket for ex.)
|
||||
"""
|
||||
return (self.__chan, None)
|
||||
|
||||
|
||||
class AgentClientProxy(object):
|
||||
"""
|
||||
Class proxying request as a client:
|
||||
-> client ask for a request_forward_agent()
|
||||
-> server creates a proxy and a fake SSH Agent
|
||||
-> server ask for establishing a connection when needed,
|
||||
|
||||
#. client ask for a request_forward_agent()
|
||||
#. server creates a proxy and a fake SSH Agent
|
||||
#. server ask for establishing a connection when needed,
|
||||
calling the forward_agent_handler at client side.
|
||||
-> the forward_agent_handler launch a thread for connecting
|
||||
#. the forward_agent_handler launch a thread for connecting
|
||||
the remote fake agent and the local agent
|
||||
-> Communication occurs ...
|
||||
#. Communication occurs ...
|
||||
"""
|
||||
def __init__(self, chanRemote):
|
||||
self._conn = None
|
||||
|
@ -205,7 +202,7 @@ class AgentClientProxy(object):
|
|||
|
||||
def connect(self):
|
||||
"""
|
||||
Method automatically called by the run() method of the AgentProxyThread
|
||||
Method automatically called by ``AgentProxyThread.run``.
|
||||
"""
|
||||
if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'):
|
||||
conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
|
@ -236,11 +233,12 @@ class AgentClientProxy(object):
|
|||
if self._conn is not None:
|
||||
self._conn.close()
|
||||
|
||||
|
||||
class AgentServerProxy(AgentSSH):
|
||||
"""
|
||||
@param t : transport used for the Forward for SSH Agent communication
|
||||
:param .Transport t: Transport used for SSH Agent communication forwarding
|
||||
|
||||
@raise SSHException: mostly if we lost the agent
|
||||
:raises SSHException: mostly if we lost the agent
|
||||
"""
|
||||
def __init__(self, t):
|
||||
AgentSSH.__init__(self)
|
||||
|
@ -276,8 +274,8 @@ class AgentServerProxy(AgentSSH):
|
|||
"""
|
||||
Helper for the environnement under unix
|
||||
|
||||
@return: the SSH_AUTH_SOCK Environnement variables
|
||||
@rtype: dict
|
||||
:return:
|
||||
a dict containing the ``SSH_AUTH_SOCK`` environnement variables
|
||||
"""
|
||||
env = {}
|
||||
env['SSH_AUTH_SOCK'] = self._get_filename()
|
||||
|
@ -286,6 +284,7 @@ class AgentServerProxy(AgentSSH):
|
|||
def _get_filename(self):
|
||||
return self._file
|
||||
|
||||
|
||||
class AgentRequestHandler(object):
|
||||
def __init__(self, chanClient):
|
||||
self._conn = None
|
||||
|
@ -303,27 +302,22 @@ class AgentRequestHandler(object):
|
|||
for p in self.__clientProxys:
|
||||
p.close()
|
||||
|
||||
|
||||
class Agent(AgentSSH):
|
||||
"""
|
||||
Client interface for using private keys from an SSH agent running on the
|
||||
local machine. If an SSH agent is running, this class can be used to
|
||||
connect to it and retreive L{PKey} objects which can be used when
|
||||
connect to it and retreive `.PKey` objects which can be used when
|
||||
attempting to authenticate to remote SSH servers.
|
||||
|
||||
Because the SSH agent protocol uses environment variables and unix-domain
|
||||
sockets, this probably doesn't work on Windows. It does work on most
|
||||
posix platforms though (Linux and MacOS X, for example).
|
||||
"""
|
||||
Upon initialization, a session with the local machine's SSH agent is
|
||||
opened, if one is running. If no agent is running, initialization will
|
||||
succeed, but `get_keys` will return an empty tuple.
|
||||
|
||||
:raises SSHException:
|
||||
if an SSH agent is found, but speaks an incompatible protocol
|
||||
"""
|
||||
def __init__(self):
|
||||
"""
|
||||
Open a session with the local machine's SSH agent, if one is running.
|
||||
If no agent is running, initialization will succeed, but L{get_keys}
|
||||
will return an empty tuple.
|
||||
|
||||
@raise SSHException: if an SSH agent is found, but speaks an
|
||||
incompatible protocol
|
||||
"""
|
||||
AgentSSH.__init__(self)
|
||||
|
||||
if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'):
|
||||
|
@ -350,13 +344,13 @@ class Agent(AgentSSH):
|
|||
"""
|
||||
self._close()
|
||||
|
||||
|
||||
class AgentKey(PKey):
|
||||
"""
|
||||
Private key held in a local SSH agent. This type of key can be used for
|
||||
authenticating to a remote server (signing). Most other key operations
|
||||
work as expected.
|
||||
"""
|
||||
|
||||
def __init__(self, agent, blob):
|
||||
self.agent = agent
|
||||
self.blob = blob
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
L{AuthHandler}
|
||||
`.AuthHandler`
|
||||
"""
|
||||
|
||||
import threading
|
||||
|
@ -168,7 +168,7 @@ class AuthHandler (object):
|
|||
e = self.transport.get_exception()
|
||||
if e is None:
|
||||
e = AuthenticationException('Authentication failed.')
|
||||
# this is horrible. python Exception isn't yet descended from
|
||||
# this is horrible. Python Exception isn't yet descended from
|
||||
# object, so type(e) won't work. :(
|
||||
if issubclass(e.__class__, PartialAuthentication):
|
||||
return e.allowed_types
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
Attempt to generalize the "feeder" part of a Channel: an object which can be
|
||||
Attempt to generalize the "feeder" part of a `.Channel`: an object which can be
|
||||
read from and closed, but is reading from a buffer fed by another thread. The
|
||||
read operations are blocking and can have a timeout set.
|
||||
"""
|
||||
|
@ -29,7 +29,7 @@ import time
|
|||
|
||||
class PipeTimeout (IOError):
|
||||
"""
|
||||
Indicates that a timeout was reached on a read from a L{BufferedPipe}.
|
||||
Indicates that a timeout was reached on a read from a `.BufferedPipe`.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
@ -38,7 +38,7 @@ class BufferedPipe (object):
|
|||
"""
|
||||
A buffer that obeys normal read (with timeout) & close semantics for a
|
||||
file or socket, but is fed data from another thread. This is used by
|
||||
L{Channel}.
|
||||
`.Channel`.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
@ -54,8 +54,7 @@ class BufferedPipe (object):
|
|||
buffer has been closed), the event will be set. When no data is
|
||||
ready, the event will be cleared.
|
||||
|
||||
@param event: the event to set/clear
|
||||
@type event: Event
|
||||
:param threading.Event event: the event to set/clear
|
||||
"""
|
||||
self._event = event
|
||||
if len(self._buffer) > 0:
|
||||
|
@ -68,8 +67,7 @@ class BufferedPipe (object):
|
|||
Feed new data into this pipe. This method is assumed to be called
|
||||
from a separate thread, so synchronization is done.
|
||||
|
||||
@param data: the data to add
|
||||
@type data: str
|
||||
:param data: the data to add, as a `str`
|
||||
"""
|
||||
self._lock.acquire()
|
||||
try:
|
||||
|
@ -83,12 +81,12 @@ class BufferedPipe (object):
|
|||
def read_ready(self):
|
||||
"""
|
||||
Returns true if data is buffered and ready to be read from this
|
||||
feeder. A C{False} result does not mean that the feeder has closed;
|
||||
feeder. A ``False`` result does not mean that the feeder has closed;
|
||||
it means you may need to wait before more data arrives.
|
||||
|
||||
@return: C{True} if a L{read} call would immediately return at least
|
||||
one byte; C{False} otherwise.
|
||||
@rtype: bool
|
||||
:return:
|
||||
``True`` if a `read` call would immediately return at least one
|
||||
byte; ``False`` otherwise.
|
||||
"""
|
||||
self._lock.acquire()
|
||||
try:
|
||||
|
@ -102,24 +100,22 @@ class BufferedPipe (object):
|
|||
"""
|
||||
Read data from the pipe. The return value is a string representing
|
||||
the data received. The maximum amount of data to be received at once
|
||||
is specified by C{nbytes}. If a string of length zero is returned,
|
||||
is specified by ``nbytes``. If a string of length zero is returned,
|
||||
the pipe has been closed.
|
||||
|
||||
The optional C{timeout} argument can be a nonnegative float expressing
|
||||
seconds, or C{None} for no timeout. If a float is given, a
|
||||
C{PipeTimeout} will be raised if the timeout period value has
|
||||
elapsed before any data arrives.
|
||||
The optional ``timeout`` argument can be a nonnegative float expressing
|
||||
seconds, or ``None`` for no timeout. If a float is given, a
|
||||
`.PipeTimeout` will be raised if the timeout period value has elapsed
|
||||
before any data arrives.
|
||||
|
||||
@param nbytes: maximum number of bytes to read
|
||||
@type nbytes: int
|
||||
@param timeout: maximum seconds to wait (or C{None}, the default, to
|
||||
wait forever)
|
||||
@type timeout: float
|
||||
@return: data
|
||||
@rtype: str
|
||||
:param int nbytes: maximum number of bytes to read
|
||||
:param float timeout:
|
||||
maximum seconds to wait (or ``None``, the default, to wait forever)
|
||||
:return: the read data, as a `str`
|
||||
|
||||
@raise PipeTimeout: if a timeout was specified and no data was ready
|
||||
before that timeout
|
||||
:raises PipeTimeout:
|
||||
if a timeout was specified and no data was ready before that
|
||||
timeout
|
||||
"""
|
||||
out = ''
|
||||
self._lock.acquire()
|
||||
|
@ -158,8 +154,9 @@ class BufferedPipe (object):
|
|||
"""
|
||||
Clear out the buffer and return all data that was in it.
|
||||
|
||||
@return: any data that was in the buffer prior to clearing it out
|
||||
@rtype: str
|
||||
:return:
|
||||
any data that was in the buffer prior to clearing it out, as a
|
||||
`str`
|
||||
"""
|
||||
self._lock.acquire()
|
||||
try:
|
||||
|
@ -173,7 +170,7 @@ class BufferedPipe (object):
|
|||
|
||||
def close(self):
|
||||
"""
|
||||
Close this pipe object. Future calls to L{read} after the buffer
|
||||
Close this pipe object. Future calls to `read` after the buffer
|
||||
has been emptied will return immediately with an empty string.
|
||||
"""
|
||||
self._lock.acquire()
|
||||
|
@ -189,8 +186,7 @@ class BufferedPipe (object):
|
|||
"""
|
||||
Return the number of bytes buffered.
|
||||
|
||||
@return: number of bytes bufferes
|
||||
@rtype: int
|
||||
:return: number (`int`) of bytes buffered
|
||||
"""
|
||||
self._lock.acquire()
|
||||
try:
|
||||
|
|
|
@ -42,29 +42,28 @@ MIN_PACKET_SIZE = 1024
|
|||
|
||||
class Channel (object):
|
||||
"""
|
||||
A secure tunnel across an SSH L{Transport}. A Channel is meant to behave
|
||||
A secure tunnel across an SSH `.Transport`. A Channel is meant to behave
|
||||
like a socket, and has an API that should be indistinguishable from the
|
||||
python socket API.
|
||||
Python socket API.
|
||||
|
||||
Because SSH2 has a windowing kind of flow control, if you stop reading data
|
||||
from a Channel and its buffer fills up, the server will be unable to send
|
||||
you any more data until you read some of it. (This won't affect other
|
||||
channels on the same transport -- all channels on a single transport are
|
||||
flow-controlled independently.) Similarly, if the server isn't reading
|
||||
data you send, calls to L{send} may block, unless you set a timeout. This
|
||||
data you send, calls to `send` may block, unless you set a timeout. This
|
||||
is exactly like a normal network socket, so it shouldn't be too surprising.
|
||||
"""
|
||||
|
||||
def __init__(self, chanid):
|
||||
"""
|
||||
Create a new channel. The channel is not associated with any
|
||||
particular session or L{Transport} until the Transport attaches it.
|
||||
particular session or `.Transport` until the Transport attaches it.
|
||||
Normally you would only call this method from the constructor of a
|
||||
subclass of L{Channel}.
|
||||
subclass of `.Channel`.
|
||||
|
||||
@param chanid: the ID of this channel, as passed by an existing
|
||||
L{Transport}.
|
||||
@type chanid: int
|
||||
:param int chanid:
|
||||
the ID of this channel, as passed by an existing `.Transport`.
|
||||
"""
|
||||
self.chanid = chanid
|
||||
self.remote_chanid = 0
|
||||
|
@ -104,8 +103,6 @@ class Channel (object):
|
|||
def __repr__(self):
|
||||
"""
|
||||
Return a string representation of this object, for debugging.
|
||||
|
||||
@rtype: str
|
||||
"""
|
||||
out = '<paramiko.Channel %d' % self.chanid
|
||||
if self.closed:
|
||||
|
@ -127,23 +124,18 @@ class Channel (object):
|
|||
"""
|
||||
Request a pseudo-terminal from the server. This is usually used right
|
||||
after creating a client channel, to ask the server to provide some
|
||||
basic terminal semantics for a shell invoked with L{invoke_shell}.
|
||||
basic terminal semantics for a shell invoked with `invoke_shell`.
|
||||
It isn't necessary (or desirable) to call this method if you're going
|
||||
to exectue a single command with L{exec_command}.
|
||||
to exectue a single command with `exec_command`.
|
||||
|
||||
@param term: the terminal type to emulate (for example, C{'vt100'})
|
||||
@type term: str
|
||||
@param width: width (in characters) of the terminal screen
|
||||
@type width: int
|
||||
@param height: height (in characters) of the terminal screen
|
||||
@type height: int
|
||||
@param width_pixels: width (in pixels) of the terminal screen
|
||||
@type width_pixels: int
|
||||
@param height_pixels: height (in pixels) of the terminal screen
|
||||
@type height_pixels: int
|
||||
:param str term: the terminal type to emulate (for example, ``'vt100'``)
|
||||
:param int width: width (in characters) of the terminal screen
|
||||
:param int height: height (in characters) of the terminal screen
|
||||
:param int width_pixels: width (in pixels) of the terminal screen
|
||||
:param int height_pixels: height (in pixels) of the terminal screen
|
||||
|
||||
@raise SSHException: if the request was rejected or the channel was
|
||||
closed
|
||||
:raises SSHException:
|
||||
if the request was rejected or the channel was closed
|
||||
"""
|
||||
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
||||
raise SSHException('Channel is not open')
|
||||
|
@ -168,14 +160,14 @@ class Channel (object):
|
|||
allows it, the channel will then be directly connected to the stdin,
|
||||
stdout, and stderr of the shell.
|
||||
|
||||
Normally you would call L{get_pty} before this, in which case the
|
||||
Normally you would call `get_pty` before this, in which case the
|
||||
shell will operate through the pty, and the channel will be connected
|
||||
to the stdin and stdout of the pty.
|
||||
|
||||
When the shell exits, the channel will be closed and can't be reused.
|
||||
You must open a new channel if you wish to open another shell.
|
||||
|
||||
@raise SSHException: if the request was rejected or the channel was
|
||||
:raises SSHException: if the request was rejected or the channel was
|
||||
closed
|
||||
"""
|
||||
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
||||
|
@ -199,10 +191,9 @@ class Channel (object):
|
|||
can't be reused. You must open a new channel if you wish to execute
|
||||
another command.
|
||||
|
||||
@param command: a shell command to execute.
|
||||
@type command: str
|
||||
:param str command: a shell command to execute.
|
||||
|
||||
@raise SSHException: if the request was rejected or the channel was
|
||||
:raises SSHException: if the request was rejected or the channel was
|
||||
closed
|
||||
"""
|
||||
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
||||
|
@ -219,18 +210,17 @@ class Channel (object):
|
|||
|
||||
def invoke_subsystem(self, subsystem):
|
||||
"""
|
||||
Request a subsystem on the server (for example, C{sftp}). If the
|
||||
Request a subsystem on the server (for example, ``sftp``). If the
|
||||
server allows it, the channel will then be directly connected to the
|
||||
requested subsystem.
|
||||
|
||||
When the subsystem finishes, the channel will be closed and can't be
|
||||
reused.
|
||||
|
||||
@param subsystem: name of the subsystem being requested.
|
||||
@type subsystem: str
|
||||
:param str subsystem: name of the subsystem being requested.
|
||||
|
||||
@raise SSHException: if the request was rejected or the channel was
|
||||
closed
|
||||
:raises SSHException:
|
||||
if the request was rejected or the channel was closed
|
||||
"""
|
||||
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
||||
raise SSHException('Channel is not open')
|
||||
|
@ -247,19 +237,15 @@ class Channel (object):
|
|||
def resize_pty(self, width=80, height=24, width_pixels=0, height_pixels=0):
|
||||
"""
|
||||
Resize the pseudo-terminal. This can be used to change the width and
|
||||
height of the terminal emulation created in a previous L{get_pty} call.
|
||||
height of the terminal emulation created in a previous `get_pty` call.
|
||||
|
||||
@param width: new width (in characters) of the terminal screen
|
||||
@type width: int
|
||||
@param height: new height (in characters) of the terminal screen
|
||||
@type height: int
|
||||
@param width_pixels: new width (in pixels) of the terminal screen
|
||||
@type width_pixels: int
|
||||
@param height_pixels: new height (in pixels) of the terminal screen
|
||||
@type height_pixels: int
|
||||
:param int width: new width (in characters) of the terminal screen
|
||||
:param int height: new height (in characters) of the terminal screen
|
||||
:param int width_pixels: new width (in pixels) of the terminal screen
|
||||
:param int height_pixels: new height (in pixels) of the terminal screen
|
||||
|
||||
@raise SSHException: if the request was rejected or the channel was
|
||||
closed
|
||||
:raises SSHException:
|
||||
if the request was rejected or the channel was closed
|
||||
"""
|
||||
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
||||
raise SSHException('Channel is not open')
|
||||
|
@ -278,27 +264,27 @@ class Channel (object):
|
|||
"""
|
||||
Return true if the remote process has exited and returned an exit
|
||||
status. You may use this to poll the process status if you don't
|
||||
want to block in L{recv_exit_status}. Note that the server may not
|
||||
want to block in `recv_exit_status`. Note that the server may not
|
||||
return an exit status in some cases (like bad servers).
|
||||
|
||||
@return: True if L{recv_exit_status} will return immediately
|
||||
@rtype: bool
|
||||
@since: 1.7.3
|
||||
:return:
|
||||
``True`` if `recv_exit_status` will return immediately, else ``False``.
|
||||
|
||||
.. versionadded:: 1.7.3
|
||||
"""
|
||||
return self.closed or self.status_event.isSet()
|
||||
|
||||
def recv_exit_status(self):
|
||||
"""
|
||||
Return the exit status from the process on the server. This is
|
||||
mostly useful for retrieving the reults of an L{exec_command}.
|
||||
mostly useful for retrieving the reults of an `exec_command`.
|
||||
If the command hasn't finished yet, this method will wait until
|
||||
it does, or until the channel is closed. If no exit status is
|
||||
provided by the server, -1 is returned.
|
||||
|
||||
@return: the exit code of the process on the server.
|
||||
@rtype: int
|
||||
:return: the exit code (as an `int`) of the process on the server.
|
||||
|
||||
@since: 1.2
|
||||
.. versionadded:: 1.2
|
||||
"""
|
||||
self.status_event.wait()
|
||||
assert self.status_event.isSet()
|
||||
|
@ -311,10 +297,9 @@ class Channel (object):
|
|||
get some sort of status code back from an executed command after
|
||||
it completes.
|
||||
|
||||
@param status: the exit code of the process
|
||||
@type status: int
|
||||
:param int status: the exit code of the process
|
||||
|
||||
@since: 1.2
|
||||
.. versionadded:: 1.2
|
||||
"""
|
||||
# in many cases, the channel will not still be open here.
|
||||
# that's fine.
|
||||
|
@ -347,25 +332,24 @@ class Channel (object):
|
|||
If a handler is passed in, the handler is called from another thread
|
||||
whenever a new x11 connection arrives. The default handler queues up
|
||||
incoming x11 connections, which may be retrieved using
|
||||
L{Transport.accept}. The handler's calling signature is::
|
||||
`.Transport.accept`. The handler's calling signature is::
|
||||
|
||||
handler(channel: Channel, (address: str, port: int))
|
||||
|
||||
@param screen_number: the x11 screen number (0, 10, etc)
|
||||
@type screen_number: int
|
||||
@param auth_protocol: the name of the X11 authentication method used;
|
||||
if none is given, C{"MIT-MAGIC-COOKIE-1"} is used
|
||||
@type auth_protocol: str
|
||||
@param auth_cookie: hexadecimal string containing the x11 auth cookie;
|
||||
if none is given, a secure random 128-bit value is generated
|
||||
@type auth_cookie: str
|
||||
@param single_connection: if True, only a single x11 connection will be
|
||||
forwarded (by default, any number of x11 connections can arrive
|
||||
over this session)
|
||||
@type single_connection: bool
|
||||
@param handler: an optional handler to use for incoming X11 connections
|
||||
@type handler: function
|
||||
@return: the auth_cookie used
|
||||
:param int screen_number: the x11 screen number (0, 10, etc)
|
||||
:param str auth_protocol:
|
||||
the name of the X11 authentication method used; if none is given,
|
||||
``"MIT-MAGIC-COOKIE-1"`` is used
|
||||
:param str auth_cookie:
|
||||
hexadecimal string containing the x11 auth cookie; if none is
|
||||
given, a secure random 128-bit value is generated
|
||||
:param bool single_connection:
|
||||
if True, only a single x11 connection will be forwarded (by
|
||||
default, any number of x11 connections can arrive over this
|
||||
session)
|
||||
:param function handler:
|
||||
an optional handler to use for incoming X11 connections
|
||||
:return: the auth_cookie used
|
||||
"""
|
||||
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
||||
raise SSHException('Channel is not open')
|
||||
|
@ -392,15 +376,14 @@ class Channel (object):
|
|||
def request_forward_agent(self, handler):
|
||||
"""
|
||||
Request for a forward SSH Agent on this channel.
|
||||
This is only valid for an ssh-agent from openssh !!!
|
||||
This is only valid for an ssh-agent from OpenSSH !!!
|
||||
|
||||
@param handler: a required handler to use for incoming SSH Agent connections
|
||||
@type handler: function
|
||||
:param function handler:
|
||||
a required handler to use for incoming SSH Agent connections
|
||||
|
||||
@return: if we are ok or not (at that time we always return ok)
|
||||
@rtype: boolean
|
||||
:return: True if we are ok, else False (at that time we always return ok)
|
||||
|
||||
@raise: SSHException in case of channel problem.
|
||||
:raises: SSHException in case of channel problem.
|
||||
"""
|
||||
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
||||
raise SSHException('Channel is not open')
|
||||
|
@ -416,10 +399,7 @@ class Channel (object):
|
|||
|
||||
def get_transport(self):
|
||||
"""
|
||||
Return the L{Transport} associated with this channel.
|
||||
|
||||
@return: the L{Transport} that was used to create this channel.
|
||||
@rtype: L{Transport}
|
||||
Return the `.Transport` associated with this channel.
|
||||
"""
|
||||
return self.transport
|
||||
|
||||
|
@ -427,55 +407,49 @@ class Channel (object):
|
|||
"""
|
||||
Set a name for this channel. Currently it's only used to set the name
|
||||
of the channel in logfile entries. The name can be fetched with the
|
||||
L{get_name} method.
|
||||
`get_name` method.
|
||||
|
||||
@param name: new channel name
|
||||
@type name: str
|
||||
:param str name: new channel name
|
||||
"""
|
||||
self._name = name
|
||||
|
||||
def get_name(self):
|
||||
"""
|
||||
Get the name of this channel that was previously set by L{set_name}.
|
||||
|
||||
@return: the name of this channel.
|
||||
@rtype: str
|
||||
Get the name of this channel that was previously set by `set_name`.
|
||||
"""
|
||||
return self._name
|
||||
|
||||
def get_id(self):
|
||||
"""
|
||||
Return the ID # for this channel. The channel ID is unique across
|
||||
a L{Transport} and usually a small number. It's also the number
|
||||
passed to L{ServerInterface.check_channel_request} when determining
|
||||
whether to accept a channel request in server mode.
|
||||
Return the `int` ID # for this channel.
|
||||
|
||||
@return: the ID of this channel.
|
||||
@rtype: int
|
||||
The channel ID is unique across a `.Transport` and usually a small
|
||||
number. It's also the number passed to
|
||||
`.ServerInterface.check_channel_request` when determining whether to
|
||||
accept a channel request in server mode.
|
||||
"""
|
||||
return self.chanid
|
||||
|
||||
def set_combine_stderr(self, combine):
|
||||
"""
|
||||
Set whether stderr should be combined into stdout on this channel.
|
||||
The default is C{False}, but in some cases it may be convenient to
|
||||
The default is ``False``, but in some cases it may be convenient to
|
||||
have both streams combined.
|
||||
|
||||
If this is C{False}, and L{exec_command} is called (or C{invoke_shell}
|
||||
with no pty), output to stderr will not show up through the L{recv}
|
||||
and L{recv_ready} calls. You will have to use L{recv_stderr} and
|
||||
L{recv_stderr_ready} to get stderr output.
|
||||
If this is ``False``, and `exec_command` is called (or ``invoke_shell``
|
||||
with no pty), output to stderr will not show up through the `recv`
|
||||
and `recv_ready` calls. You will have to use `recv_stderr` and
|
||||
`recv_stderr_ready` to get stderr output.
|
||||
|
||||
If this is C{True}, data will never show up via L{recv_stderr} or
|
||||
L{recv_stderr_ready}.
|
||||
If this is ``True``, data will never show up via `recv_stderr` or
|
||||
`recv_stderr_ready`.
|
||||
|
||||
@param combine: C{True} if stderr output should be combined into
|
||||
stdout on this channel.
|
||||
@type combine: bool
|
||||
@return: previous setting.
|
||||
@rtype: bool
|
||||
:param bool combine:
|
||||
``True`` if stderr output should be combined into stdout on this
|
||||
channel.
|
||||
:return: the previous setting (a `bool`).
|
||||
|
||||
@since: 1.1
|
||||
.. versionadded:: 1.1
|
||||
"""
|
||||
data = ''
|
||||
self.lock.acquire()
|
||||
|
@ -497,51 +471,47 @@ class Channel (object):
|
|||
|
||||
def settimeout(self, timeout):
|
||||
"""
|
||||
Set a timeout on blocking read/write operations. The C{timeout}
|
||||
argument can be a nonnegative float expressing seconds, or C{None}. If
|
||||
Set a timeout on blocking read/write operations. The ``timeout``
|
||||
argument can be a nonnegative float expressing seconds, or ``None``. If
|
||||
a float is given, subsequent channel read/write operations will raise
|
||||
a timeout exception if the timeout period value has elapsed before the
|
||||
operation has completed. Setting a timeout of C{None} disables
|
||||
operation has completed. Setting a timeout of ``None`` disables
|
||||
timeouts on socket operations.
|
||||
|
||||
C{chan.settimeout(0.0)} is equivalent to C{chan.setblocking(0)};
|
||||
C{chan.settimeout(None)} is equivalent to C{chan.setblocking(1)}.
|
||||
``chan.settimeout(0.0)`` is equivalent to ``chan.setblocking(0)``;
|
||||
``chan.settimeout(None)`` is equivalent to ``chan.setblocking(1)``.
|
||||
|
||||
@param timeout: seconds to wait for a pending read/write operation
|
||||
before raising C{socket.timeout}, or C{None} for no timeout.
|
||||
@type timeout: float
|
||||
:param float timeout:
|
||||
seconds to wait for a pending read/write operation before raising
|
||||
``socket.timeout``, or ``None`` for no timeout.
|
||||
"""
|
||||
self.timeout = timeout
|
||||
|
||||
def gettimeout(self):
|
||||
"""
|
||||
Returns the timeout in seconds (as a float) associated with socket
|
||||
operations, or C{None} if no timeout is set. This reflects the last
|
||||
call to L{setblocking} or L{settimeout}.
|
||||
|
||||
@return: timeout in seconds, or C{None}.
|
||||
@rtype: float
|
||||
operations, or ``None`` if no timeout is set. This reflects the last
|
||||
call to `setblocking` or `settimeout`.
|
||||
"""
|
||||
return self.timeout
|
||||
|
||||
def setblocking(self, blocking):
|
||||
"""
|
||||
Set blocking or non-blocking mode of the channel: if C{blocking} is 0,
|
||||
Set blocking or non-blocking mode of the channel: if ``blocking`` is 0,
|
||||
the channel is set to non-blocking mode; otherwise it's set to blocking
|
||||
mode. Initially all channels are in blocking mode.
|
||||
|
||||
In non-blocking mode, if a L{recv} call doesn't find any data, or if a
|
||||
L{send} call can't immediately dispose of the data, an error exception
|
||||
In non-blocking mode, if a `recv` call doesn't find any data, or if a
|
||||
`send` call can't immediately dispose of the data, an error exception
|
||||
is raised. In blocking mode, the calls block until they can proceed. An
|
||||
EOF condition is considered "immediate data" for L{recv}, so if the
|
||||
EOF condition is considered "immediate data" for `recv`, so if the
|
||||
channel is closed in the read direction, it will never block.
|
||||
|
||||
C{chan.setblocking(0)} is equivalent to C{chan.settimeout(0)};
|
||||
C{chan.setblocking(1)} is equivalent to C{chan.settimeout(None)}.
|
||||
``chan.setblocking(0)`` is equivalent to ``chan.settimeout(0)``;
|
||||
``chan.setblocking(1)`` is equivalent to ``chan.settimeout(None)``.
|
||||
|
||||
@param blocking: 0 to set non-blocking mode; non-0 to set blocking
|
||||
mode.
|
||||
@type blocking: int
|
||||
:param int blocking:
|
||||
0 to set non-blocking mode; non-0 to set blocking mode.
|
||||
"""
|
||||
if blocking:
|
||||
self.settimeout(None)
|
||||
|
@ -551,12 +521,10 @@ class Channel (object):
|
|||
def getpeername(self):
|
||||
"""
|
||||
Return the address of the remote side of this Channel, if possible.
|
||||
This is just a wrapper around C{'getpeername'} on the Transport, used
|
||||
to provide enough of a socket-like interface to allow asyncore to work.
|
||||
(asyncore likes to call C{'getpeername'}.)
|
||||
|
||||
@return: the address if the remote host, if known
|
||||
@rtype: tuple(str, int)
|
||||
This simply wraps `.Transport.getpeername`, used to provide enough of a
|
||||
socket-like interface to allow asyncore to work. (asyncore likes to
|
||||
call ``'getpeername'``.)
|
||||
"""
|
||||
return self.transport.getpeername()
|
||||
|
||||
|
@ -564,7 +532,7 @@ class Channel (object):
|
|||
"""
|
||||
Close the channel. All future read/write operations on the channel
|
||||
will fail. The remote end will receive no more data (after queued data
|
||||
is flushed). Channels are automatically closed when their L{Transport}
|
||||
is flushed). Channels are automatically closed when their `.Transport`
|
||||
is closed or when they are garbage collected.
|
||||
"""
|
||||
self.lock.acquire()
|
||||
|
@ -589,12 +557,12 @@ class Channel (object):
|
|||
def recv_ready(self):
|
||||
"""
|
||||
Returns true if data is buffered and ready to be read from this
|
||||
channel. A C{False} result does not mean that the channel has closed;
|
||||
channel. A ``False`` result does not mean that the channel has closed;
|
||||
it means you may need to wait before more data arrives.
|
||||
|
||||
@return: C{True} if a L{recv} call on this channel would immediately
|
||||
return at least one byte; C{False} otherwise.
|
||||
@rtype: boolean
|
||||
:return:
|
||||
``True`` if a `recv` call on this channel would immediately return
|
||||
at least one byte; ``False`` otherwise.
|
||||
"""
|
||||
return self.in_buffer.read_ready()
|
||||
|
||||
|
@ -602,16 +570,14 @@ class Channel (object):
|
|||
"""
|
||||
Receive data from the channel. The return value is a string
|
||||
representing the data received. The maximum amount of data to be
|
||||
received at once is specified by C{nbytes}. If a string of length zero
|
||||
received at once is specified by ``nbytes``. If a string of length zero
|
||||
is returned, the channel stream has closed.
|
||||
|
||||
@param nbytes: maximum number of bytes to read.
|
||||
@type nbytes: int
|
||||
@return: data.
|
||||
@rtype: str
|
||||
:param int nbytes: maximum number of bytes to read.
|
||||
:return: received data, as a `str`
|
||||
|
||||
@raise socket.timeout: if no data is ready before the timeout set by
|
||||
L{settimeout}.
|
||||
:raises socket.timeout:
|
||||
if no data is ready before the timeout set by `settimeout`.
|
||||
"""
|
||||
try:
|
||||
out = self.in_buffer.read(nbytes, self.timeout)
|
||||
|
@ -632,36 +598,34 @@ class Channel (object):
|
|||
def recv_stderr_ready(self):
|
||||
"""
|
||||
Returns true if data is buffered and ready to be read from this
|
||||
channel's stderr stream. Only channels using L{exec_command} or
|
||||
L{invoke_shell} without a pty will ever have data on the stderr
|
||||
channel's stderr stream. Only channels using `exec_command` or
|
||||
`invoke_shell` without a pty will ever have data on the stderr
|
||||
stream.
|
||||
|
||||
@return: C{True} if a L{recv_stderr} call on this channel would
|
||||
immediately return at least one byte; C{False} otherwise.
|
||||
@rtype: boolean
|
||||
:return:
|
||||
``True`` if a `recv_stderr` call on this channel would immediately
|
||||
return at least one byte; ``False`` otherwise.
|
||||
|
||||
@since: 1.1
|
||||
.. versionadded:: 1.1
|
||||
"""
|
||||
return self.in_stderr_buffer.read_ready()
|
||||
|
||||
def recv_stderr(self, nbytes):
|
||||
"""
|
||||
Receive data from the channel's stderr stream. Only channels using
|
||||
L{exec_command} or L{invoke_shell} without a pty will ever have data
|
||||
`exec_command` or `invoke_shell` without a pty will ever have data
|
||||
on the stderr stream. The return value is a string representing the
|
||||
data received. The maximum amount of data to be received at once is
|
||||
specified by C{nbytes}. If a string of length zero is returned, the
|
||||
specified by ``nbytes``. If a string of length zero is returned, the
|
||||
channel stream has closed.
|
||||
|
||||
@param nbytes: maximum number of bytes to read.
|
||||
@type nbytes: int
|
||||
@return: data.
|
||||
@rtype: str
|
||||
:param int nbytes: maximum number of bytes to read.
|
||||
:return: received data as a `str`
|
||||
|
||||
@raise socket.timeout: if no data is ready before the timeout set by
|
||||
L{settimeout}.
|
||||
:raises socket.timeout: if no data is ready before the timeout set by
|
||||
`settimeout`.
|
||||
|
||||
@since: 1.1
|
||||
.. versionadded:: 1.1
|
||||
"""
|
||||
try:
|
||||
out = self.in_stderr_buffer.read(nbytes, self.timeout)
|
||||
|
@ -685,12 +649,12 @@ class Channel (object):
|
|||
This means the channel is either closed (so any write attempt would
|
||||
return immediately) or there is at least one byte of space in the
|
||||
outbound buffer. If there is at least one byte of space in the
|
||||
outbound buffer, a L{send} call will succeed immediately and return
|
||||
outbound buffer, a `send` call will succeed immediately and return
|
||||
the number of bytes actually written.
|
||||
|
||||
@return: C{True} if a L{send} call on this channel would immediately
|
||||
succeed or fail
|
||||
@rtype: boolean
|
||||
:return:
|
||||
``True`` if a `send` call on this channel would immediately succeed
|
||||
or fail
|
||||
"""
|
||||
self.lock.acquire()
|
||||
try:
|
||||
|
@ -708,13 +672,11 @@ class Channel (object):
|
|||
transmitted, the application needs to attempt delivery of the remaining
|
||||
data.
|
||||
|
||||
@param s: data to send
|
||||
@type s: str
|
||||
@return: number of bytes actually sent
|
||||
@rtype: int
|
||||
:param str s: data to send
|
||||
:return: number of bytes actually sent, as an `int`
|
||||
|
||||
@raise socket.timeout: if no data could be sent before the timeout set
|
||||
by L{settimeout}.
|
||||
:raises socket.timeout: if no data could be sent before the timeout set
|
||||
by `settimeout`.
|
||||
"""
|
||||
size = len(s)
|
||||
self.lock.acquire()
|
||||
|
@ -743,15 +705,13 @@ class Channel (object):
|
|||
data has been sent: if only some of the data was transmitted, the
|
||||
application needs to attempt delivery of the remaining data.
|
||||
|
||||
@param s: data to send.
|
||||
@type s: str
|
||||
@return: number of bytes actually sent.
|
||||
@rtype: int
|
||||
:param str s: data to send.
|
||||
:return: number of bytes actually sent, as an `int`.
|
||||
|
||||
@raise socket.timeout: if no data could be sent before the timeout set
|
||||
by L{settimeout}.
|
||||
:raises socket.timeout:
|
||||
if no data could be sent before the timeout set by `settimeout`.
|
||||
|
||||
@since: 1.1
|
||||
.. versionadded:: 1.1
|
||||
"""
|
||||
size = len(s)
|
||||
self.lock.acquire()
|
||||
|
@ -775,20 +735,20 @@ class Channel (object):
|
|||
def sendall(self, s):
|
||||
"""
|
||||
Send data to the channel, without allowing partial results. Unlike
|
||||
L{send}, this method continues to send data from the given string until
|
||||
`send`, this method continues to send data from the given string until
|
||||
either all data has been sent or an error occurs. Nothing is returned.
|
||||
|
||||
@param s: data to send.
|
||||
@type s: str
|
||||
:param str s: data to send.
|
||||
|
||||
@raise socket.timeout: if sending stalled for longer than the timeout
|
||||
set by L{settimeout}.
|
||||
@raise socket.error: if an error occured before the entire string was
|
||||
sent.
|
||||
:raises socket.timeout:
|
||||
if sending stalled for longer than the timeout set by `settimeout`.
|
||||
:raises socket.error:
|
||||
if an error occured before the entire string was sent.
|
||||
|
||||
@note: If the channel is closed while only part of the data hase been
|
||||
.. note::
|
||||
If the channel is closed while only part of the data hase been
|
||||
sent, there is no way to determine how much data (if any) was sent.
|
||||
This is irritating, but identically follows python's API.
|
||||
This is irritating, but identically follows Python's API.
|
||||
"""
|
||||
while s:
|
||||
if self.closed:
|
||||
|
@ -801,19 +761,18 @@ class Channel (object):
|
|||
def sendall_stderr(self, s):
|
||||
"""
|
||||
Send data to the channel's "stderr" stream, without allowing partial
|
||||
results. Unlike L{send_stderr}, this method continues to send data
|
||||
results. Unlike `send_stderr`, this method continues to send data
|
||||
from the given string until all data has been sent or an error occurs.
|
||||
Nothing is returned.
|
||||
|
||||
@param s: data to send to the client as "stderr" output.
|
||||
@type s: str
|
||||
:param str s: data to send to the client as "stderr" output.
|
||||
|
||||
@raise socket.timeout: if sending stalled for longer than the timeout
|
||||
set by L{settimeout}.
|
||||
@raise socket.error: if an error occured before the entire string was
|
||||
sent.
|
||||
:raises socket.timeout:
|
||||
if sending stalled for longer than the timeout set by `settimeout`.
|
||||
:raises socket.error:
|
||||
if an error occured before the entire string was sent.
|
||||
|
||||
@since: 1.1
|
||||
.. versionadded:: 1.1
|
||||
"""
|
||||
while s:
|
||||
if self.closed:
|
||||
|
@ -825,49 +784,46 @@ class Channel (object):
|
|||
def makefile(self, *params):
|
||||
"""
|
||||
Return a file-like object associated with this channel. The optional
|
||||
C{mode} and C{bufsize} arguments are interpreted the same way as by
|
||||
the built-in C{file()} function in python.
|
||||
``mode`` and ``bufsize`` arguments are interpreted the same way as by
|
||||
the built-in ``file()`` function in Python.
|
||||
|
||||
@return: object which can be used for python file I/O.
|
||||
@rtype: L{ChannelFile}
|
||||
:return: `.ChannelFile` object which can be used for Python file I/O.
|
||||
"""
|
||||
return ChannelFile(*([self] + list(params)))
|
||||
|
||||
def makefile_stderr(self, *params):
|
||||
"""
|
||||
Return a file-like object associated with this channel's stderr
|
||||
stream. Only channels using L{exec_command} or L{invoke_shell}
|
||||
stream. Only channels using `exec_command` or `invoke_shell`
|
||||
without a pty will ever have data on the stderr stream.
|
||||
|
||||
The optional C{mode} and C{bufsize} arguments are interpreted the
|
||||
same way as by the built-in C{file()} function in python. For a
|
||||
The optional ``mode`` and ``bufsize`` arguments are interpreted the
|
||||
same way as by the built-in ``file()`` function in Python. For a
|
||||
client, it only makes sense to open this file for reading. For a
|
||||
server, it only makes sense to open this file for writing.
|
||||
|
||||
@return: object which can be used for python file I/O.
|
||||
@rtype: L{ChannelFile}
|
||||
:return: `.ChannelFile` object which can be used for Python file I/O.
|
||||
|
||||
@since: 1.1
|
||||
.. versionadded:: 1.1
|
||||
"""
|
||||
return ChannelStderrFile(*([self] + list(params)))
|
||||
|
||||
def fileno(self):
|
||||
"""
|
||||
Returns an OS-level file descriptor which can be used for polling, but
|
||||
but I{not} for reading or writing. This is primaily to allow python's
|
||||
C{select} module to work.
|
||||
but not for reading or writing. This is primaily to allow Python's
|
||||
``select`` module to work.
|
||||
|
||||
The first time C{fileno} is called on a channel, a pipe is created to
|
||||
The first time ``fileno`` is called on a channel, a pipe is created to
|
||||
simulate real OS-level file descriptor (FD) behavior. Because of this,
|
||||
two OS-level FDs are created, which will use up FDs faster than normal.
|
||||
(You won't notice this effect unless you have hundreds of channels
|
||||
open at the same time.)
|
||||
|
||||
@return: an OS-level file descriptor
|
||||
@rtype: int
|
||||
:return: an OS-level file descriptor (`int`)
|
||||
|
||||
@warning: This method causes channel reads to be slightly less
|
||||
efficient.
|
||||
.. warning::
|
||||
This method causes channel reads to be slightly less efficient.
|
||||
"""
|
||||
self.lock.acquire()
|
||||
try:
|
||||
|
@ -884,14 +840,14 @@ class Channel (object):
|
|||
|
||||
def shutdown(self, how):
|
||||
"""
|
||||
Shut down one or both halves of the connection. If C{how} is 0,
|
||||
further receives are disallowed. If C{how} is 1, further sends
|
||||
are disallowed. If C{how} is 2, further sends and receives are
|
||||
Shut down one or both halves of the connection. If ``how`` is 0,
|
||||
further receives are disallowed. If ``how`` is 1, further sends
|
||||
are disallowed. If ``how`` is 2, further sends and receives are
|
||||
disallowed. This closes the stream in one or both directions.
|
||||
|
||||
@param how: 0 (stop receiving), 1 (stop sending), or 2 (stop
|
||||
receiving and sending).
|
||||
@type how: int
|
||||
:param int how:
|
||||
0 (stop receiving), 1 (stop sending), or 2 (stop receiving and
|
||||
sending).
|
||||
"""
|
||||
if (how == 0) or (how == 2):
|
||||
# feign "read" shutdown
|
||||
|
@ -910,10 +866,10 @@ class Channel (object):
|
|||
Shutdown the receiving side of this socket, closing the stream in
|
||||
the incoming direction. After this call, future reads on this
|
||||
channel will fail instantly. This is a convenience method, equivalent
|
||||
to C{shutdown(0)}, for people who don't make it a habit to
|
||||
to ``shutdown(0)``, for people who don't make it a habit to
|
||||
memorize unix constants from the 1970s.
|
||||
|
||||
@since: 1.2
|
||||
.. versionadded:: 1.2
|
||||
"""
|
||||
self.shutdown(0)
|
||||
|
||||
|
@ -922,10 +878,10 @@ class Channel (object):
|
|||
Shutdown the sending side of this socket, closing the stream in
|
||||
the outgoing direction. After this call, future writes on this
|
||||
channel will fail instantly. This is a convenience method, equivalent
|
||||
to C{shutdown(1)}, for people who don't make it a habit to
|
||||
to ``shutdown(1)``, for people who don't make it a habit to
|
||||
memorize unix constants from the 1970s.
|
||||
|
||||
@since: 1.2
|
||||
.. versionadded:: 1.2
|
||||
"""
|
||||
self.shutdown(1)
|
||||
|
||||
|
@ -1196,7 +1152,7 @@ class Channel (object):
|
|||
def _wait_for_send_window(self, size):
|
||||
"""
|
||||
(You are already holding the lock.)
|
||||
Wait for the send window to open up, and allocate up to C{size} bytes
|
||||
Wait for the send window to open up, and allocate up to ``size`` bytes
|
||||
for transmission. If no space opens up before the timeout, a timeout
|
||||
exception is raised. Returns the number of bytes available to send
|
||||
(may be less than requested).
|
||||
|
@ -1234,13 +1190,15 @@ class Channel (object):
|
|||
|
||||
class ChannelFile (BufferedFile):
|
||||
"""
|
||||
A file-like wrapper around L{Channel}. A ChannelFile is created by calling
|
||||
L{Channel.makefile}.
|
||||
A file-like wrapper around `.Channel`. A ChannelFile is created by calling
|
||||
`Channel.makefile`.
|
||||
|
||||
@bug: To correctly emulate the file object created from a socket's
|
||||
C{makefile} method, a L{Channel} and its C{ChannelFile} should be able
|
||||
to be closed or garbage-collected independently. Currently, closing
|
||||
the C{ChannelFile} does nothing but flush the buffer.
|
||||
.. warning::
|
||||
To correctly emulate the file object created from a socket's `makefile
|
||||
<python:socket.socket.makefile>` method, a `.Channel` and its
|
||||
`.ChannelFile` should be able to be closed or garbage-collected
|
||||
independently. Currently, closing the `ChannelFile` does nothing but
|
||||
flush the buffer.
|
||||
"""
|
||||
|
||||
def __init__(self, channel, mode = 'r', bufsize = -1):
|
||||
|
@ -1251,8 +1209,6 @@ class ChannelFile (BufferedFile):
|
|||
def __repr__(self):
|
||||
"""
|
||||
Returns a string representation of this object, for debugging.
|
||||
|
||||
@rtype: str
|
||||
"""
|
||||
return '<paramiko.ChannelFile from ' + repr(self.channel) + '>'
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
L{SSHClient}.
|
||||
SSH client & key policies
|
||||
"""
|
||||
|
||||
from binascii import hexlify
|
||||
|
@ -38,67 +38,10 @@ from paramiko.transport import Transport
|
|||
from paramiko.util import retry_on_signal
|
||||
|
||||
|
||||
class MissingHostKeyPolicy (object):
|
||||
"""
|
||||
Interface for defining the policy that L{SSHClient} should use when the
|
||||
SSH server's hostname is not in either the system host keys or the
|
||||
application's keys. Pre-made classes implement policies for automatically
|
||||
adding the key to the application's L{HostKeys} object (L{AutoAddPolicy}),
|
||||
and for automatically rejecting the key (L{RejectPolicy}).
|
||||
|
||||
This function may be used to ask the user to verify the key, for example.
|
||||
"""
|
||||
|
||||
def missing_host_key(self, client, hostname, key):
|
||||
"""
|
||||
Called when an L{SSHClient} receives a server key for a server that
|
||||
isn't in either the system or local L{HostKeys} object. To accept
|
||||
the key, simply return. To reject, raised an exception (which will
|
||||
be passed to the calling application).
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class AutoAddPolicy (MissingHostKeyPolicy):
|
||||
"""
|
||||
Policy for automatically adding the hostname and new host key to the
|
||||
local L{HostKeys} object, and saving it. This is used by L{SSHClient}.
|
||||
"""
|
||||
|
||||
def missing_host_key(self, client, hostname, key):
|
||||
client._host_keys.add(hostname, key.get_name(), key)
|
||||
if client._host_keys_filename is not None:
|
||||
client.save_host_keys(client._host_keys_filename)
|
||||
client._log(DEBUG, 'Adding %s host key for %s: %s' %
|
||||
(key.get_name(), hostname, hexlify(key.get_fingerprint())))
|
||||
|
||||
|
||||
class RejectPolicy (MissingHostKeyPolicy):
|
||||
"""
|
||||
Policy for automatically rejecting the unknown hostname & key. This is
|
||||
used by L{SSHClient}.
|
||||
"""
|
||||
|
||||
def missing_host_key(self, client, hostname, key):
|
||||
client._log(DEBUG, 'Rejecting %s host key for %s: %s' %
|
||||
(key.get_name(), hostname, hexlify(key.get_fingerprint())))
|
||||
raise SSHException('Server %r not found in known_hosts' % hostname)
|
||||
|
||||
|
||||
class WarningPolicy (MissingHostKeyPolicy):
|
||||
"""
|
||||
Policy for logging a python-style warning for an unknown host key, but
|
||||
accepting it. This is used by L{SSHClient}.
|
||||
"""
|
||||
def missing_host_key(self, client, hostname, key):
|
||||
warnings.warn('Unknown %s host key for %s: %s' %
|
||||
(key.get_name(), hostname, hexlify(key.get_fingerprint())))
|
||||
|
||||
|
||||
class SSHClient (object):
|
||||
"""
|
||||
A high-level representation of a session with an SSH server. This class
|
||||
wraps L{Transport}, L{Channel}, and L{SFTPClient} to take care of most
|
||||
wraps `.Transport`, `.Channel`, and `.SFTPClient` to take care of most
|
||||
aspects of authenticating and opening channels. A typical use case is::
|
||||
|
||||
client = SSHClient()
|
||||
|
@ -110,7 +53,7 @@ class SSHClient (object):
|
|||
checking. The default mechanism is to try to use local key files or an
|
||||
SSH agent (if one is running).
|
||||
|
||||
@since: 1.6
|
||||
.. versionadded:: 1.6
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
@ -128,22 +71,21 @@ class SSHClient (object):
|
|||
def load_system_host_keys(self, filename=None):
|
||||
"""
|
||||
Load host keys from a system (read-only) file. Host keys read with
|
||||
this method will not be saved back by L{save_host_keys}.
|
||||
this method will not be saved back by `save_host_keys`.
|
||||
|
||||
This method can be called multiple times. Each new set of host keys
|
||||
will be merged with the existing set (new replacing old if there are
|
||||
conflicts).
|
||||
|
||||
If C{filename} is left as C{None}, an attempt will be made to read
|
||||
If ``filename`` is left as ``None``, an attempt will be made to read
|
||||
keys from the user's local "known hosts" file, as used by OpenSSH,
|
||||
and no exception will be raised if the file can't be read. This is
|
||||
probably only useful on posix.
|
||||
|
||||
@param filename: the filename to read, or C{None}
|
||||
@type filename: str
|
||||
:param str filename: the filename to read, or ``None``
|
||||
|
||||
@raise IOError: if a filename was provided and the file could not be
|
||||
read
|
||||
:raises IOError:
|
||||
if a filename was provided and the file could not be read
|
||||
"""
|
||||
if filename is None:
|
||||
# try the user's .ssh key file, and mask exceptions
|
||||
|
@ -158,19 +100,18 @@ class SSHClient (object):
|
|||
def load_host_keys(self, filename):
|
||||
"""
|
||||
Load host keys from a local host-key file. Host keys read with this
|
||||
method will be checked I{after} keys loaded via L{load_system_host_keys},
|
||||
but will be saved back by L{save_host_keys} (so they can be modified).
|
||||
The missing host key policy L{AutoAddPolicy} adds keys to this set and
|
||||
method will be checked after keys loaded via `load_system_host_keys`,
|
||||
but will be saved back by `save_host_keys` (so they can be modified).
|
||||
The missing host key policy `.AutoAddPolicy` adds keys to this set and
|
||||
saves them, when connecting to a previously-unknown server.
|
||||
|
||||
This method can be called multiple times. Each new set of host keys
|
||||
will be merged with the existing set (new replacing old if there are
|
||||
conflicts). When automatically saving, the last hostname is used.
|
||||
|
||||
@param filename: the filename to read
|
||||
@type filename: str
|
||||
:param str filename: the filename to read
|
||||
|
||||
@raise IOError: if the filename could not be read
|
||||
:raises IOError: if the filename could not be read
|
||||
"""
|
||||
self._host_keys_filename = filename
|
||||
self._host_keys.load(filename)
|
||||
|
@ -178,13 +119,12 @@ class SSHClient (object):
|
|||
def save_host_keys(self, filename):
|
||||
"""
|
||||
Save the host keys back to a file. Only the host keys loaded with
|
||||
L{load_host_keys} (plus any added directly) will be saved -- not any
|
||||
host keys loaded with L{load_system_host_keys}.
|
||||
`load_host_keys` (plus any added directly) will be saved -- not any
|
||||
host keys loaded with `load_system_host_keys`.
|
||||
|
||||
@param filename: the filename to save to
|
||||
@type filename: str
|
||||
:param str filename: the filename to save to
|
||||
|
||||
@raise IOError: if the file could not be written
|
||||
:raises IOError: if the file could not be written
|
||||
"""
|
||||
|
||||
# update local host keys from file (in case other SSH clients
|
||||
|
@ -200,34 +140,32 @@ class SSHClient (object):
|
|||
|
||||
def get_host_keys(self):
|
||||
"""
|
||||
Get the local L{HostKeys} object. This can be used to examine the
|
||||
Get the local `.HostKeys` object. This can be used to examine the
|
||||
local host keys or change them.
|
||||
|
||||
@return: the local host keys
|
||||
@rtype: L{HostKeys}
|
||||
:return: the local host keys as a `.HostKeys` object.
|
||||
"""
|
||||
return self._host_keys
|
||||
|
||||
def set_log_channel(self, name):
|
||||
"""
|
||||
Set the channel for logging. The default is C{"paramiko.transport"}
|
||||
Set the channel for logging. The default is ``"paramiko.transport"``
|
||||
but it can be set to anything you want.
|
||||
|
||||
@param name: new channel name for logging
|
||||
@type name: str
|
||||
:param str name: new channel name for logging
|
||||
"""
|
||||
self._log_channel = name
|
||||
|
||||
def set_missing_host_key_policy(self, policy):
|
||||
"""
|
||||
Set the policy to use when connecting to a server that doesn't have a
|
||||
host key in either the system or local L{HostKeys} objects. The
|
||||
default policy is to reject all unknown servers (using L{RejectPolicy}).
|
||||
You may substitute L{AutoAddPolicy} or write your own policy class.
|
||||
host key in either the system or local `.HostKeys` objects. The
|
||||
default policy is to reject all unknown servers (using `.RejectPolicy`).
|
||||
You may substitute `.AutoAddPolicy` or write your own policy class.
|
||||
|
||||
@param policy: the policy to use when receiving a host key from a
|
||||
:param .MissingHostKeyPolicy policy:
|
||||
the policy to use when receiving a host key from a
|
||||
previously-unknown server
|
||||
@type policy: L{MissingHostKeyPolicy}
|
||||
"""
|
||||
self._policy = policy
|
||||
|
||||
|
@ -236,56 +174,49 @@ class SSHClient (object):
|
|||
compress=False, sock=None):
|
||||
"""
|
||||
Connect to an SSH server and authenticate to it. The server's host key
|
||||
is checked against the system host keys (see L{load_system_host_keys})
|
||||
and any local host keys (L{load_host_keys}). If the server's hostname
|
||||
is checked against the system host keys (see `load_system_host_keys`)
|
||||
and any local host keys (`load_host_keys`). If the server's hostname
|
||||
is not found in either set of host keys, the missing host key policy
|
||||
is used (see L{set_missing_host_key_policy}). The default policy is
|
||||
to reject the key and raise an L{SSHException}.
|
||||
is used (see `set_missing_host_key_policy`). The default policy is
|
||||
to reject the key and raise an `.SSHException`.
|
||||
|
||||
Authentication is attempted in the following order of priority:
|
||||
|
||||
- The C{pkey} or C{key_filename} passed in (if any)
|
||||
- The ``pkey`` or ``key_filename`` passed in (if any)
|
||||
- Any key we can find through an SSH agent
|
||||
- Any "id_rsa" or "id_dsa" key discoverable in C{~/.ssh/}
|
||||
- Any "id_rsa" or "id_dsa" key discoverable in ``~/.ssh/``
|
||||
- Plain username/password auth, if a password was given
|
||||
|
||||
If a private key requires a password to unlock it, and a password is
|
||||
passed in, that password will be used to attempt to unlock the key.
|
||||
|
||||
@param hostname: the server to connect to
|
||||
@type hostname: str
|
||||
@param port: the server port to connect to
|
||||
@type port: int
|
||||
@param username: the username to authenticate as (defaults to the
|
||||
current local username)
|
||||
@type username: str
|
||||
@param password: a password to use for authentication or for unlocking
|
||||
a private key
|
||||
@type password: str
|
||||
@param pkey: an optional private key to use for authentication
|
||||
@type pkey: L{PKey}
|
||||
@param key_filename: the filename, or list of filenames, of optional
|
||||
private key(s) to try for authentication
|
||||
@type key_filename: str or list(str)
|
||||
@param timeout: an optional timeout (in seconds) for the TCP connect
|
||||
@type timeout: float
|
||||
@param allow_agent: set to False to disable connecting to the SSH agent
|
||||
@type allow_agent: bool
|
||||
@param look_for_keys: set to False to disable searching for discoverable
|
||||
private key files in C{~/.ssh/}
|
||||
@type look_for_keys: bool
|
||||
@param compress: set to True to turn on compression
|
||||
@type compress: bool
|
||||
@param sock: an open socket or socket-like object (such as a
|
||||
L{Channel}) to use for communication to the target host
|
||||
@type sock: socket
|
||||
:param str hostname: the server to connect to
|
||||
:param int port: the server port to connect to
|
||||
:param str username:
|
||||
the username to authenticate as (defaults to the current local
|
||||
username)
|
||||
:param str password:
|
||||
a password to use for authentication or for unlocking a private key
|
||||
:param .PKey pkey: an optional private key to use for authentication
|
||||
:param str key_filename:
|
||||
the filename, or list of filenames, of optional private key(s) to
|
||||
try for authentication
|
||||
:param float timeout: an optional timeout (in seconds) for the TCP connect
|
||||
:param bool allow_agent: set to False to disable connecting to the SSH agent
|
||||
:param bool look_for_keys:
|
||||
set to False to disable searching for discoverable private key
|
||||
files in ``~/.ssh/``
|
||||
:param bool compress: set to True to turn on compression
|
||||
:param socket sock:
|
||||
an open socket or socket-like object (such as a `.Channel`) to use
|
||||
for communication to the target host
|
||||
|
||||
@raise BadHostKeyException: if the server's host key could not be
|
||||
:raises BadHostKeyException: if the server's host key could not be
|
||||
verified
|
||||
@raise AuthenticationException: if authentication failed
|
||||
@raise SSHException: if there was any other error connecting or
|
||||
:raises AuthenticationException: if authentication failed
|
||||
:raises SSHException: if there was any other error connecting or
|
||||
establishing an SSH session
|
||||
@raise socket.error: if a socket error occurred while connecting
|
||||
:raises socket.error: if a socket error occurred while connecting
|
||||
"""
|
||||
if not sock:
|
||||
for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM):
|
||||
|
@ -343,7 +274,7 @@ class SSHClient (object):
|
|||
|
||||
def close(self):
|
||||
"""
|
||||
Close this SSHClient and its underlying L{Transport}.
|
||||
Close this SSHClient and its underlying `.Transport`.
|
||||
"""
|
||||
if self._transport is None:
|
||||
return
|
||||
|
@ -356,21 +287,22 @@ class SSHClient (object):
|
|||
|
||||
def exec_command(self, command, bufsize=-1, timeout=None, get_pty=False):
|
||||
"""
|
||||
Execute a command on the SSH server. A new L{Channel} is opened and
|
||||
Execute a command on the SSH server. A new `.Channel` is opened and
|
||||
the requested command is executed. The command's input and output
|
||||
streams are returned as python C{file}-like objects representing
|
||||
streams are returned as Python ``file``-like objects representing
|
||||
stdin, stdout, and stderr.
|
||||
|
||||
@param command: the command to execute
|
||||
@type command: str
|
||||
@param bufsize: interpreted the same way as by the built-in C{file()} function in python
|
||||
@type bufsize: int
|
||||
@param timeout: set command's channel timeout. See L{Channel.settimeout}.settimeout
|
||||
@type timeout: int
|
||||
@return: the stdin, stdout, and stderr of the executing command
|
||||
@rtype: tuple(L{ChannelFile}, L{ChannelFile}, L{ChannelFile})
|
||||
:param str command: the command to execute
|
||||
:param int bufsize:
|
||||
interpreted the same way as by the built-in ``file()`` function in
|
||||
Python
|
||||
:param int timeout:
|
||||
set command's channel timeout. See `Channel.settimeout`.settimeout
|
||||
:return:
|
||||
the stdin, stdout, and stderr of the executing command, as a
|
||||
3-tuple
|
||||
|
||||
@raise SSHException: if the server fails to execute the command
|
||||
:raises SSHException: if the server fails to execute the command
|
||||
"""
|
||||
chan = self._transport.open_session()
|
||||
if(get_pty):
|
||||
|
@ -385,24 +317,19 @@ class SSHClient (object):
|
|||
def invoke_shell(self, term='vt100', width=80, height=24, width_pixels=0,
|
||||
height_pixels=0):
|
||||
"""
|
||||
Start an interactive shell session on the SSH server. A new L{Channel}
|
||||
Start an interactive shell session on the SSH server. A new `.Channel`
|
||||
is opened and connected to a pseudo-terminal using the requested
|
||||
terminal type and size.
|
||||
|
||||
@param term: the terminal type to emulate (for example, C{"vt100"})
|
||||
@type term: str
|
||||
@param width: the width (in characters) of the terminal window
|
||||
@type width: int
|
||||
@param height: the height (in characters) of the terminal window
|
||||
@type height: int
|
||||
@param width_pixels: the width (in pixels) of the terminal window
|
||||
@type width_pixels: int
|
||||
@param height_pixels: the height (in pixels) of the terminal window
|
||||
@type height_pixels: int
|
||||
@return: a new channel connected to the remote shell
|
||||
@rtype: L{Channel}
|
||||
:param str term:
|
||||
the terminal type to emulate (for example, ``"vt100"``)
|
||||
:param int width: the width (in characters) of the terminal window
|
||||
:param int height: the height (in characters) of the terminal window
|
||||
:param int width_pixels: the width (in pixels) of the terminal window
|
||||
:param int height_pixels: the height (in pixels) of the terminal window
|
||||
:return: a new `.Channel` connected to the remote shell
|
||||
|
||||
@raise SSHException: if the server fails to invoke a shell
|
||||
:raises SSHException: if the server fails to invoke a shell
|
||||
"""
|
||||
chan = self._transport.open_session()
|
||||
chan.get_pty(term, width, height, width_pixels, height_pixels)
|
||||
|
@ -413,19 +340,17 @@ class SSHClient (object):
|
|||
"""
|
||||
Open an SFTP session on the SSH server.
|
||||
|
||||
@return: a new SFTP session object
|
||||
@rtype: L{SFTPClient}
|
||||
:return: a new `.SFTPClient` session object
|
||||
"""
|
||||
return self._transport.open_sftp_client()
|
||||
|
||||
def get_transport(self):
|
||||
"""
|
||||
Return the underlying L{Transport} object for this SSH connection.
|
||||
Return the underlying `.Transport` object for this SSH connection.
|
||||
This can be used to perform lower-level tasks, like opening specific
|
||||
kinds of channels.
|
||||
|
||||
@return: the Transport for this connection
|
||||
@rtype: L{Transport}
|
||||
:return: the `.Transport` for this connection
|
||||
"""
|
||||
return self._transport
|
||||
|
||||
|
@ -536,3 +461,59 @@ class SSHClient (object):
|
|||
def _log(self, level, msg):
|
||||
self._transport._log(level, msg)
|
||||
|
||||
|
||||
class MissingHostKeyPolicy (object):
|
||||
"""
|
||||
Interface for defining the policy that `.SSHClient` should use when the
|
||||
SSH server's hostname is not in either the system host keys or the
|
||||
application's keys. Pre-made classes implement policies for automatically
|
||||
adding the key to the application's `.HostKeys` object (`.AutoAddPolicy`),
|
||||
and for automatically rejecting the key (`.RejectPolicy`).
|
||||
|
||||
This function may be used to ask the user to verify the key, for example.
|
||||
"""
|
||||
|
||||
def missing_host_key(self, client, hostname, key):
|
||||
"""
|
||||
Called when an `.SSHClient` receives a server key for a server that
|
||||
isn't in either the system or local `.HostKeys` object. To accept
|
||||
the key, simply return. To reject, raised an exception (which will
|
||||
be passed to the calling application).
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class AutoAddPolicy (MissingHostKeyPolicy):
|
||||
"""
|
||||
Policy for automatically adding the hostname and new host key to the
|
||||
local `.HostKeys` object, and saving it. This is used by `.SSHClient`.
|
||||
"""
|
||||
|
||||
def missing_host_key(self, client, hostname, key):
|
||||
client._host_keys.add(hostname, key.get_name(), key)
|
||||
if client._host_keys_filename is not None:
|
||||
client.save_host_keys(client._host_keys_filename)
|
||||
client._log(DEBUG, 'Adding %s host key for %s: %s' %
|
||||
(key.get_name(), hostname, hexlify(key.get_fingerprint())))
|
||||
|
||||
|
||||
class RejectPolicy (MissingHostKeyPolicy):
|
||||
"""
|
||||
Policy for automatically rejecting the unknown hostname & key. This is
|
||||
used by `.SSHClient`.
|
||||
"""
|
||||
|
||||
def missing_host_key(self, client, hostname, key):
|
||||
client._log(DEBUG, 'Rejecting %s host key for %s: %s' %
|
||||
(key.get_name(), hostname, hexlify(key.get_fingerprint())))
|
||||
raise SSHException('Server %r not found in known_hosts' % hostname)
|
||||
|
||||
|
||||
class WarningPolicy (MissingHostKeyPolicy):
|
||||
"""
|
||||
Policy for logging a Python-style warning for an unknown host key, but
|
||||
accepting it. This is used by `.SSHClient`.
|
||||
"""
|
||||
def missing_host_key(self, client, hostname, key):
|
||||
warnings.warn('Unknown %s host key for %s: %s' %
|
||||
(key.get_name(), hostname, hexlify(key.get_fingerprint())))
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
L{SSHConfig}.
|
||||
Configuration file (aka ``ssh_config``) support.
|
||||
"""
|
||||
|
||||
import fnmatch
|
||||
|
@ -30,69 +30,15 @@ SSH_PORT = 22
|
|||
proxy_re = re.compile(r"^(proxycommand)\s*=*\s*(.*)", re.I)
|
||||
|
||||
|
||||
class LazyFqdn(object):
|
||||
"""
|
||||
Returns the host's fqdn on request as string.
|
||||
"""
|
||||
|
||||
def __init__(self, config, host=None):
|
||||
self.fqdn = None
|
||||
self.config = config
|
||||
self.host = host
|
||||
|
||||
def __str__(self):
|
||||
if self.fqdn is None:
|
||||
#
|
||||
# If the SSH config contains AddressFamily, use that when
|
||||
# determining the local host's FQDN. Using socket.getfqdn() from
|
||||
# the standard library is the most general solution, but can
|
||||
# result in noticeable delays on some platforms when IPv6 is
|
||||
# misconfigured or not available, as it calls getaddrinfo with no
|
||||
# address family specified, so both IPv4 and IPv6 are checked.
|
||||
#
|
||||
|
||||
# Handle specific option
|
||||
fqdn = None
|
||||
address_family = self.config.get('addressfamily', 'any').lower()
|
||||
if address_family != 'any':
|
||||
try:
|
||||
family = socket.AF_INET if address_family == 'inet' \
|
||||
else socket.AF_INET6
|
||||
results = socket.getaddrinfo(
|
||||
self.host,
|
||||
None,
|
||||
family,
|
||||
socket.SOCK_DGRAM,
|
||||
socket.IPPROTO_IP,
|
||||
socket.AI_CANONNAME
|
||||
)
|
||||
for res in results:
|
||||
af, socktype, proto, canonname, sa = res
|
||||
if canonname and '.' in canonname:
|
||||
fqdn = canonname
|
||||
break
|
||||
# giaerror -> socket.getaddrinfo() can't resolve self.host
|
||||
# (which is from socket.gethostname()). Fall back to the
|
||||
# getfqdn() call below.
|
||||
except socket.gaierror:
|
||||
pass
|
||||
# Handle 'any' / unspecified
|
||||
if fqdn is None:
|
||||
fqdn = socket.getfqdn()
|
||||
# Cache
|
||||
self.fqdn = fqdn
|
||||
return self.fqdn
|
||||
|
||||
|
||||
class SSHConfig (object):
|
||||
"""
|
||||
Representation of config information as stored in the format used by
|
||||
OpenSSH. Queries can be made via L{lookup}. The format is described in
|
||||
OpenSSH's C{ssh_config} man page. This class is provided primarily as a
|
||||
OpenSSH. Queries can be made via `lookup`. The format is described in
|
||||
OpenSSH's ``ssh_config`` man page. This class is provided primarily as a
|
||||
convenience to posix users (since the OpenSSH format is a de-facto
|
||||
standard on posix) but should work fine on Windows too.
|
||||
|
||||
@since: 1.6
|
||||
.. versionadded:: 1.6
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
@ -105,8 +51,7 @@ class SSHConfig (object):
|
|||
"""
|
||||
Read an OpenSSH config from the given file object.
|
||||
|
||||
@param file_obj: a file-like object to read the config file from
|
||||
@type file_obj: file
|
||||
:param file file_obj: a file-like object to read the config file from
|
||||
"""
|
||||
host = {"host": ['*'], "config": {}}
|
||||
for line in file_obj:
|
||||
|
@ -152,22 +97,20 @@ class SSHConfig (object):
|
|||
"""
|
||||
Return a dict of config options for a given hostname.
|
||||
|
||||
The host-matching rules of OpenSSH's C{ssh_config} man page are used,
|
||||
The host-matching rules of OpenSSH's ``ssh_config`` man page are used,
|
||||
which means that all configuration options from matching host
|
||||
specifications are merged, with more specific hostmasks taking
|
||||
precedence. In other words, if C{"Port"} is set under C{"Host *"}
|
||||
and also C{"Host *.example.com"}, and the lookup is for
|
||||
C{"ssh.example.com"}, then the port entry for C{"Host *.example.com"}
|
||||
precedence. In other words, if ``"Port"`` is set under ``"Host *"``
|
||||
and also ``"Host *.example.com"``, and the lookup is for
|
||||
``"ssh.example.com"``, then the port entry for ``"Host *.example.com"``
|
||||
will win out.
|
||||
|
||||
The keys in the returned dict are all normalized to lowercase (look for
|
||||
C{"port"}, not C{"Port"}. The values are processed according to the
|
||||
rules for substitution variable expansion in C{ssh_config}.
|
||||
``"port"``, not ``"Port"``. The values are processed according to the
|
||||
rules for substitution variable expansion in ``ssh_config``.
|
||||
|
||||
@param hostname: the hostname to lookup
|
||||
@type hostname: str
|
||||
:param str hostname: the hostname to lookup
|
||||
"""
|
||||
|
||||
matches = [config for config in self._config if
|
||||
self._allowed(hostname, config['host'])]
|
||||
|
||||
|
@ -199,13 +142,11 @@ class SSHConfig (object):
|
|||
Return a dict of config options with expanded substitutions
|
||||
for a given hostname.
|
||||
|
||||
Please refer to man C{ssh_config} for the parameters that
|
||||
Please refer to man ``ssh_config`` for the parameters that
|
||||
are replaced.
|
||||
|
||||
@param config: the config for the hostname
|
||||
@type hostname: dict
|
||||
@param hostname: the hostname that the config belongs to
|
||||
@type hostname: str
|
||||
:param dict config: the config for the hostname
|
||||
:param str hostname: the hostname that the config belongs to
|
||||
"""
|
||||
|
||||
if 'hostname' in config:
|
||||
|
@ -264,3 +205,57 @@ class SSHConfig (object):
|
|||
else:
|
||||
config[k] = config[k].replace(find, str(replace))
|
||||
return config
|
||||
|
||||
|
||||
class LazyFqdn(object):
|
||||
"""
|
||||
Returns the host's fqdn on request as string.
|
||||
"""
|
||||
|
||||
def __init__(self, config, host=None):
|
||||
self.fqdn = None
|
||||
self.config = config
|
||||
self.host = host
|
||||
|
||||
def __str__(self):
|
||||
if self.fqdn is None:
|
||||
#
|
||||
# If the SSH config contains AddressFamily, use that when
|
||||
# determining the local host's FQDN. Using socket.getfqdn() from
|
||||
# the standard library is the most general solution, but can
|
||||
# result in noticeable delays on some platforms when IPv6 is
|
||||
# misconfigured or not available, as it calls getaddrinfo with no
|
||||
# address family specified, so both IPv4 and IPv6 are checked.
|
||||
#
|
||||
|
||||
# Handle specific option
|
||||
fqdn = None
|
||||
address_family = self.config.get('addressfamily', 'any').lower()
|
||||
if address_family != 'any':
|
||||
try:
|
||||
family = socket.AF_INET if address_family == 'inet' \
|
||||
else socket.AF_INET6
|
||||
results = socket.getaddrinfo(
|
||||
self.host,
|
||||
None,
|
||||
family,
|
||||
socket.SOCK_DGRAM,
|
||||
socket.IPPROTO_IP,
|
||||
socket.AI_CANONNAME
|
||||
)
|
||||
for res in results:
|
||||
af, socktype, proto, canonname, sa = res
|
||||
if canonname and '.' in canonname:
|
||||
fqdn = canonname
|
||||
break
|
||||
# giaerror -> socket.getaddrinfo() can't resolve self.host
|
||||
# (which is from socket.gethostname()). Fall back to the
|
||||
# getfqdn() call below.
|
||||
except socket.gaierror:
|
||||
pass
|
||||
# Handle 'any' / unspecified
|
||||
if fqdn is None:
|
||||
fqdn = socket.getfqdn()
|
||||
# Cache
|
||||
self.fqdn = fqdn
|
||||
return self.fqdn
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
L{DSSKey}
|
||||
DSS keys.
|
||||
"""
|
||||
|
||||
from Crypto.PublicKey import DSA
|
||||
|
@ -153,13 +153,11 @@ class DSSKey (PKey):
|
|||
Generate a new private DSS key. This factory function can be used to
|
||||
generate a new host key or authentication key.
|
||||
|
||||
@param bits: number of bits the generated key should be.
|
||||
@type bits: int
|
||||
@param progress_func: an optional function to call at key points in
|
||||
key generation (used by C{pyCrypto.PublicKey}).
|
||||
@type progress_func: function
|
||||
@return: new private key
|
||||
@rtype: L{DSSKey}
|
||||
:param int bits: number of bits the generated key should be.
|
||||
:param function progress_func:
|
||||
an optional function to call at key points in key generation (used
|
||||
by ``pyCrypto.PublicKey``).
|
||||
:return: new `.DSSKey` private key
|
||||
"""
|
||||
dsa = DSA.generate(bits, rng.read, progress_func)
|
||||
key = DSSKey(vals=(dsa.p, dsa.q, dsa.g, dsa.y))
|
||||
|
|
106
paramiko/file.py
106
paramiko/file.py
|
@ -16,16 +16,12 @@
|
|||
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
BufferedFile.
|
||||
"""
|
||||
|
||||
from cStringIO import StringIO
|
||||
|
||||
|
||||
class BufferedFile (object):
|
||||
"""
|
||||
Reusable base class to implement python-style file buffering around a
|
||||
Reusable base class to implement Python-style file buffering around a
|
||||
simpler stream.
|
||||
"""
|
||||
|
||||
|
@ -67,10 +63,7 @@ class BufferedFile (object):
|
|||
file. This iterator happens to return the file itself, since a file is
|
||||
its own iterator.
|
||||
|
||||
@raise ValueError: if the file is closed.
|
||||
|
||||
@return: an interator.
|
||||
@rtype: iterator
|
||||
:raises ValueError: if the file is closed.
|
||||
"""
|
||||
if self._closed:
|
||||
raise ValueError('I/O operation on closed file')
|
||||
|
@ -94,14 +87,13 @@ class BufferedFile (object):
|
|||
|
||||
def next(self):
|
||||
"""
|
||||
Returns the next line from the input, or raises L{StopIteration} when
|
||||
EOF is hit. Unlike python file objects, it's okay to mix calls to
|
||||
C{next} and L{readline}.
|
||||
Returns the next line from the input, or raises
|
||||
`~exceptions.StopIteration` when EOF is hit. Unlike Python file
|
||||
objects, it's okay to mix calls to `next` and `readline`.
|
||||
|
||||
@raise StopIteration: when the end of the file is reached.
|
||||
:raises StopIteration: when the end of the file is reached.
|
||||
|
||||
@return: a line read from the file.
|
||||
@rtype: str
|
||||
:return: a line (`str`) read from the file.
|
||||
"""
|
||||
line = self.readline()
|
||||
if not line:
|
||||
|
@ -110,15 +102,14 @@ class BufferedFile (object):
|
|||
|
||||
def read(self, size=None):
|
||||
"""
|
||||
Read at most C{size} bytes from the file (less if we hit the end of the
|
||||
file first). If the C{size} argument is negative or omitted, read all
|
||||
Read at most ``size`` bytes from the file (less if we hit the end of the
|
||||
file first). If the ``size`` argument is negative or omitted, read all
|
||||
the remaining data in the file.
|
||||
|
||||
@param size: maximum number of bytes to read
|
||||
@type size: int
|
||||
@return: data read from the file, or an empty string if EOF was
|
||||
:param int size: maximum number of bytes to read
|
||||
:return:
|
||||
data read from the file (as a `str`), or an empty string if EOF was
|
||||
encountered immediately
|
||||
@rtype: str
|
||||
"""
|
||||
if self._closed:
|
||||
raise IOError('File is closed')
|
||||
|
@ -171,14 +162,14 @@ class BufferedFile (object):
|
|||
incomplete line may be returned. An empty string is returned only when
|
||||
EOF is encountered immediately.
|
||||
|
||||
@note: Unlike stdio's C{fgets()}, the returned string contains null
|
||||
characters (C{'\\0'}) if they occurred in the input.
|
||||
.. note::
|
||||
Unlike stdio's ``fgets``, the returned string contains null
|
||||
characters (``'\\0'``) if they occurred in the input.
|
||||
|
||||
@param size: maximum length of returned string.
|
||||
@type size: int
|
||||
@return: next line of the file, or an empty string if the end of the
|
||||
:param int size: maximum length of returned string.
|
||||
:return:
|
||||
next line of the file (`str`), or an empty string if the end of the
|
||||
file has been reached.
|
||||
@rtype: str
|
||||
"""
|
||||
# it's almost silly how complex this function is.
|
||||
if self._closed:
|
||||
|
@ -243,15 +234,13 @@ class BufferedFile (object):
|
|||
|
||||
def readlines(self, sizehint=None):
|
||||
"""
|
||||
Read all remaining lines using L{readline} and return them as a list.
|
||||
If the optional C{sizehint} argument is present, instead of reading up
|
||||
Read all remaining lines using `readline` and return them as a list.
|
||||
If the optional ``sizehint`` argument is present, instead of reading up
|
||||
to EOF, whole lines totalling approximately sizehint bytes (possibly
|
||||
after rounding up to an internal buffer size) are read.
|
||||
|
||||
@param sizehint: desired maximum number of bytes to read.
|
||||
@type sizehint: int
|
||||
@return: list of lines read from the file.
|
||||
@rtype: list
|
||||
:param int sizehint: desired maximum number of bytes to read.
|
||||
:return: `list` of lines read from the file.
|
||||
"""
|
||||
lines = []
|
||||
bytes = 0
|
||||
|
@ -267,21 +256,20 @@ class BufferedFile (object):
|
|||
|
||||
def seek(self, offset, whence=0):
|
||||
"""
|
||||
Set the file's current position, like stdio's C{fseek}. Not all file
|
||||
Set the file's current position, like stdio's ``fseek``. Not all file
|
||||
objects support seeking.
|
||||
|
||||
@note: If a file is opened in append mode (C{'a'} or C{'a+'}), any seek
|
||||
.. note:: If a file is opened in append mode (``'a'`` or ``'a+'``), any seek
|
||||
operations will be undone at the next write (as the file position
|
||||
will move back to the end of the file).
|
||||
|
||||
@param offset: position to move to within the file, relative to
|
||||
C{whence}.
|
||||
@type offset: int
|
||||
@param whence: type of movement: 0 = absolute; 1 = relative to the
|
||||
current position; 2 = relative to the end of the file.
|
||||
@type whence: int
|
||||
:param int offset:
|
||||
position to move to within the file, relative to ``whence``.
|
||||
:param int whence:
|
||||
type of movement: 0 = absolute; 1 = relative to the current
|
||||
position; 2 = relative to the end of the file.
|
||||
|
||||
@raise IOError: if the file doesn't support random access.
|
||||
:raises IOError: if the file doesn't support random access.
|
||||
"""
|
||||
raise IOError('File does not support seeking.')
|
||||
|
||||
|
@ -291,20 +279,18 @@ class BufferedFile (object):
|
|||
useful if the underlying file doesn't support random access, or was
|
||||
opened in append mode.
|
||||
|
||||
@return: file position (in bytes).
|
||||
@rtype: int
|
||||
:return: file position (`number <int>` of bytes).
|
||||
"""
|
||||
return self._pos
|
||||
|
||||
def write(self, data):
|
||||
"""
|
||||
Write data to the file. If write buffering is on (C{bufsize} was
|
||||
Write data to the file. If write buffering is on (``bufsize`` was
|
||||
specified and non-zero), some or all of the data may not actually be
|
||||
written yet. (Use L{flush} or L{close} to force buffered data to be
|
||||
written yet. (Use `flush` or `close` to force buffered data to be
|
||||
written out.)
|
||||
|
||||
@param data: data to write.
|
||||
@type data: str
|
||||
:param str data: data to write
|
||||
"""
|
||||
if self._closed:
|
||||
raise IOError('File is closed')
|
||||
|
@ -334,11 +320,10 @@ class BufferedFile (object):
|
|||
"""
|
||||
Write a sequence of strings to the file. The sequence can be any
|
||||
iterable object producing strings, typically a list of strings. (The
|
||||
name is intended to match L{readlines}; C{writelines} does not add line
|
||||
name is intended to match `readlines`; `writelines` does not add line
|
||||
separators.)
|
||||
|
||||
@param sequence: an iterable sequence of strings.
|
||||
@type sequence: sequence
|
||||
:param iterable sequence: an iterable sequence of strings.
|
||||
"""
|
||||
for line in sequence:
|
||||
self.write(line)
|
||||
|
@ -346,11 +331,8 @@ class BufferedFile (object):
|
|||
|
||||
def xreadlines(self):
|
||||
"""
|
||||
Identical to C{iter(f)}. This is a deprecated file interface that
|
||||
predates python iterator support.
|
||||
|
||||
@return: an iterator.
|
||||
@rtype: iterator
|
||||
Identical to ``iter(f)``. This is a deprecated file interface that
|
||||
predates Python iterator support.
|
||||
"""
|
||||
return self
|
||||
|
||||
|
@ -364,25 +346,25 @@ class BufferedFile (object):
|
|||
|
||||
def _read(self, size):
|
||||
"""
|
||||
I{(subclass override)}
|
||||
Read data from the stream. Return C{None} or raise C{EOFError} to
|
||||
(subclass override)
|
||||
Read data from the stream. Return ``None`` or raise ``EOFError`` to
|
||||
indicate EOF.
|
||||
"""
|
||||
raise EOFError()
|
||||
|
||||
def _write(self, data):
|
||||
"""
|
||||
I{(subclass override)}
|
||||
(subclass override)
|
||||
Write data into the stream.
|
||||
"""
|
||||
raise IOError('write not implemented')
|
||||
|
||||
def _get_size(self):
|
||||
"""
|
||||
I{(subclass override)}
|
||||
Return the size of the file. This is called from within L{_set_mode}
|
||||
(subclass override)
|
||||
Return the size of the file. This is called from within `_set_mode`
|
||||
if the file is opened in append mode, so the file position can be
|
||||
tracked and L{seek} and L{tell} will work correctly. If the file is
|
||||
tracked and `seek` and `tell` will work correctly. If the file is
|
||||
a stream that can't be randomly accessed, you don't need to override
|
||||
this method,
|
||||
"""
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
L{HostKeys}
|
||||
"""
|
||||
|
||||
import base64
|
||||
import binascii
|
||||
|
@ -32,8 +29,225 @@ from paramiko.util import get_logger, constant_time_bytes_eq
|
|||
from paramiko.ecdsakey import ECDSAKey
|
||||
|
||||
|
||||
class InvalidHostKey(Exception):
|
||||
class HostKeys (UserDict.DictMixin):
|
||||
"""
|
||||
Representation of an OpenSSH-style "known hosts" file. Host keys can be
|
||||
read from one or more files, and then individual hosts can be looked up to
|
||||
verify server keys during SSH negotiation.
|
||||
|
||||
A `.HostKeys` object can be treated like a dict; any dict lookup is
|
||||
equivalent to calling `lookup`.
|
||||
|
||||
.. versionadded:: 1.5.3
|
||||
"""
|
||||
|
||||
def __init__(self, filename=None):
|
||||
"""
|
||||
Create a new HostKeys object, optionally loading keys from an OpenSSH
|
||||
style host-key file.
|
||||
|
||||
:param str filename: filename to load host keys from, or ``None``
|
||||
"""
|
||||
# emulate a dict of { hostname: { keytype: PKey } }
|
||||
self._entries = []
|
||||
if filename is not None:
|
||||
self.load(filename)
|
||||
|
||||
def add(self, hostname, keytype, key):
|
||||
"""
|
||||
Add a host key entry to the table. Any existing entry for a
|
||||
``(hostname, keytype)`` pair will be replaced.
|
||||
|
||||
:param str hostname: the hostname (or IP) to add
|
||||
:param str keytype: key type (``"ssh-rsa"`` or ``"ssh-dss"``)
|
||||
:param .PKey key: the key to add
|
||||
"""
|
||||
for e in self._entries:
|
||||
if (hostname in e.hostnames) and (e.key.get_name() == keytype):
|
||||
e.key = key
|
||||
return
|
||||
self._entries.append(HostKeyEntry([hostname], key))
|
||||
|
||||
def load(self, filename):
|
||||
"""
|
||||
Read a file of known SSH host keys, in the format used by OpenSSH.
|
||||
This type of file unfortunately doesn't exist on Windows, but on
|
||||
posix, it will usually be stored in
|
||||
``os.path.expanduser("~/.ssh/known_hosts")``.
|
||||
|
||||
If this method is called multiple times, the host keys are merged,
|
||||
not cleared. So multiple calls to `load` will just call `add`,
|
||||
replacing any existing entries and adding new ones.
|
||||
|
||||
:param str filename: name of the file to read host keys from
|
||||
|
||||
:raises IOError: if there was an error reading the file
|
||||
"""
|
||||
f = open(filename, 'r')
|
||||
for lineno, line in enumerate(f):
|
||||
line = line.strip()
|
||||
if (len(line) == 0) or (line[0] == '#'):
|
||||
continue
|
||||
e = HostKeyEntry.from_line(line, lineno)
|
||||
if e is not None:
|
||||
_hostnames = e.hostnames
|
||||
for h in _hostnames:
|
||||
if self.check(h, e.key):
|
||||
e.hostnames.remove(h)
|
||||
if len(e.hostnames):
|
||||
self._entries.append(e)
|
||||
f.close()
|
||||
|
||||
def save(self, filename):
|
||||
"""
|
||||
Save host keys into a file, in the format used by OpenSSH. The order of
|
||||
keys in the file will be preserved when possible (if these keys were
|
||||
loaded from a file originally). The single exception is that combined
|
||||
lines will be split into individual key lines, which is arguably a bug.
|
||||
|
||||
:param str filename: name of the file to write
|
||||
|
||||
:raises IOError: if there was an error writing the file
|
||||
|
||||
.. versionadded:: 1.6.1
|
||||
"""
|
||||
f = open(filename, 'w')
|
||||
for e in self._entries:
|
||||
line = e.to_line()
|
||||
if line:
|
||||
f.write(line)
|
||||
f.close()
|
||||
|
||||
def lookup(self, hostname):
|
||||
"""
|
||||
Find a hostkey entry for a given hostname or IP. If no entry is found,
|
||||
``None`` is returned. Otherwise a dictionary of keytype to key is
|
||||
returned. The keytype will be either ``"ssh-rsa"`` or ``"ssh-dss"``.
|
||||
|
||||
:param str hostname: the hostname (or IP) to lookup
|
||||
:return: dict of `str` -> `.PKey` keys associated with this host (or ``None``)
|
||||
"""
|
||||
class SubDict (UserDict.DictMixin):
|
||||
def __init__(self, hostname, entries, hostkeys):
|
||||
self._hostname = hostname
|
||||
self._entries = entries
|
||||
self._hostkeys = hostkeys
|
||||
|
||||
def __getitem__(self, key):
|
||||
for e in self._entries:
|
||||
if e.key.get_name() == key:
|
||||
return e.key
|
||||
raise KeyError(key)
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
for e in self._entries:
|
||||
if e.key is None:
|
||||
continue
|
||||
if e.key.get_name() == key:
|
||||
# replace
|
||||
e.key = val
|
||||
break
|
||||
else:
|
||||
# add a new one
|
||||
e = HostKeyEntry([hostname], val)
|
||||
self._entries.append(e)
|
||||
self._hostkeys._entries.append(e)
|
||||
|
||||
def keys(self):
|
||||
return [e.key.get_name() for e in self._entries if e.key is not None]
|
||||
|
||||
entries = []
|
||||
for e in self._entries:
|
||||
for h in e.hostnames:
|
||||
if h.startswith('|1|') and constant_time_bytes_eq(self.hash_host(hostname, h), h) or h == hostname:
|
||||
entries.append(e)
|
||||
if len(entries) == 0:
|
||||
return None
|
||||
return SubDict(hostname, entries, self)
|
||||
|
||||
def check(self, hostname, key):
|
||||
"""
|
||||
Return True if the given key is associated with the given hostname
|
||||
in this dictionary.
|
||||
|
||||
:param str hostname: hostname (or IP) of the SSH server
|
||||
:param .PKey key: the key to check
|
||||
:return:
|
||||
``True`` if the key is associated with the hostname; else ``False``
|
||||
"""
|
||||
k = self.lookup(hostname)
|
||||
if k is None:
|
||||
return False
|
||||
host_key = k.get(key.get_name(), None)
|
||||
if host_key is None:
|
||||
return False
|
||||
return str(host_key) == str(key)
|
||||
|
||||
def clear(self):
|
||||
"""
|
||||
Remove all host keys from the dictionary.
|
||||
"""
|
||||
self._entries = []
|
||||
|
||||
def __getitem__(self, key):
|
||||
ret = self.lookup(key)
|
||||
if ret is None:
|
||||
raise KeyError(key)
|
||||
return ret
|
||||
|
||||
def __setitem__(self, hostname, entry):
|
||||
# don't use this please.
|
||||
if len(entry) == 0:
|
||||
self._entries.append(HostKeyEntry([hostname], None))
|
||||
return
|
||||
for key_type in entry.keys():
|
||||
found = False
|
||||
for e in self._entries:
|
||||
if (hostname in e.hostnames) and (e.key.get_name() == key_type):
|
||||
# replace
|
||||
e.key = entry[key_type]
|
||||
found = True
|
||||
if not found:
|
||||
self._entries.append(HostKeyEntry([hostname], entry[key_type]))
|
||||
|
||||
def keys(self):
|
||||
# Python 2.4 sets would be nice here.
|
||||
ret = []
|
||||
for e in self._entries:
|
||||
for h in e.hostnames:
|
||||
if h not in ret:
|
||||
ret.append(h)
|
||||
return ret
|
||||
|
||||
def values(self):
|
||||
ret = []
|
||||
for k in self.keys():
|
||||
ret.append(self.lookup(k))
|
||||
return ret
|
||||
|
||||
def hash_host(hostname, salt=None):
|
||||
"""
|
||||
Return a "hashed" form of the hostname, as used by OpenSSH when storing
|
||||
hashed hostnames in the known_hosts file.
|
||||
|
||||
:param str hostname: the hostname to hash
|
||||
:param str salt: optional salt to use when hashing (must be 20 bytes long)
|
||||
:return: the hashed hostname as a `str`
|
||||
"""
|
||||
if salt is None:
|
||||
salt = rng.read(SHA.digest_size)
|
||||
else:
|
||||
if salt.startswith('|1|'):
|
||||
salt = salt.split('|')[2]
|
||||
salt = base64.decodestring(salt)
|
||||
assert len(salt) == SHA.digest_size
|
||||
hmac = HMAC.HMAC(salt, hostname, SHA).digest()
|
||||
hostkey = '|1|%s|%s' % (base64.encodestring(salt), base64.encodestring(hmac))
|
||||
return hostkey.replace('\n', '')
|
||||
hash_host = staticmethod(hash_host)
|
||||
|
||||
|
||||
class InvalidHostKey(Exception):
|
||||
def __init__(self, line, exc):
|
||||
self.line = line
|
||||
self.exc = exc
|
||||
|
@ -54,14 +268,13 @@ class HostKeyEntry:
|
|||
"""
|
||||
Parses the given line of text to find the names for the host,
|
||||
the type of key, and the key data. The line is expected to be in the
|
||||
format used by the openssh known_hosts file.
|
||||
format used by the OpenSSH known_hosts file.
|
||||
|
||||
Lines are expected to not have leading or trailing whitespace.
|
||||
We don't bother to check for comments or empty lines. All of
|
||||
that should be taken care of before sending the line to us.
|
||||
|
||||
@param line: a line from an OpenSSH known_hosts file
|
||||
@type line: str
|
||||
:param str line: a line from an OpenSSH known_hosts file
|
||||
"""
|
||||
log = get_logger('paramiko.hostkeys')
|
||||
fields = line.split(' ')
|
||||
|
@ -107,236 +320,3 @@ class HostKeyEntry:
|
|||
|
||||
def __repr__(self):
|
||||
return '<HostKeyEntry %r: %r>' % (self.hostnames, self.key)
|
||||
|
||||
|
||||
class HostKeys (UserDict.DictMixin):
|
||||
"""
|
||||
Representation of an openssh-style "known hosts" file. Host keys can be
|
||||
read from one or more files, and then individual hosts can be looked up to
|
||||
verify server keys during SSH negotiation.
|
||||
|
||||
A HostKeys object can be treated like a dict; any dict lookup is equivalent
|
||||
to calling L{lookup}.
|
||||
|
||||
@since: 1.5.3
|
||||
"""
|
||||
|
||||
def __init__(self, filename=None):
|
||||
"""
|
||||
Create a new HostKeys object, optionally loading keys from an openssh
|
||||
style host-key file.
|
||||
|
||||
@param filename: filename to load host keys from, or C{None}
|
||||
@type filename: str
|
||||
"""
|
||||
# emulate a dict of { hostname: { keytype: PKey } }
|
||||
self._entries = []
|
||||
if filename is not None:
|
||||
self.load(filename)
|
||||
|
||||
def add(self, hostname, keytype, key):
|
||||
"""
|
||||
Add a host key entry to the table. Any existing entry for a
|
||||
C{(hostname, keytype)} pair will be replaced.
|
||||
|
||||
@param hostname: the hostname (or IP) to add
|
||||
@type hostname: str
|
||||
@param keytype: key type (C{"ssh-rsa"} or C{"ssh-dss"})
|
||||
@type keytype: str
|
||||
@param key: the key to add
|
||||
@type key: L{PKey}
|
||||
"""
|
||||
for e in self._entries:
|
||||
if (hostname in e.hostnames) and (e.key.get_name() == keytype):
|
||||
e.key = key
|
||||
return
|
||||
self._entries.append(HostKeyEntry([hostname], key))
|
||||
|
||||
def load(self, filename):
|
||||
"""
|
||||
Read a file of known SSH host keys, in the format used by openssh.
|
||||
This type of file unfortunately doesn't exist on Windows, but on
|
||||
posix, it will usually be stored in
|
||||
C{os.path.expanduser("~/.ssh/known_hosts")}.
|
||||
|
||||
If this method is called multiple times, the host keys are merged,
|
||||
not cleared. So multiple calls to C{load} will just call L{add},
|
||||
replacing any existing entries and adding new ones.
|
||||
|
||||
@param filename: name of the file to read host keys from
|
||||
@type filename: str
|
||||
|
||||
@raise IOError: if there was an error reading the file
|
||||
"""
|
||||
f = open(filename, 'r')
|
||||
for lineno, line in enumerate(f):
|
||||
line = line.strip()
|
||||
if (len(line) == 0) or (line[0] == '#'):
|
||||
continue
|
||||
e = HostKeyEntry.from_line(line, lineno)
|
||||
if e is not None:
|
||||
_hostnames = e.hostnames
|
||||
for h in _hostnames:
|
||||
if self.check(h, e.key):
|
||||
e.hostnames.remove(h)
|
||||
if len(e.hostnames):
|
||||
self._entries.append(e)
|
||||
f.close()
|
||||
|
||||
def save(self, filename):
|
||||
"""
|
||||
Save host keys into a file, in the format used by openssh. The order of
|
||||
keys in the file will be preserved when possible (if these keys were
|
||||
loaded from a file originally). The single exception is that combined
|
||||
lines will be split into individual key lines, which is arguably a bug.
|
||||
|
||||
@param filename: name of the file to write
|
||||
@type filename: str
|
||||
|
||||
@raise IOError: if there was an error writing the file
|
||||
|
||||
@since: 1.6.1
|
||||
"""
|
||||
f = open(filename, 'w')
|
||||
for e in self._entries:
|
||||
line = e.to_line()
|
||||
if line:
|
||||
f.write(line)
|
||||
f.close()
|
||||
|
||||
def lookup(self, hostname):
|
||||
"""
|
||||
Find a hostkey entry for a given hostname or IP. If no entry is found,
|
||||
C{None} is returned. Otherwise a dictionary of keytype to key is
|
||||
returned. The keytype will be either C{"ssh-rsa"} or C{"ssh-dss"}.
|
||||
|
||||
@param hostname: the hostname (or IP) to lookup
|
||||
@type hostname: str
|
||||
@return: keys associated with this host (or C{None})
|
||||
@rtype: dict(str, L{PKey})
|
||||
"""
|
||||
class SubDict (UserDict.DictMixin):
|
||||
def __init__(self, hostname, entries, hostkeys):
|
||||
self._hostname = hostname
|
||||
self._entries = entries
|
||||
self._hostkeys = hostkeys
|
||||
|
||||
def __getitem__(self, key):
|
||||
for e in self._entries:
|
||||
if e.key.get_name() == key:
|
||||
return e.key
|
||||
raise KeyError(key)
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
for e in self._entries:
|
||||
if e.key is None:
|
||||
continue
|
||||
if e.key.get_name() == key:
|
||||
# replace
|
||||
e.key = val
|
||||
break
|
||||
else:
|
||||
# add a new one
|
||||
e = HostKeyEntry([hostname], val)
|
||||
self._entries.append(e)
|
||||
self._hostkeys._entries.append(e)
|
||||
|
||||
def keys(self):
|
||||
return [e.key.get_name() for e in self._entries if e.key is not None]
|
||||
|
||||
entries = []
|
||||
for e in self._entries:
|
||||
for h in e.hostnames:
|
||||
if h.startswith('|1|') and constant_time_bytes_eq(self.hash_host(hostname, h), h) or h == hostname:
|
||||
entries.append(e)
|
||||
if len(entries) == 0:
|
||||
return None
|
||||
return SubDict(hostname, entries, self)
|
||||
|
||||
def check(self, hostname, key):
|
||||
"""
|
||||
Return True if the given key is associated with the given hostname
|
||||
in this dictionary.
|
||||
|
||||
@param hostname: hostname (or IP) of the SSH server
|
||||
@type hostname: str
|
||||
@param key: the key to check
|
||||
@type key: L{PKey}
|
||||
@return: C{True} if the key is associated with the hostname; C{False}
|
||||
if not
|
||||
@rtype: bool
|
||||
"""
|
||||
k = self.lookup(hostname)
|
||||
if k is None:
|
||||
return False
|
||||
host_key = k.get(key.get_name(), None)
|
||||
if host_key is None:
|
||||
return False
|
||||
return str(host_key) == str(key)
|
||||
|
||||
def clear(self):
|
||||
"""
|
||||
Remove all host keys from the dictionary.
|
||||
"""
|
||||
self._entries = []
|
||||
|
||||
def __getitem__(self, key):
|
||||
ret = self.lookup(key)
|
||||
if ret is None:
|
||||
raise KeyError(key)
|
||||
return ret
|
||||
|
||||
def __setitem__(self, hostname, entry):
|
||||
# don't use this please.
|
||||
if len(entry) == 0:
|
||||
self._entries.append(HostKeyEntry([hostname], None))
|
||||
return
|
||||
for key_type in entry.keys():
|
||||
found = False
|
||||
for e in self._entries:
|
||||
if (hostname in e.hostnames) and (e.key.get_name() == key_type):
|
||||
# replace
|
||||
e.key = entry[key_type]
|
||||
found = True
|
||||
if not found:
|
||||
self._entries.append(HostKeyEntry([hostname], entry[key_type]))
|
||||
|
||||
def keys(self):
|
||||
# python 2.4 sets would be nice here.
|
||||
ret = []
|
||||
for e in self._entries:
|
||||
for h in e.hostnames:
|
||||
if h not in ret:
|
||||
ret.append(h)
|
||||
return ret
|
||||
|
||||
def values(self):
|
||||
ret = []
|
||||
for k in self.keys():
|
||||
ret.append(self.lookup(k))
|
||||
return ret
|
||||
|
||||
def hash_host(hostname, salt=None):
|
||||
"""
|
||||
Return a "hashed" form of the hostname, as used by openssh when storing
|
||||
hashed hostnames in the known_hosts file.
|
||||
|
||||
@param hostname: the hostname to hash
|
||||
@type hostname: str
|
||||
@param salt: optional salt to use when hashing (must be 20 bytes long)
|
||||
@type salt: str
|
||||
@return: the hashed hostname
|
||||
@rtype: str
|
||||
"""
|
||||
if salt is None:
|
||||
salt = rng.read(SHA.digest_size)
|
||||
else:
|
||||
if salt.startswith('|1|'):
|
||||
salt = salt.split('|')[2]
|
||||
salt = base64.decodestring(salt)
|
||||
assert len(salt) == SHA.digest_size
|
||||
hmac = HMAC.HMAC(salt, hostname, SHA).digest()
|
||||
hostkey = '|1|%s|%s' % (base64.encodestring(salt), base64.encodestring(hmac))
|
||||
return hostkey.replace('\n', '')
|
||||
hash_host = staticmethod(hash_host)
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
Variant on L{KexGroup1 <paramiko.kex_group1.KexGroup1>} where the prime "p" and
|
||||
Variant on `KexGroup1 <paramiko.kex_group1.KexGroup1>` where the prime "p" and
|
||||
generator "g" are provided by the server. A bit more work is required on the
|
||||
client side, and a B{lot} more on the server side.
|
||||
"""
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
Stub out logging on python < 2.3.
|
||||
Stub out logging on Python < 2.3.
|
||||
"""
|
||||
|
||||
|
||||
|
|
|
@ -28,9 +28,9 @@ from paramiko import util
|
|||
|
||||
class Message (object):
|
||||
"""
|
||||
An SSH2 I{Message} is a stream of bytes that encodes some combination of
|
||||
strings, integers, bools, and infinite-precision integers (known in python
|
||||
as I{long}s). This class builds or breaks down such a byte stream.
|
||||
An SSH2 message is a stream of bytes that encodes some combination of
|
||||
strings, integers, bools, and infinite-precision integers (known in Python
|
||||
as longs). This class builds or breaks down such a byte stream.
|
||||
|
||||
Normally you don't need to deal with anything this low-level, but it's
|
||||
exposed for people implementing custom extensions, or features that
|
||||
|
@ -39,11 +39,11 @@ class Message (object):
|
|||
|
||||
def __init__(self, content=None):
|
||||
"""
|
||||
Create a new SSH2 Message.
|
||||
Create a new SSH2 message.
|
||||
|
||||
@param content: the byte stream to use as the Message content (passed
|
||||
in only when decomposing a Message).
|
||||
@type content: string
|
||||
:param str content:
|
||||
the byte stream to use as the message content (passed in only when
|
||||
decomposing a message).
|
||||
"""
|
||||
if content != None:
|
||||
self.packet = cStringIO.StringIO(content)
|
||||
|
@ -52,18 +52,13 @@ class Message (object):
|
|||
|
||||
def __str__(self):
|
||||
"""
|
||||
Return the byte stream content of this Message, as a string.
|
||||
|
||||
@return: the contents of this Message.
|
||||
@rtype: string
|
||||
Return the byte stream content of this message, as a string.
|
||||
"""
|
||||
return self.packet.getvalue()
|
||||
|
||||
def __repr__(self):
|
||||
"""
|
||||
Returns a string representation of this object, for debugging.
|
||||
|
||||
@rtype: string
|
||||
"""
|
||||
return 'paramiko.Message(' + repr(self.packet.getvalue()) + ')'
|
||||
|
||||
|
@ -76,11 +71,8 @@ class Message (object):
|
|||
|
||||
def get_remainder(self):
|
||||
"""
|
||||
Return the bytes of this Message that haven't already been parsed and
|
||||
returned.
|
||||
|
||||
@return: a string of the bytes not parsed yet.
|
||||
@rtype: string
|
||||
Return the bytes (as a `str`) of this message that haven't already been
|
||||
parsed and returned.
|
||||
"""
|
||||
position = self.packet.tell()
|
||||
remainder = self.packet.read()
|
||||
|
@ -89,12 +81,9 @@ class Message (object):
|
|||
|
||||
def get_so_far(self):
|
||||
"""
|
||||
Returns the bytes of this Message that have been parsed and returned.
|
||||
The string passed into a Message's constructor can be regenerated by
|
||||
concatenating C{get_so_far} and L{get_remainder}.
|
||||
|
||||
@return: a string of the bytes parsed so far.
|
||||
@rtype: string
|
||||
Returns the `str` bytes of this message that have been parsed and
|
||||
returned. The string passed into a message's constructor can be
|
||||
regenerated by concatenating ``get_so_far`` and `get_remainder`.
|
||||
"""
|
||||
position = self.packet.tell()
|
||||
self.rewind()
|
||||
|
@ -102,12 +91,10 @@ class Message (object):
|
|||
|
||||
def get_bytes(self, n):
|
||||
"""
|
||||
Return the next C{n} bytes of the Message, without decomposing into
|
||||
an int, string, etc. Just the raw bytes are returned.
|
||||
|
||||
@return: a string of the next C{n} bytes of the Message, or a string
|
||||
of C{n} zero bytes, if there aren't C{n} bytes remaining.
|
||||
@rtype: string
|
||||
Return the next ``n`` bytes of the message (as a `str`), without
|
||||
decomposing into an int, decoded string, etc. Just the raw bytes are
|
||||
returned. Returns a string of ``n`` zero bytes if there weren't ``n``
|
||||
bytes remaining in the message.
|
||||
"""
|
||||
b = self.packet.read(n)
|
||||
max_pad_size = 1<<20 # Limit padding to 1 MB
|
||||
|
@ -117,21 +104,18 @@ class Message (object):
|
|||
|
||||
def get_byte(self):
|
||||
"""
|
||||
Return the next byte of the Message, without decomposing it. This
|
||||
is equivalent to L{get_bytes(1)<get_bytes>}.
|
||||
Return the next byte of the message, without decomposing it. This
|
||||
is equivalent to `get_bytes(1) <get_bytes>`.
|
||||
|
||||
@return: the next byte of the Message, or C{'\000'} if there aren't
|
||||
:return:
|
||||
the next (`str`) byte of the message, or ``'\000'`` if there aren't
|
||||
any bytes remaining.
|
||||
@rtype: string
|
||||
"""
|
||||
return self.get_bytes(1)
|
||||
|
||||
def get_boolean(self):
|
||||
"""
|
||||
Fetch a boolean from the stream.
|
||||
|
||||
@return: C{True} or C{False} (from the Message).
|
||||
@rtype: bool
|
||||
"""
|
||||
b = self.get_bytes(1)
|
||||
return b != '\x00'
|
||||
|
@ -140,8 +124,7 @@ class Message (object):
|
|||
"""
|
||||
Fetch an int from the stream.
|
||||
|
||||
@return: a 32-bit unsigned integer.
|
||||
@rtype: int
|
||||
:return: a 32-bit unsigned `int`.
|
||||
"""
|
||||
return struct.unpack('>I', self.get_bytes(4))[0]
|
||||
|
||||
|
@ -149,8 +132,7 @@ class Message (object):
|
|||
"""
|
||||
Fetch a 64-bit int from the stream.
|
||||
|
||||
@return: a 64-bit unsigned integer.
|
||||
@rtype: long
|
||||
:return: a 64-bit unsigned integer (`long`).
|
||||
"""
|
||||
return struct.unpack('>Q', self.get_bytes(8))[0]
|
||||
|
||||
|
@ -158,29 +140,23 @@ class Message (object):
|
|||
"""
|
||||
Fetch a long int (mpint) from the stream.
|
||||
|
||||
@return: an arbitrary-length integer.
|
||||
@rtype: long
|
||||
:return: an arbitrary-length integer (`long`).
|
||||
"""
|
||||
return util.inflate_long(self.get_string())
|
||||
|
||||
def get_string(self):
|
||||
"""
|
||||
Fetch a string from the stream. This could be a byte string and may
|
||||
Fetch a `str` from the stream. This could be a byte string and may
|
||||
contain unprintable characters. (It's not unheard of for a string to
|
||||
contain another byte-stream Message.)
|
||||
|
||||
@return: a string.
|
||||
@rtype: string
|
||||
contain another byte-stream message.)
|
||||
"""
|
||||
return self.get_bytes(self.get_int())
|
||||
|
||||
def get_list(self):
|
||||
"""
|
||||
Fetch a list of strings from the stream. These are trivially encoded
|
||||
as comma-separated values in a string.
|
||||
Fetch a `list` of `strings <str>` from the stream.
|
||||
|
||||
@return: a list of strings.
|
||||
@rtype: list of strings
|
||||
These are trivially encoded as comma-separated values in a string.
|
||||
"""
|
||||
return self.get_string().split(',')
|
||||
|
||||
|
@ -188,8 +164,7 @@ class Message (object):
|
|||
"""
|
||||
Write bytes to the stream, without any formatting.
|
||||
|
||||
@param b: bytes to add
|
||||
@type b: str
|
||||
:param str b: bytes to add
|
||||
"""
|
||||
self.packet.write(b)
|
||||
return self
|
||||
|
@ -198,8 +173,7 @@ class Message (object):
|
|||
"""
|
||||
Write a single byte to the stream, without any formatting.
|
||||
|
||||
@param b: byte to add
|
||||
@type b: str
|
||||
:param str b: byte to add
|
||||
"""
|
||||
self.packet.write(b)
|
||||
return self
|
||||
|
@ -208,8 +182,7 @@ class Message (object):
|
|||
"""
|
||||
Add a boolean value to the stream.
|
||||
|
||||
@param b: boolean value to add
|
||||
@type b: bool
|
||||
:param bool b: boolean value to add
|
||||
"""
|
||||
if b:
|
||||
self.add_byte('\x01')
|
||||
|
@ -221,8 +194,7 @@ class Message (object):
|
|||
"""
|
||||
Add an integer to the stream.
|
||||
|
||||
@param n: integer to add
|
||||
@type n: int
|
||||
:param int n: integer to add
|
||||
"""
|
||||
self.packet.write(struct.pack('>I', n))
|
||||
return self
|
||||
|
@ -231,8 +203,7 @@ class Message (object):
|
|||
"""
|
||||
Add a 64-bit int to the stream.
|
||||
|
||||
@param n: long int to add
|
||||
@type n: long
|
||||
:param long n: long int to add
|
||||
"""
|
||||
self.packet.write(struct.pack('>Q', n))
|
||||
return self
|
||||
|
@ -242,8 +213,7 @@ class Message (object):
|
|||
Add a long int to the stream, encoded as an infinite-precision
|
||||
integer. This method only works on positive numbers.
|
||||
|
||||
@param z: long int to add
|
||||
@type z: long
|
||||
:param long z: long int to add
|
||||
"""
|
||||
self.add_string(util.deflate_long(z))
|
||||
return self
|
||||
|
@ -252,8 +222,7 @@ class Message (object):
|
|||
"""
|
||||
Add a string to the stream.
|
||||
|
||||
@param s: string to add
|
||||
@type s: str
|
||||
:param str s: string to add
|
||||
"""
|
||||
self.add_int(len(s))
|
||||
self.packet.write(s)
|
||||
|
@ -265,8 +234,7 @@ class Message (object):
|
|||
a single string of values separated by commas. (Yes, really, that's
|
||||
how SSH2 does it.)
|
||||
|
||||
@param l: list of strings to add
|
||||
@type l: list(str)
|
||||
:param list l: list of strings to add
|
||||
"""
|
||||
self.add_string(','.join(l))
|
||||
return self
|
||||
|
@ -293,10 +261,10 @@ class Message (object):
|
|||
Add a sequence of items to the stream. The values are encoded based
|
||||
on their type: str, int, bool, list, or long.
|
||||
|
||||
@param seq: the sequence of items
|
||||
@type seq: sequence
|
||||
.. warning::
|
||||
Longs are encoded non-deterministically. Don't use this method.
|
||||
|
||||
@bug: longs are encoded non-deterministically. Don't use this method.
|
||||
:param seq: the sequence of items
|
||||
"""
|
||||
for item in seq:
|
||||
self._add(item)
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
Packetizer.
|
||||
Packet handling
|
||||
"""
|
||||
|
||||
import errno
|
||||
|
@ -103,7 +103,7 @@ class Packetizer (object):
|
|||
|
||||
def set_log(self, log):
|
||||
"""
|
||||
Set the python log object to use for logging.
|
||||
Set the Python log object to use for logging.
|
||||
"""
|
||||
self.__logger = log
|
||||
|
||||
|
@ -167,17 +167,15 @@ class Packetizer (object):
|
|||
|
||||
def need_rekey(self):
|
||||
"""
|
||||
Returns C{True} if a new set of keys needs to be negotiated. This
|
||||
Returns ``True`` if a new set of keys needs to be negotiated. This
|
||||
will be triggered during a packet read or write, so it should be
|
||||
checked after every read or write, or at least after every few.
|
||||
|
||||
@return: C{True} if a new set of keys needs to be negotiated
|
||||
"""
|
||||
return self.__need_rekey
|
||||
|
||||
def set_keepalive(self, interval, callback):
|
||||
"""
|
||||
Turn on/off the callback keepalive. If C{interval} seconds pass with
|
||||
Turn on/off the callback keepalive. If ``interval`` seconds pass with
|
||||
no data read from or written to the socket, the callback will be
|
||||
executed and the timer will be reset.
|
||||
"""
|
||||
|
@ -189,12 +187,11 @@ class Packetizer (object):
|
|||
"""
|
||||
Read as close to N bytes as possible, blocking as long as necessary.
|
||||
|
||||
@param n: number of bytes to read
|
||||
@type n: int
|
||||
@return: the data read
|
||||
@rtype: str
|
||||
@raise EOFError: if the socket was closed before all the bytes could
|
||||
be read
|
||||
:param int n: number of bytes to read
|
||||
:return: the data read, as a `str`
|
||||
|
||||
:raises EOFError:
|
||||
if the socket was closed before all the bytes could be read
|
||||
"""
|
||||
out = ''
|
||||
# handle over-reading from reading the banner line
|
||||
|
@ -331,8 +328,8 @@ class Packetizer (object):
|
|||
Only one thread should ever be in this function (no other locking is
|
||||
done).
|
||||
|
||||
@raise SSHException: if the packet is mangled
|
||||
@raise NeedRekeyException: if the transport should rekey
|
||||
:raises SSHException: if the packet is mangled
|
||||
:raises NeedRekeyException: if the transport should rekey
|
||||
"""
|
||||
header = self.read_all(self.__block_size_in, check_rekey=True)
|
||||
if self.__block_engine_in != None:
|
||||
|
|
|
@ -17,11 +17,12 @@
|
|||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
Abstraction of a one-way pipe where the read end can be used in select().
|
||||
Normally this is trivial, but Windows makes it nearly impossible.
|
||||
Abstraction of a one-way pipe where the read end can be used in
|
||||
`select.select`. Normally this is trivial, but Windows makes it nearly
|
||||
impossible.
|
||||
|
||||
The pipe acts like an Event, which can be set or cleared. When set, the pipe
|
||||
will trigger as readable in select().
|
||||
will trigger as readable in `select <select.select>`.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
|
188
paramiko/pkey.py
188
paramiko/pkey.py
|
@ -47,30 +47,26 @@ class PKey (object):
|
|||
|
||||
def __init__(self, msg=None, data=None):
|
||||
"""
|
||||
Create a new instance of this public key type. If C{msg} is given,
|
||||
Create a new instance of this public key type. If ``msg`` is given,
|
||||
the key's public part(s) will be filled in from the message. If
|
||||
C{data} is given, the key's public part(s) will be filled in from
|
||||
``data`` is given, the key's public part(s) will be filled in from
|
||||
the string.
|
||||
|
||||
@param msg: an optional SSH L{Message} containing a public key of this
|
||||
type.
|
||||
@type msg: L{Message}
|
||||
@param data: an optional string containing a public key of this type
|
||||
@type data: str
|
||||
:param .Message msg:
|
||||
an optional SSH `.Message` containing a public key of this type.
|
||||
:param str data: an optional string containing a public key of this type
|
||||
|
||||
@raise SSHException: if a key cannot be created from the C{data} or
|
||||
C{msg} given, or no key was passed in.
|
||||
:raises SSHException:
|
||||
if a key cannot be created from the ``data`` or ``msg`` given, or
|
||||
no key was passed in.
|
||||
"""
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
Return a string of an SSH L{Message} made up of the public part(s) of
|
||||
this key. This string is suitable for passing to L{__init__} to
|
||||
Return a string of an SSH `.Message` made up of the public part(s) of
|
||||
this key. This string is suitable for passing to `__init__` to
|
||||
re-create the key object later.
|
||||
|
||||
@return: string representation of an SSH key message.
|
||||
@rtype: str
|
||||
"""
|
||||
return ''
|
||||
|
||||
|
@ -81,10 +77,7 @@ class PKey (object):
|
|||
of the key are compared, so a public key will compare equal to its
|
||||
corresponding private key.
|
||||
|
||||
@param other: key to compare to.
|
||||
@type other: L{PKey}
|
||||
@return: 0 if the two keys are equivalent, non-0 otherwise.
|
||||
@rtype: int
|
||||
:param .Pkey other: key to compare to.
|
||||
"""
|
||||
hs = hash(self)
|
||||
ho = hash(other)
|
||||
|
@ -96,9 +89,9 @@ class PKey (object):
|
|||
"""
|
||||
Return the name of this private key implementation.
|
||||
|
||||
@return: name of this private key type, in SSH terminology (for
|
||||
example, C{"ssh-rsa"}).
|
||||
@rtype: str
|
||||
:return:
|
||||
name of this private key type, in SSH terminology, as a `str` (for
|
||||
example, ``"ssh-rsa"``).
|
||||
"""
|
||||
return ''
|
||||
|
||||
|
@ -107,18 +100,14 @@ class PKey (object):
|
|||
Return the number of significant bits in this key. This is useful
|
||||
for judging the relative security of a key.
|
||||
|
||||
@return: bits in the key.
|
||||
@rtype: int
|
||||
:return: bits in the key (as an `int`)
|
||||
"""
|
||||
return 0
|
||||
|
||||
def can_sign(self):
|
||||
"""
|
||||
Return C{True} if this key has the private part necessary for signing
|
||||
Return ``True`` if this key has the private part necessary for signing
|
||||
data.
|
||||
|
||||
@return: C{True} if this is a private key.
|
||||
@rtype: bool
|
||||
"""
|
||||
return False
|
||||
|
||||
|
@ -127,9 +116,9 @@ class PKey (object):
|
|||
Return an MD5 fingerprint of the public part of this key. Nothing
|
||||
secret is revealed.
|
||||
|
||||
@return: a 16-byte string (binary) of the MD5 fingerprint, in SSH
|
||||
:return:
|
||||
a 16-byte `string <str>` (binary) of the MD5 fingerprint, in SSH
|
||||
format.
|
||||
@rtype: str
|
||||
"""
|
||||
return MD5.new(str(self)).digest()
|
||||
|
||||
|
@ -139,22 +128,18 @@ class PKey (object):
|
|||
secret is revealed. This format is compatible with that used to store
|
||||
public key files or recognized host keys.
|
||||
|
||||
@return: a base64 string containing the public part of the key.
|
||||
@rtype: str
|
||||
:return: a base64 `string <str>` containing the public part of the key.
|
||||
"""
|
||||
return base64.encodestring(str(self)).replace('\n', '')
|
||||
|
||||
def sign_ssh_data(self, rng, data):
|
||||
"""
|
||||
Sign a blob of data with this private key, and return a L{Message}
|
||||
Sign a blob of data with this private key, and return a `.Message`
|
||||
representing an SSH signature message.
|
||||
|
||||
@param rng: a secure random number generator.
|
||||
@type rng: L{Crypto.Util.rng.RandomPool}
|
||||
@param data: the data to sign.
|
||||
@type data: str
|
||||
@return: an SSH signature message.
|
||||
@rtype: L{Message}
|
||||
:param .Crypto.Util.rng.RandomPool rng: a secure random number generator.
|
||||
:param str data: the data to sign.
|
||||
:return: an SSH signature `message <.Message>`.
|
||||
"""
|
||||
return ''
|
||||
|
||||
|
@ -163,37 +148,31 @@ class PKey (object):
|
|||
Given a blob of data, and an SSH message representing a signature of
|
||||
that data, verify that it was signed with this key.
|
||||
|
||||
@param data: the data that was signed.
|
||||
@type data: str
|
||||
@param msg: an SSH signature message
|
||||
@type msg: L{Message}
|
||||
@return: C{True} if the signature verifies correctly; C{False}
|
||||
otherwise.
|
||||
@rtype: boolean
|
||||
:param str data: the data that was signed.
|
||||
:param .Message msg: an SSH signature message
|
||||
:return:
|
||||
``True`` if the signature verifies correctly; ``False`` otherwise.
|
||||
"""
|
||||
return False
|
||||
|
||||
def from_private_key_file(cls, filename, password=None):
|
||||
"""
|
||||
Create a key object by reading a private key file. If the private
|
||||
key is encrypted and C{password} is not C{None}, the given password
|
||||
will be used to decrypt the key (otherwise L{PasswordRequiredException}
|
||||
is thrown). Through the magic of python, this factory method will
|
||||
exist in all subclasses of PKey (such as L{RSAKey} or L{DSSKey}), but
|
||||
key is encrypted and ``password`` is not ``None``, the given password
|
||||
will be used to decrypt the key (otherwise `.PasswordRequiredException`
|
||||
is thrown). Through the magic of Python, this factory method will
|
||||
exist in all subclasses of PKey (such as `.RSAKey` or `.DSSKey`), but
|
||||
is useless on the abstract PKey class.
|
||||
|
||||
@param filename: name of the file to read
|
||||
@type filename: str
|
||||
@param password: an optional password to use to decrypt the key file,
|
||||
:param str filename: name of the file to read
|
||||
:param str password: an optional password to use to decrypt the key file,
|
||||
if it's encrypted
|
||||
@type password: str
|
||||
@return: a new key object based on the given private key
|
||||
@rtype: L{PKey}
|
||||
:return: a new `.PKey` based on the given private key
|
||||
|
||||
@raise IOError: if there was an error reading the file
|
||||
@raise PasswordRequiredException: if the private key file is
|
||||
encrypted, and C{password} is C{None}
|
||||
@raise SSHException: if the key file is invalid
|
||||
:raises IOError: if there was an error reading the file
|
||||
:raises PasswordRequiredException: if the private key file is
|
||||
encrypted, and ``password`` is ``None``
|
||||
:raises SSHException: if the key file is invalid
|
||||
"""
|
||||
key = cls(filename=filename, password=password)
|
||||
return key
|
||||
|
@ -202,22 +181,19 @@ class PKey (object):
|
|||
def from_private_key(cls, file_obj, password=None):
|
||||
"""
|
||||
Create a key object by reading a private key from a file (or file-like)
|
||||
object. If the private key is encrypted and C{password} is not C{None},
|
||||
object. If the private key is encrypted and ``password`` is not ``None``,
|
||||
the given password will be used to decrypt the key (otherwise
|
||||
L{PasswordRequiredException} is thrown).
|
||||
`.PasswordRequiredException` is thrown).
|
||||
|
||||
@param file_obj: the file to read from
|
||||
@type file_obj: file
|
||||
@param password: an optional password to use to decrypt the key, if it's
|
||||
encrypted
|
||||
@type password: str
|
||||
@return: a new key object based on the given private key
|
||||
@rtype: L{PKey}
|
||||
:param file file_obj: the file to read from
|
||||
:param str password:
|
||||
an optional password to use to decrypt the key, if it's encrypted
|
||||
:return: a new `.PKey` based on the given private key
|
||||
|
||||
@raise IOError: if there was an error reading the key
|
||||
@raise PasswordRequiredException: if the private key file is encrypted,
|
||||
and C{password} is C{None}
|
||||
@raise SSHException: if the key file is invalid
|
||||
:raises IOError: if there was an error reading the key
|
||||
:raises PasswordRequiredException: if the private key file is encrypted,
|
||||
and ``password`` is ``None``
|
||||
:raises SSHException: if the key file is invalid
|
||||
"""
|
||||
key = cls(file_obj=file_obj, password=password)
|
||||
return key
|
||||
|
@ -226,55 +202,49 @@ class PKey (object):
|
|||
def write_private_key_file(self, filename, password=None):
|
||||
"""
|
||||
Write private key contents into a file. If the password is not
|
||||
C{None}, the key is encrypted before writing.
|
||||
``None``, the key is encrypted before writing.
|
||||
|
||||
@param filename: name of the file to write
|
||||
@type filename: str
|
||||
@param password: an optional password to use to encrypt the key file
|
||||
@type password: str
|
||||
:param str filename: name of the file to write
|
||||
:param str password:
|
||||
an optional password to use to encrypt the key file
|
||||
|
||||
@raise IOError: if there was an error writing the file
|
||||
@raise SSHException: if the key is invalid
|
||||
:raises IOError: if there was an error writing the file
|
||||
:raises SSHException: if the key is invalid
|
||||
"""
|
||||
raise Exception('Not implemented in PKey')
|
||||
|
||||
def write_private_key(self, file_obj, password=None):
|
||||
"""
|
||||
Write private key contents into a file (or file-like) object. If the
|
||||
password is not C{None}, the key is encrypted before writing.
|
||||
password is not ``None``, the key is encrypted before writing.
|
||||
|
||||
@param file_obj: the file object to write into
|
||||
@type file_obj: file
|
||||
@param password: an optional password to use to encrypt the key
|
||||
@type password: str
|
||||
:param file file_obj: the file object to write into
|
||||
:param str password: an optional password to use to encrypt the key
|
||||
|
||||
@raise IOError: if there was an error writing to the file
|
||||
@raise SSHException: if the key is invalid
|
||||
:raises IOError: if there was an error writing to the file
|
||||
:raises SSHException: if the key is invalid
|
||||
"""
|
||||
raise Exception('Not implemented in PKey')
|
||||
|
||||
def _read_private_key_file(self, tag, filename, password=None):
|
||||
"""
|
||||
Read an SSH2-format private key file, looking for a string of the type
|
||||
C{"BEGIN xxx PRIVATE KEY"} for some C{xxx}, base64-decode the text we
|
||||
``"BEGIN xxx PRIVATE KEY"`` for some ``xxx``, base64-decode the text we
|
||||
find, and return it as a string. If the private key is encrypted and
|
||||
C{password} is not C{None}, the given password will be used to decrypt
|
||||
the key (otherwise L{PasswordRequiredException} is thrown).
|
||||
``password`` is not ``None``, the given password will be used to decrypt
|
||||
the key (otherwise `.PasswordRequiredException` is thrown).
|
||||
|
||||
@param tag: C{"RSA"} or C{"DSA"}, the tag used to mark the data block.
|
||||
@type tag: str
|
||||
@param filename: name of the file to read.
|
||||
@type filename: str
|
||||
@param password: an optional password to use to decrypt the key file,
|
||||
if it's encrypted.
|
||||
@type password: str
|
||||
@return: data blob that makes up the private key.
|
||||
@rtype: str
|
||||
:param str tag: ``"RSA"`` or ``"DSA"``, the tag used to mark the data block.
|
||||
:param str filename: name of the file to read.
|
||||
:param str password:
|
||||
an optional password to use to decrypt the key file, if it's
|
||||
encrypted.
|
||||
:return: data blob (`str`) that makes up the private key.
|
||||
|
||||
@raise IOError: if there was an error reading the file.
|
||||
@raise PasswordRequiredException: if the private key file is
|
||||
encrypted, and C{password} is C{None}.
|
||||
@raise SSHException: if the key file is invalid.
|
||||
:raises IOError: if there was an error reading the file.
|
||||
:raises PasswordRequiredException: if the private key file is
|
||||
encrypted, and ``password`` is ``None``.
|
||||
:raises SSHException: if the key file is invalid.
|
||||
"""
|
||||
f = open(filename, 'r')
|
||||
data = self._read_private_key(tag, f, password)
|
||||
|
@ -335,16 +305,12 @@ class PKey (object):
|
|||
a trivially-encoded format (base64) which is completely insecure. If
|
||||
a password is given, DES-EDE3-CBC is used.
|
||||
|
||||
@param tag: C{"RSA"} or C{"DSA"}, the tag used to mark the data block.
|
||||
@type tag: str
|
||||
@param filename: name of the file to write.
|
||||
@type filename: str
|
||||
@param data: data blob that makes up the private key.
|
||||
@type data: str
|
||||
@param password: an optional password to use to encrypt the file.
|
||||
@type password: str
|
||||
:param str tag: ``"RSA"`` or ``"DSA"``, the tag used to mark the data block.
|
||||
:param file filename: name of the file to write.
|
||||
:param str data: data blob that makes up the private key.
|
||||
:param str password: an optional password to use to encrypt the file.
|
||||
|
||||
@raise IOError: if there was an error writing the file.
|
||||
:raises IOError: if there was an error writing the file.
|
||||
"""
|
||||
f = open(filename, 'w', 0600)
|
||||
# grrr... the mode doesn't always take hold
|
||||
|
|
|
@ -109,7 +109,7 @@ class ModulusPack (object):
|
|||
|
||||
def read_file(self, filename):
|
||||
"""
|
||||
@raise IOError: passed from any file operations that fail.
|
||||
:raises IOError: passed from any file operations that fail.
|
||||
"""
|
||||
self.pack = {}
|
||||
f = open(filename, 'r')
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
L{ProxyCommand}.
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
import os
|
||||
|
@ -36,18 +33,17 @@ class ProxyCommand(object):
|
|||
Wraps a subprocess running ProxyCommand-driven programs.
|
||||
|
||||
This class implements a the socket-like interface needed by the
|
||||
L{Transport} and L{Packetizer} classes. Using this class instead of a
|
||||
`.Transport` and `.Packetizer` classes. Using this class instead of a
|
||||
regular socket makes it possible to talk with a Popen'd command that will
|
||||
proxy traffic between the client and a server hosted in another machine.
|
||||
"""
|
||||
def __init__(self, command_line):
|
||||
"""
|
||||
Create a new CommandProxy instance. The instance created by this
|
||||
class can be passed as an argument to the L{Transport} class.
|
||||
class can be passed as an argument to the `.Transport` class.
|
||||
|
||||
@param command_line: the command that should be executed and
|
||||
used as the proxy.
|
||||
@type command_line: str
|
||||
:param str command_line:
|
||||
the command that should be executed and used as the proxy.
|
||||
"""
|
||||
self.cmd = shlsplit(command_line)
|
||||
self.process = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
||||
|
@ -59,8 +55,7 @@ class ProxyCommand(object):
|
|||
Write the content received from the SSH client to the standard
|
||||
input of the forked command.
|
||||
|
||||
@param content: string to be sent to the forked command
|
||||
@type content: str
|
||||
:param str content: string to be sent to the forked command
|
||||
"""
|
||||
try:
|
||||
self.process.stdin.write(content)
|
||||
|
@ -76,11 +71,9 @@ class ProxyCommand(object):
|
|||
"""
|
||||
Read from the standard output of the forked program.
|
||||
|
||||
@param size: how many chars should be read
|
||||
@type size: int
|
||||
:param int size: how many chars should be read
|
||||
|
||||
@return: the length of the read content
|
||||
@rtype: int
|
||||
:return: the length of the read content, as an `int`
|
||||
"""
|
||||
try:
|
||||
start = datetime.now()
|
||||
|
|
|
@ -28,13 +28,13 @@ class ResourceManager (object):
|
|||
A registry of objects and resources that should be closed when those
|
||||
objects are deleted.
|
||||
|
||||
This is meant to be a safer alternative to python's C{__del__} method,
|
||||
This is meant to be a safer alternative to Python's ``__del__`` method,
|
||||
which can cause reference cycles to never be collected. Objects registered
|
||||
with the ResourceManager can be collected but still free resources when
|
||||
they die.
|
||||
|
||||
Resources are registered using L{register}, and when an object is garbage
|
||||
collected, each registered resource is closed by having its C{close()}
|
||||
Resources are registered using `register`, and when an object is garbage
|
||||
collected, each registered resource is closed by having its ``close()``
|
||||
method called. Multiple resources may be registered per object, but a
|
||||
resource will only be closed once, even if multiple objects register it.
|
||||
(The last object to register it wins.)
|
||||
|
@ -47,14 +47,13 @@ class ResourceManager (object):
|
|||
"""
|
||||
Register a resource to be closed with an object is collected.
|
||||
|
||||
When the given C{obj} is garbage-collected by the python interpreter,
|
||||
the C{resource} will be closed by having its C{close()} method called.
|
||||
When the given ``obj`` is garbage-collected by the Python interpreter,
|
||||
the ``resource`` will be closed by having its ``close()`` method called.
|
||||
Any exceptions are ignored.
|
||||
|
||||
@param obj: the object to track
|
||||
@type obj: object
|
||||
@param resource: the resource to close when the object is collected
|
||||
@type resource: object
|
||||
:param object obj: the object to track
|
||||
:param object resource:
|
||||
the resource to close when the object is collected
|
||||
"""
|
||||
def callback(ref):
|
||||
try:
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
L{RSAKey}
|
||||
RSA keys.
|
||||
"""
|
||||
|
||||
from Crypto.PublicKey import RSA
|
||||
|
@ -129,13 +129,11 @@ class RSAKey (PKey):
|
|||
Generate a new private RSA key. This factory function can be used to
|
||||
generate a new host key or authentication key.
|
||||
|
||||
@param bits: number of bits the generated key should be.
|
||||
@type bits: int
|
||||
@param progress_func: an optional function to call at key points in
|
||||
key generation (used by C{pyCrypto.PublicKey}).
|
||||
@type progress_func: function
|
||||
@return: new private key
|
||||
@rtype: L{RSAKey}
|
||||
:param int bits: number of bits the generated key should be.
|
||||
:param function progress_func:
|
||||
an optional function to call at key points in key generation (used
|
||||
by ``pyCrypto.PublicKey``).
|
||||
:return: new `.RSAKey` private key
|
||||
"""
|
||||
rsa = RSA.generate(bits, rng.read, progress_func)
|
||||
key = RSAKey(vals=(rsa.e, rsa.n))
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
L{ServerInterface} is an interface to override for server support.
|
||||
`.ServerInterface` is an interface to override for server support.
|
||||
"""
|
||||
|
||||
import threading
|
||||
|
@ -25,54 +25,12 @@ from paramiko.common import *
|
|||
from paramiko import util
|
||||
|
||||
|
||||
class InteractiveQuery (object):
|
||||
"""
|
||||
A query (set of prompts) for a user during interactive authentication.
|
||||
"""
|
||||
|
||||
def __init__(self, name='', instructions='', *prompts):
|
||||
"""
|
||||
Create a new interactive query to send to the client. The name and
|
||||
instructions are optional, but are generally displayed to the end
|
||||
user. A list of prompts may be included, or they may be added via
|
||||
the L{add_prompt} method.
|
||||
|
||||
@param name: name of this query
|
||||
@type name: str
|
||||
@param instructions: user instructions (usually short) about this query
|
||||
@type instructions: str
|
||||
@param prompts: one or more authentication prompts
|
||||
@type prompts: str
|
||||
"""
|
||||
self.name = name
|
||||
self.instructions = instructions
|
||||
self.prompts = []
|
||||
for x in prompts:
|
||||
if (type(x) is str) or (type(x) is unicode):
|
||||
self.add_prompt(x)
|
||||
else:
|
||||
self.add_prompt(x[0], x[1])
|
||||
|
||||
def add_prompt(self, prompt, echo=True):
|
||||
"""
|
||||
Add a prompt to this query. The prompt should be a (reasonably short)
|
||||
string. Multiple prompts can be added to the same query.
|
||||
|
||||
@param prompt: the user prompt
|
||||
@type prompt: str
|
||||
@param echo: C{True} (default) if the user's response should be echoed;
|
||||
C{False} if not (for a password or similar)
|
||||
@type echo: bool
|
||||
"""
|
||||
self.prompts.append((prompt, echo))
|
||||
|
||||
|
||||
class ServerInterface (object):
|
||||
"""
|
||||
This class defines an interface for controlling the behavior of paramiko
|
||||
This class defines an interface for controlling the behavior of Paramiko
|
||||
in server mode.
|
||||
|
||||
Methods on this class are called from paramiko's primary thread, so you
|
||||
Methods on this class are called from Paramiko's primary thread, so you
|
||||
shouldn't do too much work in them. (Certainly nothing that blocks or
|
||||
sleeps.)
|
||||
"""
|
||||
|
@ -80,7 +38,7 @@ class ServerInterface (object):
|
|||
def check_channel_request(self, kind, chanid):
|
||||
"""
|
||||
Determine if a channel request of a given type will be granted, and
|
||||
return C{OPEN_SUCCEEDED} or an error code. This method is
|
||||
return ``OPEN_SUCCEEDED`` or an error code. This method is
|
||||
called in server mode when the client requests a channel, after
|
||||
authentication is complete.
|
||||
|
||||
|
@ -88,37 +46,37 @@ class ServerInterface (object):
|
|||
useless), you should also override some of the channel request methods
|
||||
below, which are used to determine which services will be allowed on
|
||||
a given channel:
|
||||
- L{check_channel_pty_request}
|
||||
- L{check_channel_shell_request}
|
||||
- L{check_channel_subsystem_request}
|
||||
- L{check_channel_window_change_request}
|
||||
- L{check_channel_x11_request}
|
||||
- L{check_channel_forward_agent_request}
|
||||
|
||||
The C{chanid} parameter is a small number that uniquely identifies the
|
||||
channel within a L{Transport}. A L{Channel} object is not created
|
||||
unless this method returns C{OPEN_SUCCEEDED} -- once a
|
||||
L{Channel} object is created, you can call L{Channel.get_id} to
|
||||
- `check_channel_pty_request`
|
||||
- `check_channel_shell_request`
|
||||
- `check_channel_subsystem_request`
|
||||
- `check_channel_window_change_request`
|
||||
- `check_channel_x11_request`
|
||||
- `check_channel_forward_agent_request`
|
||||
|
||||
The ``chanid`` parameter is a small number that uniquely identifies the
|
||||
channel within a `.Transport`. A `.Channel` object is not created
|
||||
unless this method returns ``OPEN_SUCCEEDED`` -- once a
|
||||
`.Channel` object is created, you can call `.Channel.get_id` to
|
||||
retrieve the channel ID.
|
||||
|
||||
The return value should either be C{OPEN_SUCCEEDED} (or
|
||||
C{0}) to allow the channel request, or one of the following error
|
||||
The return value should either be ``OPEN_SUCCEEDED`` (or
|
||||
``0``) to allow the channel request, or one of the following error
|
||||
codes to reject it:
|
||||
- C{OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED}
|
||||
- C{OPEN_FAILED_CONNECT_FAILED}
|
||||
- C{OPEN_FAILED_UNKNOWN_CHANNEL_TYPE}
|
||||
- C{OPEN_FAILED_RESOURCE_SHORTAGE}
|
||||
|
||||
- ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``
|
||||
- ``OPEN_FAILED_CONNECT_FAILED``
|
||||
- ``OPEN_FAILED_UNKNOWN_CHANNEL_TYPE``
|
||||
- ``OPEN_FAILED_RESOURCE_SHORTAGE``
|
||||
|
||||
The default implementation always returns
|
||||
C{OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED}.
|
||||
``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``.
|
||||
|
||||
@param kind: the kind of channel the client would like to open
|
||||
(usually C{"session"}).
|
||||
@type kind: str
|
||||
@param chanid: ID of the channel
|
||||
@type chanid: int
|
||||
@return: a success or failure code (listed above)
|
||||
@rtype: int
|
||||
:param str kind:
|
||||
the kind of channel the client would like to open (usually
|
||||
``"session"``).
|
||||
:param int chanid: ID of the channel
|
||||
:return: an `int` success or failure code (listed above)
|
||||
"""
|
||||
return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
|
||||
|
||||
|
@ -129,15 +87,13 @@ class ServerInterface (object):
|
|||
of authentication methods that might be successful.
|
||||
|
||||
The "list" is actually a string of comma-separated names of types of
|
||||
authentication. Possible values are C{"password"}, C{"publickey"},
|
||||
and C{"none"}.
|
||||
authentication. Possible values are ``"password"``, ``"publickey"``,
|
||||
and ``"none"``.
|
||||
|
||||
The default implementation always returns C{"password"}.
|
||||
The default implementation always returns ``"password"``.
|
||||
|
||||
@param username: the username requesting authentication.
|
||||
@type username: str
|
||||
@return: a comma-separated list of authentication types
|
||||
@rtype: str
|
||||
:param str username: the username requesting authentication.
|
||||
:return: a comma-separated `str` of authentication types
|
||||
"""
|
||||
return 'password'
|
||||
|
||||
|
@ -146,17 +102,17 @@ class ServerInterface (object):
|
|||
Determine if a client may open channels with no (further)
|
||||
authentication.
|
||||
|
||||
Return L{AUTH_FAILED} if the client must authenticate, or
|
||||
L{AUTH_SUCCESSFUL} if it's okay for the client to not
|
||||
Return `.AUTH_FAILED` if the client must authenticate, or
|
||||
`.AUTH_SUCCESSFUL` if it's okay for the client to not
|
||||
authenticate.
|
||||
|
||||
The default implementation always returns L{AUTH_FAILED}.
|
||||
The default implementation always returns `.AUTH_FAILED`.
|
||||
|
||||
@param username: the username of the client.
|
||||
@type username: str
|
||||
@return: L{AUTH_FAILED} if the authentication fails;
|
||||
L{AUTH_SUCCESSFUL} if it succeeds.
|
||||
@rtype: int
|
||||
:param str username: the username of the client.
|
||||
:return:
|
||||
`.AUTH_FAILED` if the authentication fails; `.AUTH_SUCCESSFUL` if
|
||||
it succeeds.
|
||||
:rtype: int
|
||||
"""
|
||||
return AUTH_FAILED
|
||||
|
||||
|
@ -165,25 +121,23 @@ class ServerInterface (object):
|
|||
Determine if a given username and password supplied by the client is
|
||||
acceptable for use in authentication.
|
||||
|
||||
Return L{AUTH_FAILED} if the password is not accepted,
|
||||
L{AUTH_SUCCESSFUL} if the password is accepted and completes
|
||||
the authentication, or L{AUTH_PARTIALLY_SUCCESSFUL} if your
|
||||
Return `.AUTH_FAILED` if the password is not accepted,
|
||||
`.AUTH_SUCCESSFUL` if the password is accepted and completes
|
||||
the authentication, or `.AUTH_PARTIALLY_SUCCESSFUL` if your
|
||||
authentication is stateful, and this key is accepted for
|
||||
authentication, but more authentication is required. (In this latter
|
||||
case, L{get_allowed_auths} will be called to report to the client what
|
||||
case, `get_allowed_auths` will be called to report to the client what
|
||||
options it has for continuing the authentication.)
|
||||
|
||||
The default implementation always returns L{AUTH_FAILED}.
|
||||
The default implementation always returns `.AUTH_FAILED`.
|
||||
|
||||
@param username: the username of the authenticating client.
|
||||
@type username: str
|
||||
@param password: the password given by the client.
|
||||
@type password: str
|
||||
@return: L{AUTH_FAILED} if the authentication fails;
|
||||
L{AUTH_SUCCESSFUL} if it succeeds;
|
||||
L{AUTH_PARTIALLY_SUCCESSFUL} if the password auth is
|
||||
:param str username: the username of the authenticating client.
|
||||
:param str password: the password given by the client.
|
||||
:return:
|
||||
`.AUTH_FAILED` if the authentication fails; `.AUTH_SUCCESSFUL` if
|
||||
it succeeds; `.AUTH_PARTIALLY_SUCCESSFUL` if the password auth is
|
||||
successful, but authentication must continue.
|
||||
@rtype: int
|
||||
:rtype: int
|
||||
"""
|
||||
return AUTH_FAILED
|
||||
|
||||
|
@ -194,29 +148,28 @@ class ServerInterface (object):
|
|||
check the username and key and decide if you would accept a signature
|
||||
made using this key.
|
||||
|
||||
Return L{AUTH_FAILED} if the key is not accepted,
|
||||
L{AUTH_SUCCESSFUL} if the key is accepted and completes the
|
||||
authentication, or L{AUTH_PARTIALLY_SUCCESSFUL} if your
|
||||
Return `.AUTH_FAILED` if the key is not accepted,
|
||||
`.AUTH_SUCCESSFUL` if the key is accepted and completes the
|
||||
authentication, or `.AUTH_PARTIALLY_SUCCESSFUL` if your
|
||||
authentication is stateful, and this password is accepted for
|
||||
authentication, but more authentication is required. (In this latter
|
||||
case, L{get_allowed_auths} will be called to report to the client what
|
||||
case, `get_allowed_auths` will be called to report to the client what
|
||||
options it has for continuing the authentication.)
|
||||
|
||||
Note that you don't have to actually verify any key signtature here.
|
||||
If you're willing to accept the key, paramiko will do the work of
|
||||
If you're willing to accept the key, Paramiko will do the work of
|
||||
verifying the client's signature.
|
||||
|
||||
The default implementation always returns L{AUTH_FAILED}.
|
||||
The default implementation always returns `.AUTH_FAILED`.
|
||||
|
||||
@param username: the username of the authenticating client
|
||||
@type username: str
|
||||
@param key: the key object provided by the client
|
||||
@type key: L{PKey <pkey.PKey>}
|
||||
@return: L{AUTH_FAILED} if the client can't authenticate
|
||||
with this key; L{AUTH_SUCCESSFUL} if it can;
|
||||
L{AUTH_PARTIALLY_SUCCESSFUL} if it can authenticate with
|
||||
this key but must continue with authentication
|
||||
@rtype: int
|
||||
:param str username: the username of the authenticating client
|
||||
:param .PKey key: the key object provided by the client
|
||||
:return:
|
||||
`.AUTH_FAILED` if the client can't authenticate with this key;
|
||||
`.AUTH_SUCCESSFUL` if it can; `.AUTH_PARTIALLY_SUCCESSFUL` if it
|
||||
can authenticate with this key but must continue with
|
||||
authentication
|
||||
:rtype: int
|
||||
"""
|
||||
return AUTH_FAILED
|
||||
|
||||
|
@ -224,24 +177,24 @@ class ServerInterface (object):
|
|||
"""
|
||||
Begin an interactive authentication challenge, if supported. You
|
||||
should override this method in server mode if you want to support the
|
||||
C{"keyboard-interactive"} auth type, which requires you to send a
|
||||
``"keyboard-interactive"`` auth type, which requires you to send a
|
||||
series of questions for the client to answer.
|
||||
|
||||
Return L{AUTH_FAILED} if this auth method isn't supported. Otherwise,
|
||||
you should return an L{InteractiveQuery} object containing the prompts
|
||||
Return `.AUTH_FAILED` if this auth method isn't supported. Otherwise,
|
||||
you should return an `.InteractiveQuery` object containing the prompts
|
||||
and instructions for the user. The response will be sent via a call
|
||||
to L{check_auth_interactive_response}.
|
||||
to `check_auth_interactive_response`.
|
||||
|
||||
The default implementation always returns L{AUTH_FAILED}.
|
||||
The default implementation always returns `.AUTH_FAILED`.
|
||||
|
||||
@param username: the username of the authenticating client
|
||||
@type username: str
|
||||
@param submethods: a comma-separated list of methods preferred by the
|
||||
client (usually empty)
|
||||
@type submethods: str
|
||||
@return: L{AUTH_FAILED} if this auth method isn't supported; otherwise
|
||||
an object containing queries for the user
|
||||
@rtype: int or L{InteractiveQuery}
|
||||
:param str username: the username of the authenticating client
|
||||
:param str submethods:
|
||||
a comma-separated list of methods preferred by the client (usually
|
||||
empty)
|
||||
:return:
|
||||
`.AUTH_FAILED` if this auth method isn't supported; otherwise an
|
||||
object containing queries for the user
|
||||
:rtype: int or `.InteractiveQuery`
|
||||
"""
|
||||
return AUTH_FAILED
|
||||
|
||||
|
@ -249,31 +202,30 @@ class ServerInterface (object):
|
|||
"""
|
||||
Continue or finish an interactive authentication challenge, if
|
||||
supported. You should override this method in server mode if you want
|
||||
to support the C{"keyboard-interactive"} auth type.
|
||||
to support the ``"keyboard-interactive"`` auth type.
|
||||
|
||||
Return L{AUTH_FAILED} if the responses are not accepted,
|
||||
L{AUTH_SUCCESSFUL} if the responses are accepted and complete
|
||||
the authentication, or L{AUTH_PARTIALLY_SUCCESSFUL} if your
|
||||
Return `.AUTH_FAILED` if the responses are not accepted,
|
||||
`.AUTH_SUCCESSFUL` if the responses are accepted and complete
|
||||
the authentication, or `.AUTH_PARTIALLY_SUCCESSFUL` if your
|
||||
authentication is stateful, and this set of responses is accepted for
|
||||
authentication, but more authentication is required. (In this latter
|
||||
case, L{get_allowed_auths} will be called to report to the client what
|
||||
case, `get_allowed_auths` will be called to report to the client what
|
||||
options it has for continuing the authentication.)
|
||||
|
||||
If you wish to continue interactive authentication with more questions,
|
||||
you may return an L{InteractiveQuery} object, which should cause the
|
||||
you may return an `.InteractiveQuery` object, which should cause the
|
||||
client to respond with more answers, calling this method again. This
|
||||
cycle can continue indefinitely.
|
||||
|
||||
The default implementation always returns L{AUTH_FAILED}.
|
||||
The default implementation always returns `.AUTH_FAILED`.
|
||||
|
||||
@param responses: list of responses from the client
|
||||
@type responses: list(str)
|
||||
@return: L{AUTH_FAILED} if the authentication fails;
|
||||
L{AUTH_SUCCESSFUL} if it succeeds;
|
||||
L{AUTH_PARTIALLY_SUCCESSFUL} if the interactive auth is
|
||||
successful, but authentication must continue; otherwise an object
|
||||
containing queries for the user
|
||||
@rtype: int or L{InteractiveQuery}
|
||||
:param list responses: list of `str` responses from the client
|
||||
:return:
|
||||
`.AUTH_FAILED` if the authentication fails; `.AUTH_SUCCESSFUL` if
|
||||
it succeeds; `.AUTH_PARTIALLY_SUCCESSFUL` if the interactive auth
|
||||
is successful, but authentication must continue; otherwise an
|
||||
object containing queries for the user
|
||||
:rtype: int or `.InteractiveQuery`
|
||||
"""
|
||||
return AUTH_FAILED
|
||||
|
||||
|
@ -281,22 +233,20 @@ class ServerInterface (object):
|
|||
"""
|
||||
Handle a request for port forwarding. The client is asking that
|
||||
connections to the given address and port be forwarded back across
|
||||
this ssh connection. An address of C{"0.0.0.0"} indicates a global
|
||||
address (any address associated with this server) and a port of C{0}
|
||||
this ssh connection. An address of ``"0.0.0.0"`` indicates a global
|
||||
address (any address associated with this server) and a port of ``0``
|
||||
indicates that no specific port is requested (usually the OS will pick
|
||||
a port).
|
||||
|
||||
The default implementation always returns C{False}, rejecting the
|
||||
The default implementation always returns ``False``, rejecting the
|
||||
port forwarding request. If the request is accepted, you should return
|
||||
the port opened for listening.
|
||||
|
||||
@param address: the requested address
|
||||
@type address: str
|
||||
@param port: the requested port
|
||||
@type port: int
|
||||
@return: the port number that was opened for listening, or C{False} to
|
||||
reject
|
||||
@rtype: int
|
||||
:param str address: the requested address
|
||||
:param int port: the requested port
|
||||
:return:
|
||||
the port number (`int`) that was opened for listening, or ``False``
|
||||
to reject
|
||||
"""
|
||||
return False
|
||||
|
||||
|
@ -306,19 +256,17 @@ class ServerInterface (object):
|
|||
If the given address and port is being forwarded across this ssh
|
||||
connection, the port should be closed.
|
||||
|
||||
@param address: the forwarded address
|
||||
@type address: str
|
||||
@param port: the forwarded port
|
||||
@type port: int
|
||||
:param str address: the forwarded address
|
||||
:param int port: the forwarded port
|
||||
"""
|
||||
pass
|
||||
|
||||
def check_global_request(self, kind, msg):
|
||||
"""
|
||||
Handle a global request of the given C{kind}. This method is called
|
||||
Handle a global request of the given ``kind``. This method is called
|
||||
in server mode and client mode, whenever the remote host makes a global
|
||||
request. If there are any arguments to the request, they will be in
|
||||
C{msg}.
|
||||
``msg``.
|
||||
|
||||
There aren't any useful global requests defined, aside from port
|
||||
forwarding, so usually this type of request is an extension to the
|
||||
|
@ -329,19 +277,17 @@ class ServerInterface (object):
|
|||
sent back with the successful result. (Note that the items in the
|
||||
tuple can only be strings, ints, longs, or bools.)
|
||||
|
||||
The default implementation always returns C{False}, indicating that it
|
||||
The default implementation always returns ``False``, indicating that it
|
||||
does not support any global requests.
|
||||
|
||||
@note: Port forwarding requests are handled separately, in
|
||||
L{check_port_forward_request}.
|
||||
.. note:: Port forwarding requests are handled separately, in
|
||||
`check_port_forward_request`.
|
||||
|
||||
@param kind: the kind of global request being made.
|
||||
@type kind: str
|
||||
@param msg: any extra arguments to the request.
|
||||
@type msg: L{Message}
|
||||
@return: C{True} or a tuple of data if the request was granted;
|
||||
C{False} otherwise.
|
||||
@rtype: bool
|
||||
:param str kind: the kind of global request being made.
|
||||
:param .Message msg: any extra arguments to the request.
|
||||
:return:
|
||||
``True`` or a `tuple` of data if the request was granted; ``False``
|
||||
otherwise.
|
||||
"""
|
||||
return False
|
||||
|
||||
|
@ -355,89 +301,78 @@ class ServerInterface (object):
|
|||
Determine if a pseudo-terminal of the given dimensions (usually
|
||||
requested for shell access) can be provided on the given channel.
|
||||
|
||||
The default implementation always returns C{False}.
|
||||
The default implementation always returns ``False``.
|
||||
|
||||
@param channel: the L{Channel} the pty request arrived on.
|
||||
@type channel: L{Channel}
|
||||
@param term: type of terminal requested (for example, C{"vt100"}).
|
||||
@type term: str
|
||||
@param width: width of screen in characters.
|
||||
@type width: int
|
||||
@param height: height of screen in characters.
|
||||
@type height: int
|
||||
@param pixelwidth: width of screen in pixels, if known (may be C{0} if
|
||||
unknown).
|
||||
@type pixelwidth: int
|
||||
@param pixelheight: height of screen in pixels, if known (may be C{0}
|
||||
if unknown).
|
||||
@type pixelheight: int
|
||||
@return: C{True} if the psuedo-terminal has been allocated; C{False}
|
||||
:param .Channel channel: the `.Channel` the pty request arrived on.
|
||||
:param str term: type of terminal requested (for example, ``"vt100"``).
|
||||
:param int width: width of screen in characters.
|
||||
:param int height: height of screen in characters.
|
||||
:param int pixelwidth:
|
||||
width of screen in pixels, if known (may be ``0`` if unknown).
|
||||
:param int pixelheight:
|
||||
height of screen in pixels, if known (may be ``0`` if unknown).
|
||||
:return:
|
||||
``True`` if the psuedo-terminal has been allocated; ``False``
|
||||
otherwise.
|
||||
@rtype: bool
|
||||
"""
|
||||
return False
|
||||
|
||||
def check_channel_shell_request(self, channel):
|
||||
"""
|
||||
Determine if a shell will be provided to the client on the given
|
||||
channel. If this method returns C{True}, the channel should be
|
||||
channel. If this method returns ``True``, the channel should be
|
||||
connected to the stdin/stdout of a shell (or something that acts like
|
||||
a shell).
|
||||
|
||||
The default implementation always returns C{False}.
|
||||
The default implementation always returns ``False``.
|
||||
|
||||
@param channel: the L{Channel} the request arrived on.
|
||||
@type channel: L{Channel}
|
||||
@return: C{True} if this channel is now hooked up to a shell; C{False}
|
||||
if a shell can't or won't be provided.
|
||||
@rtype: bool
|
||||
:param .Channel channel: the `.Channel` the request arrived on.
|
||||
:return:
|
||||
``True`` if this channel is now hooked up to a shell; ``False`` if
|
||||
a shell can't or won't be provided.
|
||||
"""
|
||||
return False
|
||||
|
||||
def check_channel_exec_request(self, channel, command):
|
||||
"""
|
||||
Determine if a shell command will be executed for the client. If this
|
||||
method returns C{True}, the channel should be connected to the stdin,
|
||||
method returns ``True``, the channel should be connected to the stdin,
|
||||
stdout, and stderr of the shell command.
|
||||
|
||||
The default implementation always returns C{False}.
|
||||
The default implementation always returns ``False``.
|
||||
|
||||
@param channel: the L{Channel} the request arrived on.
|
||||
@type channel: L{Channel}
|
||||
@param command: the command to execute.
|
||||
@type command: str
|
||||
@return: C{True} if this channel is now hooked up to the stdin,
|
||||
stdout, and stderr of the executing command; C{False} if the
|
||||
command will not be executed.
|
||||
@rtype: bool
|
||||
:param .Channel channel: the `.Channel` the request arrived on.
|
||||
:param str command: the command to execute.
|
||||
:return:
|
||||
``True`` if this channel is now hooked up to the stdin, stdout, and
|
||||
stderr of the executing command; ``False`` if the command will not
|
||||
be executed.
|
||||
|
||||
@since: 1.1
|
||||
.. versionadded:: 1.1
|
||||
"""
|
||||
return False
|
||||
|
||||
def check_channel_subsystem_request(self, channel, name):
|
||||
"""
|
||||
Determine if a requested subsystem will be provided to the client on
|
||||
the given channel. If this method returns C{True}, all future I/O
|
||||
the given channel. If this method returns ``True``, all future I/O
|
||||
through this channel will be assumed to be connected to the requested
|
||||
subsystem. An example of a subsystem is C{sftp}.
|
||||
subsystem. An example of a subsystem is ``sftp``.
|
||||
|
||||
The default implementation checks for a subsystem handler assigned via
|
||||
L{Transport.set_subsystem_handler}.
|
||||
`.Transport.set_subsystem_handler`.
|
||||
If one has been set, the handler is invoked and this method returns
|
||||
C{True}. Otherwise it returns C{False}.
|
||||
``True``. Otherwise it returns ``False``.
|
||||
|
||||
@note: Because the default implementation uses the L{Transport} to
|
||||
.. note:: Because the default implementation uses the `.Transport` to
|
||||
identify valid subsystems, you probably won't need to override this
|
||||
method.
|
||||
|
||||
@param channel: the L{Channel} the pty request arrived on.
|
||||
@type channel: L{Channel}
|
||||
@param name: name of the requested subsystem.
|
||||
@type name: str
|
||||
@return: C{True} if this channel is now hooked up to the requested
|
||||
subsystem; C{False} if that subsystem can't or won't be provided.
|
||||
@rtype: bool
|
||||
:param .Channel channel: the `.Channel` the pty request arrived on.
|
||||
:param str name: name of the requested subsystem.
|
||||
:return:
|
||||
``True`` if this channel is now hooked up to the requested
|
||||
subsystem; ``False`` if that subsystem can't or won't be provided.
|
||||
"""
|
||||
handler_class, larg, kwarg = channel.get_transport()._get_subsystem_handler(name)
|
||||
if handler_class is None:
|
||||
|
@ -451,102 +386,88 @@ class ServerInterface (object):
|
|||
Determine if the pseudo-terminal on the given channel can be resized.
|
||||
This only makes sense if a pty was previously allocated on it.
|
||||
|
||||
The default implementation always returns C{False}.
|
||||
The default implementation always returns ``False``.
|
||||
|
||||
@param channel: the L{Channel} the pty request arrived on.
|
||||
@type channel: L{Channel}
|
||||
@param width: width of screen in characters.
|
||||
@type width: int
|
||||
@param height: height of screen in characters.
|
||||
@type height: int
|
||||
@param pixelwidth: width of screen in pixels, if known (may be C{0} if
|
||||
unknown).
|
||||
@type pixelwidth: int
|
||||
@param pixelheight: height of screen in pixels, if known (may be C{0}
|
||||
if unknown).
|
||||
@type pixelheight: int
|
||||
@return: C{True} if the terminal was resized; C{False} if not.
|
||||
@rtype: bool
|
||||
:param .Channel channel: the `.Channel` the pty request arrived on.
|
||||
:param int width: width of screen in characters.
|
||||
:param int height: height of screen in characters.
|
||||
:param int pixelwidth:
|
||||
width of screen in pixels, if known (may be ``0`` if unknown).
|
||||
:param int pixelheight:
|
||||
height of screen in pixels, if known (may be ``0`` if unknown).
|
||||
:return: ``True`` if the terminal was resized; ``False`` if not.
|
||||
"""
|
||||
return False
|
||||
|
||||
def check_channel_x11_request(self, channel, single_connection, auth_protocol, auth_cookie, screen_number):
|
||||
"""
|
||||
Determine if the client will be provided with an X11 session. If this
|
||||
method returns C{True}, X11 applications should be routed through new
|
||||
SSH channels, using L{Transport.open_x11_channel}.
|
||||
method returns ``True``, X11 applications should be routed through new
|
||||
SSH channels, using `.Transport.open_x11_channel`.
|
||||
|
||||
The default implementation always returns C{False}.
|
||||
The default implementation always returns ``False``.
|
||||
|
||||
@param channel: the L{Channel} the X11 request arrived on
|
||||
@type channel: L{Channel}
|
||||
@param single_connection: C{True} if only a single X11 channel should
|
||||
be opened
|
||||
@type single_connection: bool
|
||||
@param auth_protocol: the protocol used for X11 authentication
|
||||
@type auth_protocol: str
|
||||
@param auth_cookie: the cookie used to authenticate to X11
|
||||
@type auth_cookie: str
|
||||
@param screen_number: the number of the X11 screen to connect to
|
||||
@type screen_number: int
|
||||
@return: C{True} if the X11 session was opened; C{False} if not
|
||||
@rtype: bool
|
||||
:param .Channel channel: the `.Channel` the X11 request arrived on
|
||||
:param bool single_connection:
|
||||
``True`` if only a single X11 channel should be opened, else
|
||||
``False``.
|
||||
:param str auth_protocol: the protocol used for X11 authentication
|
||||
:param str auth_cookie: the cookie used to authenticate to X11
|
||||
:param int screen_number: the number of the X11 screen to connect to
|
||||
:return: ``True`` if the X11 session was opened; ``False`` if not
|
||||
"""
|
||||
return False
|
||||
|
||||
def check_channel_forward_agent_request(self, channel):
|
||||
"""
|
||||
Determine if the client will be provided with an forward agent session.
|
||||
If this method returns C{True}, the server will allow SSH Agent
|
||||
If this method returns ``True``, the server will allow SSH Agent
|
||||
forwarding.
|
||||
|
||||
The default implementation always returns C{False}.
|
||||
The default implementation always returns ``False``.
|
||||
|
||||
@param channel: the L{Channel} the request arrived on
|
||||
@type channel: L{Channel}
|
||||
@return: C{True} if the AgentForward was loaded; C{False} if not
|
||||
@rtype: bool
|
||||
:param .Channel channel: the `.Channel` the request arrived on
|
||||
:return: ``True`` if the AgentForward was loaded; ``False`` if not
|
||||
"""
|
||||
return False
|
||||
|
||||
def check_channel_direct_tcpip_request(self, chanid, origin, destination):
|
||||
"""
|
||||
Determine if a local port forwarding channel will be granted, and
|
||||
return C{OPEN_SUCCEEDED} or an error code. This method is
|
||||
return ``OPEN_SUCCEEDED`` or an error code. This method is
|
||||
called in server mode when the client requests a channel, after
|
||||
authentication is complete.
|
||||
|
||||
The C{chanid} parameter is a small number that uniquely identifies the
|
||||
channel within a L{Transport}. A L{Channel} object is not created
|
||||
unless this method returns C{OPEN_SUCCEEDED} -- once a
|
||||
L{Channel} object is created, you can call L{Channel.get_id} to
|
||||
The ``chanid`` parameter is a small number that uniquely identifies the
|
||||
channel within a `.Transport`. A `.Channel` object is not created
|
||||
unless this method returns ``OPEN_SUCCEEDED`` -- once a
|
||||
`.Channel` object is created, you can call `.Channel.get_id` to
|
||||
retrieve the channel ID.
|
||||
|
||||
The origin and destination parameters are (ip_address, port) tuples
|
||||
that correspond to both ends of the TCP connection in the forwarding
|
||||
tunnel.
|
||||
|
||||
The return value should either be C{OPEN_SUCCEEDED} (or
|
||||
C{0}) to allow the channel request, or one of the following error
|
||||
The return value should either be ``OPEN_SUCCEEDED`` (or
|
||||
``0``) to allow the channel request, or one of the following error
|
||||
codes to reject it:
|
||||
- C{OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED}
|
||||
- C{OPEN_FAILED_CONNECT_FAILED}
|
||||
- C{OPEN_FAILED_UNKNOWN_CHANNEL_TYPE}
|
||||
- C{OPEN_FAILED_RESOURCE_SHORTAGE}
|
||||
|
||||
- ``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``
|
||||
- ``OPEN_FAILED_CONNECT_FAILED``
|
||||
- ``OPEN_FAILED_UNKNOWN_CHANNEL_TYPE``
|
||||
- ``OPEN_FAILED_RESOURCE_SHORTAGE``
|
||||
|
||||
The default implementation always returns
|
||||
C{OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED}.
|
||||
``OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED``.
|
||||
|
||||
@param chanid: ID of the channel
|
||||
@type chanid: int
|
||||
@param origin: 2-tuple containing the IP address and port of the
|
||||
originator (client side)
|
||||
@type origin: tuple
|
||||
@param destination: 2-tuple containing the IP address and port of the
|
||||
destination (server side)
|
||||
@type destination: tuple
|
||||
@return: a success or failure code (listed above)
|
||||
@rtype: int
|
||||
:param int chanid: ID of the channel
|
||||
:param tuple origin:
|
||||
2-tuple containing the IP address and port of the originator
|
||||
(client side)
|
||||
:param tuple destination:
|
||||
2-tuple containing the IP address and port of the destination
|
||||
(server side)
|
||||
:return: an `int` success or failure code (listed above)
|
||||
"""
|
||||
return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
|
||||
|
||||
|
@ -572,37 +493,71 @@ class ServerInterface (object):
|
|||
return False
|
||||
|
||||
|
||||
class InteractiveQuery (object):
|
||||
"""
|
||||
A query (set of prompts) for a user during interactive authentication.
|
||||
"""
|
||||
|
||||
def __init__(self, name='', instructions='', *prompts):
|
||||
"""
|
||||
Create a new interactive query to send to the client. The name and
|
||||
instructions are optional, but are generally displayed to the end
|
||||
user. A list of prompts may be included, or they may be added via
|
||||
the `add_prompt` method.
|
||||
|
||||
:param str name: name of this query
|
||||
:param str instructions:
|
||||
user instructions (usually short) about this query
|
||||
:param str prompts: one or more authentication prompts
|
||||
"""
|
||||
self.name = name
|
||||
self.instructions = instructions
|
||||
self.prompts = []
|
||||
for x in prompts:
|
||||
if (type(x) is str) or (type(x) is unicode):
|
||||
self.add_prompt(x)
|
||||
else:
|
||||
self.add_prompt(x[0], x[1])
|
||||
|
||||
def add_prompt(self, prompt, echo=True):
|
||||
"""
|
||||
Add a prompt to this query. The prompt should be a (reasonably short)
|
||||
string. Multiple prompts can be added to the same query.
|
||||
|
||||
:param str prompt: the user prompt
|
||||
:param bool echo:
|
||||
``True`` (default) if the user's response should be echoed;
|
||||
``False`` if not (for a password or similar)
|
||||
"""
|
||||
self.prompts.append((prompt, echo))
|
||||
|
||||
|
||||
class SubsystemHandler (threading.Thread):
|
||||
"""
|
||||
Handler for a subsytem in server mode. If you create a subclass of this
|
||||
class and pass it to
|
||||
L{Transport.set_subsystem_handler},
|
||||
an object of this
|
||||
class and pass it to `.Transport.set_subsystem_handler`, an object of this
|
||||
class will be created for each request for this subsystem. Each new object
|
||||
will be executed within its own new thread by calling L{start_subsystem}.
|
||||
will be executed within its own new thread by calling `start_subsystem`.
|
||||
When that method completes, the channel is closed.
|
||||
|
||||
For example, if you made a subclass C{MP3Handler} and registered it as the
|
||||
handler for subsystem C{"mp3"}, then whenever a client has successfully
|
||||
authenticated and requests subsytem C{"mp3"}, an object of class
|
||||
C{MP3Handler} will be created, and L{start_subsystem} will be called on
|
||||
For example, if you made a subclass ``MP3Handler`` and registered it as the
|
||||
handler for subsystem ``"mp3"``, then whenever a client has successfully
|
||||
authenticated and requests subsytem ``"mp3"``, an object of class
|
||||
``MP3Handler`` will be created, and `start_subsystem` will be called on
|
||||
it from a new thread.
|
||||
"""
|
||||
def __init__(self, channel, name, server):
|
||||
"""
|
||||
Create a new handler for a channel. This is used by L{ServerInterface}
|
||||
Create a new handler for a channel. This is used by `.ServerInterface`
|
||||
to start up a new handler when a channel requests this subsystem. You
|
||||
don't need to override this method, but if you do, be sure to pass the
|
||||
C{channel} and C{name} parameters through to the original C{__init__}
|
||||
``channel`` and ``name`` parameters through to the original ``__init__``
|
||||
method here.
|
||||
|
||||
@param channel: the channel associated with this subsystem request.
|
||||
@type channel: L{Channel}
|
||||
@param name: name of the requested subsystem.
|
||||
@type name: str
|
||||
@param server: the server object for the session that started this
|
||||
subsystem
|
||||
@type server: L{ServerInterface}
|
||||
:param .Channel channel: the channel associated with this subsystem request.
|
||||
:param str name: name of the requested subsystem.
|
||||
:param .ServerInterface server:
|
||||
the server object for the session that started this subsystem
|
||||
"""
|
||||
threading.Thread.__init__(self, target=self._run)
|
||||
self.__channel = channel
|
||||
|
@ -612,10 +567,8 @@ class SubsystemHandler (threading.Thread):
|
|||
|
||||
def get_server(self):
|
||||
"""
|
||||
Return the L{ServerInterface} object associated with this channel and
|
||||
Return the `.ServerInterface` object associated with this channel and
|
||||
subsystem.
|
||||
|
||||
@rtype: L{ServerInterface}
|
||||
"""
|
||||
return self.__server
|
||||
|
||||
|
@ -640,22 +593,20 @@ class SubsystemHandler (threading.Thread):
|
|||
subsystem is finished, this method will return. After this method
|
||||
returns, the channel is closed.
|
||||
|
||||
The combination of C{transport} and C{channel} are unique; this handler
|
||||
corresponds to exactly one L{Channel} on one L{Transport}.
|
||||
The combination of ``transport`` and ``channel`` are unique; this handler
|
||||
corresponds to exactly one `.Channel` on one `.Transport`.
|
||||
|
||||
@note: It is the responsibility of this method to exit if the
|
||||
underlying L{Transport} is closed. This can be done by checking
|
||||
L{Transport.is_active} or noticing an EOF
|
||||
on the L{Channel}. If this method loops forever without checking
|
||||
for this case, your python interpreter may refuse to exit because
|
||||
this thread will still be running.
|
||||
.. note::
|
||||
It is the responsibility of this method to exit if the underlying
|
||||
`.Transport` is closed. This can be done by checking
|
||||
`.Transport.is_active` or noticing an EOF on the `.Channel`. If
|
||||
this method loops forever without checking for this case, your
|
||||
Python interpreter may refuse to exit because this thread will
|
||||
still be running.
|
||||
|
||||
@param name: name of the requested subsystem.
|
||||
@type name: str
|
||||
@param transport: the server-mode L{Transport}.
|
||||
@type transport: L{Transport}
|
||||
@param channel: the channel associated with this subsystem request.
|
||||
@type channel: L{Channel}
|
||||
:param str name: name of the requested subsystem.
|
||||
:param .Transport transport: the server-mode `.Transport`.
|
||||
:param .Channel channel: the channel associated with this subsystem request.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
@ -664,6 +615,6 @@ class SubsystemHandler (threading.Thread):
|
|||
Perform any cleanup at the end of a subsystem. The default
|
||||
implementation just closes the channel.
|
||||
|
||||
@since: 1.1
|
||||
.. versionadded:: 1.1
|
||||
"""
|
||||
self.__channel.close()
|
||||
|
|
|
@ -26,18 +26,19 @@ class SFTPAttributes (object):
|
|||
"""
|
||||
Representation of the attributes of a file (or proxied file) for SFTP in
|
||||
client or server mode. It attemps to mirror the object returned by
|
||||
C{os.stat} as closely as possible, so it may have the following fields,
|
||||
with the same meanings as those returned by an C{os.stat} object:
|
||||
- st_size
|
||||
- st_uid
|
||||
- st_gid
|
||||
- st_mode
|
||||
- st_atime
|
||||
- st_mtime
|
||||
`os.stat` as closely as possible, so it may have the following fields,
|
||||
with the same meanings as those returned by an `os.stat` object:
|
||||
|
||||
- ``st_size``
|
||||
- ``st_uid``
|
||||
- ``st_gid``
|
||||
- ``st_mode``
|
||||
- ``st_atime``
|
||||
- ``st_mtime``
|
||||
|
||||
Because SFTP allows flags to have other arbitrary named attributes, these
|
||||
are stored in a dict named C{attr}. Occasionally, the filename is also
|
||||
stored, in C{filename}.
|
||||
are stored in a dict named ``attr``. Occasionally, the filename is also
|
||||
stored, in ``filename``.
|
||||
"""
|
||||
|
||||
FLAG_SIZE = 1
|
||||
|
@ -61,15 +62,12 @@ class SFTPAttributes (object):
|
|||
|
||||
def from_stat(cls, obj, filename=None):
|
||||
"""
|
||||
Create an SFTPAttributes object from an existing C{stat} object (an
|
||||
object returned by C{os.stat}).
|
||||
Create an `.SFTPAttributes` object from an existing ``stat`` object (an
|
||||
object returned by `os.stat`).
|
||||
|
||||
@param obj: an object returned by C{os.stat} (or equivalent).
|
||||
@type obj: object
|
||||
@param filename: the filename associated with this file.
|
||||
@type filename: str
|
||||
@return: new L{SFTPAttributes} object with the same attribute fields.
|
||||
@rtype: L{SFTPAttributes}
|
||||
:param object obj: an object returned by `os.stat` (or equivalent).
|
||||
:param str filename: the filename associated with this file.
|
||||
:return: new `.SFTPAttributes` object with the same attribute fields.
|
||||
"""
|
||||
attr = cls()
|
||||
attr.st_size = obj.st_size
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
|
||||
#
|
||||
# This file is part of paramiko.
|
||||
# This file is part of Paramiko.
|
||||
#
|
||||
# Paramiko is free software; you can redistribute it and/or modify it under the
|
||||
# terms of the GNU Lesser General Public License as published by the Free
|
||||
|
@ -16,9 +16,6 @@
|
|||
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
Client-mode SFTP support.
|
||||
"""
|
||||
|
||||
from binascii import hexlify
|
||||
import errno
|
||||
|
@ -49,24 +46,24 @@ def _to_unicode(s):
|
|||
return s
|
||||
|
||||
|
||||
class SFTPClient (BaseSFTP):
|
||||
"""
|
||||
SFTP client object. C{SFTPClient} is used to open an sftp session across
|
||||
an open ssh L{Transport} and do remote file operations.
|
||||
class SFTPClient(BaseSFTP):
|
||||
"""
|
||||
SFTP client object.
|
||||
|
||||
Used to open an SFTP session across an open SSH `.Transport` and perform
|
||||
remote file operations.
|
||||
"""
|
||||
def __init__(self, sock):
|
||||
"""
|
||||
Create an SFTP client from an existing L{Channel}. The channel
|
||||
should already have requested the C{"sftp"} subsystem.
|
||||
Create an SFTP client from an existing `.Channel`. The channel
|
||||
should already have requested the ``"sftp"`` subsystem.
|
||||
|
||||
An alternate way to create an SFTP client context is by using
|
||||
L{from_transport}.
|
||||
`from_transport`.
|
||||
|
||||
@param sock: an open L{Channel} using the C{"sftp"} subsystem
|
||||
@type sock: L{Channel}
|
||||
:param .Channel sock: an open `.Channel` using the ``"sftp"`` subsystem
|
||||
|
||||
@raise SSHException: if there's an exception while negotiating
|
||||
:raises SSHException: if there's an exception while negotiating
|
||||
sftp
|
||||
"""
|
||||
BaseSFTP.__init__(self)
|
||||
|
@ -91,13 +88,12 @@ class SFTPClient (BaseSFTP):
|
|||
|
||||
def from_transport(cls, t):
|
||||
"""
|
||||
Create an SFTP client channel from an open L{Transport}.
|
||||
Create an SFTP client channel from an open `.Transport`.
|
||||
|
||||
@param t: an open L{Transport} which is already authenticated
|
||||
@type t: L{Transport}
|
||||
@return: a new L{SFTPClient} object, referring to an sftp session
|
||||
(channel) across the transport
|
||||
@rtype: L{SFTPClient}
|
||||
:param .Transport t: an open `.Transport` which is already authenticated
|
||||
:return:
|
||||
a new `.SFTPClient` object, referring to an sftp session (channel)
|
||||
across the transport
|
||||
"""
|
||||
chan = t.open_session()
|
||||
if chan is None:
|
||||
|
@ -117,56 +113,49 @@ class SFTPClient (BaseSFTP):
|
|||
"""
|
||||
Close the SFTP session and its underlying channel.
|
||||
|
||||
@since: 1.4
|
||||
.. versionadded:: 1.4
|
||||
"""
|
||||
self._log(INFO, 'sftp session closed.')
|
||||
self.sock.close()
|
||||
|
||||
def get_channel(self):
|
||||
"""
|
||||
Return the underlying L{Channel} object for this SFTP session. This
|
||||
Return the underlying `.Channel` object for this SFTP session. This
|
||||
might be useful for doing things like setting a timeout on the channel.
|
||||
|
||||
@return: the SSH channel
|
||||
@rtype: L{Channel}
|
||||
|
||||
@since: 1.7.1
|
||||
.. versionadded:: 1.7.1
|
||||
"""
|
||||
return self.sock
|
||||
|
||||
def listdir(self, path='.'):
|
||||
"""
|
||||
Return a list containing the names of the entries in the given C{path}.
|
||||
The list is in arbitrary order. It does not include the special
|
||||
entries C{'.'} and C{'..'} even if they are present in the folder.
|
||||
This method is meant to mirror C{os.listdir} as closely as possible.
|
||||
For a list of full L{SFTPAttributes} objects, see L{listdir_attr}.
|
||||
Return a list containing the names of the entries in the given ``path``.
|
||||
|
||||
@param path: path to list (defaults to C{'.'})
|
||||
@type path: str
|
||||
@return: list of filenames
|
||||
@rtype: list of str
|
||||
The list is in arbitrary order. It does not include the special
|
||||
entries ``'.'`` and ``'..'`` even if they are present in the folder.
|
||||
This method is meant to mirror ``os.listdir`` as closely as possible.
|
||||
For a list of full `.SFTPAttributes` objects, see `listdir_attr`.
|
||||
|
||||
:param str path: path to list (defaults to ``'.'``)
|
||||
"""
|
||||
return [f.filename for f in self.listdir_attr(path)]
|
||||
|
||||
def listdir_attr(self, path='.'):
|
||||
"""
|
||||
Return a list containing L{SFTPAttributes} objects corresponding to
|
||||
files in the given C{path}. The list is in arbitrary order. It does
|
||||
not include the special entries C{'.'} and C{'..'} even if they are
|
||||
Return a list containing `.SFTPAttributes` objects corresponding to
|
||||
files in the given ``path``. The list is in arbitrary order. It does
|
||||
not include the special entries ``'.'`` and ``'..'`` even if they are
|
||||
present in the folder.
|
||||
|
||||
The returned L{SFTPAttributes} objects will each have an additional
|
||||
field: C{longname}, which may contain a formatted string of the file's
|
||||
The returned `.SFTPAttributes` objects will each have an additional
|
||||
field: ``longname``, which may contain a formatted string of the file's
|
||||
attributes, in unix format. The content of this string will probably
|
||||
depend on the SFTP server implementation.
|
||||
|
||||
@param path: path to list (defaults to C{'.'})
|
||||
@type path: str
|
||||
@return: list of attributes
|
||||
@rtype: list of L{SFTPAttributes}
|
||||
:param str path: path to list (defaults to ``'.'``)
|
||||
:return: list of `.SFTPAttributes` objects
|
||||
|
||||
@since: 1.2
|
||||
.. versionadded:: 1.2
|
||||
"""
|
||||
path = self._adjust_cwd(path)
|
||||
self._log(DEBUG, 'listdir(%r)' % path)
|
||||
|
@ -196,37 +185,34 @@ class SFTPClient (BaseSFTP):
|
|||
def open(self, filename, mode='r', bufsize=-1):
|
||||
"""
|
||||
Open a file on the remote server. The arguments are the same as for
|
||||
python's built-in C{file} (aka C{open}). A file-like object is
|
||||
returned, which closely mimics the behavior of a normal python file
|
||||
object, including the ability to be used as a context manager.
|
||||
Python's built-in `python:file` (aka `python:open`). A file-like
|
||||
object is returned, which closely mimics the behavior of a normal
|
||||
Python file object, including the ability to be used as a context
|
||||
manager.
|
||||
|
||||
The mode indicates how the file is to be opened: C{'r'} for reading,
|
||||
C{'w'} for writing (truncating an existing file), C{'a'} for appending,
|
||||
C{'r+'} for reading/writing, C{'w+'} for reading/writing (truncating an
|
||||
existing file), C{'a+'} for reading/appending. The python C{'b'} flag
|
||||
is ignored, since SSH treats all files as binary. The C{'U'} flag is
|
||||
supported in a compatible way.
|
||||
The mode indicates how the file is to be opened: ``'r'`` for reading,
|
||||
``'w'`` for writing (truncating an existing file), ``'a'`` for
|
||||
appending, ``'r+'`` for reading/writing, ``'w+'`` for reading/writing
|
||||
(truncating an existing file), ``'a+'`` for reading/appending. The
|
||||
Python ``'b'`` flag is ignored, since SSH treats all files as binary.
|
||||
The ``'U'`` flag is supported in a compatible way.
|
||||
|
||||
Since 1.5.2, an C{'x'} flag indicates that the operation should only
|
||||
Since 1.5.2, an ``'x'`` flag indicates that the operation should only
|
||||
succeed if the file was created and did not previously exist. This has
|
||||
no direct mapping to python's file flags, but is commonly known as the
|
||||
C{O_EXCL} flag in posix.
|
||||
no direct mapping to Python's file flags, but is commonly known as the
|
||||
``O_EXCL`` flag in posix.
|
||||
|
||||
The file will be buffered in standard python style by default, but
|
||||
can be altered with the C{bufsize} parameter. C{0} turns off
|
||||
buffering, C{1} uses line buffering, and any number greater than 1
|
||||
(C{>1}) uses that specific buffer size.
|
||||
The file will be buffered in standard Python style by default, but
|
||||
can be altered with the ``bufsize`` parameter. ``0`` turns off
|
||||
buffering, ``1`` uses line buffering, and any number greater than 1
|
||||
(``>1``) uses that specific buffer size.
|
||||
|
||||
@param filename: name of the file to open
|
||||
@type filename: str
|
||||
@param mode: mode (python-style) to open in
|
||||
@type mode: str
|
||||
@param bufsize: desired buffering (-1 = default buffer size)
|
||||
@type bufsize: int
|
||||
@return: a file object representing the open file
|
||||
@rtype: SFTPFile
|
||||
:param str filename: name of the file to open
|
||||
:param str mode: mode (Python-style) to open in
|
||||
:param int bufsize: desired buffering (-1 = default buffer size)
|
||||
:return: an `.SFTPFile` object representing the open file
|
||||
|
||||
@raise IOError: if the file could not be opened.
|
||||
:raises IOError: if the file could not be opened.
|
||||
"""
|
||||
filename = self._adjust_cwd(filename)
|
||||
self._log(DEBUG, 'open(%r, %r)' % (filename, mode))
|
||||
|
@ -249,18 +235,17 @@ class SFTPClient (BaseSFTP):
|
|||
self._log(DEBUG, 'open(%r, %r) -> %s' % (filename, mode, hexlify(handle)))
|
||||
return SFTPFile(self, handle, mode, bufsize)
|
||||
|
||||
# python continues to vacillate about "open" vs "file"...
|
||||
# Python continues to vacillate about "open" vs "file"...
|
||||
file = open
|
||||
|
||||
def remove(self, path):
|
||||
"""
|
||||
Remove the file at the given path. This only works on files; for
|
||||
removing folders (directories), use L{rmdir}.
|
||||
removing folders (directories), use `rmdir`.
|
||||
|
||||
@param path: path (absolute or relative) of the file to remove
|
||||
@type path: str
|
||||
:param str path: path (absolute or relative) of the file to remove
|
||||
|
||||
@raise IOError: if the path refers to a folder (directory)
|
||||
:raises IOError: if the path refers to a folder (directory)
|
||||
"""
|
||||
path = self._adjust_cwd(path)
|
||||
self._log(DEBUG, 'remove(%r)' % path)
|
||||
|
@ -270,14 +255,12 @@ class SFTPClient (BaseSFTP):
|
|||
|
||||
def rename(self, oldpath, newpath):
|
||||
"""
|
||||
Rename a file or folder from C{oldpath} to C{newpath}.
|
||||
Rename a file or folder from ``oldpath`` to ``newpath``.
|
||||
|
||||
@param oldpath: existing name of the file or folder
|
||||
@type oldpath: str
|
||||
@param newpath: new name for the file or folder
|
||||
@type newpath: str
|
||||
:param str oldpath: existing name of the file or folder
|
||||
:param str newpath: new name for the file or folder
|
||||
|
||||
@raise IOError: if C{newpath} is a folder, or something else goes
|
||||
:raises IOError: if ``newpath`` is a folder, or something else goes
|
||||
wrong
|
||||
"""
|
||||
oldpath = self._adjust_cwd(oldpath)
|
||||
|
@ -287,14 +270,12 @@ class SFTPClient (BaseSFTP):
|
|||
|
||||
def mkdir(self, path, mode=0777):
|
||||
"""
|
||||
Create a folder (directory) named C{path} with numeric mode C{mode}.
|
||||
Create a folder (directory) named ``path`` with numeric mode ``mode``.
|
||||
The default mode is 0777 (octal). On some systems, mode is ignored.
|
||||
Where it is used, the current umask value is first masked out.
|
||||
|
||||
@param path: name of the folder to create
|
||||
@type path: str
|
||||
@param mode: permissions (posix-style) for the newly-created folder
|
||||
@type mode: int
|
||||
:param str path: name of the folder to create
|
||||
:param int mode: permissions (posix-style) for the newly-created folder
|
||||
"""
|
||||
path = self._adjust_cwd(path)
|
||||
self._log(DEBUG, 'mkdir(%r, %r)' % (path, mode))
|
||||
|
@ -304,10 +285,9 @@ class SFTPClient (BaseSFTP):
|
|||
|
||||
def rmdir(self, path):
|
||||
"""
|
||||
Remove the folder named C{path}.
|
||||
Remove the folder named ``path``.
|
||||
|
||||
@param path: name of the folder to remove
|
||||
@type path: str
|
||||
:param str path: name of the folder to remove
|
||||
"""
|
||||
path = self._adjust_cwd(path)
|
||||
self._log(DEBUG, 'rmdir(%r)' % path)
|
||||
|
@ -317,20 +297,20 @@ class SFTPClient (BaseSFTP):
|
|||
"""
|
||||
Retrieve information about a file on the remote system. The return
|
||||
value is an object whose attributes correspond to the attributes of
|
||||
python's C{stat} structure as returned by C{os.stat}, except that it
|
||||
Python's ``stat`` structure as returned by ``os.stat``, except that it
|
||||
contains fewer fields. An SFTP server may return as much or as little
|
||||
info as it wants, so the results may vary from server to server.
|
||||
|
||||
Unlike a python C{stat} object, the result may not be accessed as a
|
||||
tuple. This is mostly due to the author's slack factor.
|
||||
Unlike a Python `python:stat` object, the result may not be accessed as
|
||||
a tuple. This is mostly due to the author's slack factor.
|
||||
|
||||
The fields supported are: C{st_mode}, C{st_size}, C{st_uid}, C{st_gid},
|
||||
C{st_atime}, and C{st_mtime}.
|
||||
The fields supported are: ``st_mode``, ``st_size``, ``st_uid``,
|
||||
``st_gid``, ``st_atime``, and ``st_mtime``.
|
||||
|
||||
@param path: the filename to stat
|
||||
@type path: str
|
||||
@return: an object containing attributes about the given file
|
||||
@rtype: SFTPAttributes
|
||||
:param str path: the filename to stat
|
||||
:return:
|
||||
an `.SFTPAttributes` object containing attributes about the given
|
||||
file
|
||||
"""
|
||||
path = self._adjust_cwd(path)
|
||||
self._log(DEBUG, 'stat(%r)' % path)
|
||||
|
@ -343,12 +323,12 @@ class SFTPClient (BaseSFTP):
|
|||
"""
|
||||
Retrieve information about a file on the remote system, without
|
||||
following symbolic links (shortcuts). This otherwise behaves exactly
|
||||
the same as L{stat}.
|
||||
the same as `stat`.
|
||||
|
||||
@param path: the filename to stat
|
||||
@type path: str
|
||||
@return: an object containing attributes about the given file
|
||||
@rtype: SFTPAttributes
|
||||
:param str path: the filename to stat
|
||||
:return:
|
||||
an `.SFTPAttributes` object containing attributes about the given
|
||||
file
|
||||
"""
|
||||
path = self._adjust_cwd(path)
|
||||
self._log(DEBUG, 'lstat(%r)' % path)
|
||||
|
@ -359,13 +339,11 @@ class SFTPClient (BaseSFTP):
|
|||
|
||||
def symlink(self, source, dest):
|
||||
"""
|
||||
Create a symbolic link (shortcut) of the C{source} path at
|
||||
C{destination}.
|
||||
Create a symbolic link (shortcut) of the ``source`` path at
|
||||
``destination``.
|
||||
|
||||
@param source: path of the original file
|
||||
@type source: str
|
||||
@param dest: path of the newly created symlink
|
||||
@type dest: str
|
||||
:param str source: path of the original file
|
||||
:param str dest: path of the newly created symlink
|
||||
"""
|
||||
dest = self._adjust_cwd(dest)
|
||||
self._log(DEBUG, 'symlink(%r, %r)' % (source, dest))
|
||||
|
@ -376,13 +354,11 @@ class SFTPClient (BaseSFTP):
|
|||
def chmod(self, path, mode):
|
||||
"""
|
||||
Change the mode (permissions) of a file. The permissions are
|
||||
unix-style and identical to those used by python's C{os.chmod}
|
||||
unix-style and identical to those used by Python's `os.chmod`
|
||||
function.
|
||||
|
||||
@param path: path of the file to change the permissions of
|
||||
@type path: str
|
||||
@param mode: new permissions
|
||||
@type mode: int
|
||||
:param str path: path of the file to change the permissions of
|
||||
:param int mode: new permissions
|
||||
"""
|
||||
path = self._adjust_cwd(path)
|
||||
self._log(DEBUG, 'chmod(%r, %r)' % (path, mode))
|
||||
|
@ -392,17 +368,14 @@ class SFTPClient (BaseSFTP):
|
|||
|
||||
def chown(self, path, uid, gid):
|
||||
"""
|
||||
Change the owner (C{uid}) and group (C{gid}) of a file. As with
|
||||
python's C{os.chown} function, you must pass both arguments, so if you
|
||||
only want to change one, use L{stat} first to retrieve the current
|
||||
Change the owner (``uid``) and group (``gid``) of a file. As with
|
||||
Python's `os.chown` function, you must pass both arguments, so if you
|
||||
only want to change one, use `stat` first to retrieve the current
|
||||
owner and group.
|
||||
|
||||
@param path: path of the file to change the owner and group of
|
||||
@type path: str
|
||||
@param uid: new owner's uid
|
||||
@type uid: int
|
||||
@param gid: new group id
|
||||
@type gid: int
|
||||
:param str path: path of the file to change the owner and group of
|
||||
:param int uid: new owner's uid
|
||||
:param int gid: new group id
|
||||
"""
|
||||
path = self._adjust_cwd(path)
|
||||
self._log(DEBUG, 'chown(%r, %r, %r)' % (path, uid, gid))
|
||||
|
@ -412,18 +385,17 @@ class SFTPClient (BaseSFTP):
|
|||
|
||||
def utime(self, path, times):
|
||||
"""
|
||||
Set the access and modified times of the file specified by C{path}. If
|
||||
C{times} is C{None}, then the file's access and modified times are set
|
||||
to the current time. Otherwise, C{times} must be a 2-tuple of numbers,
|
||||
of the form C{(atime, mtime)}, which is used to set the access and
|
||||
modified times, respectively. This bizarre API is mimicked from python
|
||||
Set the access and modified times of the file specified by ``path``. If
|
||||
``times`` is ``None``, then the file's access and modified times are set
|
||||
to the current time. Otherwise, ``times`` must be a 2-tuple of numbers,
|
||||
of the form ``(atime, mtime)``, which is used to set the access and
|
||||
modified times, respectively. This bizarre API is mimicked from Python
|
||||
for the sake of consistency -- I apologize.
|
||||
|
||||
@param path: path of the file to modify
|
||||
@type path: str
|
||||
@param times: C{None} or a tuple of (access time, modified time) in
|
||||
standard internet epoch time (seconds since 01 January 1970 GMT)
|
||||
@type times: tuple(int)
|
||||
:param str path: path of the file to modify
|
||||
:param tuple times:
|
||||
``None`` or a tuple of (access time, modified time) in standard
|
||||
internet epoch time (seconds since 01 January 1970 GMT)
|
||||
"""
|
||||
path = self._adjust_cwd(path)
|
||||
if times is None:
|
||||
|
@ -435,14 +407,13 @@ class SFTPClient (BaseSFTP):
|
|||
|
||||
def truncate(self, path, size):
|
||||
"""
|
||||
Change the size of the file specified by C{path}. This usually extends
|
||||
or shrinks the size of the file, just like the C{truncate()} method on
|
||||
python file objects.
|
||||
Change the size of the file specified by ``path``. This usually
|
||||
extends or shrinks the size of the file, just like the `~file.truncate`
|
||||
method on Python file objects.
|
||||
|
||||
@param path: path of the file to modify
|
||||
@type path: str
|
||||
@param size: the new size of the file
|
||||
@type size: int or long
|
||||
:param str path: path of the file to modify
|
||||
:param size: the new size of the file
|
||||
:type size: int or long
|
||||
"""
|
||||
path = self._adjust_cwd(path)
|
||||
self._log(DEBUG, 'truncate(%r, %r)' % (path, size))
|
||||
|
@ -453,13 +424,11 @@ class SFTPClient (BaseSFTP):
|
|||
def readlink(self, path):
|
||||
"""
|
||||
Return the target of a symbolic link (shortcut). You can use
|
||||
L{symlink} to create these. The result may be either an absolute or
|
||||
`symlink` to create these. The result may be either an absolute or
|
||||
relative pathname.
|
||||
|
||||
@param path: path of the symbolic link file
|
||||
@type path: str
|
||||
@return: target path
|
||||
@rtype: str
|
||||
:param str path: path of the symbolic link file
|
||||
:return: target path, as a `str`
|
||||
"""
|
||||
path = self._adjust_cwd(path)
|
||||
self._log(DEBUG, 'readlink(%r)' % path)
|
||||
|
@ -477,15 +446,13 @@ class SFTPClient (BaseSFTP):
|
|||
"""
|
||||
Return the normalized path (on the server) of a given path. This
|
||||
can be used to quickly resolve symbolic links or determine what the
|
||||
server is considering to be the "current folder" (by passing C{'.'}
|
||||
as C{path}).
|
||||
server is considering to be the "current folder" (by passing ``'.'``
|
||||
as ``path``).
|
||||
|
||||
@param path: path to be normalized
|
||||
@type path: str
|
||||
@return: normalized form of the given path
|
||||
@rtype: str
|
||||
:param str path: path to be normalized
|
||||
:return: normalized form of the given path (as a `str`)
|
||||
|
||||
@raise IOError: if the path can't be resolved on the server
|
||||
:raises IOError: if the path can't be resolved on the server
|
||||
"""
|
||||
path = self._adjust_cwd(path)
|
||||
self._log(DEBUG, 'normalize(%r)' % path)
|
||||
|
@ -500,18 +467,17 @@ class SFTPClient (BaseSFTP):
|
|||
def chdir(self, path):
|
||||
"""
|
||||
Change the "current directory" of this SFTP session. Since SFTP
|
||||
doesn't really have the concept of a current working directory, this
|
||||
is emulated by paramiko. Once you use this method to set a working
|
||||
directory, all operations on this SFTPClient object will be relative
|
||||
to that path. You can pass in C{None} to stop using a current working
|
||||
doesn't really have the concept of a current working directory, this is
|
||||
emulated by Paramiko. Once you use this method to set a working
|
||||
directory, all operations on this `.SFTPClient` object will be relative
|
||||
to that path. You can pass in ``None`` to stop using a current working
|
||||
directory.
|
||||
|
||||
@param path: new current working directory
|
||||
@type path: str
|
||||
:param str path: new current working directory
|
||||
|
||||
@raise IOError: if the requested path doesn't exist on the server
|
||||
:raises IOError: if the requested path doesn't exist on the server
|
||||
|
||||
@since: 1.4
|
||||
.. versionadded:: 1.4
|
||||
"""
|
||||
if path is None:
|
||||
self._cwd = None
|
||||
|
@ -523,43 +489,41 @@ class SFTPClient (BaseSFTP):
|
|||
def getcwd(self):
|
||||
"""
|
||||
Return the "current working directory" for this SFTP session, as
|
||||
emulated by paramiko. If no directory has been set with L{chdir},
|
||||
this method will return C{None}.
|
||||
emulated by Paramiko. If no directory has been set with `chdir`,
|
||||
this method will return ``None``.
|
||||
|
||||
@return: the current working directory on the server, or C{None}
|
||||
@rtype: str
|
||||
|
||||
@since: 1.4
|
||||
.. versionadded:: 1.4
|
||||
"""
|
||||
return self._cwd
|
||||
|
||||
def putfo(self, fl, remotepath, file_size=0, callback=None, confirm=True):
|
||||
"""
|
||||
Copy the contents of an open file object (C{fl}) to the SFTP server as
|
||||
C{remotepath}. Any exception raised by operations will be passed through.
|
||||
Copy the contents of an open file object (``fl``) to the SFTP server as
|
||||
``remotepath``. Any exception raised by operations will be passed
|
||||
through.
|
||||
|
||||
The SFTP operations use pipelining for speed.
|
||||
|
||||
@param fl: opened file or file-like object to copy
|
||||
@type localpath: object
|
||||
@param remotepath: the destination path on the SFTP server
|
||||
@type remotepath: str
|
||||
@param file_size: optional size parameter passed to callback. If none is
|
||||
specified, size defaults to 0
|
||||
@type file_size: int
|
||||
@param callback: optional callback function that accepts the bytes
|
||||
transferred so far and the total bytes to be transferred
|
||||
:param file fl: opened file or file-like object to copy
|
||||
:param str remotepath: the destination path on the SFTP server
|
||||
:param int file_size:
|
||||
optional size parameter passed to callback. If none is specified,
|
||||
size defaults to 0
|
||||
:param callable callback:
|
||||
optional callback function (form: ``func(int, int)``) that accepts
|
||||
the bytes transferred so far and the total bytes to be transferred
|
||||
(since 1.7.4)
|
||||
@type callback: function(int, int)
|
||||
@param confirm: whether to do a stat() on the file afterwards to
|
||||
confirm the file size (since 1.7.7)
|
||||
@type confirm: bool
|
||||
:param bool confirm:
|
||||
whether to do a stat() on the file afterwards to confirm the file
|
||||
size (since 1.7.7)
|
||||
|
||||
@return: an object containing attributes about the given file
|
||||
(since 1.7.4)
|
||||
@rtype: SFTPAttributes
|
||||
:return:
|
||||
an `.SFTPAttributes` object containing attributes about the given
|
||||
file.
|
||||
|
||||
@since: 1.4
|
||||
.. versionadded:: 1.4
|
||||
.. versionchanged:: 1.7.4
|
||||
Began returning rich attribute objects.
|
||||
"""
|
||||
fr = self.file(remotepath, 'wb')
|
||||
fr.set_pipelined(True)
|
||||
|
@ -585,29 +549,28 @@ class SFTPClient (BaseSFTP):
|
|||
|
||||
def put(self, localpath, remotepath, callback=None, confirm=True):
|
||||
"""
|
||||
Copy a local file (C{localpath}) to the SFTP server as C{remotepath}.
|
||||
Copy a local file (``localpath``) to the SFTP server as ``remotepath``.
|
||||
Any exception raised by operations will be passed through. This
|
||||
method is primarily provided as a convenience.
|
||||
|
||||
The SFTP operations use pipelining for speed.
|
||||
|
||||
@param localpath: the local file to copy
|
||||
@type localpath: str
|
||||
@param remotepath: the destination path on the SFTP server
|
||||
@type remotepath: str
|
||||
@param callback: optional callback function that accepts the bytes
|
||||
transferred so far and the total bytes to be transferred
|
||||
(since 1.7.4)
|
||||
@type callback: function(int, int)
|
||||
@param confirm: whether to do a stat() on the file afterwards to
|
||||
confirm the file size (since 1.7.7)
|
||||
@type confirm: bool
|
||||
:param str localpath: the local file to copy
|
||||
:param str remotepath: the destination path on the SFTP server
|
||||
:param callable callback:
|
||||
optional callback function (form: ``func(int, int)``) that accepts
|
||||
the bytes transferred so far and the total bytes to be transferred
|
||||
:param bool confirm:
|
||||
whether to do a stat() on the file afterwards to confirm the file
|
||||
size
|
||||
|
||||
@return: an object containing attributes about the given file
|
||||
(since 1.7.4)
|
||||
@rtype: SFTPAttributes
|
||||
:return: an `.SFTPAttributes` object containing attributes about the given file
|
||||
|
||||
@since: 1.4
|
||||
.. versionadded:: 1.4
|
||||
.. versionchanged:: 1.7.4
|
||||
``callback`` and rich attribute return value added.
|
||||
.. versionchanged:: 1.7.7
|
||||
``confirm`` param added.
|
||||
"""
|
||||
file_size = os.stat(localpath).st_size
|
||||
fl = file(localpath, 'rb')
|
||||
|
@ -618,23 +581,22 @@ class SFTPClient (BaseSFTP):
|
|||
|
||||
def getfo(self, remotepath, fl, callback=None):
|
||||
"""
|
||||
Copy a remote file (C{remotepath}) from the SFTP server and write to
|
||||
an open file or file-like object, C{fl}. Any exception raised by
|
||||
Copy a remote file (``remotepath``) from the SFTP server and write to
|
||||
an open file or file-like object, ``fl``. Any exception raised by
|
||||
operations will be passed through. This method is primarily provided
|
||||
as a convenience.
|
||||
|
||||
@param remotepath: opened file or file-like object to copy to
|
||||
@type remotepath: object
|
||||
@param fl: the destination path on the local host or open file
|
||||
object
|
||||
@type localpath: str
|
||||
@param callback: optional callback function that accepts the bytes
|
||||
transferred so far and the total bytes to be transferred
|
||||
(since 1.7.4)
|
||||
@type callback: function(int, int)
|
||||
@return: the number of bytes written to the opened file object
|
||||
:param object remotepath: opened file or file-like object to copy to
|
||||
:param str fl:
|
||||
the destination path on the local host or open file object
|
||||
:param callable callback:
|
||||
optional callback function (form: ``func(int, int)``) that accepts
|
||||
the bytes transferred so far and the total bytes to be transferred
|
||||
:return: the `number <int>` of bytes written to the opened file object
|
||||
|
||||
@since: 1.4
|
||||
.. versionadded:: 1.4
|
||||
.. versionchanged:: 1.7.4
|
||||
Added the ``callable`` param.
|
||||
"""
|
||||
fr = self.file(remotepath, 'rb')
|
||||
file_size = self.stat(remotepath).st_size
|
||||
|
@ -655,20 +617,19 @@ class SFTPClient (BaseSFTP):
|
|||
|
||||
def get(self, remotepath, localpath, callback=None):
|
||||
"""
|
||||
Copy a remote file (C{remotepath}) from the SFTP server to the local
|
||||
host as C{localpath}. Any exception raised by operations will be
|
||||
Copy a remote file (``remotepath``) from the SFTP server to the local
|
||||
host as ``localpath``. Any exception raised by operations will be
|
||||
passed through. This method is primarily provided as a convenience.
|
||||
|
||||
@param remotepath: the remote file to copy
|
||||
@type remotepath: str
|
||||
@param localpath: the destination path on the local host
|
||||
@type localpath: str
|
||||
@param callback: optional callback function that accepts the bytes
|
||||
transferred so far and the total bytes to be transferred
|
||||
(since 1.7.4)
|
||||
@type callback: function(int, int)
|
||||
:param str remotepath: the remote file to copy
|
||||
:param str localpath: the destination path on the local host
|
||||
:param callable callback:
|
||||
optional callback function (form: ``func(int, int)``) that accepts
|
||||
the bytes transferred so far and the total bytes to be transferred
|
||||
|
||||
@since: 1.4
|
||||
.. versionadded:: 1.4
|
||||
.. versionchanged:: 1.7.4
|
||||
Added the ``callback`` param
|
||||
"""
|
||||
file_size = self.stat(remotepath).st_size
|
||||
fl = file(localpath, 'wb')
|
||||
|
@ -782,6 +743,8 @@ class SFTPClient (BaseSFTP):
|
|||
return self._cwd + '/' + path
|
||||
|
||||
|
||||
class SFTP (SFTPClient):
|
||||
"an alias for L{SFTPClient} for backwards compatability"
|
||||
class SFTP(SFTPClient):
|
||||
"""
|
||||
An alias for `.SFTPClient` for backwards compatability.
|
||||
"""
|
||||
pass
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
L{SFTPFile}
|
||||
SFTP file object
|
||||
"""
|
||||
|
||||
from __future__ import with_statement
|
||||
|
@ -64,6 +64,9 @@ class SFTPFile (BufferedFile):
|
|||
self._close(async=True)
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Close the file.
|
||||
"""
|
||||
self._close(async=False)
|
||||
|
||||
def _close(self, async=False):
|
||||
|
@ -181,34 +184,34 @@ class SFTPFile (BufferedFile):
|
|||
def settimeout(self, timeout):
|
||||
"""
|
||||
Set a timeout on read/write operations on the underlying socket or
|
||||
ssh L{Channel}.
|
||||
ssh `.Channel`.
|
||||
|
||||
@see: L{Channel.settimeout}
|
||||
@param timeout: seconds to wait for a pending read/write operation
|
||||
before raising C{socket.timeout}, or C{None} for no timeout
|
||||
@type timeout: float
|
||||
:param float timeout:
|
||||
seconds to wait for a pending read/write operation before raising
|
||||
``socket.timeout``, or ``None`` for no timeout
|
||||
|
||||
.. seealso:: `.Channel.settimeout`
|
||||
"""
|
||||
self.sftp.sock.settimeout(timeout)
|
||||
|
||||
def gettimeout(self):
|
||||
"""
|
||||
Returns the timeout in seconds (as a float) associated with the socket
|
||||
or ssh L{Channel} used for this file.
|
||||
Returns the timeout in seconds (as a `float`) associated with the
|
||||
socket or ssh `.Channel` used for this file.
|
||||
|
||||
@see: L{Channel.gettimeout}
|
||||
@rtype: float
|
||||
.. seealso:: `.Channel.gettimeout`
|
||||
"""
|
||||
return self.sftp.sock.gettimeout()
|
||||
|
||||
def setblocking(self, blocking):
|
||||
"""
|
||||
Set blocking or non-blocking mode on the underiying socket or ssh
|
||||
L{Channel}.
|
||||
`.Channel`.
|
||||
|
||||
@see: L{Channel.setblocking}
|
||||
@param blocking: 0 to set non-blocking mode; non-0 to set blocking
|
||||
mode.
|
||||
@type blocking: int
|
||||
:param int blocking:
|
||||
0 to set non-blocking mode; non-0 to set blocking mode.
|
||||
|
||||
.. seealso:: `.Channel.setblocking`
|
||||
"""
|
||||
self.sftp.sock.setblocking(blocking)
|
||||
|
||||
|
@ -226,11 +229,10 @@ class SFTPFile (BufferedFile):
|
|||
def stat(self):
|
||||
"""
|
||||
Retrieve information about this file from the remote system. This is
|
||||
exactly like L{SFTP.stat}, except that it operates on an already-open
|
||||
file.
|
||||
exactly like `.SFTPClient.stat`, except that it operates on an
|
||||
already-open file.
|
||||
|
||||
@return: an object containing attributes about this file.
|
||||
@rtype: SFTPAttributes
|
||||
:return: an `.SFTPAttributes` object containing attributes about this file.
|
||||
"""
|
||||
t, msg = self.sftp._request(CMD_FSTAT, self.handle)
|
||||
if t != CMD_ATTRS:
|
||||
|
@ -240,11 +242,10 @@ class SFTPFile (BufferedFile):
|
|||
def chmod(self, mode):
|
||||
"""
|
||||
Change the mode (permissions) of this file. The permissions are
|
||||
unix-style and identical to those used by python's C{os.chmod}
|
||||
unix-style and identical to those used by Python's `os.chmod`
|
||||
function.
|
||||
|
||||
@param mode: new permissions
|
||||
@type mode: int
|
||||
:param int mode: new permissions
|
||||
"""
|
||||
self.sftp._log(DEBUG, 'chmod(%s, %r)' % (hexlify(self.handle), mode))
|
||||
attr = SFTPAttributes()
|
||||
|
@ -253,15 +254,13 @@ class SFTPFile (BufferedFile):
|
|||
|
||||
def chown(self, uid, gid):
|
||||
"""
|
||||
Change the owner (C{uid}) and group (C{gid}) of this file. As with
|
||||
python's C{os.chown} function, you must pass both arguments, so if you
|
||||
only want to change one, use L{stat} first to retrieve the current
|
||||
Change the owner (``uid``) and group (``gid``) of this file. As with
|
||||
Python's `os.chown` function, you must pass both arguments, so if you
|
||||
only want to change one, use `stat` first to retrieve the current
|
||||
owner and group.
|
||||
|
||||
@param uid: new owner's uid
|
||||
@type uid: int
|
||||
@param gid: new group id
|
||||
@type gid: int
|
||||
:param int uid: new owner's uid
|
||||
:param int gid: new group id
|
||||
"""
|
||||
self.sftp._log(DEBUG, 'chown(%s, %r, %r)' % (hexlify(self.handle), uid, gid))
|
||||
attr = SFTPAttributes()
|
||||
|
@ -271,15 +270,15 @@ class SFTPFile (BufferedFile):
|
|||
def utime(self, times):
|
||||
"""
|
||||
Set the access and modified times of this file. If
|
||||
C{times} is C{None}, then the file's access and modified times are set
|
||||
to the current time. Otherwise, C{times} must be a 2-tuple of numbers,
|
||||
of the form C{(atime, mtime)}, which is used to set the access and
|
||||
modified times, respectively. This bizarre API is mimicked from python
|
||||
``times`` is ``None``, then the file's access and modified times are set
|
||||
to the current time. Otherwise, ``times`` must be a 2-tuple of numbers,
|
||||
of the form ``(atime, mtime)``, which is used to set the access and
|
||||
modified times, respectively. This bizarre API is mimicked from Python
|
||||
for the sake of consistency -- I apologize.
|
||||
|
||||
@param times: C{None} or a tuple of (access time, modified time) in
|
||||
standard internet epoch time (seconds since 01 January 1970 GMT)
|
||||
@type times: tuple(int)
|
||||
:param tuple times:
|
||||
``None`` or a tuple of (access time, modified time) in standard
|
||||
internet epoch time (seconds since 01 January 1970 GMT)
|
||||
"""
|
||||
if times is None:
|
||||
times = (time.time(), time.time())
|
||||
|
@ -291,11 +290,11 @@ class SFTPFile (BufferedFile):
|
|||
def truncate(self, size):
|
||||
"""
|
||||
Change the size of this file. This usually extends
|
||||
or shrinks the size of the file, just like the C{truncate()} method on
|
||||
python file objects.
|
||||
or shrinks the size of the file, just like the ``truncate()`` method on
|
||||
Python file objects.
|
||||
|
||||
@param size: the new size of the file
|
||||
@type size: int or long
|
||||
:param size: the new size of the file
|
||||
:type size: int or long
|
||||
"""
|
||||
self.sftp._log(DEBUG, 'truncate(%s, %r)' % (hexlify(self.handle), size))
|
||||
attr = SFTPAttributes()
|
||||
|
@ -308,46 +307,48 @@ class SFTPFile (BufferedFile):
|
|||
to verify a successful upload or download, or for various rsync-like
|
||||
operations.
|
||||
|
||||
The file is hashed from C{offset}, for C{length} bytes. If C{length}
|
||||
is 0, the remainder of the file is hashed. Thus, if both C{offset}
|
||||
and C{length} are zero, the entire file is hashed.
|
||||
The file is hashed from ``offset``, for ``length`` bytes. If ``length``
|
||||
is 0, the remainder of the file is hashed. Thus, if both ``offset``
|
||||
and ``length`` are zero, the entire file is hashed.
|
||||
|
||||
Normally, C{block_size} will be 0 (the default), and this method will
|
||||
Normally, ``block_size`` will be 0 (the default), and this method will
|
||||
return a byte string representing the requested hash (for example, a
|
||||
string of length 16 for MD5, or 20 for SHA-1). If a non-zero
|
||||
C{block_size} is given, each chunk of the file (from C{offset} to
|
||||
C{offset + length}) of C{block_size} bytes is computed as a separate
|
||||
``block_size`` is given, each chunk of the file (from ``offset`` to
|
||||
``offset + length``) of ``block_size`` bytes is computed as a separate
|
||||
hash. The hash results are all concatenated and returned as a single
|
||||
string.
|
||||
|
||||
For example, C{check('sha1', 0, 1024, 512)} will return a string of
|
||||
For example, ``check('sha1', 0, 1024, 512)`` will return a string of
|
||||
length 40. The first 20 bytes will be the SHA-1 of the first 512 bytes
|
||||
of the file, and the last 20 bytes will be the SHA-1 of the next 512
|
||||
bytes.
|
||||
|
||||
@param hash_algorithm: the name of the hash algorithm to use (normally
|
||||
C{"sha1"} or C{"md5"})
|
||||
@type hash_algorithm: str
|
||||
@param offset: offset into the file to begin hashing (0 means to start
|
||||
from the beginning)
|
||||
@type offset: int or long
|
||||
@param length: number of bytes to hash (0 means continue to the end of
|
||||
the file)
|
||||
@type length: int or long
|
||||
@param block_size: number of bytes to hash per result (must not be less
|
||||
than 256; 0 means to compute only one hash of the entire segment)
|
||||
@type block_size: int
|
||||
@return: string of bytes representing the hash of each block,
|
||||
concatenated together
|
||||
@rtype: str
|
||||
:param str hash_algorithm:
|
||||
the name of the hash algorithm to use (normally ``"sha1"`` or
|
||||
``"md5"``)
|
||||
:param offset:
|
||||
offset into the file to begin hashing (0 means to start from the
|
||||
beginning)
|
||||
:type offset: int or long
|
||||
:param length:
|
||||
number of bytes to hash (0 means continue to the end of the file)
|
||||
:type length: int or long
|
||||
:param int block_size:
|
||||
number of bytes to hash per result (must not be less than 256; 0
|
||||
means to compute only one hash of the entire segment)
|
||||
:type block_size: int
|
||||
:return:
|
||||
`str` of bytes representing the hash of each block, concatenated
|
||||
together
|
||||
|
||||
@note: Many (most?) servers don't support this extension yet.
|
||||
|
||||
@raise IOError: if the server doesn't support the "check-file"
|
||||
:raises IOError: if the server doesn't support the "check-file"
|
||||
extension, or possibly doesn't support the hash algorithm
|
||||
requested
|
||||
|
||||
@since: 1.4
|
||||
.. note:: Many (most?) servers don't support this extension yet.
|
||||
|
||||
.. versionadded:: 1.4
|
||||
"""
|
||||
t, msg = self.sftp._request(CMD_EXTENDED, 'check-file', self.handle,
|
||||
hash_algorithm, long(offset), long(length), block_size)
|
||||
|
@ -360,35 +361,35 @@ class SFTPFile (BufferedFile):
|
|||
"""
|
||||
Turn on/off the pipelining of write operations to this file. When
|
||||
pipelining is on, paramiko won't wait for the server response after
|
||||
each write operation. Instead, they're collected as they come in.
|
||||
At the first non-write operation (including L{close}), all remaining
|
||||
each write operation. Instead, they're collected as they come in. At
|
||||
the first non-write operation (including `.close`), all remaining
|
||||
server responses are collected. This means that if there was an error
|
||||
with one of your later writes, an exception might be thrown from
|
||||
within L{close} instead of L{write}.
|
||||
with one of your later writes, an exception might be thrown from within
|
||||
`.close` instead of `.write`.
|
||||
|
||||
By default, files are I{not} pipelined.
|
||||
By default, files are not pipelined.
|
||||
|
||||
@param pipelined: C{True} if pipelining should be turned on for this
|
||||
file; C{False} otherwise
|
||||
@type pipelined: bool
|
||||
:param bool pipelined:
|
||||
``True`` if pipelining should be turned on for this file; ``False``
|
||||
otherwise
|
||||
|
||||
@since: 1.5
|
||||
.. versionadded:: 1.5
|
||||
"""
|
||||
self.pipelined = pipelined
|
||||
|
||||
def prefetch(self):
|
||||
"""
|
||||
Pre-fetch the remaining contents of this file in anticipation of
|
||||
future L{read} calls. If reading the entire file, pre-fetching can
|
||||
Pre-fetch the remaining contents of this file in anticipation of future
|
||||
`.read` calls. If reading the entire file, pre-fetching can
|
||||
dramatically improve the download speed by avoiding roundtrip latency.
|
||||
The file's contents are incrementally buffered in a background thread.
|
||||
|
||||
The prefetched data is stored in a buffer until read via the L{read}
|
||||
The prefetched data is stored in a buffer until read via the `.read`
|
||||
method. Once data has been read, it's removed from the buffer. The
|
||||
data may be read in a random order (using L{seek}); chunks of the
|
||||
data may be read in a random order (using `.seek`); chunks of the
|
||||
buffer that haven't been read will continue to be buffered.
|
||||
|
||||
@since: 1.5.1
|
||||
.. versionadded:: 1.5.1
|
||||
"""
|
||||
size = self.stat().st_size
|
||||
# queue up async reads for the rest of the file
|
||||
|
@ -404,17 +405,17 @@ class SFTPFile (BufferedFile):
|
|||
def readv(self, chunks):
|
||||
"""
|
||||
Read a set of blocks from the file by (offset, length). This is more
|
||||
efficient than doing a series of L{seek} and L{read} calls, since the
|
||||
efficient than doing a series of `.seek` and `.read` calls, since the
|
||||
prefetch machinery is used to retrieve all the requested blocks at
|
||||
once.
|
||||
|
||||
@param chunks: a list of (offset, length) tuples indicating which
|
||||
sections of the file to read
|
||||
@type chunks: list(tuple(long, int))
|
||||
@return: a list of blocks read, in the same order as in C{chunks}
|
||||
@rtype: list(str)
|
||||
:param chunks:
|
||||
a list of (offset, length) tuples indicating which sections of the
|
||||
file to read
|
||||
:type chunks: list(tuple(long, int))
|
||||
:return: a list of blocks read, in the same order as in ``chunks``
|
||||
|
||||
@since: 1.5.4
|
||||
.. versionadded:: 1.5.4
|
||||
"""
|
||||
self.sftp._log(DEBUG, 'readv(%s, %r)' % (hexlify(self.handle), chunks))
|
||||
|
||||
|
|
|
@ -33,16 +33,15 @@ class SFTPHandle (object):
|
|||
by the client to refer to the underlying file.
|
||||
|
||||
Server implementations can (and should) subclass SFTPHandle to implement
|
||||
features of a file handle, like L{stat} or L{chattr}.
|
||||
features of a file handle, like `stat` or `chattr`.
|
||||
"""
|
||||
def __init__(self, flags=0):
|
||||
"""
|
||||
Create a new file handle representing a local file being served over
|
||||
SFTP. If C{flags} is passed in, it's used to determine if the file
|
||||
SFTP. If ``flags`` is passed in, it's used to determine if the file
|
||||
is open in append mode.
|
||||
|
||||
@param flags: optional flags as passed to L{SFTPServerInterface.open}
|
||||
@type flags: int
|
||||
:param int flags: optional flags as passed to `.SFTPServerInterface.open`
|
||||
"""
|
||||
self.__flags = flags
|
||||
self.__name = None
|
||||
|
@ -56,10 +55,10 @@ class SFTPHandle (object):
|
|||
Normally you would use this method to close the underlying OS level
|
||||
file object(s).
|
||||
|
||||
The default implementation checks for attributes on C{self} named
|
||||
C{readfile} and/or C{writefile}, and if either or both are present,
|
||||
their C{close()} methods are called. This means that if you are
|
||||
using the default implementations of L{read} and L{write}, this
|
||||
The default implementation checks for attributes on ``self`` named
|
||||
``readfile`` and/or ``writefile``, and if either or both are present,
|
||||
their ``close()`` methods are called. This means that if you are
|
||||
using the default implementations of `read` and `write`, this
|
||||
method's default implementation should be fine also.
|
||||
"""
|
||||
readfile = getattr(self, 'readfile', None)
|
||||
|
@ -71,24 +70,22 @@ class SFTPHandle (object):
|
|||
|
||||
def read(self, offset, length):
|
||||
"""
|
||||
Read up to C{length} bytes from this file, starting at position
|
||||
C{offset}. The offset may be a python long, since SFTP allows it
|
||||
Read up to ``length`` bytes from this file, starting at position
|
||||
``offset``. The offset may be a Python long, since SFTP allows it
|
||||
to be 64 bits.
|
||||
|
||||
If the end of the file has been reached, this method may return an
|
||||
empty string to signify EOF, or it may also return L{SFTP_EOF}.
|
||||
empty string to signify EOF, or it may also return `.SFTP_EOF`.
|
||||
|
||||
The default implementation checks for an attribute on C{self} named
|
||||
C{readfile}, and if present, performs the read operation on the python
|
||||
The default implementation checks for an attribute on ``self`` named
|
||||
``readfile``, and if present, performs the read operation on the Python
|
||||
file-like object found there. (This is meant as a time saver for the
|
||||
common case where you are wrapping a python file object.)
|
||||
common case where you are wrapping a Python file object.)
|
||||
|
||||
@param offset: position in the file to start reading from.
|
||||
@type offset: int or long
|
||||
@param length: number of bytes to attempt to read.
|
||||
@type length: int
|
||||
@return: data read from the file, or an SFTP error code.
|
||||
@rtype: str
|
||||
:param offset: position in the file to start reading from.
|
||||
:type offset: int or long
|
||||
:param int length: number of bytes to attempt to read.
|
||||
:return: data read from the file, or an SFTP error code, as a `str`.
|
||||
"""
|
||||
readfile = getattr(self, 'readfile', None)
|
||||
if readfile is None:
|
||||
|
@ -108,23 +105,22 @@ class SFTPHandle (object):
|
|||
|
||||
def write(self, offset, data):
|
||||
"""
|
||||
Write C{data} into this file at position C{offset}. Extending the
|
||||
file past its original end is expected. Unlike python's normal
|
||||
C{write()} methods, this method cannot do a partial write: it must
|
||||
write all of C{data} or else return an error.
|
||||
Write ``data`` into this file at position ``offset``. Extending the
|
||||
file past its original end is expected. Unlike Python's normal
|
||||
``write()`` methods, this method cannot do a partial write: it must
|
||||
write all of ``data`` or else return an error.
|
||||
|
||||
The default implementation checks for an attribute on C{self} named
|
||||
C{writefile}, and if present, performs the write operation on the
|
||||
python file-like object found there. The attribute is named
|
||||
differently from C{readfile} to make it easy to implement read-only
|
||||
The default implementation checks for an attribute on ``self`` named
|
||||
``writefile``, and if present, performs the write operation on the
|
||||
Python file-like object found there. The attribute is named
|
||||
differently from ``readfile`` to make it easy to implement read-only
|
||||
(or write-only) files, but if both attributes are present, they should
|
||||
refer to the same file.
|
||||
|
||||
@param offset: position in the file to start reading from.
|
||||
@type offset: int or long
|
||||
@param data: data to write into the file.
|
||||
@type data: str
|
||||
@return: an SFTP error code like L{SFTP_OK}.
|
||||
:param offset: position in the file to start reading from.
|
||||
:type offset: int or long
|
||||
:param str data: data to write into the file.
|
||||
:return: an SFTP error code like `.SFTP_OK`.
|
||||
"""
|
||||
writefile = getattr(self, 'writefile', None)
|
||||
if writefile is None:
|
||||
|
@ -148,26 +144,25 @@ class SFTPHandle (object):
|
|||
|
||||
def stat(self):
|
||||
"""
|
||||
Return an L{SFTPAttributes} object referring to this open file, or an
|
||||
error code. This is equivalent to L{SFTPServerInterface.stat}, except
|
||||
Return an `.SFTPAttributes` object referring to this open file, or an
|
||||
error code. This is equivalent to `.SFTPServerInterface.stat`, except
|
||||
it's called on an open file instead of a path.
|
||||
|
||||
@return: an attributes object for the given file, or an SFTP error
|
||||
code (like L{SFTP_PERMISSION_DENIED}).
|
||||
@rtype: L{SFTPAttributes} I{or error code}
|
||||
:return:
|
||||
an attributes object for the given file, or an SFTP error code
|
||||
(like `.SFTP_PERMISSION_DENIED`).
|
||||
:rtype: `.SFTPAttributes` or error code
|
||||
"""
|
||||
return SFTP_OP_UNSUPPORTED
|
||||
|
||||
def chattr(self, attr):
|
||||
"""
|
||||
Change the attributes of this file. The C{attr} object will contain
|
||||
Change the attributes of this file. The ``attr`` object will contain
|
||||
only those fields provided by the client in its request, so you should
|
||||
check for the presence of fields before using them.
|
||||
|
||||
@param attr: the attributes to change on this file.
|
||||
@type attr: L{SFTPAttributes}
|
||||
@return: an error code like L{SFTP_OK}.
|
||||
@rtype: int
|
||||
:param .SFTPAttributes attr: the attributes to change on this file.
|
||||
:return: an `int` error code like `.SFTP_OK`.
|
||||
"""
|
||||
return SFTP_OP_UNSUPPORTED
|
||||
|
||||
|
|
|
@ -40,28 +40,25 @@ _hash_class = {
|
|||
|
||||
class SFTPServer (BaseSFTP, SubsystemHandler):
|
||||
"""
|
||||
Server-side SFTP subsystem support. Since this is a L{SubsystemHandler},
|
||||
it can be (and is meant to be) set as the handler for C{"sftp"} requests.
|
||||
Use L{Transport.set_subsystem_handler} to activate this class.
|
||||
Server-side SFTP subsystem support. Since this is a `.SubsystemHandler`,
|
||||
it can be (and is meant to be) set as the handler for ``"sftp"`` requests.
|
||||
Use `.Transport.set_subsystem_handler` to activate this class.
|
||||
"""
|
||||
|
||||
def __init__(self, channel, name, server, sftp_si=SFTPServerInterface, *largs, **kwargs):
|
||||
"""
|
||||
The constructor for SFTPServer is meant to be called from within the
|
||||
L{Transport} as a subsystem handler. C{server} and any additional
|
||||
`.Transport` as a subsystem handler. ``server`` and any additional
|
||||
parameters or keyword parameters are passed from the original call to
|
||||
L{Transport.set_subsystem_handler}.
|
||||
`.Transport.set_subsystem_handler`.
|
||||
|
||||
@param channel: channel passed from the L{Transport}.
|
||||
@type channel: L{Channel}
|
||||
@param name: name of the requested subsystem.
|
||||
@type name: str
|
||||
@param server: the server object associated with this channel and
|
||||
subsystem
|
||||
@type server: L{ServerInterface}
|
||||
@param sftp_si: a subclass of L{SFTPServerInterface} to use for handling
|
||||
individual requests.
|
||||
@type sftp_si: class
|
||||
:param .Channel channel: channel passed from the `.Transport`.
|
||||
:param str name: name of the requested subsystem.
|
||||
:param .ServerInterface server:
|
||||
the server object associated with this channel and subsystem
|
||||
:param class sftp_si:
|
||||
a subclass of `.SFTPServerInterface` to use for handling individual
|
||||
requests.
|
||||
"""
|
||||
BaseSFTP.__init__(self)
|
||||
SubsystemHandler.__init__(self, channel, name, server)
|
||||
|
@ -122,14 +119,12 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
|
|||
|
||||
def convert_errno(e):
|
||||
"""
|
||||
Convert an errno value (as from an C{OSError} or C{IOError}) into a
|
||||
Convert an errno value (as from an ``OSError`` or ``IOError``) into a
|
||||
standard SFTP result code. This is a convenience function for trapping
|
||||
exceptions in server code and returning an appropriate result.
|
||||
|
||||
@param e: an errno code, as from C{OSError.errno}.
|
||||
@type e: int
|
||||
@return: an SFTP error code like L{SFTP_NO_SUCH_FILE}.
|
||||
@rtype: int
|
||||
:param int e: an errno code, as from ``OSError.errno``.
|
||||
:return: an `int` SFTP error code like ``SFTP_NO_SUCH_FILE``.
|
||||
"""
|
||||
if e == errno.EACCES:
|
||||
# permission denied
|
||||
|
@ -144,18 +139,16 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
|
|||
def set_file_attr(filename, attr):
|
||||
"""
|
||||
Change a file's attributes on the local filesystem. The contents of
|
||||
C{attr} are used to change the permissions, owner, group ownership,
|
||||
``attr`` are used to change the permissions, owner, group ownership,
|
||||
and/or modification & access time of the file, depending on which
|
||||
attributes are present in C{attr}.
|
||||
attributes are present in ``attr``.
|
||||
|
||||
This is meant to be a handy helper function for translating SFTP file
|
||||
requests into local file operations.
|
||||
|
||||
@param filename: name of the file to alter (should usually be an
|
||||
absolute path).
|
||||
@type filename: str
|
||||
@param attr: attributes to change.
|
||||
@type attr: L{SFTPAttributes}
|
||||
:param str filename:
|
||||
name of the file to alter (should usually be an absolute path).
|
||||
:param .SFTPAttributes attr: attributes to change.
|
||||
"""
|
||||
if sys.platform != 'win32':
|
||||
# mode operations are meaningless on win32
|
||||
|
@ -296,7 +289,7 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
|
|||
self._send_packet(CMD_EXTENDED_REPLY, str(msg))
|
||||
|
||||
def _convert_pflags(self, pflags):
|
||||
"convert SFTP-style open() flags to python's os.open() flags"
|
||||
"convert SFTP-style open() flags to Python's os.open() flags"
|
||||
if (pflags & SFTP_FLAG_READ) and (pflags & SFTP_FLAG_WRITE):
|
||||
flags = os.O_RDWR
|
||||
elif pflags & SFTP_FLAG_WRITE:
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
L{SFTPServerInterface} is an interface to override for SFTP server support.
|
||||
An interface to override for SFTP server support.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
@ -29,7 +29,7 @@ from paramiko.sftp import *
|
|||
class SFTPServerInterface (object):
|
||||
"""
|
||||
This class defines an interface for controlling the behavior of paramiko
|
||||
when using the L{SFTPServer} subsystem to provide an SFTP server.
|
||||
when using the `.SFTPServer` subsystem to provide an SFTP server.
|
||||
|
||||
Methods on this class are called from the SFTP session's thread, so you can
|
||||
block as long as necessary without affecting other sessions (even other
|
||||
|
@ -46,9 +46,8 @@ class SFTPServerInterface (object):
|
|||
Create a new SFTPServerInterface object. This method does nothing by
|
||||
default and is meant to be overridden by subclasses.
|
||||
|
||||
@param server: the server object associated with this channel and
|
||||
SFTP subsystem
|
||||
@type server: L{ServerInterface}
|
||||
:param .ServerInterface server:
|
||||
the server object associated with this channel and SFTP subsystem
|
||||
"""
|
||||
super(SFTPServerInterface, self).__init__(*largs, **kwargs)
|
||||
|
||||
|
@ -64,7 +63,7 @@ class SFTPServerInterface (object):
|
|||
"""
|
||||
The SFTP server session has just ended, either cleanly or via an
|
||||
exception. This method is meant to be overridden to perform any
|
||||
necessary cleanup before this C{SFTPServerInterface} object is
|
||||
necessary cleanup before this `.SFTPServerInterface` object is
|
||||
destroyed.
|
||||
"""
|
||||
pass
|
||||
|
@ -72,103 +71,105 @@ class SFTPServerInterface (object):
|
|||
def open(self, path, flags, attr):
|
||||
"""
|
||||
Open a file on the server and create a handle for future operations
|
||||
on that file. On success, a new object subclassed from L{SFTPHandle}
|
||||
on that file. On success, a new object subclassed from `.SFTPHandle`
|
||||
should be returned. This handle will be used for future operations
|
||||
on the file (read, write, etc). On failure, an error code such as
|
||||
L{SFTP_PERMISSION_DENIED} should be returned.
|
||||
`.SFTP_PERMISSION_DENIED` should be returned.
|
||||
|
||||
C{flags} contains the requested mode for opening (read-only,
|
||||
write-append, etc) as a bitset of flags from the C{os} module:
|
||||
- C{os.O_RDONLY}
|
||||
- C{os.O_WRONLY}
|
||||
- C{os.O_RDWR}
|
||||
- C{os.O_APPEND}
|
||||
- C{os.O_CREAT}
|
||||
- C{os.O_TRUNC}
|
||||
- C{os.O_EXCL}
|
||||
(One of C{os.O_RDONLY}, C{os.O_WRONLY}, or C{os.O_RDWR} will always
|
||||
``flags`` contains the requested mode for opening (read-only,
|
||||
write-append, etc) as a bitset of flags from the ``os`` module:
|
||||
|
||||
- ``os.O_RDONLY``
|
||||
- ``os.O_WRONLY``
|
||||
- ``os.O_RDWR``
|
||||
- ``os.O_APPEND``
|
||||
- ``os.O_CREAT``
|
||||
- ``os.O_TRUNC``
|
||||
- ``os.O_EXCL``
|
||||
|
||||
(One of ``os.O_RDONLY``, ``os.O_WRONLY``, or ``os.O_RDWR`` will always
|
||||
be set.)
|
||||
|
||||
The C{attr} object contains requested attributes of the file if it
|
||||
The ``attr`` object contains requested attributes of the file if it
|
||||
has to be created. Some or all attribute fields may be missing if
|
||||
the client didn't specify them.
|
||||
|
||||
@note: The SFTP protocol defines all files to be in "binary" mode.
|
||||
There is no equivalent to python's "text" mode.
|
||||
.. note:: The SFTP protocol defines all files to be in "binary" mode.
|
||||
There is no equivalent to Python's "text" mode.
|
||||
|
||||
@param path: the requested path (relative or absolute) of the file
|
||||
to be opened.
|
||||
@type path: str
|
||||
@param flags: flags or'd together from the C{os} module indicating the
|
||||
requested mode for opening the file.
|
||||
@type flags: int
|
||||
@param attr: requested attributes of the file if it is newly created.
|
||||
@type attr: L{SFTPAttributes}
|
||||
@return: a new L{SFTPHandle} I{or error code}.
|
||||
@rtype L{SFTPHandle}
|
||||
:param str path:
|
||||
the requested path (relative or absolute) of the file to be opened.
|
||||
:param int flags:
|
||||
flags or'd together from the ``os`` module indicating the requested
|
||||
mode for opening the file.
|
||||
:param .SFTPAttributes attr:
|
||||
requested attributes of the file if it is newly created.
|
||||
:return: a new `.SFTPHandle` or error code.
|
||||
"""
|
||||
return SFTP_OP_UNSUPPORTED
|
||||
|
||||
def list_folder(self, path):
|
||||
"""
|
||||
Return a list of files within a given folder. The C{path} will use
|
||||
posix notation (C{"/"} separates folder names) and may be an absolute
|
||||
Return a list of files within a given folder. The ``path`` will use
|
||||
posix notation (``"/"`` separates folder names) and may be an absolute
|
||||
or relative path.
|
||||
|
||||
The list of files is expected to be a list of L{SFTPAttributes}
|
||||
The list of files is expected to be a list of `.SFTPAttributes`
|
||||
objects, which are similar in structure to the objects returned by
|
||||
C{os.stat}. In addition, each object should have its C{filename}
|
||||
``os.stat``. In addition, each object should have its ``filename``
|
||||
field filled in, since this is important to a directory listing and
|
||||
not normally present in C{os.stat} results. The method
|
||||
L{SFTPAttributes.from_stat} will usually do what you want.
|
||||
not normally present in ``os.stat`` results. The method
|
||||
`.SFTPAttributes.from_stat` will usually do what you want.
|
||||
|
||||
In case of an error, you should return one of the C{SFTP_*} error
|
||||
codes, such as L{SFTP_PERMISSION_DENIED}.
|
||||
In case of an error, you should return one of the ``SFTP_*`` error
|
||||
codes, such as `.SFTP_PERMISSION_DENIED`.
|
||||
|
||||
@param path: the requested path (relative or absolute) to be listed.
|
||||
@type path: str
|
||||
@return: a list of the files in the given folder, using
|
||||
L{SFTPAttributes} objects.
|
||||
@rtype: list of L{SFTPAttributes} I{or error code}
|
||||
:param str path: the requested path (relative or absolute) to be listed.
|
||||
:return:
|
||||
a list of the files in the given folder, using `.SFTPAttributes`
|
||||
objects.
|
||||
|
||||
@note: You should normalize the given C{path} first (see the
|
||||
C{os.path} module) and check appropriate permissions before returning
|
||||
the list of files. Be careful of malicious clients attempting to use
|
||||
relative paths to escape restricted folders, if you're doing a direct
|
||||
translation from the SFTP server path to your local filesystem.
|
||||
.. note::
|
||||
You should normalize the given ``path`` first (see the `os.path`
|
||||
module) and check appropriate permissions before returning the list
|
||||
of files. Be careful of malicious clients attempting to use
|
||||
relative paths to escape restricted folders, if you're doing a
|
||||
direct translation from the SFTP server path to your local
|
||||
filesystem.
|
||||
"""
|
||||
return SFTP_OP_UNSUPPORTED
|
||||
|
||||
def stat(self, path):
|
||||
"""
|
||||
Return an L{SFTPAttributes} object for a path on the server, or an
|
||||
Return an `.SFTPAttributes` object for a path on the server, or an
|
||||
error code. If your server supports symbolic links (also known as
|
||||
"aliases"), you should follow them. (L{lstat} is the corresponding
|
||||
"aliases"), you should follow them. (`lstat` is the corresponding
|
||||
call that doesn't follow symlinks/aliases.)
|
||||
|
||||
@param path: the requested path (relative or absolute) to fetch
|
||||
file statistics for.
|
||||
@type path: str
|
||||
@return: an attributes object for the given file, or an SFTP error
|
||||
code (like L{SFTP_PERMISSION_DENIED}).
|
||||
@rtype: L{SFTPAttributes} I{or error code}
|
||||
:param str path:
|
||||
the requested path (relative or absolute) to fetch file statistics
|
||||
for.
|
||||
:return:
|
||||
an `.SFTPAttributes` object for the given file, or an SFTP error
|
||||
code (like `.SFTP_PERMISSION_DENIED`).
|
||||
"""
|
||||
return SFTP_OP_UNSUPPORTED
|
||||
|
||||
def lstat(self, path):
|
||||
"""
|
||||
Return an L{SFTPAttributes} object for a path on the server, or an
|
||||
Return an `.SFTPAttributes` object for a path on the server, or an
|
||||
error code. If your server supports symbolic links (also known as
|
||||
"aliases"), you should I{not} follow them -- instead, you should
|
||||
return data on the symlink or alias itself. (L{stat} is the
|
||||
"aliases"), you should not follow them -- instead, you should
|
||||
return data on the symlink or alias itself. (`stat` is the
|
||||
corresponding call that follows symlinks/aliases.)
|
||||
|
||||
@param path: the requested path (relative or absolute) to fetch
|
||||
file statistics for.
|
||||
@type path: str
|
||||
@return: an attributes object for the given file, or an SFTP error
|
||||
code (like L{SFTP_PERMISSION_DENIED}).
|
||||
@rtype: L{SFTPAttributes} I{or error code}
|
||||
:param str path:
|
||||
the requested path (relative or absolute) to fetch file statistics
|
||||
for.
|
||||
:type path: str
|
||||
:return:
|
||||
an `.SFTPAttributes` object for the given file, or an SFTP error
|
||||
code (like `.SFTP_PERMISSION_DENIED`).
|
||||
"""
|
||||
return SFTP_OP_UNSUPPORTED
|
||||
|
||||
|
@ -176,11 +177,9 @@ class SFTPServerInterface (object):
|
|||
"""
|
||||
Delete a file, if possible.
|
||||
|
||||
@param path: the requested path (relative or absolute) of the file
|
||||
to delete.
|
||||
@type path: str
|
||||
@return: an SFTP error code like L{SFTP_OK}.
|
||||
@rtype: int
|
||||
:param str path:
|
||||
the requested path (relative or absolute) of the file to delete.
|
||||
:return: an SFTP error code `int` like `.SFTP_OK`.
|
||||
"""
|
||||
return SFTP_OP_UNSUPPORTED
|
||||
|
||||
|
@ -192,83 +191,74 @@ class SFTPServerInterface (object):
|
|||
probably a good idea to implement "move" in this method too, even for
|
||||
files that cross disk partition boundaries, if at all possible.
|
||||
|
||||
@note: You should return an error if a file with the same name as
|
||||
C{newpath} already exists. (The rename operation should be
|
||||
.. note:: You should return an error if a file with the same name as
|
||||
``newpath`` already exists. (The rename operation should be
|
||||
non-desctructive.)
|
||||
|
||||
@param oldpath: the requested path (relative or absolute) of the
|
||||
existing file.
|
||||
@type oldpath: str
|
||||
@param newpath: the requested new path of the file.
|
||||
@type newpath: str
|
||||
@return: an SFTP error code like L{SFTP_OK}.
|
||||
@rtype: int
|
||||
:param str oldpath:
|
||||
the requested path (relative or absolute) of the existing file.
|
||||
:param str newpath: the requested new path of the file.
|
||||
:return: an SFTP error code `int` like `.SFTP_OK`.
|
||||
"""
|
||||
return SFTP_OP_UNSUPPORTED
|
||||
|
||||
def mkdir(self, path, attr):
|
||||
"""
|
||||
Create a new directory with the given attributes. The C{attr}
|
||||
Create a new directory with the given attributes. The ``attr``
|
||||
object may be considered a "hint" and ignored.
|
||||
|
||||
The C{attr} object will contain only those fields provided by the
|
||||
client in its request, so you should use C{hasattr} to check for
|
||||
the presense of fields before using them. In some cases, the C{attr}
|
||||
The ``attr`` object will contain only those fields provided by the
|
||||
client in its request, so you should use ``hasattr`` to check for
|
||||
the presense of fields before using them. In some cases, the ``attr``
|
||||
object may be completely empty.
|
||||
|
||||
@param path: requested path (relative or absolute) of the new
|
||||
folder.
|
||||
@type path: str
|
||||
@param attr: requested attributes of the new folder.
|
||||
@type attr: L{SFTPAttributes}
|
||||
@return: an SFTP error code like L{SFTP_OK}.
|
||||
@rtype: int
|
||||
:param str path:
|
||||
requested path (relative or absolute) of the new folder.
|
||||
:param .SFTPAttributes attr: requested attributes of the new folder.
|
||||
:return: an SFTP error code `int` like `.SFTP_OK`.
|
||||
"""
|
||||
return SFTP_OP_UNSUPPORTED
|
||||
|
||||
def rmdir(self, path):
|
||||
"""
|
||||
Remove a directory if it exists. The C{path} should refer to an
|
||||
Remove a directory if it exists. The ``path`` should refer to an
|
||||
existing, empty folder -- otherwise this method should return an
|
||||
error.
|
||||
|
||||
@param path: requested path (relative or absolute) of the folder
|
||||
to remove.
|
||||
@type path: str
|
||||
@return: an SFTP error code like L{SFTP_OK}.
|
||||
@rtype: int
|
||||
:param str path:
|
||||
requested path (relative or absolute) of the folder to remove.
|
||||
:return: an SFTP error code `int` like `.SFTP_OK`.
|
||||
"""
|
||||
return SFTP_OP_UNSUPPORTED
|
||||
|
||||
def chattr(self, path, attr):
|
||||
"""
|
||||
Change the attributes of a file. The C{attr} object will contain
|
||||
Change the attributes of a file. The ``attr`` object will contain
|
||||
only those fields provided by the client in its request, so you
|
||||
should check for the presence of fields before using them.
|
||||
|
||||
@param path: requested path (relative or absolute) of the file to
|
||||
change.
|
||||
@type path: str
|
||||
@param attr: requested attributes to change on the file.
|
||||
@type attr: L{SFTPAttributes}
|
||||
@return: an error code like L{SFTP_OK}.
|
||||
@rtype: int
|
||||
:param str path:
|
||||
requested path (relative or absolute) of the file to change.
|
||||
:param attr:
|
||||
requested attributes to change on the file (an `.SFTPAttributes`
|
||||
object)
|
||||
:return: an error code `int` like `.SFTP_OK`.
|
||||
"""
|
||||
return SFTP_OP_UNSUPPORTED
|
||||
|
||||
def canonicalize(self, path):
|
||||
"""
|
||||
Return the canonical form of a path on the server. For example,
|
||||
if the server's home folder is C{/home/foo}, the path
|
||||
C{"../betty"} would be canonicalized to C{"/home/betty"}. Note
|
||||
if the server's home folder is ``/home/foo``, the path
|
||||
``"../betty"`` would be canonicalized to ``"/home/betty"``. Note
|
||||
the obvious security issues: if you're serving files only from a
|
||||
specific folder, you probably don't want this method to reveal path
|
||||
names outside that folder.
|
||||
|
||||
You may find the python methods in C{os.path} useful, especially
|
||||
C{os.path.normpath} and C{os.path.realpath}.
|
||||
You may find the Python methods in ``os.path`` useful, especially
|
||||
``os.path.normpath`` and ``os.path.realpath``.
|
||||
|
||||
The default implementation returns C{os.path.normpath('/' + path)}.
|
||||
The default implementation returns ``os.path.normpath('/' + path)``.
|
||||
"""
|
||||
if os.path.isabs(path):
|
||||
out = os.path.normpath(path)
|
||||
|
@ -285,26 +275,23 @@ class SFTPServerInterface (object):
|
|||
If the specified path doesn't refer to a symbolic link, an error
|
||||
should be returned.
|
||||
|
||||
@param path: path (relative or absolute) of the symbolic link.
|
||||
@type path: str
|
||||
@return: the target path of the symbolic link, or an error code like
|
||||
L{SFTP_NO_SUCH_FILE}.
|
||||
@rtype: str I{or error code}
|
||||
:param str path: path (relative or absolute) of the symbolic link.
|
||||
:return:
|
||||
the target `str` path of the symbolic link, or an error code like
|
||||
`.SFTP_NO_SUCH_FILE`.
|
||||
"""
|
||||
return SFTP_OP_UNSUPPORTED
|
||||
|
||||
def symlink(self, target_path, path):
|
||||
"""
|
||||
Create a symbolic link on the server, as new pathname C{path},
|
||||
with C{target_path} as the target of the link.
|
||||
Create a symbolic link on the server, as new pathname ``path``,
|
||||
with ``target_path`` as the target of the link.
|
||||
|
||||
@param target_path: path (relative or absolute) of the target for
|
||||
this new symbolic link.
|
||||
@type target_path: str
|
||||
@param path: path (relative or absolute) of the symbolic link to
|
||||
create.
|
||||
@type path: str
|
||||
@return: an error code like C{SFTP_OK}.
|
||||
@rtype: int
|
||||
:param str target_path:
|
||||
path (relative or absolute) of the target for this new symbolic
|
||||
link.
|
||||
:param str path:
|
||||
path (relative or absolute) of the symbolic link to create.
|
||||
:return: an error code `int` like ``SFTP_OK``.
|
||||
"""
|
||||
return SFTP_OP_UNSUPPORTED
|
||||
|
|
|
@ -16,10 +16,6 @@
|
|||
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
"""
|
||||
Exceptions defined by paramiko.
|
||||
"""
|
||||
|
||||
|
||||
class SSHException (Exception):
|
||||
"""
|
||||
|
@ -34,7 +30,7 @@ class AuthenticationException (SSHException):
|
|||
possible to retry with different credentials. (Other classes specify more
|
||||
specific reasons.)
|
||||
|
||||
@since: 1.6
|
||||
.. versionadded:: 1.6
|
||||
"""
|
||||
pass
|
||||
|
||||
|
@ -52,12 +48,11 @@ class BadAuthenticationType (AuthenticationException):
|
|||
the server isn't allowing that type. (It may only allow public-key, for
|
||||
example.)
|
||||
|
||||
@ivar allowed_types: list of allowed authentication types provided by the
|
||||
server (possible values are: C{"none"}, C{"password"}, and
|
||||
C{"publickey"}).
|
||||
@type allowed_types: list
|
||||
:ivar list allowed_types:
|
||||
list of allowed authentication types provided by the server (possible
|
||||
values are: ``"none"``, ``"password"``, and ``"publickey"``).
|
||||
|
||||
@since: 1.1
|
||||
.. versionadded:: 1.1
|
||||
"""
|
||||
allowed_types = []
|
||||
|
||||
|
@ -82,12 +77,11 @@ class PartialAuthentication (AuthenticationException):
|
|||
|
||||
class ChannelException (SSHException):
|
||||
"""
|
||||
Exception raised when an attempt to open a new L{Channel} fails.
|
||||
Exception raised when an attempt to open a new `.Channel` fails.
|
||||
|
||||
@ivar code: the error code returned by the server
|
||||
@type code: int
|
||||
:ivar int code: the error code returned by the server
|
||||
|
||||
@since: 1.6
|
||||
.. versionadded:: 1.6
|
||||
"""
|
||||
def __init__(self, code, text):
|
||||
SSHException.__init__(self, text)
|
||||
|
@ -98,14 +92,11 @@ class BadHostKeyException (SSHException):
|
|||
"""
|
||||
The host key given by the SSH server did not match what we were expecting.
|
||||
|
||||
@ivar hostname: the hostname of the SSH server
|
||||
@type hostname: str
|
||||
@ivar key: the host key presented by the server
|
||||
@type key: L{PKey}
|
||||
@ivar expected_key: the host key expected
|
||||
@type expected_key: L{PKey}
|
||||
:ivar str hostname: the hostname of the SSH server
|
||||
:ivar PKey got_key: the host key presented by the server
|
||||
:ivar PKey expected_key: the host key expected
|
||||
|
||||
@since: 1.6
|
||||
.. versionadded:: 1.6
|
||||
"""
|
||||
def __init__(self, hostname, got_key, expected_key):
|
||||
SSHException.__init__(self, 'Host key for server %s does not match!' % hostname)
|
||||
|
@ -118,10 +109,8 @@ class ProxyCommandFailure (SSHException):
|
|||
"""
|
||||
The "ProxyCommand" found in the .ssh/config file returned an error.
|
||||
|
||||
@ivar command: The command line that is generating this exception.
|
||||
@type command: str
|
||||
@ivar error: The error captured from the proxy command output.
|
||||
@type error: str
|
||||
:ivar str command: The command line that is generating this exception.
|
||||
:ivar str error: The error captured from the proxy command output.
|
||||
"""
|
||||
def __init__(self, command, error):
|
||||
SSHException.__init__(self,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -34,7 +34,7 @@ from paramiko.common import *
|
|||
from paramiko.config import SSHConfig
|
||||
|
||||
|
||||
# Change by RogerB - python < 2.3 doesn't have enumerate so we implement it
|
||||
# Change by RogerB - Python < 2.3 doesn't have enumerate so we implement it
|
||||
if sys.version_info < (2,3):
|
||||
class enumerate:
|
||||
def __init__ (self, sequence):
|
||||
|
@ -154,17 +154,13 @@ def generate_key_bytes(hashclass, salt, key, nbytes):
|
|||
through a secure hash into some keyworthy bytes. This specific algorithm
|
||||
is used for encrypting/decrypting private key files.
|
||||
|
||||
@param hashclass: class from L{Crypto.Hash} that can be used as a secure
|
||||
hashing function (like C{MD5} or C{SHA}).
|
||||
@type hashclass: L{Crypto.Hash}
|
||||
@param salt: data to salt the hash with.
|
||||
@type salt: string
|
||||
@param key: human-entered password or passphrase.
|
||||
@type key: string
|
||||
@param nbytes: number of bytes to generate.
|
||||
@type nbytes: int
|
||||
@return: key data
|
||||
@rtype: string
|
||||
:param class hashclass:
|
||||
class from `Crypto.Hash` that can be used as a secure hashing function
|
||||
(like ``MD5`` or ``SHA``).
|
||||
:param str salt: data to salt the hash with.
|
||||
:param str key: human-entered password or passphrase.
|
||||
:param int nbytes: number of bytes to generate.
|
||||
:return: Key data `str`
|
||||
"""
|
||||
keydata = ''
|
||||
digest = ''
|
||||
|
@ -185,26 +181,25 @@ def generate_key_bytes(hashclass, salt, key, nbytes):
|
|||
def load_host_keys(filename):
|
||||
"""
|
||||
Read a file of known SSH host keys, in the format used by openssh, and
|
||||
return a compound dict of C{hostname -> keytype ->} L{PKey <paramiko.pkey.PKey>}.
|
||||
The hostname may be an IP address or DNS name. The keytype will be either
|
||||
C{"ssh-rsa"} or C{"ssh-dss"}.
|
||||
return a compound dict of ``hostname -> keytype ->`` `PKey
|
||||
<paramiko.pkey.PKey>`. The hostname may be an IP address or DNS name. The
|
||||
keytype will be either ``"ssh-rsa"`` or ``"ssh-dss"``.
|
||||
|
||||
This type of file unfortunately doesn't exist on Windows, but on posix,
|
||||
it will usually be stored in C{os.path.expanduser("~/.ssh/known_hosts")}.
|
||||
it will usually be stored in ``os.path.expanduser("~/.ssh/known_hosts")``.
|
||||
|
||||
Since 1.5.3, this is just a wrapper around L{HostKeys}.
|
||||
Since 1.5.3, this is just a wrapper around `.HostKeys`.
|
||||
|
||||
@param filename: name of the file to read host keys from
|
||||
@type filename: str
|
||||
@return: dict of host keys, indexed by hostname and then keytype
|
||||
@rtype: dict(hostname, dict(keytype, L{PKey <paramiko.pkey.PKey>}))
|
||||
:param str filename: name of the file to read host keys from
|
||||
:return:
|
||||
nested dict of `.PKey` objects, indexed by hostname and then keytype
|
||||
"""
|
||||
from paramiko.hostkeys import HostKeys
|
||||
return HostKeys(filename)
|
||||
|
||||
def parse_ssh_config(file_obj):
|
||||
"""
|
||||
Provided only as a backward-compatible wrapper around L{SSHConfig}.
|
||||
Provided only as a backward-compatible wrapper around `.SSHConfig`.
|
||||
"""
|
||||
config = SSHConfig()
|
||||
config.parse(file_obj)
|
||||
|
@ -212,12 +207,12 @@ def parse_ssh_config(file_obj):
|
|||
|
||||
def lookup_ssh_host_config(hostname, config):
|
||||
"""
|
||||
Provided only as a backward-compatible wrapper around L{SSHConfig}.
|
||||
Provided only as a backward-compatible wrapper around `.SSHConfig`.
|
||||
"""
|
||||
return config.lookup(hostname)
|
||||
|
||||
def mod_inverse(x, m):
|
||||
# it's crazy how small python can make this function.
|
||||
# it's crazy how small Python can make this function.
|
||||
u1, u2, u3 = 1, 0, m
|
||||
v1, v2, v3 = 0, 1, x
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
SSH Agents
|
||||
==========
|
||||
|
||||
.. automodule:: paramiko.agent
|
||||
:inherited-members:
|
||||
:no-special-members:
|
|
@ -0,0 +1,4 @@
|
|||
Buffered pipes
|
||||
==============
|
||||
|
||||
.. automodule:: paramiko.buffered_pipe
|
|
@ -0,0 +1,4 @@
|
|||
Channel
|
||||
=======
|
||||
|
||||
.. automodule:: paramiko.channel
|
|
@ -0,0 +1,5 @@
|
|||
Client
|
||||
======
|
||||
|
||||
.. automodule:: paramiko.client
|
||||
:member-order: bysource
|
|
@ -0,0 +1,5 @@
|
|||
Configuration
|
||||
=============
|
||||
|
||||
.. automodule:: paramiko.config
|
||||
:member-order: bysource
|
|
@ -0,0 +1,4 @@
|
|||
Buffered files
|
||||
==============
|
||||
|
||||
.. automodule:: paramiko.file
|
|
@ -0,0 +1,5 @@
|
|||
Host keys / ``known_hosts`` files
|
||||
=================================
|
||||
|
||||
.. automodule:: paramiko.hostkeys
|
||||
:member-order: bysource
|
|
@ -0,0 +1,6 @@
|
|||
Key handling
|
||||
============
|
||||
|
||||
.. automodule:: paramiko.pkey
|
||||
.. automodule:: paramiko.dsskey
|
||||
.. automodule:: paramiko.rsakey
|
|
@ -0,0 +1,4 @@
|
|||
Message
|
||||
=======
|
||||
|
||||
.. automodule:: paramiko.message
|
|
@ -0,0 +1,4 @@
|
|||
Packetizer
|
||||
==========
|
||||
|
||||
.. automodule:: paramiko.packet
|
|
@ -0,0 +1,4 @@
|
|||
Cross-platform pipe implementations
|
||||
===================================
|
||||
|
||||
.. automodule:: paramiko.pipe
|
|
@ -0,0 +1,4 @@
|
|||
``ProxyCommand`` support
|
||||
========================
|
||||
|
||||
.. automodule:: paramiko.proxy
|
|
@ -0,0 +1,5 @@
|
|||
Server implementation
|
||||
=====================
|
||||
|
||||
.. automodule:: paramiko.server
|
||||
:member-order: bysource
|
|
@ -0,0 +1,13 @@
|
|||
SFTP
|
||||
====
|
||||
|
||||
.. automodule:: paramiko.sftp
|
||||
.. automodule:: paramiko.sftp_client
|
||||
.. automodule:: paramiko.sftp_server
|
||||
.. automodule:: paramiko.sftp_attr
|
||||
.. automodule:: paramiko.sftp_file
|
||||
:inherited-members:
|
||||
:no-special-members:
|
||||
:show-inheritance:
|
||||
.. automodule:: paramiko.sftp_handle
|
||||
.. automodule:: paramiko.sftp_si
|
|
@ -0,0 +1,4 @@
|
|||
Exceptions
|
||||
==========
|
||||
|
||||
.. automodule:: paramiko.ssh_exception
|
|
@ -0,0 +1,5 @@
|
|||
Transport
|
||||
=========
|
||||
|
||||
.. automodule:: paramiko.transport
|
||||
:member-order: bysource
|
|
@ -1,4 +1,16 @@
|
|||
# Obtain shared config values
|
||||
import os, sys
|
||||
sys.path.append(os.path.abspath('..'))
|
||||
sys.path.append(os.path.abspath('../..'))
|
||||
from shared_conf import *
|
||||
|
||||
# Enable autodoc, intersphinx
|
||||
extensions.extend(['sphinx.ext.autodoc', 'sphinx.ext.intersphinx'])
|
||||
|
||||
# Autodoc settings
|
||||
autodoc_default_flags = ['members', 'special-members']
|
||||
|
||||
# Intersphinx connection to stdlib
|
||||
intersphinx_mapping = {
|
||||
'python': ('http://docs.python.org/2.6', None),
|
||||
}
|
||||
|
|
|
@ -1,6 +1,72 @@
|
|||
====================================
|
||||
Welcome to Paramiko's documentation!
|
||||
====================================
|
||||
|
||||
This site covers Paramiko's usage & API documentation. For basic info on what
|
||||
Paramiko is, including its public changelog & how the project is maintained,
|
||||
please see `the main website <http://paramiko.org>`_.
|
||||
|
||||
|
||||
API documentation
|
||||
=================
|
||||
|
||||
The high-level client API starts with creation of an `.SSHClient` object. For
|
||||
more direct control, pass a socket (or socket-like object) to a `.Transport`,
|
||||
and use `start_server <.Transport.start_server>` or `start_client
|
||||
<.Transport.start_client>` to negotiate with the remote host as either a server
|
||||
or client.
|
||||
|
||||
As a client, you are responsible for authenticating using a password or private
|
||||
key, and checking the server's host key. (Key signature and verification is
|
||||
done by paramiko, but you will need to provide private keys and check that the
|
||||
content of a public key matches what you expected to see.)
|
||||
|
||||
As a server, you are responsible for deciding which users, passwords, and keys
|
||||
to allow, and what kind of channels to allow.
|
||||
|
||||
Once you have finished, either side may request flow-controlled `channels
|
||||
<.Channel>` to the other side, which are Python objects that act like sockets,
|
||||
but send and receive data over the encrypted session.
|
||||
|
||||
For details, please see the following tables of contents (which are organized
|
||||
by area of interest.)
|
||||
|
||||
|
||||
Core SSH protocol classes
|
||||
-------------------------
|
||||
|
||||
.. toctree::
|
||||
api/channel
|
||||
api/client
|
||||
api/message
|
||||
api/packet
|
||||
api/transport
|
||||
|
||||
|
||||
Authentication & keys
|
||||
---------------------
|
||||
|
||||
.. toctree::
|
||||
api/agent
|
||||
api/hostkeys
|
||||
api/keys
|
||||
|
||||
|
||||
Other primary functions
|
||||
-----------------------
|
||||
|
||||
.. toctree::
|
||||
api/config
|
||||
api/proxy
|
||||
api/server
|
||||
api/sftp
|
||||
|
||||
|
||||
Miscellany
|
||||
----------
|
||||
|
||||
.. toctree::
|
||||
api/buffered_pipe
|
||||
api/file
|
||||
api/pipe
|
||||
api/ssh_exception
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
* :support:`256` Convert API documentation to Sphinx, yielding a new API
|
||||
docs website to replace the old Epydoc one. Thanks to Olle Lundberg for the
|
||||
initial conversion work.
|
||||
* :bug:`-` Use constant-time hash comparison operations where possible, to
|
||||
protect against `timing-based attacks
|
||||
<http://codahale.com/a-lesson-in-timing-attacks/>`_. Thanks to Alex Gaynor
|
||||
|
|
|
@ -24,10 +24,10 @@ extensions.append('sphinx.ext.intersphinx')
|
|||
target = join(dirname(__file__), '..', 'docs', '_build')
|
||||
if os.environ.get('READTHEDOCS') == 'True':
|
||||
# TODO: switch to docs.paramiko.org post go-live of sphinx API docs
|
||||
target = 'http://paramiko-docs.readthedocs.org/en/latest/'
|
||||
#intersphinx_mapping = {
|
||||
# 'docs': (target, None),
|
||||
#}
|
||||
target = 'http://docs.paramiko.org/en/latest/'
|
||||
intersphinx_mapping = {
|
||||
'docs': (target, None),
|
||||
}
|
||||
|
||||
# Sister-site links to API docs
|
||||
html_theme_options['extra_nav_links'] = {
|
||||
|
|
29
tasks.py
29
tasks.py
|
@ -1,23 +1,27 @@
|
|||
from os import mkdir
|
||||
from os.path import join
|
||||
from shutil import rmtree, copytree
|
||||
|
||||
from invoke import Collection, ctask as task
|
||||
from invocations import docs as _docs
|
||||
from invocations.packaging import publish
|
||||
|
||||
|
||||
d = 'sites'
|
||||
|
||||
# Usage doc/API site (published as docs.paramiko.org)
|
||||
path = join(d, 'docs')
|
||||
docs_path = join(d, 'docs')
|
||||
docs_build = join(docs_path, '_build')
|
||||
docs = Collection.from_module(_docs, name='docs', config={
|
||||
'sphinx.source': path,
|
||||
'sphinx.target': join(path, '_build'),
|
||||
'sphinx.source': docs_path,
|
||||
'sphinx.target': docs_build,
|
||||
})
|
||||
|
||||
# Main/about/changelog site ((www.)?paramiko.org)
|
||||
path = join(d, 'www')
|
||||
www_path = join(d, 'www')
|
||||
www = Collection.from_module(_docs, name='www', config={
|
||||
'sphinx.source': path,
|
||||
'sphinx.target': join(path, '_build'),
|
||||
'sphinx.source': www_path,
|
||||
'sphinx.target': join(www_path, '_build'),
|
||||
})
|
||||
|
||||
|
||||
|
@ -31,4 +35,15 @@ def coverage(ctx):
|
|||
ctx.run("coverage run --source=paramiko test.py --verbose")
|
||||
|
||||
|
||||
ns = Collection(test, coverage, docs=docs, www=www)
|
||||
# Until we stop bundling docs w/ releases. Need to discover use cases first.
|
||||
@task('docs') # Will invoke the API doc site build
|
||||
def release(ctx):
|
||||
# Move the built docs into where Epydocs used to live
|
||||
target = 'docs'
|
||||
rmtree(target, ignore_errors=True)
|
||||
copytree(docs_build, target)
|
||||
# Publish
|
||||
publish(ctx)
|
||||
|
||||
|
||||
ns = Collection(test, coverage, release, docs=docs, www=www)
|
||||
|
|
Loading…
Reference in New Issue