Merge branch '1.10' into 1.11
Conflicts: fabfile.py paramiko/__init__.py
This commit is contained in:
commit
72a73f55fa
|
@ -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.,
|
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
# 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
|
import sys
|
||||||
|
|
||||||
if sys.version_info < (2, 5):
|
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>"
|
__author__ = "Jeff Forcier <jeff@bitprophet.org>"
|
||||||
|
@ -88,13 +54,6 @@ from hostkeys import HostKeys
|
||||||
from config import SSHConfig
|
from config import SSHConfig
|
||||||
from proxy import ProxyCommand
|
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, \
|
from common import AUTH_SUCCESSFUL, AUTH_PARTIALLY_SUCCESSFUL, AUTH_FAILED, \
|
||||||
OPEN_SUCCEEDED, OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, OPEN_FAILED_CONNECT_FAILED, \
|
OPEN_SUCCEEDED, OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, OPEN_FAILED_CONNECT_FAILED, \
|
||||||
OPEN_FAILED_UNKNOWN_CHANNEL_TYPE, OPEN_FAILED_RESOURCE_SHORTAGE
|
OPEN_FAILED_UNKNOWN_CHANNEL_TYPE, OPEN_FAILED_RESOURCE_SHORTAGE
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
SSH Agent interface for Unix clients.
|
SSH Agent interface
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
@ -40,17 +40,8 @@ from paramiko.util import retry_on_signal
|
||||||
SSH2_AGENTC_REQUEST_IDENTITIES, SSH2_AGENT_IDENTITIES_ANSWER, \
|
SSH2_AGENTC_REQUEST_IDENTITIES, SSH2_AGENT_IDENTITIES_ANSWER, \
|
||||||
SSH2_AGENTC_SIGN_REQUEST, SSH2_AGENT_SIGN_RESPONSE = range(11, 15)
|
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
|
class AgentSSH(object):
|
||||||
sockets, this probably doesn't work on Windows. It does work on most
|
|
||||||
posix platforms though (Linux and MacOS X, for example).
|
|
||||||
"""
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._conn = None
|
self._conn = None
|
||||||
self._keys = ()
|
self._keys = ()
|
||||||
|
@ -61,8 +52,9 @@ class AgentSSH(object):
|
||||||
no SSH agent was running (or it couldn't be contacted), an empty list
|
no SSH agent was running (or it couldn't be contacted), an empty list
|
||||||
will be returned.
|
will be returned.
|
||||||
|
|
||||||
@return: a list of keys available on the SSH agent
|
:return:
|
||||||
@rtype: tuple of L{AgentKey}
|
a tuple of `.AgentKey` objects representing keys available on the
|
||||||
|
SSH agent
|
||||||
"""
|
"""
|
||||||
return self._keys
|
return self._keys
|
||||||
|
|
||||||
|
@ -100,8 +92,11 @@ class AgentSSH(object):
|
||||||
result += extra
|
result += extra
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
class AgentProxyThread(threading.Thread):
|
class AgentProxyThread(threading.Thread):
|
||||||
""" Class in charge of communication between two chan """
|
"""
|
||||||
|
Class in charge of communication between two channels.
|
||||||
|
"""
|
||||||
def __init__(self, agent):
|
def __init__(self, agent):
|
||||||
threading.Thread.__init__(self, target=self.run)
|
threading.Thread.__init__(self, target=self.run)
|
||||||
self._agent = agent
|
self._agent = agent
|
||||||
|
@ -146,6 +141,7 @@ class AgentProxyThread(threading.Thread):
|
||||||
self.__inr.close()
|
self.__inr.close()
|
||||||
self._agent._conn.close()
|
self._agent._conn.close()
|
||||||
|
|
||||||
|
|
||||||
class AgentLocalProxy(AgentProxyThread):
|
class AgentLocalProxy(AgentProxyThread):
|
||||||
"""
|
"""
|
||||||
Class to be used when wanting to ask a local SSH Agent being
|
Class to be used when wanting to ask a local SSH Agent being
|
||||||
|
@ -155,8 +151,10 @@ class AgentLocalProxy(AgentProxyThread):
|
||||||
AgentProxyThread.__init__(self, agent)
|
AgentProxyThread.__init__(self, agent)
|
||||||
|
|
||||||
def get_connection(self):
|
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)
|
conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
try:
|
try:
|
||||||
|
@ -168,6 +166,7 @@ class AgentLocalProxy(AgentProxyThread):
|
||||||
raise
|
raise
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class AgentRemoteProxy(AgentProxyThread):
|
class AgentRemoteProxy(AgentProxyThread):
|
||||||
"""
|
"""
|
||||||
Class to be used when wanting to ask a remote SSH Agent
|
Class to be used when wanting to ask a remote SSH Agent
|
||||||
|
@ -177,22 +176,20 @@ class AgentRemoteProxy(AgentProxyThread):
|
||||||
self.__chan = chan
|
self.__chan = chan
|
||||||
|
|
||||||
def get_connection(self):
|
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)
|
return (self.__chan, None)
|
||||||
|
|
||||||
|
|
||||||
class AgentClientProxy(object):
|
class AgentClientProxy(object):
|
||||||
"""
|
"""
|
||||||
Class proxying request as a client:
|
Class proxying request as a client:
|
||||||
-> client ask for a request_forward_agent()
|
|
||||||
-> server creates a proxy and a fake SSH Agent
|
#. client ask for a request_forward_agent()
|
||||||
-> server ask for establishing a connection when needed,
|
#. 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.
|
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
|
the remote fake agent and the local agent
|
||||||
-> Communication occurs ...
|
#. Communication occurs ...
|
||||||
"""
|
"""
|
||||||
def __init__(self, chanRemote):
|
def __init__(self, chanRemote):
|
||||||
self._conn = None
|
self._conn = None
|
||||||
|
@ -205,7 +202,7 @@ class AgentClientProxy(object):
|
||||||
|
|
||||||
def connect(self):
|
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'):
|
if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'):
|
||||||
conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||||
|
@ -236,11 +233,12 @@ class AgentClientProxy(object):
|
||||||
if self._conn is not None:
|
if self._conn is not None:
|
||||||
self._conn.close()
|
self._conn.close()
|
||||||
|
|
||||||
|
|
||||||
class AgentServerProxy(AgentSSH):
|
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):
|
def __init__(self, t):
|
||||||
AgentSSH.__init__(self)
|
AgentSSH.__init__(self)
|
||||||
|
@ -276,8 +274,8 @@ class AgentServerProxy(AgentSSH):
|
||||||
"""
|
"""
|
||||||
Helper for the environnement under unix
|
Helper for the environnement under unix
|
||||||
|
|
||||||
@return: the SSH_AUTH_SOCK Environnement variables
|
:return:
|
||||||
@rtype: dict
|
a dict containing the ``SSH_AUTH_SOCK`` environnement variables
|
||||||
"""
|
"""
|
||||||
env = {}
|
env = {}
|
||||||
env['SSH_AUTH_SOCK'] = self._get_filename()
|
env['SSH_AUTH_SOCK'] = self._get_filename()
|
||||||
|
@ -286,6 +284,7 @@ class AgentServerProxy(AgentSSH):
|
||||||
def _get_filename(self):
|
def _get_filename(self):
|
||||||
return self._file
|
return self._file
|
||||||
|
|
||||||
|
|
||||||
class AgentRequestHandler(object):
|
class AgentRequestHandler(object):
|
||||||
def __init__(self, chanClient):
|
def __init__(self, chanClient):
|
||||||
self._conn = None
|
self._conn = None
|
||||||
|
@ -303,27 +302,22 @@ class AgentRequestHandler(object):
|
||||||
for p in self.__clientProxys:
|
for p in self.__clientProxys:
|
||||||
p.close()
|
p.close()
|
||||||
|
|
||||||
|
|
||||||
class Agent(AgentSSH):
|
class Agent(AgentSSH):
|
||||||
"""
|
"""
|
||||||
Client interface for using private keys from an SSH agent running on the
|
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
|
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.
|
attempting to authenticate to remote SSH servers.
|
||||||
|
|
||||||
Because the SSH agent protocol uses environment variables and unix-domain
|
Upon initialization, a session with the local machine's SSH agent is
|
||||||
sockets, this probably doesn't work on Windows. It does work on most
|
opened, if one is running. If no agent is running, initialization will
|
||||||
posix platforms though (Linux and MacOS X, for example).
|
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):
|
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)
|
AgentSSH.__init__(self)
|
||||||
|
|
||||||
if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'):
|
if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'):
|
||||||
|
@ -350,13 +344,13 @@ class Agent(AgentSSH):
|
||||||
"""
|
"""
|
||||||
self._close()
|
self._close()
|
||||||
|
|
||||||
|
|
||||||
class AgentKey(PKey):
|
class AgentKey(PKey):
|
||||||
"""
|
"""
|
||||||
Private key held in a local SSH agent. This type of key can be used for
|
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
|
authenticating to a remote server (signing). Most other key operations
|
||||||
work as expected.
|
work as expected.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, agent, blob):
|
def __init__(self, agent, blob):
|
||||||
self.agent = agent
|
self.agent = agent
|
||||||
self.blob = blob
|
self.blob = blob
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
L{AuthHandler}
|
`.AuthHandler`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
|
@ -167,7 +167,7 @@ class AuthHandler (object):
|
||||||
e = self.transport.get_exception()
|
e = self.transport.get_exception()
|
||||||
if e is None:
|
if e is None:
|
||||||
e = AuthenticationException('Authentication failed.')
|
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. :(
|
# object, so type(e) won't work. :(
|
||||||
if issubclass(e.__class__, PartialAuthentication):
|
if issubclass(e.__class__, PartialAuthentication):
|
||||||
return e.allowed_types
|
return e.allowed_types
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
# 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 from and closed, but is reading from a buffer fed by another thread. The
|
||||||
read operations are blocking and can have a timeout set.
|
read operations are blocking and can have a timeout set.
|
||||||
"""
|
"""
|
||||||
|
@ -29,7 +29,7 @@ import time
|
||||||
|
|
||||||
class PipeTimeout (IOError):
|
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
|
pass
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ class BufferedPipe (object):
|
||||||
"""
|
"""
|
||||||
A buffer that obeys normal read (with timeout) & close semantics for a
|
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
|
file or socket, but is fed data from another thread. This is used by
|
||||||
L{Channel}.
|
`.Channel`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -54,8 +54,7 @@ class BufferedPipe (object):
|
||||||
buffer has been closed), the event will be set. When no data is
|
buffer has been closed), the event will be set. When no data is
|
||||||
ready, the event will be cleared.
|
ready, the event will be cleared.
|
||||||
|
|
||||||
@param event: the event to set/clear
|
:param threading.Event event: the event to set/clear
|
||||||
@type event: Event
|
|
||||||
"""
|
"""
|
||||||
self._event = event
|
self._event = event
|
||||||
if len(self._buffer) > 0:
|
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
|
Feed new data into this pipe. This method is assumed to be called
|
||||||
from a separate thread, so synchronization is done.
|
from a separate thread, so synchronization is done.
|
||||||
|
|
||||||
@param data: the data to add
|
:param data: the data to add, as a `str`
|
||||||
@type data: str
|
|
||||||
"""
|
"""
|
||||||
self._lock.acquire()
|
self._lock.acquire()
|
||||||
try:
|
try:
|
||||||
|
@ -83,12 +81,12 @@ class BufferedPipe (object):
|
||||||
def read_ready(self):
|
def read_ready(self):
|
||||||
"""
|
"""
|
||||||
Returns true if data is buffered and ready to be read from this
|
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.
|
it means you may need to wait before more data arrives.
|
||||||
|
|
||||||
@return: C{True} if a L{read} call would immediately return at least
|
:return:
|
||||||
one byte; C{False} otherwise.
|
``True`` if a `read` call would immediately return at least one
|
||||||
@rtype: bool
|
byte; ``False`` otherwise.
|
||||||
"""
|
"""
|
||||||
self._lock.acquire()
|
self._lock.acquire()
|
||||||
try:
|
try:
|
||||||
|
@ -102,24 +100,22 @@ class BufferedPipe (object):
|
||||||
"""
|
"""
|
||||||
Read data from the pipe. The return value is a string representing
|
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
|
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 pipe has been closed.
|
||||||
|
|
||||||
The optional C{timeout} argument can be a nonnegative float expressing
|
The optional ``timeout`` argument can be a nonnegative float expressing
|
||||||
seconds, or C{None} for no timeout. If a float is given, a
|
seconds, or ``None`` for no timeout. If a float is given, a
|
||||||
C{PipeTimeout} will be raised if the timeout period value has
|
`.PipeTimeout` will be raised if the timeout period value has elapsed
|
||||||
elapsed before any data arrives.
|
before any data arrives.
|
||||||
|
|
||||||
@param nbytes: maximum number of bytes to read
|
:param int nbytes: maximum number of bytes to read
|
||||||
@type nbytes: int
|
:param float timeout:
|
||||||
@param timeout: maximum seconds to wait (or C{None}, the default, to
|
maximum seconds to wait (or ``None``, the default, to wait forever)
|
||||||
wait forever)
|
:return: the read data, as a `str`
|
||||||
@type timeout: float
|
|
||||||
@return: data
|
|
||||||
@rtype: str
|
|
||||||
|
|
||||||
@raise PipeTimeout: if a timeout was specified and no data was ready
|
:raises PipeTimeout:
|
||||||
before that timeout
|
if a timeout was specified and no data was ready before that
|
||||||
|
timeout
|
||||||
"""
|
"""
|
||||||
out = ''
|
out = ''
|
||||||
self._lock.acquire()
|
self._lock.acquire()
|
||||||
|
@ -158,8 +154,9 @@ class BufferedPipe (object):
|
||||||
"""
|
"""
|
||||||
Clear out the buffer and return all data that was in it.
|
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
|
:return:
|
||||||
@rtype: str
|
any data that was in the buffer prior to clearing it out, as a
|
||||||
|
`str`
|
||||||
"""
|
"""
|
||||||
self._lock.acquire()
|
self._lock.acquire()
|
||||||
try:
|
try:
|
||||||
|
@ -173,7 +170,7 @@ class BufferedPipe (object):
|
||||||
|
|
||||||
def close(self):
|
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.
|
has been emptied will return immediately with an empty string.
|
||||||
"""
|
"""
|
||||||
self._lock.acquire()
|
self._lock.acquire()
|
||||||
|
@ -189,8 +186,7 @@ class BufferedPipe (object):
|
||||||
"""
|
"""
|
||||||
Return the number of bytes buffered.
|
Return the number of bytes buffered.
|
||||||
|
|
||||||
@return: number of bytes bufferes
|
:return: number (`int`) of bytes buffered
|
||||||
@rtype: int
|
|
||||||
"""
|
"""
|
||||||
self._lock.acquire()
|
self._lock.acquire()
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -42,29 +42,28 @@ MIN_PACKET_SIZE = 1024
|
||||||
|
|
||||||
class Channel (object):
|
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
|
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
|
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
|
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
|
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
|
channels on the same transport -- all channels on a single transport are
|
||||||
flow-controlled independently.) Similarly, if the server isn't reading
|
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.
|
is exactly like a normal network socket, so it shouldn't be too surprising.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, chanid):
|
def __init__(self, chanid):
|
||||||
"""
|
"""
|
||||||
Create a new channel. The channel is not associated with any
|
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
|
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
|
:param int chanid:
|
||||||
L{Transport}.
|
the ID of this channel, as passed by an existing `.Transport`.
|
||||||
@type chanid: int
|
|
||||||
"""
|
"""
|
||||||
self.chanid = chanid
|
self.chanid = chanid
|
||||||
self.remote_chanid = 0
|
self.remote_chanid = 0
|
||||||
|
@ -104,8 +103,6 @@ class Channel (object):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""
|
"""
|
||||||
Return a string representation of this object, for debugging.
|
Return a string representation of this object, for debugging.
|
||||||
|
|
||||||
@rtype: str
|
|
||||||
"""
|
"""
|
||||||
out = '<paramiko.Channel %d' % self.chanid
|
out = '<paramiko.Channel %d' % self.chanid
|
||||||
if self.closed:
|
if self.closed:
|
||||||
|
@ -127,23 +124,18 @@ class Channel (object):
|
||||||
"""
|
"""
|
||||||
Request a pseudo-terminal from the server. This is usually used right
|
Request a pseudo-terminal from the server. This is usually used right
|
||||||
after creating a client channel, to ask the server to provide some
|
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
|
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'})
|
:param str term: the terminal type to emulate (for example, ``'vt100'``)
|
||||||
@type term: str
|
:param int width: width (in characters) of the terminal screen
|
||||||
@param width: width (in characters) of the terminal screen
|
:param int height: height (in characters) of the terminal screen
|
||||||
@type width: int
|
:param int width_pixels: width (in pixels) of the terminal screen
|
||||||
@param height: height (in characters) of the terminal screen
|
:param int height_pixels: height (in pixels) 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
|
|
||||||
|
|
||||||
@raise SSHException: if the request was rejected or the channel was
|
:raises SSHException:
|
||||||
closed
|
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:
|
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
||||||
raise SSHException('Channel is not open')
|
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,
|
allows it, the channel will then be directly connected to the stdin,
|
||||||
stdout, and stderr of the shell.
|
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
|
shell will operate through the pty, and the channel will be connected
|
||||||
to the stdin and stdout of the pty.
|
to the stdin and stdout of the pty.
|
||||||
|
|
||||||
When the shell exits, the channel will be closed and can't be reused.
|
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.
|
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
|
closed
|
||||||
"""
|
"""
|
||||||
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
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
|
can't be reused. You must open a new channel if you wish to execute
|
||||||
another command.
|
another command.
|
||||||
|
|
||||||
@param command: a shell command to execute.
|
:param str command: a shell command to execute.
|
||||||
@type command: str
|
|
||||||
|
|
||||||
@raise SSHException: if the request was rejected or the channel was
|
:raises SSHException: if the request was rejected or the channel was
|
||||||
closed
|
closed
|
||||||
"""
|
"""
|
||||||
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
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):
|
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
|
server allows it, the channel will then be directly connected to the
|
||||||
requested subsystem.
|
requested subsystem.
|
||||||
|
|
||||||
When the subsystem finishes, the channel will be closed and can't be
|
When the subsystem finishes, the channel will be closed and can't be
|
||||||
reused.
|
reused.
|
||||||
|
|
||||||
@param subsystem: name of the subsystem being requested.
|
:param str subsystem: name of the subsystem being requested.
|
||||||
@type subsystem: str
|
|
||||||
|
|
||||||
@raise SSHException: if the request was rejected or the channel was
|
:raises SSHException:
|
||||||
closed
|
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:
|
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
||||||
raise SSHException('Channel is not open')
|
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):
|
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
|
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
|
:param int width: new width (in characters) of the terminal screen
|
||||||
@type width: int
|
:param int height: new height (in characters) of the terminal screen
|
||||||
@param height: new height (in characters) of the terminal screen
|
:param int width_pixels: new width (in pixels) of the terminal screen
|
||||||
@type height: int
|
:param int height_pixels: new height (in pixels) of the terminal screen
|
||||||
@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
|
|
||||||
|
|
||||||
@raise SSHException: if the request was rejected or the channel was
|
:raises SSHException:
|
||||||
closed
|
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:
|
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
||||||
raise SSHException('Channel is not open')
|
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
|
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
|
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 an exit status in some cases (like bad servers).
|
||||||
|
|
||||||
@return: True if L{recv_exit_status} will return immediately
|
:return:
|
||||||
@rtype: bool
|
``True`` if `recv_exit_status` will return immediately, else ``False``.
|
||||||
@since: 1.7.3
|
|
||||||
|
.. versionadded:: 1.7.3
|
||||||
"""
|
"""
|
||||||
return self.closed or self.status_event.isSet()
|
return self.closed or self.status_event.isSet()
|
||||||
|
|
||||||
def recv_exit_status(self):
|
def recv_exit_status(self):
|
||||||
"""
|
"""
|
||||||
Return the exit status from the process on the server. This is
|
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
|
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
|
it does, or until the channel is closed. If no exit status is
|
||||||
provided by the server, -1 is returned.
|
provided by the server, -1 is returned.
|
||||||
|
|
||||||
@return: the exit code of the process on the server.
|
:return: the exit code (as an `int`) of the process on the server.
|
||||||
@rtype: int
|
|
||||||
|
|
||||||
@since: 1.2
|
.. versionadded:: 1.2
|
||||||
"""
|
"""
|
||||||
self.status_event.wait()
|
self.status_event.wait()
|
||||||
assert self.status_event.isSet()
|
assert self.status_event.isSet()
|
||||||
|
@ -311,10 +297,9 @@ class Channel (object):
|
||||||
get some sort of status code back from an executed command after
|
get some sort of status code back from an executed command after
|
||||||
it completes.
|
it completes.
|
||||||
|
|
||||||
@param status: the exit code of the process
|
:param int status: the exit code of the process
|
||||||
@type status: int
|
|
||||||
|
|
||||||
@since: 1.2
|
.. versionadded:: 1.2
|
||||||
"""
|
"""
|
||||||
# in many cases, the channel will not still be open here.
|
# in many cases, the channel will not still be open here.
|
||||||
# that's fine.
|
# that's fine.
|
||||||
|
@ -347,25 +332,24 @@ class Channel (object):
|
||||||
If a handler is passed in, the handler is called from another thread
|
If a handler is passed in, the handler is called from another thread
|
||||||
whenever a new x11 connection arrives. The default handler queues up
|
whenever a new x11 connection arrives. The default handler queues up
|
||||||
incoming x11 connections, which may be retrieved using
|
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))
|
handler(channel: Channel, (address: str, port: int))
|
||||||
|
|
||||||
@param screen_number: the x11 screen number (0, 10, etc)
|
:param int screen_number: the x11 screen number (0, 10, etc)
|
||||||
@type screen_number: int
|
:param str auth_protocol:
|
||||||
@param auth_protocol: the name of the X11 authentication method used;
|
the name of the X11 authentication method used; if none is given,
|
||||||
if none is given, C{"MIT-MAGIC-COOKIE-1"} is used
|
``"MIT-MAGIC-COOKIE-1"`` is used
|
||||||
@type auth_protocol: str
|
:param str auth_cookie:
|
||||||
@param auth_cookie: hexadecimal string containing the x11 auth cookie;
|
hexadecimal string containing the x11 auth cookie; if none is
|
||||||
if none is given, a secure random 128-bit value is generated
|
given, a secure random 128-bit value is generated
|
||||||
@type auth_cookie: str
|
:param bool single_connection:
|
||||||
@param single_connection: if True, only a single x11 connection will be
|
if True, only a single x11 connection will be forwarded (by
|
||||||
forwarded (by default, any number of x11 connections can arrive
|
default, any number of x11 connections can arrive over this
|
||||||
over this session)
|
session)
|
||||||
@type single_connection: bool
|
:param function handler:
|
||||||
@param handler: an optional handler to use for incoming X11 connections
|
an optional handler to use for incoming X11 connections
|
||||||
@type handler: function
|
:return: the auth_cookie used
|
||||||
@return: the auth_cookie used
|
|
||||||
"""
|
"""
|
||||||
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
||||||
raise SSHException('Channel is not open')
|
raise SSHException('Channel is not open')
|
||||||
|
@ -392,15 +376,14 @@ class Channel (object):
|
||||||
def request_forward_agent(self, handler):
|
def request_forward_agent(self, handler):
|
||||||
"""
|
"""
|
||||||
Request for a forward SSH Agent on this channel.
|
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
|
:param function handler:
|
||||||
@type handler: function
|
a required handler to use for incoming SSH Agent connections
|
||||||
|
|
||||||
@return: if we are ok or not (at that time we always return ok)
|
:return: True if we are ok, else False (at that time we always return ok)
|
||||||
@rtype: boolean
|
|
||||||
|
|
||||||
@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:
|
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
||||||
raise SSHException('Channel is not open')
|
raise SSHException('Channel is not open')
|
||||||
|
@ -416,10 +399,7 @@ class Channel (object):
|
||||||
|
|
||||||
def get_transport(self):
|
def get_transport(self):
|
||||||
"""
|
"""
|
||||||
Return the L{Transport} associated with this channel.
|
Return the `.Transport` associated with this channel.
|
||||||
|
|
||||||
@return: the L{Transport} that was used to create this channel.
|
|
||||||
@rtype: L{Transport}
|
|
||||||
"""
|
"""
|
||||||
return self.transport
|
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
|
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
|
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
|
:param str name: new channel name
|
||||||
@type name: str
|
|
||||||
"""
|
"""
|
||||||
self._name = name
|
self._name = name
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
"""
|
"""
|
||||||
Get the name of this channel that was previously set by L{set_name}.
|
Get the name of this channel that was previously set by `set_name`.
|
||||||
|
|
||||||
@return: the name of this channel.
|
|
||||||
@rtype: str
|
|
||||||
"""
|
"""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
def get_id(self):
|
def get_id(self):
|
||||||
"""
|
"""
|
||||||
Return the ID # for this channel. The channel ID is unique across
|
Return the `int` ID # for this channel.
|
||||||
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 ID of this channel.
|
The channel ID is unique across a `.Transport` and usually a small
|
||||||
@rtype: int
|
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
|
return self.chanid
|
||||||
|
|
||||||
def set_combine_stderr(self, combine):
|
def set_combine_stderr(self, combine):
|
||||||
"""
|
"""
|
||||||
Set whether stderr should be combined into stdout on this channel.
|
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.
|
have both streams combined.
|
||||||
|
|
||||||
If this is C{False}, and L{exec_command} is called (or C{invoke_shell}
|
If this is ``False``, and `exec_command` is called (or ``invoke_shell``
|
||||||
with no pty), output to stderr will not show up through the L{recv}
|
with no pty), output to stderr will not show up through the `recv`
|
||||||
and L{recv_ready} calls. You will have to use L{recv_stderr} and
|
and `recv_ready` calls. You will have to use `recv_stderr` and
|
||||||
L{recv_stderr_ready} to get stderr output.
|
`recv_stderr_ready` to get stderr output.
|
||||||
|
|
||||||
If this is C{True}, data will never show up via L{recv_stderr} or
|
If this is ``True``, data will never show up via `recv_stderr` or
|
||||||
L{recv_stderr_ready}.
|
`recv_stderr_ready`.
|
||||||
|
|
||||||
@param combine: C{True} if stderr output should be combined into
|
:param bool combine:
|
||||||
stdout on this channel.
|
``True`` if stderr output should be combined into stdout on this
|
||||||
@type combine: bool
|
channel.
|
||||||
@return: previous setting.
|
:return: the previous setting (a `bool`).
|
||||||
@rtype: bool
|
|
||||||
|
|
||||||
@since: 1.1
|
.. versionadded:: 1.1
|
||||||
"""
|
"""
|
||||||
data = ''
|
data = ''
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
|
@ -497,51 +471,47 @@ class Channel (object):
|
||||||
|
|
||||||
def settimeout(self, timeout):
|
def settimeout(self, timeout):
|
||||||
"""
|
"""
|
||||||
Set a timeout on blocking read/write operations. The C{timeout}
|
Set a timeout on blocking read/write operations. The ``timeout``
|
||||||
argument can be a nonnegative float expressing seconds, or C{None}. If
|
argument can be a nonnegative float expressing seconds, or ``None``. If
|
||||||
a float is given, subsequent channel read/write operations will raise
|
a float is given, subsequent channel read/write operations will raise
|
||||||
a timeout exception if the timeout period value has elapsed before the
|
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.
|
timeouts on socket operations.
|
||||||
|
|
||||||
C{chan.settimeout(0.0)} is equivalent to C{chan.setblocking(0)};
|
``chan.settimeout(0.0)`` is equivalent to ``chan.setblocking(0)``;
|
||||||
C{chan.settimeout(None)} is equivalent to C{chan.setblocking(1)}.
|
``chan.settimeout(None)`` is equivalent to ``chan.setblocking(1)``.
|
||||||
|
|
||||||
@param timeout: seconds to wait for a pending read/write operation
|
:param float timeout:
|
||||||
before raising C{socket.timeout}, or C{None} for no timeout.
|
seconds to wait for a pending read/write operation before raising
|
||||||
@type timeout: float
|
``socket.timeout``, or ``None`` for no timeout.
|
||||||
"""
|
"""
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
|
||||||
def gettimeout(self):
|
def gettimeout(self):
|
||||||
"""
|
"""
|
||||||
Returns the timeout in seconds (as a float) associated with socket
|
Returns the timeout in seconds (as a float) associated with socket
|
||||||
operations, or C{None} if no timeout is set. This reflects the last
|
operations, or ``None`` if no timeout is set. This reflects the last
|
||||||
call to L{setblocking} or L{settimeout}.
|
call to `setblocking` or `settimeout`.
|
||||||
|
|
||||||
@return: timeout in seconds, or C{None}.
|
|
||||||
@rtype: float
|
|
||||||
"""
|
"""
|
||||||
return self.timeout
|
return self.timeout
|
||||||
|
|
||||||
def setblocking(self, blocking):
|
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
|
the channel is set to non-blocking mode; otherwise it's set to blocking
|
||||||
mode. Initially all channels are in blocking mode.
|
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
|
In non-blocking mode, if a `recv` call doesn't find any data, or if a
|
||||||
L{send} call can't immediately dispose of the data, an error exception
|
`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
|
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.
|
channel is closed in the read direction, it will never block.
|
||||||
|
|
||||||
C{chan.setblocking(0)} is equivalent to C{chan.settimeout(0)};
|
``chan.setblocking(0)`` is equivalent to ``chan.settimeout(0)``;
|
||||||
C{chan.setblocking(1)} is equivalent to C{chan.settimeout(None)}.
|
``chan.setblocking(1)`` is equivalent to ``chan.settimeout(None)``.
|
||||||
|
|
||||||
@param blocking: 0 to set non-blocking mode; non-0 to set blocking
|
:param int blocking:
|
||||||
mode.
|
0 to set non-blocking mode; non-0 to set blocking mode.
|
||||||
@type blocking: int
|
|
||||||
"""
|
"""
|
||||||
if blocking:
|
if blocking:
|
||||||
self.settimeout(None)
|
self.settimeout(None)
|
||||||
|
@ -551,12 +521,10 @@ class Channel (object):
|
||||||
def getpeername(self):
|
def getpeername(self):
|
||||||
"""
|
"""
|
||||||
Return the address of the remote side of this Channel, if possible.
|
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
|
This simply wraps `.Transport.getpeername`, used to provide enough of a
|
||||||
@rtype: tuple(str, int)
|
socket-like interface to allow asyncore to work. (asyncore likes to
|
||||||
|
call ``'getpeername'``.)
|
||||||
"""
|
"""
|
||||||
return self.transport.getpeername()
|
return self.transport.getpeername()
|
||||||
|
|
||||||
|
@ -564,7 +532,7 @@ class Channel (object):
|
||||||
"""
|
"""
|
||||||
Close the channel. All future read/write operations on the channel
|
Close the channel. All future read/write operations on the channel
|
||||||
will fail. The remote end will receive no more data (after queued data
|
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.
|
is closed or when they are garbage collected.
|
||||||
"""
|
"""
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
|
@ -589,12 +557,12 @@ class Channel (object):
|
||||||
def recv_ready(self):
|
def recv_ready(self):
|
||||||
"""
|
"""
|
||||||
Returns true if data is buffered and ready to be read from this
|
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.
|
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:
|
||||||
return at least one byte; C{False} otherwise.
|
``True`` if a `recv` call on this channel would immediately return
|
||||||
@rtype: boolean
|
at least one byte; ``False`` otherwise.
|
||||||
"""
|
"""
|
||||||
return self.in_buffer.read_ready()
|
return self.in_buffer.read_ready()
|
||||||
|
|
||||||
|
@ -602,16 +570,14 @@ class Channel (object):
|
||||||
"""
|
"""
|
||||||
Receive data from the channel. The return value is a string
|
Receive data from the channel. The return value is a string
|
||||||
representing the data received. The maximum amount of data to be
|
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.
|
is returned, the channel stream has closed.
|
||||||
|
|
||||||
@param nbytes: maximum number of bytes to read.
|
:param int nbytes: maximum number of bytes to read.
|
||||||
@type nbytes: int
|
:return: received data, as a `str`
|
||||||
@return: data.
|
|
||||||
@rtype: str
|
|
||||||
|
|
||||||
@raise socket.timeout: if no data is ready before the timeout set by
|
:raises socket.timeout:
|
||||||
L{settimeout}.
|
if no data is ready before the timeout set by `settimeout`.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
out = self.in_buffer.read(nbytes, self.timeout)
|
out = self.in_buffer.read(nbytes, self.timeout)
|
||||||
|
@ -632,36 +598,34 @@ class Channel (object):
|
||||||
def recv_stderr_ready(self):
|
def recv_stderr_ready(self):
|
||||||
"""
|
"""
|
||||||
Returns true if data is buffered and ready to be read from this
|
Returns true if data is buffered and ready to be read from this
|
||||||
channel's stderr stream. Only channels using L{exec_command} or
|
channel's stderr stream. Only channels using `exec_command` or
|
||||||
L{invoke_shell} without a pty will ever have data on the stderr
|
`invoke_shell` without a pty will ever have data on the stderr
|
||||||
stream.
|
stream.
|
||||||
|
|
||||||
@return: C{True} if a L{recv_stderr} call on this channel would
|
:return:
|
||||||
immediately return at least one byte; C{False} otherwise.
|
``True`` if a `recv_stderr` call on this channel would immediately
|
||||||
@rtype: boolean
|
return at least one byte; ``False`` otherwise.
|
||||||
|
|
||||||
@since: 1.1
|
.. versionadded:: 1.1
|
||||||
"""
|
"""
|
||||||
return self.in_stderr_buffer.read_ready()
|
return self.in_stderr_buffer.read_ready()
|
||||||
|
|
||||||
def recv_stderr(self, nbytes):
|
def recv_stderr(self, nbytes):
|
||||||
"""
|
"""
|
||||||
Receive data from the channel's stderr stream. Only channels using
|
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
|
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
|
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.
|
channel stream has closed.
|
||||||
|
|
||||||
@param nbytes: maximum number of bytes to read.
|
:param int nbytes: maximum number of bytes to read.
|
||||||
@type nbytes: int
|
:return: received data as a `str`
|
||||||
@return: data.
|
|
||||||
@rtype: str
|
|
||||||
|
|
||||||
@raise socket.timeout: if no data is ready before the timeout set by
|
:raises socket.timeout: if no data is ready before the timeout set by
|
||||||
L{settimeout}.
|
`settimeout`.
|
||||||
|
|
||||||
@since: 1.1
|
.. versionadded:: 1.1
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
out = self.in_stderr_buffer.read(nbytes, self.timeout)
|
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
|
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
|
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. 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.
|
the number of bytes actually written.
|
||||||
|
|
||||||
@return: C{True} if a L{send} call on this channel would immediately
|
:return:
|
||||||
succeed or fail
|
``True`` if a `send` call on this channel would immediately succeed
|
||||||
@rtype: boolean
|
or fail
|
||||||
"""
|
"""
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
try:
|
try:
|
||||||
|
@ -708,13 +672,11 @@ class Channel (object):
|
||||||
transmitted, the application needs to attempt delivery of the remaining
|
transmitted, the application needs to attempt delivery of the remaining
|
||||||
data.
|
data.
|
||||||
|
|
||||||
@param s: data to send
|
:param str s: data to send
|
||||||
@type s: str
|
:return: number of bytes actually sent, as an `int`
|
||||||
@return: number of bytes actually sent
|
|
||||||
@rtype: int
|
|
||||||
|
|
||||||
@raise socket.timeout: if no data could be sent before the timeout set
|
:raises socket.timeout: if no data could be sent before the timeout set
|
||||||
by L{settimeout}.
|
by `settimeout`.
|
||||||
"""
|
"""
|
||||||
size = len(s)
|
size = len(s)
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
|
@ -743,15 +705,13 @@ class Channel (object):
|
||||||
data has been sent: if only some of the data was transmitted, the
|
data has been sent: if only some of the data was transmitted, the
|
||||||
application needs to attempt delivery of the remaining data.
|
application needs to attempt delivery of the remaining data.
|
||||||
|
|
||||||
@param s: data to send.
|
:param str s: data to send.
|
||||||
@type s: str
|
:return: number of bytes actually sent, as an `int`.
|
||||||
@return: number of bytes actually sent.
|
|
||||||
@rtype: int
|
|
||||||
|
|
||||||
@raise socket.timeout: if no data could be sent before the timeout set
|
:raises socket.timeout:
|
||||||
by L{settimeout}.
|
if no data could be sent before the timeout set by `settimeout`.
|
||||||
|
|
||||||
@since: 1.1
|
.. versionadded:: 1.1
|
||||||
"""
|
"""
|
||||||
size = len(s)
|
size = len(s)
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
|
@ -775,20 +735,20 @@ class Channel (object):
|
||||||
def sendall(self, s):
|
def sendall(self, s):
|
||||||
"""
|
"""
|
||||||
Send data to the channel, without allowing partial results. Unlike
|
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.
|
either all data has been sent or an error occurs. Nothing is returned.
|
||||||
|
|
||||||
@param s: data to send.
|
:param str s: data to send.
|
||||||
@type s: str
|
|
||||||
|
|
||||||
@raise socket.timeout: if sending stalled for longer than the timeout
|
:raises socket.timeout:
|
||||||
set by L{settimeout}.
|
if sending stalled for longer than the timeout set by `settimeout`.
|
||||||
@raise socket.error: if an error occured before the entire string was
|
:raises socket.error:
|
||||||
sent.
|
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.
|
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:
|
while s:
|
||||||
if self.closed:
|
if self.closed:
|
||||||
|
@ -801,19 +761,18 @@ class Channel (object):
|
||||||
def sendall_stderr(self, s):
|
def sendall_stderr(self, s):
|
||||||
"""
|
"""
|
||||||
Send data to the channel's "stderr" stream, without allowing partial
|
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.
|
from the given string until all data has been sent or an error occurs.
|
||||||
Nothing is returned.
|
Nothing is returned.
|
||||||
|
|
||||||
@param s: data to send to the client as "stderr" output.
|
:param str s: data to send to the client as "stderr" output.
|
||||||
@type s: str
|
|
||||||
|
|
||||||
@raise socket.timeout: if sending stalled for longer than the timeout
|
:raises socket.timeout:
|
||||||
set by L{settimeout}.
|
if sending stalled for longer than the timeout set by `settimeout`.
|
||||||
@raise socket.error: if an error occured before the entire string was
|
:raises socket.error:
|
||||||
sent.
|
if an error occured before the entire string was sent.
|
||||||
|
|
||||||
@since: 1.1
|
.. versionadded:: 1.1
|
||||||
"""
|
"""
|
||||||
while s:
|
while s:
|
||||||
if self.closed:
|
if self.closed:
|
||||||
|
@ -825,49 +784,46 @@ class Channel (object):
|
||||||
def makefile(self, *params):
|
def makefile(self, *params):
|
||||||
"""
|
"""
|
||||||
Return a file-like object associated with this channel. The optional
|
Return a file-like object associated with this channel. The optional
|
||||||
C{mode} and C{bufsize} arguments are interpreted the same way as by
|
``mode`` and ``bufsize`` arguments are interpreted the same way as by
|
||||||
the built-in C{file()} function in python.
|
the built-in ``file()`` function in Python.
|
||||||
|
|
||||||
@return: object which can be used for python file I/O.
|
:return: `.ChannelFile` object which can be used for Python file I/O.
|
||||||
@rtype: L{ChannelFile}
|
|
||||||
"""
|
"""
|
||||||
return ChannelFile(*([self] + list(params)))
|
return ChannelFile(*([self] + list(params)))
|
||||||
|
|
||||||
def makefile_stderr(self, *params):
|
def makefile_stderr(self, *params):
|
||||||
"""
|
"""
|
||||||
Return a file-like object associated with this channel's stderr
|
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.
|
without a pty will ever have data on the stderr stream.
|
||||||
|
|
||||||
The optional C{mode} and C{bufsize} arguments are interpreted the
|
The optional ``mode`` and ``bufsize`` arguments are interpreted the
|
||||||
same way as by the built-in C{file()} function in python. For a
|
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
|
client, it only makes sense to open this file for reading. For a
|
||||||
server, it only makes sense to open this file for writing.
|
server, it only makes sense to open this file for writing.
|
||||||
|
|
||||||
@return: object which can be used for python file I/O.
|
:return: `.ChannelFile` object which can be used for Python file I/O.
|
||||||
@rtype: L{ChannelFile}
|
|
||||||
|
|
||||||
@since: 1.1
|
.. versionadded:: 1.1
|
||||||
"""
|
"""
|
||||||
return ChannelStderrFile(*([self] + list(params)))
|
return ChannelStderrFile(*([self] + list(params)))
|
||||||
|
|
||||||
def fileno(self):
|
def fileno(self):
|
||||||
"""
|
"""
|
||||||
Returns an OS-level file descriptor which can be used for polling, but
|
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
|
but not for reading or writing. This is primaily to allow Python's
|
||||||
C{select} module to work.
|
``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,
|
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.
|
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
|
(You won't notice this effect unless you have hundreds of channels
|
||||||
open at the same time.)
|
open at the same time.)
|
||||||
|
|
||||||
@return: an OS-level file descriptor
|
:return: an OS-level file descriptor (`int`)
|
||||||
@rtype: int
|
|
||||||
|
|
||||||
@warning: This method causes channel reads to be slightly less
|
.. warning::
|
||||||
efficient.
|
This method causes channel reads to be slightly less efficient.
|
||||||
"""
|
"""
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
try:
|
try:
|
||||||
|
@ -884,14 +840,14 @@ class Channel (object):
|
||||||
|
|
||||||
def shutdown(self, how):
|
def shutdown(self, how):
|
||||||
"""
|
"""
|
||||||
Shut down one or both halves of the connection. If C{how} is 0,
|
Shut down one or both halves of the connection. If ``how`` is 0,
|
||||||
further receives are disallowed. If C{how} is 1, further sends
|
further receives are disallowed. If ``how`` is 1, further sends
|
||||||
are disallowed. If C{how} is 2, further sends and receives are
|
are disallowed. If ``how`` is 2, further sends and receives are
|
||||||
disallowed. This closes the stream in one or both directions.
|
disallowed. This closes the stream in one or both directions.
|
||||||
|
|
||||||
@param how: 0 (stop receiving), 1 (stop sending), or 2 (stop
|
:param int how:
|
||||||
receiving and sending).
|
0 (stop receiving), 1 (stop sending), or 2 (stop receiving and
|
||||||
@type how: int
|
sending).
|
||||||
"""
|
"""
|
||||||
if (how == 0) or (how == 2):
|
if (how == 0) or (how == 2):
|
||||||
# feign "read" shutdown
|
# feign "read" shutdown
|
||||||
|
@ -910,10 +866,10 @@ class Channel (object):
|
||||||
Shutdown the receiving side of this socket, closing the stream in
|
Shutdown the receiving side of this socket, closing the stream in
|
||||||
the incoming direction. After this call, future reads on this
|
the incoming direction. After this call, future reads on this
|
||||||
channel will fail instantly. This is a convenience method, equivalent
|
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.
|
memorize unix constants from the 1970s.
|
||||||
|
|
||||||
@since: 1.2
|
.. versionadded:: 1.2
|
||||||
"""
|
"""
|
||||||
self.shutdown(0)
|
self.shutdown(0)
|
||||||
|
|
||||||
|
@ -922,10 +878,10 @@ class Channel (object):
|
||||||
Shutdown the sending side of this socket, closing the stream in
|
Shutdown the sending side of this socket, closing the stream in
|
||||||
the outgoing direction. After this call, future writes on this
|
the outgoing direction. After this call, future writes on this
|
||||||
channel will fail instantly. This is a convenience method, equivalent
|
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.
|
memorize unix constants from the 1970s.
|
||||||
|
|
||||||
@since: 1.2
|
.. versionadded:: 1.2
|
||||||
"""
|
"""
|
||||||
self.shutdown(1)
|
self.shutdown(1)
|
||||||
|
|
||||||
|
@ -1189,7 +1145,7 @@ class Channel (object):
|
||||||
def _wait_for_send_window(self, size):
|
def _wait_for_send_window(self, size):
|
||||||
"""
|
"""
|
||||||
(You are already holding the lock.)
|
(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
|
for transmission. If no space opens up before the timeout, a timeout
|
||||||
exception is raised. Returns the number of bytes available to send
|
exception is raised. Returns the number of bytes available to send
|
||||||
(may be less than requested).
|
(may be less than requested).
|
||||||
|
@ -1227,13 +1183,15 @@ class Channel (object):
|
||||||
|
|
||||||
class ChannelFile (BufferedFile):
|
class ChannelFile (BufferedFile):
|
||||||
"""
|
"""
|
||||||
A file-like wrapper around L{Channel}. A ChannelFile is created by calling
|
A file-like wrapper around `.Channel`. A ChannelFile is created by calling
|
||||||
L{Channel.makefile}.
|
`Channel.makefile`.
|
||||||
|
|
||||||
@bug: To correctly emulate the file object created from a socket's
|
.. warning::
|
||||||
C{makefile} method, a L{Channel} and its C{ChannelFile} should be able
|
To correctly emulate the file object created from a socket's `makefile
|
||||||
to be closed or garbage-collected independently. Currently, closing
|
<python:socket.socket.makefile>` method, a `.Channel` and its
|
||||||
the C{ChannelFile} does nothing but flush the buffer.
|
`.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):
|
def __init__(self, channel, mode = 'r', bufsize = -1):
|
||||||
|
@ -1244,8 +1202,6 @@ class ChannelFile (BufferedFile):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""
|
"""
|
||||||
Returns a string representation of this object, for debugging.
|
Returns a string representation of this object, for debugging.
|
||||||
|
|
||||||
@rtype: str
|
|
||||||
"""
|
"""
|
||||||
return '<paramiko.ChannelFile from ' + repr(self.channel) + '>'
|
return '<paramiko.ChannelFile from ' + repr(self.channel) + '>'
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
L{SSHClient}.
|
SSH client & key policies
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
|
@ -38,67 +38,10 @@ from paramiko.transport import Transport
|
||||||
from paramiko.util import retry_on_signal
|
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):
|
class SSHClient (object):
|
||||||
"""
|
"""
|
||||||
A high-level representation of a session with an SSH server. This class
|
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::
|
aspects of authenticating and opening channels. A typical use case is::
|
||||||
|
|
||||||
client = SSHClient()
|
client = SSHClient()
|
||||||
|
@ -110,7 +53,7 @@ class SSHClient (object):
|
||||||
checking. The default mechanism is to try to use local key files or an
|
checking. The default mechanism is to try to use local key files or an
|
||||||
SSH agent (if one is running).
|
SSH agent (if one is running).
|
||||||
|
|
||||||
@since: 1.6
|
.. versionadded:: 1.6
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -128,22 +71,21 @@ class SSHClient (object):
|
||||||
def load_system_host_keys(self, filename=None):
|
def load_system_host_keys(self, filename=None):
|
||||||
"""
|
"""
|
||||||
Load host keys from a system (read-only) file. Host keys read with
|
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
|
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
|
will be merged with the existing set (new replacing old if there are
|
||||||
conflicts).
|
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,
|
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
|
and no exception will be raised if the file can't be read. This is
|
||||||
probably only useful on posix.
|
probably only useful on posix.
|
||||||
|
|
||||||
@param filename: the filename to read, or C{None}
|
:param str filename: the filename to read, or ``None``
|
||||||
@type filename: str
|
|
||||||
|
|
||||||
@raise IOError: if a filename was provided and the file could not be
|
:raises IOError:
|
||||||
read
|
if a filename was provided and the file could not be read
|
||||||
"""
|
"""
|
||||||
if filename is None:
|
if filename is None:
|
||||||
# try the user's .ssh key file, and mask exceptions
|
# try the user's .ssh key file, and mask exceptions
|
||||||
|
@ -158,19 +100,18 @@ class SSHClient (object):
|
||||||
def load_host_keys(self, filename):
|
def load_host_keys(self, filename):
|
||||||
"""
|
"""
|
||||||
Load host keys from a local host-key file. Host keys read with this
|
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},
|
method will be checked after keys loaded via `load_system_host_keys`,
|
||||||
but will be saved back by L{save_host_keys} (so they can be modified).
|
but will be saved back by `save_host_keys` (so they can be modified).
|
||||||
The missing host key policy L{AutoAddPolicy} adds keys to this set and
|
The missing host key policy `.AutoAddPolicy` adds keys to this set and
|
||||||
saves them, when connecting to a previously-unknown server.
|
saves them, when connecting to a previously-unknown server.
|
||||||
|
|
||||||
This method can be called multiple times. Each new set of 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
|
will be merged with the existing set (new replacing old if there are
|
||||||
conflicts). When automatically saving, the last hostname is used.
|
conflicts). When automatically saving, the last hostname is used.
|
||||||
|
|
||||||
@param filename: the filename to read
|
:param str filename: the filename to read
|
||||||
@type filename: str
|
|
||||||
|
|
||||||
@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_filename = filename
|
||||||
self._host_keys.load(filename)
|
self._host_keys.load(filename)
|
||||||
|
@ -178,13 +119,12 @@ class SSHClient (object):
|
||||||
def save_host_keys(self, filename):
|
def save_host_keys(self, filename):
|
||||||
"""
|
"""
|
||||||
Save the host keys back to a file. Only the host keys loaded with
|
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
|
`load_host_keys` (plus any added directly) will be saved -- not any
|
||||||
host keys loaded with L{load_system_host_keys}.
|
host keys loaded with `load_system_host_keys`.
|
||||||
|
|
||||||
@param filename: the filename to save to
|
:param str filename: the filename to save to
|
||||||
@type filename: str
|
|
||||||
|
|
||||||
@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
|
# update local host keys from file (in case other SSH clients
|
||||||
|
@ -200,34 +140,32 @@ class SSHClient (object):
|
||||||
|
|
||||||
def get_host_keys(self):
|
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.
|
local host keys or change them.
|
||||||
|
|
||||||
@return: the local host keys
|
:return: the local host keys as a `.HostKeys` object.
|
||||||
@rtype: L{HostKeys}
|
|
||||||
"""
|
"""
|
||||||
return self._host_keys
|
return self._host_keys
|
||||||
|
|
||||||
def set_log_channel(self, name):
|
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.
|
but it can be set to anything you want.
|
||||||
|
|
||||||
@param name: new channel name for logging
|
:param str name: new channel name for logging
|
||||||
@type name: str
|
|
||||||
"""
|
"""
|
||||||
self._log_channel = name
|
self._log_channel = name
|
||||||
|
|
||||||
def set_missing_host_key_policy(self, policy):
|
def set_missing_host_key_policy(self, policy):
|
||||||
"""
|
"""
|
||||||
Set the policy to use when connecting to a server that doesn't have a
|
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
|
host key in either the system or local `.HostKeys` objects. The
|
||||||
default policy is to reject all unknown servers (using L{RejectPolicy}).
|
default policy is to reject all unknown servers (using `.RejectPolicy`).
|
||||||
You may substitute L{AutoAddPolicy} or write your own policy class.
|
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
|
previously-unknown server
|
||||||
@type policy: L{MissingHostKeyPolicy}
|
|
||||||
"""
|
"""
|
||||||
self._policy = policy
|
self._policy = policy
|
||||||
|
|
||||||
|
@ -236,56 +174,49 @@ class SSHClient (object):
|
||||||
compress=False, sock=None):
|
compress=False, sock=None):
|
||||||
"""
|
"""
|
||||||
Connect to an SSH server and authenticate to it. The server's host key
|
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})
|
is checked against the system host keys (see `load_system_host_keys`)
|
||||||
and any local host keys (L{load_host_keys}). If the server's hostname
|
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 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
|
is used (see `set_missing_host_key_policy`). The default policy is
|
||||||
to reject the key and raise an L{SSHException}.
|
to reject the key and raise an `.SSHException`.
|
||||||
|
|
||||||
Authentication is attempted in the following order of priority:
|
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 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
|
- Plain username/password auth, if a password was given
|
||||||
|
|
||||||
If a private key requires a password to unlock it, and a password is
|
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.
|
passed in, that password will be used to attempt to unlock the key.
|
||||||
|
|
||||||
@param hostname: the server to connect to
|
:param str hostname: the server to connect to
|
||||||
@type hostname: str
|
:param int port: the server port to connect to
|
||||||
@param port: the server port to connect to
|
:param str username:
|
||||||
@type port: int
|
the username to authenticate as (defaults to the current local
|
||||||
@param username: the username to authenticate as (defaults to the
|
username)
|
||||||
current local username)
|
:param str password:
|
||||||
@type username: str
|
a password to use for authentication or for unlocking a private key
|
||||||
@param password: a password to use for authentication or for unlocking
|
:param .PKey pkey: an optional private key to use for authentication
|
||||||
a private key
|
:param str key_filename:
|
||||||
@type password: str
|
the filename, or list of filenames, of optional private key(s) to
|
||||||
@param pkey: an optional private key to use for authentication
|
try for authentication
|
||||||
@type pkey: L{PKey}
|
:param float timeout: an optional timeout (in seconds) for the TCP connect
|
||||||
@param key_filename: the filename, or list of filenames, of optional
|
:param bool allow_agent: set to False to disable connecting to the SSH agent
|
||||||
private key(s) to try for authentication
|
:param bool look_for_keys:
|
||||||
@type key_filename: str or list(str)
|
set to False to disable searching for discoverable private key
|
||||||
@param timeout: an optional timeout (in seconds) for the TCP connect
|
files in ``~/.ssh/``
|
||||||
@type timeout: float
|
:param bool compress: set to True to turn on compression
|
||||||
@param allow_agent: set to False to disable connecting to the SSH agent
|
:param socket sock:
|
||||||
@type allow_agent: bool
|
an open socket or socket-like object (such as a `.Channel`) to use
|
||||||
@param look_for_keys: set to False to disable searching for discoverable
|
for communication to the target host
|
||||||
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
|
|
||||||
|
|
||||||
@raise BadHostKeyException: if the server's host key could not be
|
:raises BadHostKeyException: if the server's host key could not be
|
||||||
verified
|
verified
|
||||||
@raise AuthenticationException: if authentication failed
|
:raises AuthenticationException: if authentication failed
|
||||||
@raise SSHException: if there was any other error connecting or
|
:raises SSHException: if there was any other error connecting or
|
||||||
establishing an SSH session
|
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:
|
if not sock:
|
||||||
for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM):
|
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):
|
def close(self):
|
||||||
"""
|
"""
|
||||||
Close this SSHClient and its underlying L{Transport}.
|
Close this SSHClient and its underlying `.Transport`.
|
||||||
"""
|
"""
|
||||||
if self._transport is None:
|
if self._transport is None:
|
||||||
return
|
return
|
||||||
|
@ -356,21 +287,22 @@ class SSHClient (object):
|
||||||
|
|
||||||
def exec_command(self, command, bufsize=-1, timeout=None, get_pty=False):
|
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
|
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.
|
stdin, stdout, and stderr.
|
||||||
|
|
||||||
@param command: the command to execute
|
:param str command: the command to execute
|
||||||
@type command: str
|
:param int bufsize:
|
||||||
@param bufsize: interpreted the same way as by the built-in C{file()} function in python
|
interpreted the same way as by the built-in ``file()`` function in
|
||||||
@type bufsize: int
|
Python
|
||||||
@param timeout: set command's channel timeout. See L{Channel.settimeout}.settimeout
|
:param int timeout:
|
||||||
@type timeout: int
|
set command's channel timeout. See `Channel.settimeout`.settimeout
|
||||||
@return: the stdin, stdout, and stderr of the executing command
|
:return:
|
||||||
@rtype: tuple(L{ChannelFile}, L{ChannelFile}, L{ChannelFile})
|
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()
|
chan = self._transport.open_session()
|
||||||
if(get_pty):
|
if(get_pty):
|
||||||
|
@ -385,24 +317,19 @@ class SSHClient (object):
|
||||||
def invoke_shell(self, term='vt100', width=80, height=24, width_pixels=0,
|
def invoke_shell(self, term='vt100', width=80, height=24, width_pixels=0,
|
||||||
height_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
|
is opened and connected to a pseudo-terminal using the requested
|
||||||
terminal type and size.
|
terminal type and size.
|
||||||
|
|
||||||
@param term: the terminal type to emulate (for example, C{"vt100"})
|
:param str term:
|
||||||
@type term: str
|
the terminal type to emulate (for example, ``"vt100"``)
|
||||||
@param width: the width (in characters) of the terminal window
|
:param int width: the width (in characters) of the terminal window
|
||||||
@type width: int
|
:param int height: the height (in characters) of the terminal window
|
||||||
@param height: the height (in characters) of the terminal window
|
:param int width_pixels: the width (in pixels) of the terminal window
|
||||||
@type height: int
|
:param int height_pixels: the height (in pixels) of the terminal window
|
||||||
@param width_pixels: the width (in pixels) of the terminal window
|
:return: a new `.Channel` connected to the remote shell
|
||||||
@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}
|
|
||||||
|
|
||||||
@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 = self._transport.open_session()
|
||||||
chan.get_pty(term, width, height, width_pixels, height_pixels)
|
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.
|
Open an SFTP session on the SSH server.
|
||||||
|
|
||||||
@return: a new SFTP session object
|
:return: a new `.SFTPClient` session object
|
||||||
@rtype: L{SFTPClient}
|
|
||||||
"""
|
"""
|
||||||
return self._transport.open_sftp_client()
|
return self._transport.open_sftp_client()
|
||||||
|
|
||||||
def get_transport(self):
|
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
|
This can be used to perform lower-level tasks, like opening specific
|
||||||
kinds of channels.
|
kinds of channels.
|
||||||
|
|
||||||
@return: the Transport for this connection
|
:return: the `.Transport` for this connection
|
||||||
@rtype: L{Transport}
|
|
||||||
"""
|
"""
|
||||||
return self._transport
|
return self._transport
|
||||||
|
|
||||||
|
@ -536,3 +461,59 @@ class SSHClient (object):
|
||||||
def _log(self, level, msg):
|
def _log(self, level, msg):
|
||||||
self._transport._log(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.
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
L{SSHConfig}.
|
Configuration file (aka ``ssh_config``) support.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import fnmatch
|
import fnmatch
|
||||||
|
@ -30,69 +30,15 @@ SSH_PORT = 22
|
||||||
proxy_re = re.compile(r"^(proxycommand)\s*=*\s*(.*)", re.I)
|
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):
|
class SSHConfig (object):
|
||||||
"""
|
"""
|
||||||
Representation of config information as stored in the format used by
|
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. Queries can be made via `lookup`. The format is described in
|
||||||
OpenSSH's C{ssh_config} man page. This class is provided primarily as a
|
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
|
convenience to posix users (since the OpenSSH format is a de-facto
|
||||||
standard on posix) but should work fine on Windows too.
|
standard on posix) but should work fine on Windows too.
|
||||||
|
|
||||||
@since: 1.6
|
.. versionadded:: 1.6
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -105,8 +51,7 @@ class SSHConfig (object):
|
||||||
"""
|
"""
|
||||||
Read an OpenSSH config from the given file object.
|
Read an OpenSSH config from the given file object.
|
||||||
|
|
||||||
@param file_obj: a file-like object to read the config file from
|
:param file file_obj: a file-like object to read the config file from
|
||||||
@type file_obj: file
|
|
||||||
"""
|
"""
|
||||||
host = {"host": ['*'], "config": {}}
|
host = {"host": ['*'], "config": {}}
|
||||||
for line in file_obj:
|
for line in file_obj:
|
||||||
|
@ -152,22 +97,20 @@ class SSHConfig (object):
|
||||||
"""
|
"""
|
||||||
Return a dict of config options for a given hostname.
|
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
|
which means that all configuration options from matching host
|
||||||
specifications are merged, with more specific hostmasks taking
|
specifications are merged, with more specific hostmasks taking
|
||||||
precedence. In other words, if C{"Port"} is set under C{"Host *"}
|
precedence. In other words, if ``"Port"`` is set under ``"Host *"``
|
||||||
and also C{"Host *.example.com"}, and the lookup is for
|
and also ``"Host *.example.com"``, and the lookup is for
|
||||||
C{"ssh.example.com"}, then the port entry for C{"Host *.example.com"}
|
``"ssh.example.com"``, then the port entry for ``"Host *.example.com"``
|
||||||
will win out.
|
will win out.
|
||||||
|
|
||||||
The keys in the returned dict are all normalized to lowercase (look for
|
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
|
``"port"``, not ``"Port"``. The values are processed according to the
|
||||||
rules for substitution variable expansion in C{ssh_config}.
|
rules for substitution variable expansion in ``ssh_config``.
|
||||||
|
|
||||||
@param hostname: the hostname to lookup
|
:param str hostname: the hostname to lookup
|
||||||
@type hostname: str
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
matches = [config for config in self._config if
|
matches = [config for config in self._config if
|
||||||
self._allowed(hostname, config['host'])]
|
self._allowed(hostname, config['host'])]
|
||||||
|
|
||||||
|
@ -199,13 +142,11 @@ class SSHConfig (object):
|
||||||
Return a dict of config options with expanded substitutions
|
Return a dict of config options with expanded substitutions
|
||||||
for a given hostname.
|
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.
|
are replaced.
|
||||||
|
|
||||||
@param config: the config for the hostname
|
:param dict config: the config for the hostname
|
||||||
@type hostname: dict
|
:param str hostname: the hostname that the config belongs to
|
||||||
@param hostname: the hostname that the config belongs to
|
|
||||||
@type hostname: str
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if 'hostname' in config:
|
if 'hostname' in config:
|
||||||
|
@ -264,3 +205,57 @@ class SSHConfig (object):
|
||||||
else:
|
else:
|
||||||
config[k] = config[k].replace(find, str(replace))
|
config[k] = config[k].replace(find, str(replace))
|
||||||
return config
|
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.
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
L{DSSKey}
|
DSS keys.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from Crypto.PublicKey import DSA
|
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 private DSS key. This factory function can be used to
|
||||||
generate a new host key or authentication key.
|
generate a new host key or authentication key.
|
||||||
|
|
||||||
@param bits: number of bits the generated key should be.
|
:param int bits: number of bits the generated key should be.
|
||||||
@type bits: int
|
:param function progress_func:
|
||||||
@param progress_func: an optional function to call at key points in
|
an optional function to call at key points in key generation (used
|
||||||
key generation (used by C{pyCrypto.PublicKey}).
|
by ``pyCrypto.PublicKey``).
|
||||||
@type progress_func: function
|
:return: new `.DSSKey` private key
|
||||||
@return: new private key
|
|
||||||
@rtype: L{DSSKey}
|
|
||||||
"""
|
"""
|
||||||
dsa = DSA.generate(bits, rng.read, progress_func)
|
dsa = DSA.generate(bits, rng.read, progress_func)
|
||||||
key = DSSKey(vals=(dsa.p, dsa.q, dsa.g, dsa.y))
|
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.,
|
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
"""
|
|
||||||
BufferedFile.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
|
|
||||||
|
|
||||||
class BufferedFile (object):
|
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.
|
simpler stream.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -67,10 +63,7 @@ class BufferedFile (object):
|
||||||
file. This iterator happens to return the file itself, since a file is
|
file. This iterator happens to return the file itself, since a file is
|
||||||
its own iterator.
|
its own iterator.
|
||||||
|
|
||||||
@raise ValueError: if the file is closed.
|
:raises ValueError: if the file is closed.
|
||||||
|
|
||||||
@return: an interator.
|
|
||||||
@rtype: iterator
|
|
||||||
"""
|
"""
|
||||||
if self._closed:
|
if self._closed:
|
||||||
raise ValueError('I/O operation on closed file')
|
raise ValueError('I/O operation on closed file')
|
||||||
|
@ -94,14 +87,13 @@ class BufferedFile (object):
|
||||||
|
|
||||||
def next(self):
|
def next(self):
|
||||||
"""
|
"""
|
||||||
Returns the next line from the input, or raises L{StopIteration} when
|
Returns the next line from the input, or raises
|
||||||
EOF is hit. Unlike python file objects, it's okay to mix calls to
|
`~exceptions.StopIteration` when EOF is hit. Unlike Python file
|
||||||
C{next} and L{readline}.
|
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.
|
:return: a line (`str`) read from the file.
|
||||||
@rtype: str
|
|
||||||
"""
|
"""
|
||||||
line = self.readline()
|
line = self.readline()
|
||||||
if not line:
|
if not line:
|
||||||
|
@ -110,15 +102,14 @@ class BufferedFile (object):
|
||||||
|
|
||||||
def read(self, size=None):
|
def read(self, size=None):
|
||||||
"""
|
"""
|
||||||
Read at most C{size} bytes from the file (less if we hit the end of the
|
Read at most ``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
|
file first). If the ``size`` argument is negative or omitted, read all
|
||||||
the remaining data in the file.
|
the remaining data in the file.
|
||||||
|
|
||||||
@param size: maximum number of bytes to read
|
:param int size: maximum number of bytes to read
|
||||||
@type size: int
|
:return:
|
||||||
@return: data read from the file, or an empty string if EOF was
|
data read from the file (as a `str`), or an empty string if EOF was
|
||||||
encountered immediately
|
encountered immediately
|
||||||
@rtype: str
|
|
||||||
"""
|
"""
|
||||||
if self._closed:
|
if self._closed:
|
||||||
raise IOError('File is 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
|
incomplete line may be returned. An empty string is returned only when
|
||||||
EOF is encountered immediately.
|
EOF is encountered immediately.
|
||||||
|
|
||||||
@note: Unlike stdio's C{fgets()}, the returned string contains null
|
.. note::
|
||||||
characters (C{'\\0'}) if they occurred in the input.
|
Unlike stdio's ``fgets``, the returned string contains null
|
||||||
|
characters (``'\\0'``) if they occurred in the input.
|
||||||
|
|
||||||
@param size: maximum length of returned string.
|
:param int size: maximum length of returned string.
|
||||||
@type size: int
|
:return:
|
||||||
@return: next line of the file, or an empty string if the end of the
|
next line of the file (`str`), or an empty string if the end of the
|
||||||
file has been reached.
|
file has been reached.
|
||||||
@rtype: str
|
|
||||||
"""
|
"""
|
||||||
# it's almost silly how complex this function is.
|
# it's almost silly how complex this function is.
|
||||||
if self._closed:
|
if self._closed:
|
||||||
|
@ -243,15 +234,13 @@ class BufferedFile (object):
|
||||||
|
|
||||||
def readlines(self, sizehint=None):
|
def readlines(self, sizehint=None):
|
||||||
"""
|
"""
|
||||||
Read all remaining lines using L{readline} and return them as a list.
|
Read all remaining lines using `readline` and return them as a list.
|
||||||
If the optional C{sizehint} argument is present, instead of reading up
|
If the optional ``sizehint`` argument is present, instead of reading up
|
||||||
to EOF, whole lines totalling approximately sizehint bytes (possibly
|
to EOF, whole lines totalling approximately sizehint bytes (possibly
|
||||||
after rounding up to an internal buffer size) are read.
|
after rounding up to an internal buffer size) are read.
|
||||||
|
|
||||||
@param sizehint: desired maximum number of bytes to read.
|
:param int sizehint: desired maximum number of bytes to read.
|
||||||
@type sizehint: int
|
:return: `list` of lines read from the file.
|
||||||
@return: list of lines read from the file.
|
|
||||||
@rtype: list
|
|
||||||
"""
|
"""
|
||||||
lines = []
|
lines = []
|
||||||
bytes = 0
|
bytes = 0
|
||||||
|
@ -267,21 +256,20 @@ class BufferedFile (object):
|
||||||
|
|
||||||
def seek(self, offset, whence=0):
|
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.
|
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
|
operations will be undone at the next write (as the file position
|
||||||
will move back to the end of the file).
|
will move back to the end of the file).
|
||||||
|
|
||||||
@param offset: position to move to within the file, relative to
|
:param int offset:
|
||||||
C{whence}.
|
position to move to within the file, relative to ``whence``.
|
||||||
@type offset: int
|
:param int whence:
|
||||||
@param whence: type of movement: 0 = absolute; 1 = relative to the
|
type of movement: 0 = absolute; 1 = relative to the current
|
||||||
current position; 2 = relative to the end of the file.
|
position; 2 = relative to the end of the file.
|
||||||
@type whence: int
|
|
||||||
|
|
||||||
@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.')
|
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
|
useful if the underlying file doesn't support random access, or was
|
||||||
opened in append mode.
|
opened in append mode.
|
||||||
|
|
||||||
@return: file position (in bytes).
|
:return: file position (`number <int>` of bytes).
|
||||||
@rtype: int
|
|
||||||
"""
|
"""
|
||||||
return self._pos
|
return self._pos
|
||||||
|
|
||||||
def write(self, data):
|
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
|
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.)
|
written out.)
|
||||||
|
|
||||||
@param data: data to write.
|
:param str data: data to write
|
||||||
@type data: str
|
|
||||||
"""
|
"""
|
||||||
if self._closed:
|
if self._closed:
|
||||||
raise IOError('File is 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
|
Write a sequence of strings to the file. The sequence can be any
|
||||||
iterable object producing strings, typically a list of strings. (The
|
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.)
|
separators.)
|
||||||
|
|
||||||
@param sequence: an iterable sequence of strings.
|
:param iterable sequence: an iterable sequence of strings.
|
||||||
@type sequence: sequence
|
|
||||||
"""
|
"""
|
||||||
for line in sequence:
|
for line in sequence:
|
||||||
self.write(line)
|
self.write(line)
|
||||||
|
@ -346,11 +331,8 @@ class BufferedFile (object):
|
||||||
|
|
||||||
def xreadlines(self):
|
def xreadlines(self):
|
||||||
"""
|
"""
|
||||||
Identical to C{iter(f)}. This is a deprecated file interface that
|
Identical to ``iter(f)``. This is a deprecated file interface that
|
||||||
predates python iterator support.
|
predates Python iterator support.
|
||||||
|
|
||||||
@return: an iterator.
|
|
||||||
@rtype: iterator
|
|
||||||
"""
|
"""
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -364,25 +346,25 @@ class BufferedFile (object):
|
||||||
|
|
||||||
def _read(self, size):
|
def _read(self, size):
|
||||||
"""
|
"""
|
||||||
I{(subclass override)}
|
(subclass override)
|
||||||
Read data from the stream. Return C{None} or raise C{EOFError} to
|
Read data from the stream. Return ``None`` or raise ``EOFError`` to
|
||||||
indicate EOF.
|
indicate EOF.
|
||||||
"""
|
"""
|
||||||
raise EOFError()
|
raise EOFError()
|
||||||
|
|
||||||
def _write(self, data):
|
def _write(self, data):
|
||||||
"""
|
"""
|
||||||
I{(subclass override)}
|
(subclass override)
|
||||||
Write data into the stream.
|
Write data into the stream.
|
||||||
"""
|
"""
|
||||||
raise IOError('write not implemented')
|
raise IOError('write not implemented')
|
||||||
|
|
||||||
def _get_size(self):
|
def _get_size(self):
|
||||||
"""
|
"""
|
||||||
I{(subclass override)}
|
(subclass override)
|
||||||
Return the size of the file. This is called from within L{_set_mode}
|
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
|
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
|
a stream that can't be randomly accessed, you don't need to override
|
||||||
this method,
|
this method,
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -16,9 +16,6 @@
|
||||||
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
"""
|
|
||||||
L{HostKeys}
|
|
||||||
"""
|
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import binascii
|
import binascii
|
||||||
|
@ -31,99 +28,24 @@ from paramiko.rsakey import RSAKey
|
||||||
from paramiko.util import get_logger, constant_time_bytes_eq
|
from paramiko.util import get_logger, constant_time_bytes_eq
|
||||||
|
|
||||||
|
|
||||||
class InvalidHostKey(Exception):
|
|
||||||
|
|
||||||
def __init__(self, line, exc):
|
|
||||||
self.line = line
|
|
||||||
self.exc = exc
|
|
||||||
self.args = (line, exc)
|
|
||||||
|
|
||||||
|
|
||||||
class HostKeyEntry:
|
|
||||||
"""
|
|
||||||
Representation of a line in an OpenSSH-style "known hosts" file.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, hostnames=None, key=None):
|
|
||||||
self.valid = (hostnames is not None) and (key is not None)
|
|
||||||
self.hostnames = hostnames
|
|
||||||
self.key = key
|
|
||||||
|
|
||||||
def from_line(cls, line, lineno=None):
|
|
||||||
"""
|
|
||||||
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.
|
|
||||||
|
|
||||||
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
|
|
||||||
"""
|
|
||||||
log = get_logger('paramiko.hostkeys')
|
|
||||||
fields = line.split(' ')
|
|
||||||
if len(fields) < 3:
|
|
||||||
# Bad number of fields
|
|
||||||
log.info("Not enough fields found in known_hosts in line %s (%r)" %
|
|
||||||
(lineno, line))
|
|
||||||
return None
|
|
||||||
fields = fields[:3]
|
|
||||||
|
|
||||||
names, keytype, key = fields
|
|
||||||
names = names.split(',')
|
|
||||||
|
|
||||||
# Decide what kind of key we're looking at and create an object
|
|
||||||
# to hold it accordingly.
|
|
||||||
try:
|
|
||||||
if keytype == 'ssh-rsa':
|
|
||||||
key = RSAKey(data=base64.decodestring(key))
|
|
||||||
elif keytype == 'ssh-dss':
|
|
||||||
key = DSSKey(data=base64.decodestring(key))
|
|
||||||
else:
|
|
||||||
log.info("Unable to handle key of type %s" % (keytype,))
|
|
||||||
return None
|
|
||||||
except binascii.Error, e:
|
|
||||||
raise InvalidHostKey(line, e)
|
|
||||||
|
|
||||||
return cls(names, key)
|
|
||||||
from_line = classmethod(from_line)
|
|
||||||
|
|
||||||
def to_line(self):
|
|
||||||
"""
|
|
||||||
Returns a string in OpenSSH known_hosts file format, or None if
|
|
||||||
the object is not in a valid state. A trailing newline is
|
|
||||||
included.
|
|
||||||
"""
|
|
||||||
if self.valid:
|
|
||||||
return '%s %s %s\n' % (','.join(self.hostnames), self.key.get_name(),
|
|
||||||
self.key.get_base64())
|
|
||||||
return None
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return '<HostKeyEntry %r: %r>' % (self.hostnames, self.key)
|
|
||||||
|
|
||||||
|
|
||||||
class HostKeys (UserDict.DictMixin):
|
class HostKeys (UserDict.DictMixin):
|
||||||
"""
|
"""
|
||||||
Representation of an openssh-style "known hosts" file. Host keys can be
|
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
|
read from one or more files, and then individual hosts can be looked up to
|
||||||
verify server keys during SSH negotiation.
|
verify server keys during SSH negotiation.
|
||||||
|
|
||||||
A HostKeys object can be treated like a dict; any dict lookup is equivalent
|
A `.HostKeys` object can be treated like a dict; any dict lookup is
|
||||||
to calling L{lookup}.
|
equivalent to calling `lookup`.
|
||||||
|
|
||||||
@since: 1.5.3
|
.. versionadded:: 1.5.3
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, filename=None):
|
def __init__(self, filename=None):
|
||||||
"""
|
"""
|
||||||
Create a new HostKeys object, optionally loading keys from an openssh
|
Create a new HostKeys object, optionally loading keys from an OpenSSH
|
||||||
style host-key file.
|
style host-key file.
|
||||||
|
|
||||||
@param filename: filename to load host keys from, or C{None}
|
:param str filename: filename to load host keys from, or ``None``
|
||||||
@type filename: str
|
|
||||||
"""
|
"""
|
||||||
# emulate a dict of { hostname: { keytype: PKey } }
|
# emulate a dict of { hostname: { keytype: PKey } }
|
||||||
self._entries = []
|
self._entries = []
|
||||||
|
@ -133,14 +55,11 @@ class HostKeys (UserDict.DictMixin):
|
||||||
def add(self, hostname, keytype, key):
|
def add(self, hostname, keytype, key):
|
||||||
"""
|
"""
|
||||||
Add a host key entry to the table. Any existing entry for a
|
Add a host key entry to the table. Any existing entry for a
|
||||||
C{(hostname, keytype)} pair will be replaced.
|
``(hostname, keytype)`` pair will be replaced.
|
||||||
|
|
||||||
@param hostname: the hostname (or IP) to add
|
:param str hostname: the hostname (or IP) to add
|
||||||
@type hostname: str
|
:param str keytype: key type (``"ssh-rsa"`` or ``"ssh-dss"``)
|
||||||
@param keytype: key type (C{"ssh-rsa"} or C{"ssh-dss"})
|
:param .PKey key: the key to add
|
||||||
@type keytype: str
|
|
||||||
@param key: the key to add
|
|
||||||
@type key: L{PKey}
|
|
||||||
"""
|
"""
|
||||||
for e in self._entries:
|
for e in self._entries:
|
||||||
if (hostname in e.hostnames) and (e.key.get_name() == keytype):
|
if (hostname in e.hostnames) and (e.key.get_name() == keytype):
|
||||||
|
@ -150,19 +69,18 @@ class HostKeys (UserDict.DictMixin):
|
||||||
|
|
||||||
def load(self, filename):
|
def load(self, filename):
|
||||||
"""
|
"""
|
||||||
Read a file of known SSH host keys, in the format used by openssh.
|
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
|
This type of file unfortunately doesn't exist on Windows, but on
|
||||||
posix, it will usually be stored in
|
posix, it will usually be stored in
|
||||||
C{os.path.expanduser("~/.ssh/known_hosts")}.
|
``os.path.expanduser("~/.ssh/known_hosts")``.
|
||||||
|
|
||||||
If this method is called multiple times, the host keys are merged,
|
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},
|
not cleared. So multiple calls to `load` will just call `add`,
|
||||||
replacing any existing entries and adding new ones.
|
replacing any existing entries and adding new ones.
|
||||||
|
|
||||||
@param filename: name of the file to read host keys from
|
:param str filename: name of the file to read host keys from
|
||||||
@type filename: str
|
|
||||||
|
|
||||||
@raise IOError: if there was an error reading the file
|
:raises IOError: if there was an error reading the file
|
||||||
"""
|
"""
|
||||||
f = open(filename, 'r')
|
f = open(filename, 'r')
|
||||||
for lineno, line in enumerate(f):
|
for lineno, line in enumerate(f):
|
||||||
|
@ -181,17 +99,16 @@ class HostKeys (UserDict.DictMixin):
|
||||||
|
|
||||||
def save(self, filename):
|
def save(self, filename):
|
||||||
"""
|
"""
|
||||||
Save host keys into a file, in the format used by openssh. The order of
|
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
|
keys in the file will be preserved when possible (if these keys were
|
||||||
loaded from a file originally). The single exception is that combined
|
loaded from a file originally). The single exception is that combined
|
||||||
lines will be split into individual key lines, which is arguably a bug.
|
lines will be split into individual key lines, which is arguably a bug.
|
||||||
|
|
||||||
@param filename: name of the file to write
|
:param str filename: name of the file to write
|
||||||
@type filename: str
|
|
||||||
|
|
||||||
@raise IOError: if there was an error writing the file
|
:raises IOError: if there was an error writing the file
|
||||||
|
|
||||||
@since: 1.6.1
|
.. versionadded:: 1.6.1
|
||||||
"""
|
"""
|
||||||
f = open(filename, 'w')
|
f = open(filename, 'w')
|
||||||
for e in self._entries:
|
for e in self._entries:
|
||||||
|
@ -203,13 +120,11 @@ class HostKeys (UserDict.DictMixin):
|
||||||
def lookup(self, hostname):
|
def lookup(self, hostname):
|
||||||
"""
|
"""
|
||||||
Find a hostkey entry for a given hostname or IP. If no entry is found,
|
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
|
``None`` is returned. Otherwise a dictionary of keytype to key is
|
||||||
returned. The keytype will be either C{"ssh-rsa"} or C{"ssh-dss"}.
|
returned. The keytype will be either ``"ssh-rsa"`` or ``"ssh-dss"``.
|
||||||
|
|
||||||
@param hostname: the hostname (or IP) to lookup
|
:param str hostname: the hostname (or IP) to lookup
|
||||||
@type hostname: str
|
:return: dict of `str` -> `.PKey` keys associated with this host (or ``None``)
|
||||||
@return: keys associated with this host (or C{None})
|
|
||||||
@rtype: dict(str, L{PKey})
|
|
||||||
"""
|
"""
|
||||||
class SubDict (UserDict.DictMixin):
|
class SubDict (UserDict.DictMixin):
|
||||||
def __init__(self, hostname, entries, hostkeys):
|
def __init__(self, hostname, entries, hostkeys):
|
||||||
|
@ -254,13 +169,10 @@ class HostKeys (UserDict.DictMixin):
|
||||||
Return True if the given key is associated with the given hostname
|
Return True if the given key is associated with the given hostname
|
||||||
in this dictionary.
|
in this dictionary.
|
||||||
|
|
||||||
@param hostname: hostname (or IP) of the SSH server
|
:param str hostname: hostname (or IP) of the SSH server
|
||||||
@type hostname: str
|
:param .PKey key: the key to check
|
||||||
@param key: the key to check
|
:return:
|
||||||
@type key: L{PKey}
|
``True`` if the key is associated with the hostname; else ``False``
|
||||||
@return: C{True} if the key is associated with the hostname; C{False}
|
|
||||||
if not
|
|
||||||
@rtype: bool
|
|
||||||
"""
|
"""
|
||||||
k = self.lookup(hostname)
|
k = self.lookup(hostname)
|
||||||
if k is None:
|
if k is None:
|
||||||
|
@ -298,7 +210,7 @@ class HostKeys (UserDict.DictMixin):
|
||||||
self._entries.append(HostKeyEntry([hostname], entry[key_type]))
|
self._entries.append(HostKeyEntry([hostname], entry[key_type]))
|
||||||
|
|
||||||
def keys(self):
|
def keys(self):
|
||||||
# python 2.4 sets would be nice here.
|
# Python 2.4 sets would be nice here.
|
||||||
ret = []
|
ret = []
|
||||||
for e in self._entries:
|
for e in self._entries:
|
||||||
for h in e.hostnames:
|
for h in e.hostnames:
|
||||||
|
@ -314,15 +226,12 @@ class HostKeys (UserDict.DictMixin):
|
||||||
|
|
||||||
def hash_host(hostname, salt=None):
|
def hash_host(hostname, salt=None):
|
||||||
"""
|
"""
|
||||||
Return a "hashed" form of the hostname, as used by openssh when storing
|
Return a "hashed" form of the hostname, as used by OpenSSH when storing
|
||||||
hashed hostnames in the known_hosts file.
|
hashed hostnames in the known_hosts file.
|
||||||
|
|
||||||
@param hostname: the hostname to hash
|
:param str hostname: the hostname to hash
|
||||||
@type hostname: str
|
:param str salt: optional salt to use when hashing (must be 20 bytes long)
|
||||||
@param salt: optional salt to use when hashing (must be 20 bytes long)
|
:return: the hashed hostname as a `str`
|
||||||
@type salt: str
|
|
||||||
@return: the hashed hostname
|
|
||||||
@rtype: str
|
|
||||||
"""
|
"""
|
||||||
if salt is None:
|
if salt is None:
|
||||||
salt = rng.read(SHA.digest_size)
|
salt = rng.read(SHA.digest_size)
|
||||||
|
@ -336,3 +245,74 @@ class HostKeys (UserDict.DictMixin):
|
||||||
return hostkey.replace('\n', '')
|
return hostkey.replace('\n', '')
|
||||||
hash_host = staticmethod(hash_host)
|
hash_host = staticmethod(hash_host)
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidHostKey(Exception):
|
||||||
|
def __init__(self, line, exc):
|
||||||
|
self.line = line
|
||||||
|
self.exc = exc
|
||||||
|
self.args = (line, exc)
|
||||||
|
|
||||||
|
|
||||||
|
class HostKeyEntry:
|
||||||
|
"""
|
||||||
|
Representation of a line in an OpenSSH-style "known hosts" file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, hostnames=None, key=None):
|
||||||
|
self.valid = (hostnames is not None) and (key is not None)
|
||||||
|
self.hostnames = hostnames
|
||||||
|
self.key = key
|
||||||
|
|
||||||
|
def from_line(cls, line, lineno=None):
|
||||||
|
"""
|
||||||
|
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.
|
||||||
|
|
||||||
|
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 str line: a line from an OpenSSH known_hosts file
|
||||||
|
"""
|
||||||
|
log = get_logger('paramiko.hostkeys')
|
||||||
|
fields = line.split(' ')
|
||||||
|
if len(fields) < 3:
|
||||||
|
# Bad number of fields
|
||||||
|
log.info("Not enough fields found in known_hosts in line %s (%r)" %
|
||||||
|
(lineno, line))
|
||||||
|
return None
|
||||||
|
fields = fields[:3]
|
||||||
|
|
||||||
|
names, keytype, key = fields
|
||||||
|
names = names.split(',')
|
||||||
|
|
||||||
|
# Decide what kind of key we're looking at and create an object
|
||||||
|
# to hold it accordingly.
|
||||||
|
try:
|
||||||
|
if keytype == 'ssh-rsa':
|
||||||
|
key = RSAKey(data=base64.decodestring(key))
|
||||||
|
elif keytype == 'ssh-dss':
|
||||||
|
key = DSSKey(data=base64.decodestring(key))
|
||||||
|
else:
|
||||||
|
log.info("Unable to handle key of type %s" % (keytype,))
|
||||||
|
return None
|
||||||
|
except binascii.Error, e:
|
||||||
|
raise InvalidHostKey(line, e)
|
||||||
|
|
||||||
|
return cls(names, key)
|
||||||
|
from_line = classmethod(from_line)
|
||||||
|
|
||||||
|
def to_line(self):
|
||||||
|
"""
|
||||||
|
Returns a string in OpenSSH known_hosts file format, or None if
|
||||||
|
the object is not in a valid state. A trailing newline is
|
||||||
|
included.
|
||||||
|
"""
|
||||||
|
if self.valid:
|
||||||
|
return '%s %s %s\n' % (','.join(self.hostnames), self.key.get_name(),
|
||||||
|
self.key.get_base64())
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<HostKeyEntry %r: %r>' % (self.hostnames, self.key)
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
# 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
|
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.
|
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.
|
# 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):
|
class Message (object):
|
||||||
"""
|
"""
|
||||||
An SSH2 I{Message} is a stream of bytes that encodes some combination of
|
An SSH2 message is a stream of bytes that encodes some combination of
|
||||||
strings, integers, bools, and infinite-precision integers (known in python
|
strings, integers, bools, and infinite-precision integers (known in Python
|
||||||
as I{long}s). This class builds or breaks down such a byte stream.
|
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
|
Normally you don't need to deal with anything this low-level, but it's
|
||||||
exposed for people implementing custom extensions, or features that
|
exposed for people implementing custom extensions, or features that
|
||||||
|
@ -39,11 +39,11 @@ class Message (object):
|
||||||
|
|
||||||
def __init__(self, content=None):
|
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
|
:param str content:
|
||||||
in only when decomposing a Message).
|
the byte stream to use as the message content (passed in only when
|
||||||
@type content: string
|
decomposing a message).
|
||||||
"""
|
"""
|
||||||
if content != None:
|
if content != None:
|
||||||
self.packet = cStringIO.StringIO(content)
|
self.packet = cStringIO.StringIO(content)
|
||||||
|
@ -52,18 +52,13 @@ class Message (object):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""
|
"""
|
||||||
Return the byte stream content of this Message, as a string.
|
Return the byte stream content of this message, as a string.
|
||||||
|
|
||||||
@return: the contents of this Message.
|
|
||||||
@rtype: string
|
|
||||||
"""
|
"""
|
||||||
return self.packet.getvalue()
|
return self.packet.getvalue()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""
|
"""
|
||||||
Returns a string representation of this object, for debugging.
|
Returns a string representation of this object, for debugging.
|
||||||
|
|
||||||
@rtype: string
|
|
||||||
"""
|
"""
|
||||||
return 'paramiko.Message(' + repr(self.packet.getvalue()) + ')'
|
return 'paramiko.Message(' + repr(self.packet.getvalue()) + ')'
|
||||||
|
|
||||||
|
@ -76,11 +71,8 @@ class Message (object):
|
||||||
|
|
||||||
def get_remainder(self):
|
def get_remainder(self):
|
||||||
"""
|
"""
|
||||||
Return the bytes of this Message that haven't already been parsed and
|
Return the bytes (as a `str`) of this message that haven't already been
|
||||||
returned.
|
parsed and returned.
|
||||||
|
|
||||||
@return: a string of the bytes not parsed yet.
|
|
||||||
@rtype: string
|
|
||||||
"""
|
"""
|
||||||
position = self.packet.tell()
|
position = self.packet.tell()
|
||||||
remainder = self.packet.read()
|
remainder = self.packet.read()
|
||||||
|
@ -89,12 +81,9 @@ class Message (object):
|
||||||
|
|
||||||
def get_so_far(self):
|
def get_so_far(self):
|
||||||
"""
|
"""
|
||||||
Returns the bytes of this Message that have been parsed and returned.
|
Returns the `str` bytes of this message that have been parsed and
|
||||||
The string passed into a Message's constructor can be regenerated by
|
returned. The string passed into a message's constructor can be
|
||||||
concatenating C{get_so_far} and L{get_remainder}.
|
regenerated by concatenating ``get_so_far`` and `get_remainder`.
|
||||||
|
|
||||||
@return: a string of the bytes parsed so far.
|
|
||||||
@rtype: string
|
|
||||||
"""
|
"""
|
||||||
position = self.packet.tell()
|
position = self.packet.tell()
|
||||||
self.rewind()
|
self.rewind()
|
||||||
|
@ -102,12 +91,10 @@ class Message (object):
|
||||||
|
|
||||||
def get_bytes(self, n):
|
def get_bytes(self, n):
|
||||||
"""
|
"""
|
||||||
Return the next C{n} bytes of the Message, without decomposing into
|
Return the next ``n`` bytes of the message (as a `str`), without
|
||||||
an int, string, etc. Just the raw bytes are returned.
|
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``
|
||||||
@return: a string of the next C{n} bytes of the Message, or a string
|
bytes remaining in the message.
|
||||||
of C{n} zero bytes, if there aren't C{n} bytes remaining.
|
|
||||||
@rtype: string
|
|
||||||
"""
|
"""
|
||||||
b = self.packet.read(n)
|
b = self.packet.read(n)
|
||||||
max_pad_size = 1<<20 # Limit padding to 1 MB
|
max_pad_size = 1<<20 # Limit padding to 1 MB
|
||||||
|
@ -117,21 +104,18 @@ class Message (object):
|
||||||
|
|
||||||
def get_byte(self):
|
def get_byte(self):
|
||||||
"""
|
"""
|
||||||
Return the next byte of the Message, without decomposing it. This
|
Return the next byte of the message, without decomposing it. This
|
||||||
is equivalent to L{get_bytes(1)<get_bytes>}.
|
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.
|
any bytes remaining.
|
||||||
@rtype: string
|
|
||||||
"""
|
"""
|
||||||
return self.get_bytes(1)
|
return self.get_bytes(1)
|
||||||
|
|
||||||
def get_boolean(self):
|
def get_boolean(self):
|
||||||
"""
|
"""
|
||||||
Fetch a boolean from the stream.
|
Fetch a boolean from the stream.
|
||||||
|
|
||||||
@return: C{True} or C{False} (from the Message).
|
|
||||||
@rtype: bool
|
|
||||||
"""
|
"""
|
||||||
b = self.get_bytes(1)
|
b = self.get_bytes(1)
|
||||||
return b != '\x00'
|
return b != '\x00'
|
||||||
|
@ -140,8 +124,7 @@ class Message (object):
|
||||||
"""
|
"""
|
||||||
Fetch an int from the stream.
|
Fetch an int from the stream.
|
||||||
|
|
||||||
@return: a 32-bit unsigned integer.
|
:return: a 32-bit unsigned `int`.
|
||||||
@rtype: int
|
|
||||||
"""
|
"""
|
||||||
return struct.unpack('>I', self.get_bytes(4))[0]
|
return struct.unpack('>I', self.get_bytes(4))[0]
|
||||||
|
|
||||||
|
@ -149,8 +132,7 @@ class Message (object):
|
||||||
"""
|
"""
|
||||||
Fetch a 64-bit int from the stream.
|
Fetch a 64-bit int from the stream.
|
||||||
|
|
||||||
@return: a 64-bit unsigned integer.
|
:return: a 64-bit unsigned integer (`long`).
|
||||||
@rtype: long
|
|
||||||
"""
|
"""
|
||||||
return struct.unpack('>Q', self.get_bytes(8))[0]
|
return struct.unpack('>Q', self.get_bytes(8))[0]
|
||||||
|
|
||||||
|
@ -158,29 +140,23 @@ class Message (object):
|
||||||
"""
|
"""
|
||||||
Fetch a long int (mpint) from the stream.
|
Fetch a long int (mpint) from the stream.
|
||||||
|
|
||||||
@return: an arbitrary-length integer.
|
:return: an arbitrary-length integer (`long`).
|
||||||
@rtype: long
|
|
||||||
"""
|
"""
|
||||||
return util.inflate_long(self.get_string())
|
return util.inflate_long(self.get_string())
|
||||||
|
|
||||||
def get_string(self):
|
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 unprintable characters. (It's not unheard of for a string to
|
||||||
contain another byte-stream Message.)
|
contain another byte-stream message.)
|
||||||
|
|
||||||
@return: a string.
|
|
||||||
@rtype: string
|
|
||||||
"""
|
"""
|
||||||
return self.get_bytes(self.get_int())
|
return self.get_bytes(self.get_int())
|
||||||
|
|
||||||
def get_list(self):
|
def get_list(self):
|
||||||
"""
|
"""
|
||||||
Fetch a list of strings from the stream. These are trivially encoded
|
Fetch a `list` of `strings <str>` from the stream.
|
||||||
as comma-separated values in a string.
|
|
||||||
|
|
||||||
@return: a list of strings.
|
These are trivially encoded as comma-separated values in a string.
|
||||||
@rtype: list of strings
|
|
||||||
"""
|
"""
|
||||||
return self.get_string().split(',')
|
return self.get_string().split(',')
|
||||||
|
|
||||||
|
@ -188,8 +164,7 @@ class Message (object):
|
||||||
"""
|
"""
|
||||||
Write bytes to the stream, without any formatting.
|
Write bytes to the stream, without any formatting.
|
||||||
|
|
||||||
@param b: bytes to add
|
:param str b: bytes to add
|
||||||
@type b: str
|
|
||||||
"""
|
"""
|
||||||
self.packet.write(b)
|
self.packet.write(b)
|
||||||
return self
|
return self
|
||||||
|
@ -198,8 +173,7 @@ class Message (object):
|
||||||
"""
|
"""
|
||||||
Write a single byte to the stream, without any formatting.
|
Write a single byte to the stream, without any formatting.
|
||||||
|
|
||||||
@param b: byte to add
|
:param str b: byte to add
|
||||||
@type b: str
|
|
||||||
"""
|
"""
|
||||||
self.packet.write(b)
|
self.packet.write(b)
|
||||||
return self
|
return self
|
||||||
|
@ -208,8 +182,7 @@ class Message (object):
|
||||||
"""
|
"""
|
||||||
Add a boolean value to the stream.
|
Add a boolean value to the stream.
|
||||||
|
|
||||||
@param b: boolean value to add
|
:param bool b: boolean value to add
|
||||||
@type b: bool
|
|
||||||
"""
|
"""
|
||||||
if b:
|
if b:
|
||||||
self.add_byte('\x01')
|
self.add_byte('\x01')
|
||||||
|
@ -221,8 +194,7 @@ class Message (object):
|
||||||
"""
|
"""
|
||||||
Add an integer to the stream.
|
Add an integer to the stream.
|
||||||
|
|
||||||
@param n: integer to add
|
:param int n: integer to add
|
||||||
@type n: int
|
|
||||||
"""
|
"""
|
||||||
self.packet.write(struct.pack('>I', n))
|
self.packet.write(struct.pack('>I', n))
|
||||||
return self
|
return self
|
||||||
|
@ -231,8 +203,7 @@ class Message (object):
|
||||||
"""
|
"""
|
||||||
Add a 64-bit int to the stream.
|
Add a 64-bit int to the stream.
|
||||||
|
|
||||||
@param n: long int to add
|
:param long n: long int to add
|
||||||
@type n: long
|
|
||||||
"""
|
"""
|
||||||
self.packet.write(struct.pack('>Q', n))
|
self.packet.write(struct.pack('>Q', n))
|
||||||
return self
|
return self
|
||||||
|
@ -242,8 +213,7 @@ class Message (object):
|
||||||
Add a long int to the stream, encoded as an infinite-precision
|
Add a long int to the stream, encoded as an infinite-precision
|
||||||
integer. This method only works on positive numbers.
|
integer. This method only works on positive numbers.
|
||||||
|
|
||||||
@param z: long int to add
|
:param long z: long int to add
|
||||||
@type z: long
|
|
||||||
"""
|
"""
|
||||||
self.add_string(util.deflate_long(z))
|
self.add_string(util.deflate_long(z))
|
||||||
return self
|
return self
|
||||||
|
@ -252,8 +222,7 @@ class Message (object):
|
||||||
"""
|
"""
|
||||||
Add a string to the stream.
|
Add a string to the stream.
|
||||||
|
|
||||||
@param s: string to add
|
:param str s: string to add
|
||||||
@type s: str
|
|
||||||
"""
|
"""
|
||||||
self.add_int(len(s))
|
self.add_int(len(s))
|
||||||
self.packet.write(s)
|
self.packet.write(s)
|
||||||
|
@ -265,8 +234,7 @@ class Message (object):
|
||||||
a single string of values separated by commas. (Yes, really, that's
|
a single string of values separated by commas. (Yes, really, that's
|
||||||
how SSH2 does it.)
|
how SSH2 does it.)
|
||||||
|
|
||||||
@param l: list of strings to add
|
:param list l: list of strings to add
|
||||||
@type l: list(str)
|
|
||||||
"""
|
"""
|
||||||
self.add_string(','.join(l))
|
self.add_string(','.join(l))
|
||||||
return self
|
return self
|
||||||
|
@ -293,10 +261,10 @@ class Message (object):
|
||||||
Add a sequence of items to the stream. The values are encoded based
|
Add a sequence of items to the stream. The values are encoded based
|
||||||
on their type: str, int, bool, list, or long.
|
on their type: str, int, bool, list, or long.
|
||||||
|
|
||||||
@param seq: the sequence of items
|
.. warning::
|
||||||
@type seq: sequence
|
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:
|
for item in seq:
|
||||||
self._add(item)
|
self._add(item)
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Packetizer.
|
Packet handling
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import errno
|
import errno
|
||||||
|
@ -103,7 +103,7 @@ class Packetizer (object):
|
||||||
|
|
||||||
def set_log(self, log):
|
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
|
self.__logger = log
|
||||||
|
|
||||||
|
@ -167,17 +167,15 @@ class Packetizer (object):
|
||||||
|
|
||||||
def need_rekey(self):
|
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
|
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.
|
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
|
return self.__need_rekey
|
||||||
|
|
||||||
def set_keepalive(self, interval, callback):
|
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
|
no data read from or written to the socket, the callback will be
|
||||||
executed and the timer will be reset.
|
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.
|
Read as close to N bytes as possible, blocking as long as necessary.
|
||||||
|
|
||||||
@param n: number of bytes to read
|
:param int n: number of bytes to read
|
||||||
@type n: int
|
:return: the data read, as a `str`
|
||||||
@return: the data read
|
|
||||||
@rtype: str
|
:raises EOFError:
|
||||||
@raise EOFError: if the socket was closed before all the bytes could
|
if the socket was closed before all the bytes could be read
|
||||||
be read
|
|
||||||
"""
|
"""
|
||||||
out = ''
|
out = ''
|
||||||
# handle over-reading from reading the banner line
|
# 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
|
Only one thread should ever be in this function (no other locking is
|
||||||
done).
|
done).
|
||||||
|
|
||||||
@raise SSHException: if the packet is mangled
|
:raises SSHException: if the packet is mangled
|
||||||
@raise NeedRekeyException: if the transport should rekey
|
:raises NeedRekeyException: if the transport should rekey
|
||||||
"""
|
"""
|
||||||
header = self.read_all(self.__block_size_in, check_rekey=True)
|
header = self.read_all(self.__block_size_in, check_rekey=True)
|
||||||
if self.__block_engine_in != None:
|
if self.__block_engine_in != None:
|
||||||
|
|
|
@ -17,11 +17,12 @@
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
# 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().
|
Abstraction of a one-way pipe where the read end can be used in
|
||||||
Normally this is trivial, but Windows makes it nearly impossible.
|
`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
|
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
|
import sys
|
||||||
|
|
188
paramiko/pkey.py
188
paramiko/pkey.py
|
@ -47,30 +47,26 @@ class PKey (object):
|
||||||
|
|
||||||
def __init__(self, msg=None, data=None):
|
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
|
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.
|
the string.
|
||||||
|
|
||||||
@param msg: an optional SSH L{Message} containing a public key of this
|
:param .Message msg:
|
||||||
type.
|
an optional SSH `.Message` containing a public key of this type.
|
||||||
@type msg: L{Message}
|
:param str data: an optional string containing a public key of this type
|
||||||
@param data: an optional string containing a public key of this type
|
|
||||||
@type data: str
|
|
||||||
|
|
||||||
@raise SSHException: if a key cannot be created from the C{data} or
|
:raises SSHException:
|
||||||
C{msg} given, or no key was passed in.
|
if a key cannot be created from the ``data`` or ``msg`` given, or
|
||||||
|
no key was passed in.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""
|
"""
|
||||||
Return a string of an SSH L{Message} made up of the public part(s) of
|
Return a string of an SSH `.Message` made up of the public part(s) of
|
||||||
this key. This string is suitable for passing to L{__init__} to
|
this key. This string is suitable for passing to `__init__` to
|
||||||
re-create the key object later.
|
re-create the key object later.
|
||||||
|
|
||||||
@return: string representation of an SSH key message.
|
|
||||||
@rtype: str
|
|
||||||
"""
|
"""
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
@ -81,10 +77,7 @@ class PKey (object):
|
||||||
of the key are compared, so a public key will compare equal to its
|
of the key are compared, so a public key will compare equal to its
|
||||||
corresponding private key.
|
corresponding private key.
|
||||||
|
|
||||||
@param other: key to compare to.
|
:param .Pkey other: key to compare to.
|
||||||
@type other: L{PKey}
|
|
||||||
@return: 0 if the two keys are equivalent, non-0 otherwise.
|
|
||||||
@rtype: int
|
|
||||||
"""
|
"""
|
||||||
hs = hash(self)
|
hs = hash(self)
|
||||||
ho = hash(other)
|
ho = hash(other)
|
||||||
|
@ -96,9 +89,9 @@ class PKey (object):
|
||||||
"""
|
"""
|
||||||
Return the name of this private key implementation.
|
Return the name of this private key implementation.
|
||||||
|
|
||||||
@return: name of this private key type, in SSH terminology (for
|
:return:
|
||||||
example, C{"ssh-rsa"}).
|
name of this private key type, in SSH terminology, as a `str` (for
|
||||||
@rtype: str
|
example, ``"ssh-rsa"``).
|
||||||
"""
|
"""
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
@ -107,18 +100,14 @@ class PKey (object):
|
||||||
Return the number of significant bits in this key. This is useful
|
Return the number of significant bits in this key. This is useful
|
||||||
for judging the relative security of a key.
|
for judging the relative security of a key.
|
||||||
|
|
||||||
@return: bits in the key.
|
:return: bits in the key (as an `int`)
|
||||||
@rtype: int
|
|
||||||
"""
|
"""
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def can_sign(self):
|
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.
|
data.
|
||||||
|
|
||||||
@return: C{True} if this is a private key.
|
|
||||||
@rtype: bool
|
|
||||||
"""
|
"""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -127,9 +116,9 @@ class PKey (object):
|
||||||
Return an MD5 fingerprint of the public part of this key. Nothing
|
Return an MD5 fingerprint of the public part of this key. Nothing
|
||||||
secret is revealed.
|
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.
|
format.
|
||||||
@rtype: str
|
|
||||||
"""
|
"""
|
||||||
return MD5.new(str(self)).digest()
|
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
|
secret is revealed. This format is compatible with that used to store
|
||||||
public key files or recognized host keys.
|
public key files or recognized host keys.
|
||||||
|
|
||||||
@return: a base64 string containing the public part of the key.
|
:return: a base64 `string <str>` containing the public part of the key.
|
||||||
@rtype: str
|
|
||||||
"""
|
"""
|
||||||
return base64.encodestring(str(self)).replace('\n', '')
|
return base64.encodestring(str(self)).replace('\n', '')
|
||||||
|
|
||||||
def sign_ssh_data(self, rng, data):
|
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.
|
representing an SSH signature message.
|
||||||
|
|
||||||
@param rng: a secure random number generator.
|
:param .Crypto.Util.rng.RandomPool rng: a secure random number generator.
|
||||||
@type rng: L{Crypto.Util.rng.RandomPool}
|
:param str data: the data to sign.
|
||||||
@param data: the data to sign.
|
:return: an SSH signature `message <.Message>`.
|
||||||
@type data: str
|
|
||||||
@return: an SSH signature message.
|
|
||||||
@rtype: L{Message}
|
|
||||||
"""
|
"""
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
@ -163,37 +148,31 @@ class PKey (object):
|
||||||
Given a blob of data, and an SSH message representing a signature of
|
Given a blob of data, and an SSH message representing a signature of
|
||||||
that data, verify that it was signed with this key.
|
that data, verify that it was signed with this key.
|
||||||
|
|
||||||
@param data: the data that was signed.
|
:param str data: the data that was signed.
|
||||||
@type data: str
|
:param .Message msg: an SSH signature message
|
||||||
@param msg: an SSH signature message
|
:return:
|
||||||
@type msg: L{Message}
|
``True`` if the signature verifies correctly; ``False`` otherwise.
|
||||||
@return: C{True} if the signature verifies correctly; C{False}
|
|
||||||
otherwise.
|
|
||||||
@rtype: boolean
|
|
||||||
"""
|
"""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def from_private_key_file(cls, filename, password=None):
|
def from_private_key_file(cls, filename, password=None):
|
||||||
"""
|
"""
|
||||||
Create a key object by reading a private key file. If the private
|
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
|
key is encrypted and ``password`` is not ``None``, the given password
|
||||||
will be used to decrypt the key (otherwise L{PasswordRequiredException}
|
will be used to decrypt the key (otherwise `.PasswordRequiredException`
|
||||||
is thrown). Through the magic of python, this factory method will
|
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
|
exist in all subclasses of PKey (such as `.RSAKey` or `.DSSKey`), but
|
||||||
is useless on the abstract PKey class.
|
is useless on the abstract PKey class.
|
||||||
|
|
||||||
@param filename: name of the file to read
|
:param str filename: name of the file to read
|
||||||
@type filename: str
|
:param str password: an optional password to use to decrypt the key file,
|
||||||
@param password: an optional password to use to decrypt the key file,
|
|
||||||
if it's encrypted
|
if it's encrypted
|
||||||
@type password: str
|
:return: a new `.PKey` based on the given private key
|
||||||
@return: a new key object based on the given private key
|
|
||||||
@rtype: L{PKey}
|
|
||||||
|
|
||||||
@raise IOError: if there was an error reading the file
|
:raises IOError: if there was an error reading the file
|
||||||
@raise PasswordRequiredException: if the private key file is
|
:raises PasswordRequiredException: if the private key file is
|
||||||
encrypted, and C{password} is C{None}
|
encrypted, and ``password`` is ``None``
|
||||||
@raise SSHException: if the key file is invalid
|
:raises SSHException: if the key file is invalid
|
||||||
"""
|
"""
|
||||||
key = cls(filename=filename, password=password)
|
key = cls(filename=filename, password=password)
|
||||||
return key
|
return key
|
||||||
|
@ -202,22 +181,19 @@ class PKey (object):
|
||||||
def from_private_key(cls, file_obj, password=None):
|
def from_private_key(cls, file_obj, password=None):
|
||||||
"""
|
"""
|
||||||
Create a key object by reading a private key from a file (or file-like)
|
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
|
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
|
:param file file_obj: the file to read from
|
||||||
@type file_obj: file
|
:param str password:
|
||||||
@param password: an optional password to use to decrypt the key, if it's
|
an optional password to use to decrypt the key, if it's encrypted
|
||||||
encrypted
|
:return: a new `.PKey` based on the given private key
|
||||||
@type password: str
|
|
||||||
@return: a new key object based on the given private key
|
|
||||||
@rtype: L{PKey}
|
|
||||||
|
|
||||||
@raise IOError: if there was an error reading the key
|
:raises IOError: if there was an error reading the key
|
||||||
@raise PasswordRequiredException: if the private key file is encrypted,
|
:raises PasswordRequiredException: if the private key file is encrypted,
|
||||||
and C{password} is C{None}
|
and ``password`` is ``None``
|
||||||
@raise SSHException: if the key file is invalid
|
:raises SSHException: if the key file is invalid
|
||||||
"""
|
"""
|
||||||
key = cls(file_obj=file_obj, password=password)
|
key = cls(file_obj=file_obj, password=password)
|
||||||
return key
|
return key
|
||||||
|
@ -226,55 +202,49 @@ class PKey (object):
|
||||||
def write_private_key_file(self, filename, password=None):
|
def write_private_key_file(self, filename, password=None):
|
||||||
"""
|
"""
|
||||||
Write private key contents into a file. If the password is not
|
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
|
:param str filename: name of the file to write
|
||||||
@type filename: str
|
:param str password:
|
||||||
@param password: an optional password to use to encrypt the key file
|
an optional password to use to encrypt the key file
|
||||||
@type password: str
|
|
||||||
|
|
||||||
@raise IOError: if there was an error writing the file
|
:raises IOError: if there was an error writing the file
|
||||||
@raise SSHException: if the key is invalid
|
:raises SSHException: if the key is invalid
|
||||||
"""
|
"""
|
||||||
raise Exception('Not implemented in PKey')
|
raise Exception('Not implemented in PKey')
|
||||||
|
|
||||||
def write_private_key(self, file_obj, password=None):
|
def write_private_key(self, file_obj, password=None):
|
||||||
"""
|
"""
|
||||||
Write private key contents into a file (or file-like) object. If the
|
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
|
:param file file_obj: the file object to write into
|
||||||
@type file_obj: file
|
:param str password: an optional password to use to encrypt the key
|
||||||
@param password: an optional password to use to encrypt the key
|
|
||||||
@type password: str
|
|
||||||
|
|
||||||
@raise IOError: if there was an error writing to the file
|
:raises IOError: if there was an error writing to the file
|
||||||
@raise SSHException: if the key is invalid
|
:raises SSHException: if the key is invalid
|
||||||
"""
|
"""
|
||||||
raise Exception('Not implemented in PKey')
|
raise Exception('Not implemented in PKey')
|
||||||
|
|
||||||
def _read_private_key_file(self, tag, filename, password=None):
|
def _read_private_key_file(self, tag, filename, password=None):
|
||||||
"""
|
"""
|
||||||
Read an SSH2-format private key file, looking for a string of the type
|
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
|
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
|
``password`` is not ``None``, the given password will be used to decrypt
|
||||||
the key (otherwise L{PasswordRequiredException} is thrown).
|
the key (otherwise `.PasswordRequiredException` is thrown).
|
||||||
|
|
||||||
@param tag: C{"RSA"} or C{"DSA"}, the tag used to mark the data block.
|
:param str tag: ``"RSA"`` or ``"DSA"``, the tag used to mark the data block.
|
||||||
@type tag: str
|
:param str filename: name of the file to read.
|
||||||
@param filename: name of the file to read.
|
:param str password:
|
||||||
@type filename: str
|
an optional password to use to decrypt the key file, if it's
|
||||||
@param password: an optional password to use to decrypt the key file,
|
encrypted.
|
||||||
if it's encrypted.
|
:return: data blob (`str`) that makes up the private key.
|
||||||
@type password: str
|
|
||||||
@return: data blob that makes up the private key.
|
|
||||||
@rtype: str
|
|
||||||
|
|
||||||
@raise IOError: if there was an error reading the file.
|
:raises IOError: if there was an error reading the file.
|
||||||
@raise PasswordRequiredException: if the private key file is
|
:raises PasswordRequiredException: if the private key file is
|
||||||
encrypted, and C{password} is C{None}.
|
encrypted, and ``password`` is ``None``.
|
||||||
@raise SSHException: if the key file is invalid.
|
:raises SSHException: if the key file is invalid.
|
||||||
"""
|
"""
|
||||||
f = open(filename, 'r')
|
f = open(filename, 'r')
|
||||||
data = self._read_private_key(tag, f, password)
|
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 trivially-encoded format (base64) which is completely insecure. If
|
||||||
a password is given, DES-EDE3-CBC is used.
|
a password is given, DES-EDE3-CBC is used.
|
||||||
|
|
||||||
@param tag: C{"RSA"} or C{"DSA"}, the tag used to mark the data block.
|
:param str tag: ``"RSA"`` or ``"DSA"``, the tag used to mark the data block.
|
||||||
@type tag: str
|
:param file filename: name of the file to write.
|
||||||
@param filename: name of the file to write.
|
:param str data: data blob that makes up the private key.
|
||||||
@type filename: str
|
:param str password: an optional password to use to encrypt the file.
|
||||||
@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
|
|
||||||
|
|
||||||
@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)
|
f = open(filename, 'w', 0600)
|
||||||
# grrr... the mode doesn't always take hold
|
# grrr... the mode doesn't always take hold
|
||||||
|
|
|
@ -109,7 +109,7 @@ class ModulusPack (object):
|
||||||
|
|
||||||
def read_file(self, filename):
|
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 = {}
|
self.pack = {}
|
||||||
f = open(filename, 'r')
|
f = open(filename, 'r')
|
||||||
|
|
|
@ -16,9 +16,6 @@
|
||||||
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
"""
|
|
||||||
L{ProxyCommand}.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import os
|
import os
|
||||||
|
@ -36,18 +33,17 @@ class ProxyCommand(object):
|
||||||
Wraps a subprocess running ProxyCommand-driven programs.
|
Wraps a subprocess running ProxyCommand-driven programs.
|
||||||
|
|
||||||
This class implements a the socket-like interface needed by the
|
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
|
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.
|
proxy traffic between the client and a server hosted in another machine.
|
||||||
"""
|
"""
|
||||||
def __init__(self, command_line):
|
def __init__(self, command_line):
|
||||||
"""
|
"""
|
||||||
Create a new CommandProxy instance. The instance created by this
|
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
|
:param str command_line:
|
||||||
used as the proxy.
|
the command that should be executed and used as the proxy.
|
||||||
@type command_line: str
|
|
||||||
"""
|
"""
|
||||||
self.cmd = shlsplit(command_line)
|
self.cmd = shlsplit(command_line)
|
||||||
self.process = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
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
|
Write the content received from the SSH client to the standard
|
||||||
input of the forked command.
|
input of the forked command.
|
||||||
|
|
||||||
@param content: string to be sent to the forked command
|
:param str content: string to be sent to the forked command
|
||||||
@type content: str
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.process.stdin.write(content)
|
self.process.stdin.write(content)
|
||||||
|
@ -76,11 +71,9 @@ class ProxyCommand(object):
|
||||||
"""
|
"""
|
||||||
Read from the standard output of the forked program.
|
Read from the standard output of the forked program.
|
||||||
|
|
||||||
@param size: how many chars should be read
|
:param int size: how many chars should be read
|
||||||
@type size: int
|
|
||||||
|
|
||||||
@return: the length of the read content
|
:return: the length of the read content, as an `int`
|
||||||
@rtype: int
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
start = datetime.now()
|
start = datetime.now()
|
||||||
|
|
|
@ -28,13 +28,13 @@ class ResourceManager (object):
|
||||||
A registry of objects and resources that should be closed when those
|
A registry of objects and resources that should be closed when those
|
||||||
objects are deleted.
|
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
|
which can cause reference cycles to never be collected. Objects registered
|
||||||
with the ResourceManager can be collected but still free resources when
|
with the ResourceManager can be collected but still free resources when
|
||||||
they die.
|
they die.
|
||||||
|
|
||||||
Resources are registered using L{register}, and when an object is garbage
|
Resources are registered using `register`, and when an object is garbage
|
||||||
collected, each registered resource is closed by having its C{close()}
|
collected, each registered resource is closed by having its ``close()``
|
||||||
method called. Multiple resources may be registered per object, but a
|
method called. Multiple resources may be registered per object, but a
|
||||||
resource will only be closed once, even if multiple objects register it.
|
resource will only be closed once, even if multiple objects register it.
|
||||||
(The last object to register it wins.)
|
(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.
|
Register a resource to be closed with an object is collected.
|
||||||
|
|
||||||
When the given C{obj} is garbage-collected by the python interpreter,
|
When the given ``obj`` is garbage-collected by the Python interpreter,
|
||||||
the C{resource} will be closed by having its C{close()} method called.
|
the ``resource`` will be closed by having its ``close()`` method called.
|
||||||
Any exceptions are ignored.
|
Any exceptions are ignored.
|
||||||
|
|
||||||
@param obj: the object to track
|
:param object obj: the object to track
|
||||||
@type obj: object
|
:param object resource:
|
||||||
@param resource: the resource to close when the object is collected
|
the resource to close when the object is collected
|
||||||
@type resource: object
|
|
||||||
"""
|
"""
|
||||||
def callback(ref):
|
def callback(ref):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
L{RSAKey}
|
RSA keys.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from Crypto.PublicKey import RSA
|
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 private RSA key. This factory function can be used to
|
||||||
generate a new host key or authentication key.
|
generate a new host key or authentication key.
|
||||||
|
|
||||||
@param bits: number of bits the generated key should be.
|
:param int bits: number of bits the generated key should be.
|
||||||
@type bits: int
|
:param function progress_func:
|
||||||
@param progress_func: an optional function to call at key points in
|
an optional function to call at key points in key generation (used
|
||||||
key generation (used by C{pyCrypto.PublicKey}).
|
by ``pyCrypto.PublicKey``).
|
||||||
@type progress_func: function
|
:return: new `.RSAKey` private key
|
||||||
@return: new private key
|
|
||||||
@rtype: L{RSAKey}
|
|
||||||
"""
|
"""
|
||||||
rsa = RSA.generate(bits, rng.read, progress_func)
|
rsa = RSA.generate(bits, rng.read, progress_func)
|
||||||
key = RSAKey(vals=(rsa.e, rsa.n))
|
key = RSAKey(vals=(rsa.e, rsa.n))
|
||||||
|
|
1015
paramiko/server.py
1015
paramiko/server.py
File diff suppressed because it is too large
Load Diff
|
@ -26,18 +26,19 @@ class SFTPAttributes (object):
|
||||||
"""
|
"""
|
||||||
Representation of the attributes of a file (or proxied file) for SFTP in
|
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
|
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,
|
`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:
|
with the same meanings as those returned by an `os.stat` object:
|
||||||
- st_size
|
|
||||||
- st_uid
|
- ``st_size``
|
||||||
- st_gid
|
- ``st_uid``
|
||||||
- st_mode
|
- ``st_gid``
|
||||||
- st_atime
|
- ``st_mode``
|
||||||
- st_mtime
|
- ``st_atime``
|
||||||
|
- ``st_mtime``
|
||||||
|
|
||||||
Because SFTP allows flags to have other arbitrary named attributes, these
|
Because SFTP allows flags to have other arbitrary named attributes, these
|
||||||
are stored in a dict named C{attr}. Occasionally, the filename is also
|
are stored in a dict named ``attr``. Occasionally, the filename is also
|
||||||
stored, in C{filename}.
|
stored, in ``filename``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
FLAG_SIZE = 1
|
FLAG_SIZE = 1
|
||||||
|
@ -61,15 +62,12 @@ class SFTPAttributes (object):
|
||||||
|
|
||||||
def from_stat(cls, obj, filename=None):
|
def from_stat(cls, obj, filename=None):
|
||||||
"""
|
"""
|
||||||
Create an SFTPAttributes object from an existing C{stat} object (an
|
Create an `.SFTPAttributes` object from an existing ``stat`` object (an
|
||||||
object returned by C{os.stat}).
|
object returned by `os.stat`).
|
||||||
|
|
||||||
@param obj: an object returned by C{os.stat} (or equivalent).
|
:param object obj: an object returned by `os.stat` (or equivalent).
|
||||||
@type obj: object
|
:param str filename: the filename associated with this file.
|
||||||
@param filename: the filename associated with this file.
|
:return: new `.SFTPAttributes` object with the same attribute fields.
|
||||||
@type filename: str
|
|
||||||
@return: new L{SFTPAttributes} object with the same attribute fields.
|
|
||||||
@rtype: L{SFTPAttributes}
|
|
||||||
"""
|
"""
|
||||||
attr = cls()
|
attr = cls()
|
||||||
attr.st_size = obj.st_size
|
attr.st_size = obj.st_size
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
|
# 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
|
# 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
|
# 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.,
|
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
"""
|
|
||||||
Client-mode SFTP support.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from binascii import hexlify
|
from binascii import hexlify
|
||||||
import errno
|
import errno
|
||||||
|
@ -49,24 +46,24 @@ def _to_unicode(s):
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
class SFTPClient (BaseSFTP):
|
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.
|
|
||||||
"""
|
"""
|
||||||
|
SFTP client object.
|
||||||
|
|
||||||
|
Used to open an SFTP session across an open SSH `.Transport` and perform
|
||||||
|
remote file operations.
|
||||||
|
"""
|
||||||
def __init__(self, sock):
|
def __init__(self, sock):
|
||||||
"""
|
"""
|
||||||
Create an SFTP client from an existing L{Channel}. The channel
|
Create an SFTP client from an existing `.Channel`. The channel
|
||||||
should already have requested the C{"sftp"} subsystem.
|
should already have requested the ``"sftp"`` subsystem.
|
||||||
|
|
||||||
An alternate way to create an SFTP client context is by using
|
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
|
:param .Channel sock: an open `.Channel` using the ``"sftp"`` subsystem
|
||||||
@type sock: L{Channel}
|
|
||||||
|
|
||||||
@raise SSHException: if there's an exception while negotiating
|
:raises SSHException: if there's an exception while negotiating
|
||||||
sftp
|
sftp
|
||||||
"""
|
"""
|
||||||
BaseSFTP.__init__(self)
|
BaseSFTP.__init__(self)
|
||||||
|
@ -91,13 +88,12 @@ class SFTPClient (BaseSFTP):
|
||||||
|
|
||||||
def from_transport(cls, t):
|
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
|
:param .Transport t: an open `.Transport` which is already authenticated
|
||||||
@type t: L{Transport}
|
:return:
|
||||||
@return: a new L{SFTPClient} object, referring to an sftp session
|
a new `.SFTPClient` object, referring to an sftp session (channel)
|
||||||
(channel) across the transport
|
across the transport
|
||||||
@rtype: L{SFTPClient}
|
|
||||||
"""
|
"""
|
||||||
chan = t.open_session()
|
chan = t.open_session()
|
||||||
if chan is None:
|
if chan is None:
|
||||||
|
@ -117,56 +113,49 @@ class SFTPClient (BaseSFTP):
|
||||||
"""
|
"""
|
||||||
Close the SFTP session and its underlying channel.
|
Close the SFTP session and its underlying channel.
|
||||||
|
|
||||||
@since: 1.4
|
.. versionadded:: 1.4
|
||||||
"""
|
"""
|
||||||
self._log(INFO, 'sftp session closed.')
|
self._log(INFO, 'sftp session closed.')
|
||||||
self.sock.close()
|
self.sock.close()
|
||||||
|
|
||||||
def get_channel(self):
|
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.
|
might be useful for doing things like setting a timeout on the channel.
|
||||||
|
|
||||||
@return: the SSH channel
|
.. versionadded:: 1.7.1
|
||||||
@rtype: L{Channel}
|
|
||||||
|
|
||||||
@since: 1.7.1
|
|
||||||
"""
|
"""
|
||||||
return self.sock
|
return self.sock
|
||||||
|
|
||||||
def listdir(self, path='.'):
|
def listdir(self, path='.'):
|
||||||
"""
|
"""
|
||||||
Return a list containing the names of the entries in the given C{path}.
|
Return a list containing the names of the entries in the given ``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}.
|
|
||||||
|
|
||||||
@param path: path to list (defaults to C{'.'})
|
The list is in arbitrary order. It does not include the special
|
||||||
@type path: str
|
entries ``'.'`` and ``'..'`` even if they are present in the folder.
|
||||||
@return: list of filenames
|
This method is meant to mirror ``os.listdir`` as closely as possible.
|
||||||
@rtype: list of str
|
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)]
|
return [f.filename for f in self.listdir_attr(path)]
|
||||||
|
|
||||||
def listdir_attr(self, path='.'):
|
def listdir_attr(self, path='.'):
|
||||||
"""
|
"""
|
||||||
Return a list containing L{SFTPAttributes} objects corresponding to
|
Return a list containing `.SFTPAttributes` objects corresponding to
|
||||||
files in the given C{path}. The list is in arbitrary order. It does
|
files in the given ``path``. The list is in arbitrary order. It does
|
||||||
not include the special entries C{'.'} and C{'..'} even if they are
|
not include the special entries ``'.'`` and ``'..'`` even if they are
|
||||||
present in the folder.
|
present in the folder.
|
||||||
|
|
||||||
The returned L{SFTPAttributes} objects will each have an additional
|
The returned `.SFTPAttributes` objects will each have an additional
|
||||||
field: C{longname}, which may contain a formatted string of the file's
|
field: ``longname``, which may contain a formatted string of the file's
|
||||||
attributes, in unix format. The content of this string will probably
|
attributes, in unix format. The content of this string will probably
|
||||||
depend on the SFTP server implementation.
|
depend on the SFTP server implementation.
|
||||||
|
|
||||||
@param path: path to list (defaults to C{'.'})
|
:param str path: path to list (defaults to ``'.'``)
|
||||||
@type path: str
|
:return: list of `.SFTPAttributes` objects
|
||||||
@return: list of attributes
|
|
||||||
@rtype: list of L{SFTPAttributes}
|
|
||||||
|
|
||||||
@since: 1.2
|
.. versionadded:: 1.2
|
||||||
"""
|
"""
|
||||||
path = self._adjust_cwd(path)
|
path = self._adjust_cwd(path)
|
||||||
self._log(DEBUG, 'listdir(%r)' % path)
|
self._log(DEBUG, 'listdir(%r)' % path)
|
||||||
|
@ -196,37 +185,34 @@ class SFTPClient (BaseSFTP):
|
||||||
def open(self, filename, mode='r', bufsize=-1):
|
def open(self, filename, mode='r', bufsize=-1):
|
||||||
"""
|
"""
|
||||||
Open a file on the remote server. The arguments are the same as for
|
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
|
Python's built-in `python:file` (aka `python:open`). A file-like
|
||||||
returned, which closely mimics the behavior of a normal python file
|
object is returned, which closely mimics the behavior of a normal
|
||||||
object, including the ability to be used as a context manager.
|
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,
|
The mode indicates how the file is to be opened: ``'r'`` for reading,
|
||||||
C{'w'} for writing (truncating an existing file), C{'a'} for appending,
|
``'w'`` for writing (truncating an existing file), ``'a'`` for
|
||||||
C{'r+'} for reading/writing, C{'w+'} for reading/writing (truncating an
|
appending, ``'r+'`` for reading/writing, ``'w+'`` for reading/writing
|
||||||
existing file), C{'a+'} for reading/appending. The python C{'b'} flag
|
(truncating an existing file), ``'a+'`` for reading/appending. The
|
||||||
is ignored, since SSH treats all files as binary. The C{'U'} flag is
|
Python ``'b'`` flag is ignored, since SSH treats all files as binary.
|
||||||
supported in a compatible way.
|
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
|
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
|
no direct mapping to Python's file flags, but is commonly known as the
|
||||||
C{O_EXCL} flag in posix.
|
``O_EXCL`` flag in posix.
|
||||||
|
|
||||||
The file will be buffered in standard python style by default, but
|
The file will be buffered in standard Python style by default, but
|
||||||
can be altered with the C{bufsize} parameter. C{0} turns off
|
can be altered with the ``bufsize`` parameter. ``0`` turns off
|
||||||
buffering, C{1} uses line buffering, and any number greater than 1
|
buffering, ``1`` uses line buffering, and any number greater than 1
|
||||||
(C{>1}) uses that specific buffer size.
|
(``>1``) uses that specific buffer size.
|
||||||
|
|
||||||
@param filename: name of the file to open
|
:param str filename: name of the file to open
|
||||||
@type filename: str
|
:param str mode: mode (Python-style) to open in
|
||||||
@param mode: mode (python-style) to open in
|
:param int bufsize: desired buffering (-1 = default buffer size)
|
||||||
@type mode: str
|
:return: an `.SFTPFile` object representing the open file
|
||||||
@param bufsize: desired buffering (-1 = default buffer size)
|
|
||||||
@type bufsize: int
|
|
||||||
@return: a file object representing the open file
|
|
||||||
@rtype: SFTPFile
|
|
||||||
|
|
||||||
@raise IOError: if the file could not be opened.
|
:raises IOError: if the file could not be opened.
|
||||||
"""
|
"""
|
||||||
filename = self._adjust_cwd(filename)
|
filename = self._adjust_cwd(filename)
|
||||||
self._log(DEBUG, 'open(%r, %r)' % (filename, mode))
|
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)))
|
self._log(DEBUG, 'open(%r, %r) -> %s' % (filename, mode, hexlify(handle)))
|
||||||
return SFTPFile(self, handle, mode, bufsize)
|
return SFTPFile(self, handle, mode, bufsize)
|
||||||
|
|
||||||
# python continues to vacillate about "open" vs "file"...
|
# Python continues to vacillate about "open" vs "file"...
|
||||||
file = open
|
file = open
|
||||||
|
|
||||||
def remove(self, path):
|
def remove(self, path):
|
||||||
"""
|
"""
|
||||||
Remove the file at the given path. This only works on files; for
|
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
|
:param str path: path (absolute or relative) of the file to remove
|
||||||
@type path: str
|
|
||||||
|
|
||||||
@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)
|
path = self._adjust_cwd(path)
|
||||||
self._log(DEBUG, 'remove(%r)' % path)
|
self._log(DEBUG, 'remove(%r)' % path)
|
||||||
|
@ -270,14 +255,12 @@ class SFTPClient (BaseSFTP):
|
||||||
|
|
||||||
def rename(self, oldpath, newpath):
|
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
|
:param str oldpath: existing name of the file or folder
|
||||||
@type oldpath: str
|
:param str newpath: new name for the file or folder
|
||||||
@param newpath: new name for the file or folder
|
|
||||||
@type newpath: str
|
|
||||||
|
|
||||||
@raise IOError: if C{newpath} is a folder, or something else goes
|
:raises IOError: if ``newpath`` is a folder, or something else goes
|
||||||
wrong
|
wrong
|
||||||
"""
|
"""
|
||||||
oldpath = self._adjust_cwd(oldpath)
|
oldpath = self._adjust_cwd(oldpath)
|
||||||
|
@ -287,14 +270,12 @@ class SFTPClient (BaseSFTP):
|
||||||
|
|
||||||
def mkdir(self, path, mode=0777):
|
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.
|
The default mode is 0777 (octal). On some systems, mode is ignored.
|
||||||
Where it is used, the current umask value is first masked out.
|
Where it is used, the current umask value is first masked out.
|
||||||
|
|
||||||
@param path: name of the folder to create
|
:param str path: name of the folder to create
|
||||||
@type path: str
|
:param int mode: permissions (posix-style) for the newly-created folder
|
||||||
@param mode: permissions (posix-style) for the newly-created folder
|
|
||||||
@type mode: int
|
|
||||||
"""
|
"""
|
||||||
path = self._adjust_cwd(path)
|
path = self._adjust_cwd(path)
|
||||||
self._log(DEBUG, 'mkdir(%r, %r)' % (path, mode))
|
self._log(DEBUG, 'mkdir(%r, %r)' % (path, mode))
|
||||||
|
@ -304,10 +285,9 @@ class SFTPClient (BaseSFTP):
|
||||||
|
|
||||||
def rmdir(self, path):
|
def rmdir(self, path):
|
||||||
"""
|
"""
|
||||||
Remove the folder named C{path}.
|
Remove the folder named ``path``.
|
||||||
|
|
||||||
@param path: name of the folder to remove
|
:param str path: name of the folder to remove
|
||||||
@type path: str
|
|
||||||
"""
|
"""
|
||||||
path = self._adjust_cwd(path)
|
path = self._adjust_cwd(path)
|
||||||
self._log(DEBUG, 'rmdir(%r)' % 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
|
Retrieve information about a file on the remote system. The return
|
||||||
value is an object whose attributes correspond to the attributes of
|
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
|
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.
|
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
|
Unlike a Python `python:stat` object, the result may not be accessed as
|
||||||
tuple. This is mostly due to the author's slack factor.
|
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},
|
The fields supported are: ``st_mode``, ``st_size``, ``st_uid``,
|
||||||
C{st_atime}, and C{st_mtime}.
|
``st_gid``, ``st_atime``, and ``st_mtime``.
|
||||||
|
|
||||||
@param path: the filename to stat
|
:param str path: the filename to stat
|
||||||
@type path: str
|
:return:
|
||||||
@return: an object containing attributes about the given file
|
an `.SFTPAttributes` object containing attributes about the given
|
||||||
@rtype: SFTPAttributes
|
file
|
||||||
"""
|
"""
|
||||||
path = self._adjust_cwd(path)
|
path = self._adjust_cwd(path)
|
||||||
self._log(DEBUG, 'stat(%r)' % path)
|
self._log(DEBUG, 'stat(%r)' % path)
|
||||||
|
@ -343,12 +323,12 @@ class SFTPClient (BaseSFTP):
|
||||||
"""
|
"""
|
||||||
Retrieve information about a file on the remote system, without
|
Retrieve information about a file on the remote system, without
|
||||||
following symbolic links (shortcuts). This otherwise behaves exactly
|
following symbolic links (shortcuts). This otherwise behaves exactly
|
||||||
the same as L{stat}.
|
the same as `stat`.
|
||||||
|
|
||||||
@param path: the filename to stat
|
:param str path: the filename to stat
|
||||||
@type path: str
|
:return:
|
||||||
@return: an object containing attributes about the given file
|
an `.SFTPAttributes` object containing attributes about the given
|
||||||
@rtype: SFTPAttributes
|
file
|
||||||
"""
|
"""
|
||||||
path = self._adjust_cwd(path)
|
path = self._adjust_cwd(path)
|
||||||
self._log(DEBUG, 'lstat(%r)' % path)
|
self._log(DEBUG, 'lstat(%r)' % path)
|
||||||
|
@ -359,13 +339,11 @@ class SFTPClient (BaseSFTP):
|
||||||
|
|
||||||
def symlink(self, source, dest):
|
def symlink(self, source, dest):
|
||||||
"""
|
"""
|
||||||
Create a symbolic link (shortcut) of the C{source} path at
|
Create a symbolic link (shortcut) of the ``source`` path at
|
||||||
C{destination}.
|
``destination``.
|
||||||
|
|
||||||
@param source: path of the original file
|
:param str source: path of the original file
|
||||||
@type source: str
|
:param str dest: path of the newly created symlink
|
||||||
@param dest: path of the newly created symlink
|
|
||||||
@type dest: str
|
|
||||||
"""
|
"""
|
||||||
dest = self._adjust_cwd(dest)
|
dest = self._adjust_cwd(dest)
|
||||||
self._log(DEBUG, 'symlink(%r, %r)' % (source, dest))
|
self._log(DEBUG, 'symlink(%r, %r)' % (source, dest))
|
||||||
|
@ -376,13 +354,11 @@ class SFTPClient (BaseSFTP):
|
||||||
def chmod(self, path, mode):
|
def chmod(self, path, mode):
|
||||||
"""
|
"""
|
||||||
Change the mode (permissions) of a file. The permissions are
|
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.
|
function.
|
||||||
|
|
||||||
@param path: path of the file to change the permissions of
|
:param str path: path of the file to change the permissions of
|
||||||
@type path: str
|
:param int mode: new permissions
|
||||||
@param mode: new permissions
|
|
||||||
@type mode: int
|
|
||||||
"""
|
"""
|
||||||
path = self._adjust_cwd(path)
|
path = self._adjust_cwd(path)
|
||||||
self._log(DEBUG, 'chmod(%r, %r)' % (path, mode))
|
self._log(DEBUG, 'chmod(%r, %r)' % (path, mode))
|
||||||
|
@ -392,17 +368,14 @@ class SFTPClient (BaseSFTP):
|
||||||
|
|
||||||
def chown(self, path, uid, gid):
|
def chown(self, path, uid, gid):
|
||||||
"""
|
"""
|
||||||
Change the owner (C{uid}) and group (C{gid}) of a file. As with
|
Change the owner (``uid``) and group (``gid``) of a file. As with
|
||||||
python's C{os.chown} function, you must pass both arguments, so if you
|
Python's `os.chown` function, you must pass both arguments, so if you
|
||||||
only want to change one, use L{stat} first to retrieve the current
|
only want to change one, use `stat` first to retrieve the current
|
||||||
owner and group.
|
owner and group.
|
||||||
|
|
||||||
@param path: path of the file to change the owner and group of
|
:param str path: path of the file to change the owner and group of
|
||||||
@type path: str
|
:param int uid: new owner's uid
|
||||||
@param uid: new owner's uid
|
:param int gid: new group id
|
||||||
@type uid: int
|
|
||||||
@param gid: new group id
|
|
||||||
@type gid: int
|
|
||||||
"""
|
"""
|
||||||
path = self._adjust_cwd(path)
|
path = self._adjust_cwd(path)
|
||||||
self._log(DEBUG, 'chown(%r, %r, %r)' % (path, uid, gid))
|
self._log(DEBUG, 'chown(%r, %r, %r)' % (path, uid, gid))
|
||||||
|
@ -412,18 +385,17 @@ class SFTPClient (BaseSFTP):
|
||||||
|
|
||||||
def utime(self, path, times):
|
def utime(self, path, times):
|
||||||
"""
|
"""
|
||||||
Set the access and modified times of the file specified by C{path}. If
|
Set the access and modified times of the file specified by ``path``. If
|
||||||
C{times} is C{None}, then the file's access and modified times are set
|
``times`` is ``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,
|
to the current time. Otherwise, ``times`` must be a 2-tuple of numbers,
|
||||||
of the form C{(atime, mtime)}, which is used to set the access and
|
of the form ``(atime, mtime)``, which is used to set the access and
|
||||||
modified times, respectively. This bizarre API is mimicked from python
|
modified times, respectively. This bizarre API is mimicked from Python
|
||||||
for the sake of consistency -- I apologize.
|
for the sake of consistency -- I apologize.
|
||||||
|
|
||||||
@param path: path of the file to modify
|
:param str path: path of the file to modify
|
||||||
@type path: str
|
:param tuple times:
|
||||||
@param times: C{None} or a tuple of (access time, modified time) in
|
``None`` or a tuple of (access time, modified time) in standard
|
||||||
standard internet epoch time (seconds since 01 January 1970 GMT)
|
internet epoch time (seconds since 01 January 1970 GMT)
|
||||||
@type times: tuple(int)
|
|
||||||
"""
|
"""
|
||||||
path = self._adjust_cwd(path)
|
path = self._adjust_cwd(path)
|
||||||
if times is None:
|
if times is None:
|
||||||
|
@ -435,14 +407,13 @@ class SFTPClient (BaseSFTP):
|
||||||
|
|
||||||
def truncate(self, path, size):
|
def truncate(self, path, size):
|
||||||
"""
|
"""
|
||||||
Change the size of the file specified by C{path}. This usually extends
|
Change the size of the file specified by ``path``. This usually
|
||||||
or shrinks the size of the file, just like the C{truncate()} method on
|
extends or shrinks the size of the file, just like the `~file.truncate`
|
||||||
python file objects.
|
method on Python file objects.
|
||||||
|
|
||||||
@param path: path of the file to modify
|
:param str path: path of the file to modify
|
||||||
@type path: str
|
:param size: the new size of the file
|
||||||
@param size: the new size of the file
|
:type size: int or long
|
||||||
@type size: int or long
|
|
||||||
"""
|
"""
|
||||||
path = self._adjust_cwd(path)
|
path = self._adjust_cwd(path)
|
||||||
self._log(DEBUG, 'truncate(%r, %r)' % (path, size))
|
self._log(DEBUG, 'truncate(%r, %r)' % (path, size))
|
||||||
|
@ -453,13 +424,11 @@ class SFTPClient (BaseSFTP):
|
||||||
def readlink(self, path):
|
def readlink(self, path):
|
||||||
"""
|
"""
|
||||||
Return the target of a symbolic link (shortcut). You can use
|
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.
|
relative pathname.
|
||||||
|
|
||||||
@param path: path of the symbolic link file
|
:param str path: path of the symbolic link file
|
||||||
@type path: str
|
:return: target path, as a `str`
|
||||||
@return: target path
|
|
||||||
@rtype: str
|
|
||||||
"""
|
"""
|
||||||
path = self._adjust_cwd(path)
|
path = self._adjust_cwd(path)
|
||||||
self._log(DEBUG, 'readlink(%r)' % 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
|
Return the normalized path (on the server) of a given path. This
|
||||||
can be used to quickly resolve symbolic links or determine what the
|
can be used to quickly resolve symbolic links or determine what the
|
||||||
server is considering to be the "current folder" (by passing C{'.'}
|
server is considering to be the "current folder" (by passing ``'.'``
|
||||||
as C{path}).
|
as ``path``).
|
||||||
|
|
||||||
@param path: path to be normalized
|
:param str path: path to be normalized
|
||||||
@type path: str
|
:return: normalized form of the given path (as a `str`)
|
||||||
@return: normalized form of the given path
|
|
||||||
@rtype: 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)
|
path = self._adjust_cwd(path)
|
||||||
self._log(DEBUG, 'normalize(%r)' % path)
|
self._log(DEBUG, 'normalize(%r)' % path)
|
||||||
|
@ -500,18 +467,17 @@ class SFTPClient (BaseSFTP):
|
||||||
def chdir(self, path):
|
def chdir(self, path):
|
||||||
"""
|
"""
|
||||||
Change the "current directory" of this SFTP session. Since SFTP
|
Change the "current directory" of this SFTP session. Since SFTP
|
||||||
doesn't really have the concept of a current working directory, this
|
doesn't really have the concept of a current working directory, this is
|
||||||
is emulated by paramiko. Once you use this method to set a working
|
emulated by Paramiko. Once you use this method to set a working
|
||||||
directory, all operations on this SFTPClient object will be relative
|
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
|
to that path. You can pass in ``None`` to stop using a current working
|
||||||
directory.
|
directory.
|
||||||
|
|
||||||
@param path: new current working directory
|
:param str path: new current working directory
|
||||||
@type path: str
|
|
||||||
|
|
||||||
@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:
|
if path is None:
|
||||||
self._cwd = None
|
self._cwd = None
|
||||||
|
@ -523,43 +489,41 @@ class SFTPClient (BaseSFTP):
|
||||||
def getcwd(self):
|
def getcwd(self):
|
||||||
"""
|
"""
|
||||||
Return the "current working directory" for this SFTP session, as
|
Return the "current working directory" for this SFTP session, as
|
||||||
emulated by paramiko. If no directory has been set with L{chdir},
|
emulated by Paramiko. If no directory has been set with `chdir`,
|
||||||
this method will return C{None}.
|
this method will return ``None``.
|
||||||
|
|
||||||
@return: the current working directory on the server, or C{None}
|
.. versionadded:: 1.4
|
||||||
@rtype: str
|
|
||||||
|
|
||||||
@since: 1.4
|
|
||||||
"""
|
"""
|
||||||
return self._cwd
|
return self._cwd
|
||||||
|
|
||||||
def putfo(self, fl, remotepath, file_size=0, callback=None, confirm=True):
|
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
|
Copy the contents of an open file object (``fl``) to the SFTP server as
|
||||||
C{remotepath}. Any exception raised by operations will be passed through.
|
``remotepath``. Any exception raised by operations will be passed
|
||||||
|
through.
|
||||||
|
|
||||||
The SFTP operations use pipelining for speed.
|
The SFTP operations use pipelining for speed.
|
||||||
|
|
||||||
@param fl: opened file or file-like object to copy
|
:param file fl: opened file or file-like object to copy
|
||||||
@type localpath: object
|
:param str remotepath: the destination path on the SFTP server
|
||||||
@param remotepath: the destination path on the SFTP server
|
:param int file_size:
|
||||||
@type remotepath: str
|
optional size parameter passed to callback. If none is specified,
|
||||||
@param file_size: optional size parameter passed to callback. If none is
|
size defaults to 0
|
||||||
specified, size defaults to 0
|
:param callable callback:
|
||||||
@type file_size: int
|
optional callback function (form: ``func(int, int)``) that accepts
|
||||||
@param callback: optional callback function that accepts the bytes
|
the bytes transferred so far and the total bytes to be transferred
|
||||||
transferred so far and the total bytes to be transferred
|
|
||||||
(since 1.7.4)
|
(since 1.7.4)
|
||||||
@type callback: function(int, int)
|
:param bool confirm:
|
||||||
@param confirm: whether to do a stat() on the file afterwards to
|
whether to do a stat() on the file afterwards to confirm the file
|
||||||
confirm the file size (since 1.7.7)
|
size (since 1.7.7)
|
||||||
@type confirm: bool
|
|
||||||
|
|
||||||
@return: an object containing attributes about the given file
|
:return:
|
||||||
(since 1.7.4)
|
an `.SFTPAttributes` object containing attributes about the given
|
||||||
@rtype: SFTPAttributes
|
file.
|
||||||
|
|
||||||
@since: 1.4
|
.. versionadded:: 1.4
|
||||||
|
.. versionchanged:: 1.7.4
|
||||||
|
Began returning rich attribute objects.
|
||||||
"""
|
"""
|
||||||
fr = self.file(remotepath, 'wb')
|
fr = self.file(remotepath, 'wb')
|
||||||
fr.set_pipelined(True)
|
fr.set_pipelined(True)
|
||||||
|
@ -585,29 +549,28 @@ class SFTPClient (BaseSFTP):
|
||||||
|
|
||||||
def put(self, localpath, remotepath, callback=None, confirm=True):
|
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
|
Any exception raised by operations will be passed through. This
|
||||||
method is primarily provided as a convenience.
|
method is primarily provided as a convenience.
|
||||||
|
|
||||||
The SFTP operations use pipelining for speed.
|
The SFTP operations use pipelining for speed.
|
||||||
|
|
||||||
@param localpath: the local file to copy
|
:param str localpath: the local file to copy
|
||||||
@type localpath: str
|
:param str remotepath: the destination path on the SFTP server
|
||||||
@param remotepath: the destination path on the SFTP server
|
:param callable callback:
|
||||||
@type remotepath: str
|
optional callback function (form: ``func(int, int)``) that accepts
|
||||||
@param callback: optional callback function that accepts the bytes
|
the bytes transferred so far and the total bytes to be transferred
|
||||||
transferred so far and the total bytes to be transferred
|
:param bool confirm:
|
||||||
(since 1.7.4)
|
whether to do a stat() on the file afterwards to confirm the file
|
||||||
@type callback: function(int, int)
|
size
|
||||||
@param confirm: whether to do a stat() on the file afterwards to
|
|
||||||
confirm the file size (since 1.7.7)
|
|
||||||
@type confirm: bool
|
|
||||||
|
|
||||||
@return: an object containing attributes about the given file
|
:return: an `.SFTPAttributes` object containing attributes about the given file
|
||||||
(since 1.7.4)
|
|
||||||
@rtype: SFTPAttributes
|
|
||||||
|
|
||||||
@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
|
file_size = os.stat(localpath).st_size
|
||||||
fl = file(localpath, 'rb')
|
fl = file(localpath, 'rb')
|
||||||
|
@ -618,23 +581,22 @@ class SFTPClient (BaseSFTP):
|
||||||
|
|
||||||
def getfo(self, remotepath, fl, callback=None):
|
def getfo(self, remotepath, fl, callback=None):
|
||||||
"""
|
"""
|
||||||
Copy a remote file (C{remotepath}) from the SFTP server and write to
|
Copy a remote file (``remotepath``) from the SFTP server and write to
|
||||||
an open file or file-like object, C{fl}. Any exception raised by
|
an open file or file-like object, ``fl``. Any exception raised by
|
||||||
operations will be passed through. This method is primarily provided
|
operations will be passed through. This method is primarily provided
|
||||||
as a convenience.
|
as a convenience.
|
||||||
|
|
||||||
@param remotepath: opened file or file-like object to copy to
|
:param object remotepath: opened file or file-like object to copy to
|
||||||
@type remotepath: object
|
:param str fl:
|
||||||
@param fl: the destination path on the local host or open file
|
the destination path on the local host or open file object
|
||||||
object
|
:param callable callback:
|
||||||
@type localpath: str
|
optional callback function (form: ``func(int, int)``) that accepts
|
||||||
@param callback: optional callback function that accepts the bytes
|
the bytes transferred so far and the total bytes to be transferred
|
||||||
transferred so far and the total bytes to be transferred
|
:return: the `number <int>` of bytes written to the opened file object
|
||||||
(since 1.7.4)
|
|
||||||
@type callback: function(int, int)
|
|
||||||
@return: the number 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')
|
fr = self.file(remotepath, 'rb')
|
||||||
file_size = self.stat(remotepath).st_size
|
file_size = self.stat(remotepath).st_size
|
||||||
|
@ -655,20 +617,19 @@ class SFTPClient (BaseSFTP):
|
||||||
|
|
||||||
def get(self, remotepath, localpath, callback=None):
|
def get(self, remotepath, localpath, callback=None):
|
||||||
"""
|
"""
|
||||||
Copy a remote file (C{remotepath}) from the SFTP server to the local
|
Copy a remote file (``remotepath``) from the SFTP server to the local
|
||||||
host as C{localpath}. Any exception raised by operations will be
|
host as ``localpath``. Any exception raised by operations will be
|
||||||
passed through. This method is primarily provided as a convenience.
|
passed through. This method is primarily provided as a convenience.
|
||||||
|
|
||||||
@param remotepath: the remote file to copy
|
:param str remotepath: the remote file to copy
|
||||||
@type remotepath: str
|
:param str localpath: the destination path on the local host
|
||||||
@param localpath: the destination path on the local host
|
:param callable callback:
|
||||||
@type localpath: str
|
optional callback function (form: ``func(int, int)``) that accepts
|
||||||
@param callback: optional callback function that accepts the bytes
|
the bytes transferred so far and the total bytes to be transferred
|
||||||
transferred so far and the total bytes to be transferred
|
|
||||||
(since 1.7.4)
|
|
||||||
@type callback: function(int, int)
|
|
||||||
|
|
||||||
@since: 1.4
|
.. versionadded:: 1.4
|
||||||
|
.. versionchanged:: 1.7.4
|
||||||
|
Added the ``callback`` param
|
||||||
"""
|
"""
|
||||||
file_size = self.stat(remotepath).st_size
|
file_size = self.stat(remotepath).st_size
|
||||||
fl = file(localpath, 'wb')
|
fl = file(localpath, 'wb')
|
||||||
|
@ -782,6 +743,8 @@ class SFTPClient (BaseSFTP):
|
||||||
return self._cwd + '/' + path
|
return self._cwd + '/' + path
|
||||||
|
|
||||||
|
|
||||||
class SFTP (SFTPClient):
|
class SFTP(SFTPClient):
|
||||||
"an alias for L{SFTPClient} for backwards compatability"
|
"""
|
||||||
|
An alias for `.SFTPClient` for backwards compatability.
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
L{SFTPFile}
|
SFTP file object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
|
@ -64,6 +64,9 @@ class SFTPFile (BufferedFile):
|
||||||
self._close(async=True)
|
self._close(async=True)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
"""
|
||||||
|
Close the file.
|
||||||
|
"""
|
||||||
self._close(async=False)
|
self._close(async=False)
|
||||||
|
|
||||||
def _close(self, async=False):
|
def _close(self, async=False):
|
||||||
|
@ -181,34 +184,34 @@ class SFTPFile (BufferedFile):
|
||||||
def settimeout(self, timeout):
|
def settimeout(self, timeout):
|
||||||
"""
|
"""
|
||||||
Set a timeout on read/write operations on the underlying socket or
|
Set a timeout on read/write operations on the underlying socket or
|
||||||
ssh L{Channel}.
|
ssh `.Channel`.
|
||||||
|
|
||||||
@see: L{Channel.settimeout}
|
:param float timeout:
|
||||||
@param timeout: seconds to wait for a pending read/write operation
|
seconds to wait for a pending read/write operation before raising
|
||||||
before raising C{socket.timeout}, or C{None} for no timeout
|
``socket.timeout``, or ``None`` for no timeout
|
||||||
@type timeout: float
|
|
||||||
|
.. seealso:: `.Channel.settimeout`
|
||||||
"""
|
"""
|
||||||
self.sftp.sock.settimeout(timeout)
|
self.sftp.sock.settimeout(timeout)
|
||||||
|
|
||||||
def gettimeout(self):
|
def gettimeout(self):
|
||||||
"""
|
"""
|
||||||
Returns the timeout in seconds (as a float) associated with the socket
|
Returns the timeout in seconds (as a `float`) associated with the
|
||||||
or ssh L{Channel} used for this file.
|
socket or ssh `.Channel` used for this file.
|
||||||
|
|
||||||
@see: L{Channel.gettimeout}
|
.. seealso:: `.Channel.gettimeout`
|
||||||
@rtype: float
|
|
||||||
"""
|
"""
|
||||||
return self.sftp.sock.gettimeout()
|
return self.sftp.sock.gettimeout()
|
||||||
|
|
||||||
def setblocking(self, blocking):
|
def setblocking(self, blocking):
|
||||||
"""
|
"""
|
||||||
Set blocking or non-blocking mode on the underiying socket or ssh
|
Set blocking or non-blocking mode on the underiying socket or ssh
|
||||||
L{Channel}.
|
`.Channel`.
|
||||||
|
|
||||||
@see: L{Channel.setblocking}
|
:param int blocking:
|
||||||
@param blocking: 0 to set non-blocking mode; non-0 to set blocking
|
0 to set non-blocking mode; non-0 to set blocking mode.
|
||||||
mode.
|
|
||||||
@type blocking: int
|
.. seealso:: `.Channel.setblocking`
|
||||||
"""
|
"""
|
||||||
self.sftp.sock.setblocking(blocking)
|
self.sftp.sock.setblocking(blocking)
|
||||||
|
|
||||||
|
@ -226,11 +229,10 @@ class SFTPFile (BufferedFile):
|
||||||
def stat(self):
|
def stat(self):
|
||||||
"""
|
"""
|
||||||
Retrieve information about this file from the remote system. This is
|
Retrieve information about this file from the remote system. This is
|
||||||
exactly like L{SFTP.stat}, except that it operates on an already-open
|
exactly like `.SFTPClient.stat`, except that it operates on an
|
||||||
file.
|
already-open file.
|
||||||
|
|
||||||
@return: an object containing attributes about this file.
|
:return: an `.SFTPAttributes` object containing attributes about this file.
|
||||||
@rtype: SFTPAttributes
|
|
||||||
"""
|
"""
|
||||||
t, msg = self.sftp._request(CMD_FSTAT, self.handle)
|
t, msg = self.sftp._request(CMD_FSTAT, self.handle)
|
||||||
if t != CMD_ATTRS:
|
if t != CMD_ATTRS:
|
||||||
|
@ -240,11 +242,10 @@ class SFTPFile (BufferedFile):
|
||||||
def chmod(self, mode):
|
def chmod(self, mode):
|
||||||
"""
|
"""
|
||||||
Change the mode (permissions) of this file. The permissions are
|
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.
|
function.
|
||||||
|
|
||||||
@param mode: new permissions
|
:param int mode: new permissions
|
||||||
@type mode: int
|
|
||||||
"""
|
"""
|
||||||
self.sftp._log(DEBUG, 'chmod(%s, %r)' % (hexlify(self.handle), mode))
|
self.sftp._log(DEBUG, 'chmod(%s, %r)' % (hexlify(self.handle), mode))
|
||||||
attr = SFTPAttributes()
|
attr = SFTPAttributes()
|
||||||
|
@ -253,15 +254,13 @@ class SFTPFile (BufferedFile):
|
||||||
|
|
||||||
def chown(self, uid, gid):
|
def chown(self, uid, gid):
|
||||||
"""
|
"""
|
||||||
Change the owner (C{uid}) and group (C{gid}) of this file. As with
|
Change the owner (``uid``) and group (``gid``) of this file. As with
|
||||||
python's C{os.chown} function, you must pass both arguments, so if you
|
Python's `os.chown` function, you must pass both arguments, so if you
|
||||||
only want to change one, use L{stat} first to retrieve the current
|
only want to change one, use `stat` first to retrieve the current
|
||||||
owner and group.
|
owner and group.
|
||||||
|
|
||||||
@param uid: new owner's uid
|
:param int uid: new owner's uid
|
||||||
@type uid: int
|
:param int gid: new group id
|
||||||
@param gid: new group id
|
|
||||||
@type gid: int
|
|
||||||
"""
|
"""
|
||||||
self.sftp._log(DEBUG, 'chown(%s, %r, %r)' % (hexlify(self.handle), uid, gid))
|
self.sftp._log(DEBUG, 'chown(%s, %r, %r)' % (hexlify(self.handle), uid, gid))
|
||||||
attr = SFTPAttributes()
|
attr = SFTPAttributes()
|
||||||
|
@ -271,15 +270,15 @@ class SFTPFile (BufferedFile):
|
||||||
def utime(self, times):
|
def utime(self, times):
|
||||||
"""
|
"""
|
||||||
Set the access and modified times of this file. If
|
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
|
``times`` is ``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,
|
to the current time. Otherwise, ``times`` must be a 2-tuple of numbers,
|
||||||
of the form C{(atime, mtime)}, which is used to set the access and
|
of the form ``(atime, mtime)``, which is used to set the access and
|
||||||
modified times, respectively. This bizarre API is mimicked from python
|
modified times, respectively. This bizarre API is mimicked from Python
|
||||||
for the sake of consistency -- I apologize.
|
for the sake of consistency -- I apologize.
|
||||||
|
|
||||||
@param times: C{None} or a tuple of (access time, modified time) in
|
:param tuple times:
|
||||||
standard internet epoch time (seconds since 01 January 1970 GMT)
|
``None`` or a tuple of (access time, modified time) in standard
|
||||||
@type times: tuple(int)
|
internet epoch time (seconds since 01 January 1970 GMT)
|
||||||
"""
|
"""
|
||||||
if times is None:
|
if times is None:
|
||||||
times = (time.time(), time.time())
|
times = (time.time(), time.time())
|
||||||
|
@ -291,11 +290,11 @@ class SFTPFile (BufferedFile):
|
||||||
def truncate(self, size):
|
def truncate(self, size):
|
||||||
"""
|
"""
|
||||||
Change the size of this file. This usually extends
|
Change the size of this file. This usually extends
|
||||||
or shrinks the size of the file, just like the C{truncate()} method on
|
or shrinks the size of the file, just like the ``truncate()`` method on
|
||||||
python file objects.
|
Python file objects.
|
||||||
|
|
||||||
@param size: the new size of the file
|
:param size: the new size of the file
|
||||||
@type size: int or long
|
:type size: int or long
|
||||||
"""
|
"""
|
||||||
self.sftp._log(DEBUG, 'truncate(%s, %r)' % (hexlify(self.handle), size))
|
self.sftp._log(DEBUG, 'truncate(%s, %r)' % (hexlify(self.handle), size))
|
||||||
attr = SFTPAttributes()
|
attr = SFTPAttributes()
|
||||||
|
@ -308,46 +307,48 @@ class SFTPFile (BufferedFile):
|
||||||
to verify a successful upload or download, or for various rsync-like
|
to verify a successful upload or download, or for various rsync-like
|
||||||
operations.
|
operations.
|
||||||
|
|
||||||
The file is hashed from C{offset}, for C{length} bytes. If C{length}
|
The file is hashed from ``offset``, for ``length`` bytes. If ``length``
|
||||||
is 0, the remainder of the file is hashed. Thus, if both C{offset}
|
is 0, the remainder of the file is hashed. Thus, if both ``offset``
|
||||||
and C{length} are zero, the entire file is hashed.
|
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
|
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
|
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
|
``block_size`` is given, each chunk of the file (from ``offset`` to
|
||||||
C{offset + length}) of C{block_size} bytes is computed as a separate
|
``offset + length``) of ``block_size`` bytes is computed as a separate
|
||||||
hash. The hash results are all concatenated and returned as a single
|
hash. The hash results are all concatenated and returned as a single
|
||||||
string.
|
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
|
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
|
of the file, and the last 20 bytes will be the SHA-1 of the next 512
|
||||||
bytes.
|
bytes.
|
||||||
|
|
||||||
@param hash_algorithm: the name of the hash algorithm to use (normally
|
:param str hash_algorithm:
|
||||||
C{"sha1"} or C{"md5"})
|
the name of the hash algorithm to use (normally ``"sha1"`` or
|
||||||
@type hash_algorithm: str
|
``"md5"``)
|
||||||
@param offset: offset into the file to begin hashing (0 means to start
|
:param offset:
|
||||||
from the beginning)
|
offset into the file to begin hashing (0 means to start from the
|
||||||
@type offset: int or long
|
beginning)
|
||||||
@param length: number of bytes to hash (0 means continue to the end of
|
:type offset: int or long
|
||||||
the file)
|
:param length:
|
||||||
@type length: int or long
|
number of bytes to hash (0 means continue to the end of the file)
|
||||||
@param block_size: number of bytes to hash per result (must not be less
|
:type length: int or long
|
||||||
than 256; 0 means to compute only one hash of the entire segment)
|
:param int block_size:
|
||||||
@type block_size: int
|
number of bytes to hash per result (must not be less than 256; 0
|
||||||
@return: string of bytes representing the hash of each block,
|
means to compute only one hash of the entire segment)
|
||||||
concatenated together
|
:type block_size: int
|
||||||
@rtype: str
|
:return:
|
||||||
|
`str` of bytes representing the hash of each block, concatenated
|
||||||
|
together
|
||||||
|
|
||||||
@note: Many (most?) servers don't support this extension yet.
|
:raises IOError: if the server doesn't support the "check-file"
|
||||||
|
|
||||||
@raise IOError: if the server doesn't support the "check-file"
|
|
||||||
extension, or possibly doesn't support the hash algorithm
|
extension, or possibly doesn't support the hash algorithm
|
||||||
requested
|
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,
|
t, msg = self.sftp._request(CMD_EXTENDED, 'check-file', self.handle,
|
||||||
hash_algorithm, long(offset), long(length), block_size)
|
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
|
Turn on/off the pipelining of write operations to this file. When
|
||||||
pipelining is on, paramiko won't wait for the server response after
|
pipelining is on, paramiko won't wait for the server response after
|
||||||
each write operation. Instead, they're collected as they come in.
|
each write operation. Instead, they're collected as they come in. At
|
||||||
At the first non-write operation (including L{close}), all remaining
|
the first non-write operation (including `.close`), all remaining
|
||||||
server responses are collected. This means that if there was an error
|
server responses are collected. This means that if there was an error
|
||||||
with one of your later writes, an exception might be thrown from
|
with one of your later writes, an exception might be thrown from within
|
||||||
within L{close} instead of L{write}.
|
`.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
|
:param bool pipelined:
|
||||||
file; C{False} otherwise
|
``True`` if pipelining should be turned on for this file; ``False``
|
||||||
@type pipelined: bool
|
otherwise
|
||||||
|
|
||||||
@since: 1.5
|
.. versionadded:: 1.5
|
||||||
"""
|
"""
|
||||||
self.pipelined = pipelined
|
self.pipelined = pipelined
|
||||||
|
|
||||||
def prefetch(self):
|
def prefetch(self):
|
||||||
"""
|
"""
|
||||||
Pre-fetch the remaining contents of this file in anticipation of
|
Pre-fetch the remaining contents of this file in anticipation of future
|
||||||
future L{read} calls. If reading the entire file, pre-fetching can
|
`.read` calls. If reading the entire file, pre-fetching can
|
||||||
dramatically improve the download speed by avoiding roundtrip latency.
|
dramatically improve the download speed by avoiding roundtrip latency.
|
||||||
The file's contents are incrementally buffered in a background thread.
|
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
|
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.
|
buffer that haven't been read will continue to be buffered.
|
||||||
|
|
||||||
@since: 1.5.1
|
.. versionadded:: 1.5.1
|
||||||
"""
|
"""
|
||||||
size = self.stat().st_size
|
size = self.stat().st_size
|
||||||
# queue up async reads for the rest of the file
|
# queue up async reads for the rest of the file
|
||||||
|
@ -404,17 +405,17 @@ class SFTPFile (BufferedFile):
|
||||||
def readv(self, chunks):
|
def readv(self, chunks):
|
||||||
"""
|
"""
|
||||||
Read a set of blocks from the file by (offset, length). This is more
|
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
|
prefetch machinery is used to retrieve all the requested blocks at
|
||||||
once.
|
once.
|
||||||
|
|
||||||
@param chunks: a list of (offset, length) tuples indicating which
|
:param chunks:
|
||||||
sections of the file to read
|
a list of (offset, length) tuples indicating which sections of the
|
||||||
@type chunks: list(tuple(long, int))
|
file to read
|
||||||
@return: a list of blocks read, in the same order as in C{chunks}
|
:type chunks: list(tuple(long, int))
|
||||||
@rtype: list(str)
|
: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))
|
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.
|
by the client to refer to the underlying file.
|
||||||
|
|
||||||
Server implementations can (and should) subclass SFTPHandle to implement
|
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):
|
def __init__(self, flags=0):
|
||||||
"""
|
"""
|
||||||
Create a new file handle representing a local file being served over
|
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.
|
is open in append mode.
|
||||||
|
|
||||||
@param flags: optional flags as passed to L{SFTPServerInterface.open}
|
:param int flags: optional flags as passed to `.SFTPServerInterface.open`
|
||||||
@type flags: int
|
|
||||||
"""
|
"""
|
||||||
self.__flags = flags
|
self.__flags = flags
|
||||||
self.__name = None
|
self.__name = None
|
||||||
|
@ -56,10 +55,10 @@ class SFTPHandle (object):
|
||||||
Normally you would use this method to close the underlying OS level
|
Normally you would use this method to close the underlying OS level
|
||||||
file object(s).
|
file object(s).
|
||||||
|
|
||||||
The default implementation checks for attributes on C{self} named
|
The default implementation checks for attributes on ``self`` named
|
||||||
C{readfile} and/or C{writefile}, and if either or both are present,
|
``readfile`` and/or ``writefile``, and if either or both are present,
|
||||||
their C{close()} methods are called. This means that if you are
|
their ``close()`` methods are called. This means that if you are
|
||||||
using the default implementations of L{read} and L{write}, this
|
using the default implementations of `read` and `write`, this
|
||||||
method's default implementation should be fine also.
|
method's default implementation should be fine also.
|
||||||
"""
|
"""
|
||||||
readfile = getattr(self, 'readfile', None)
|
readfile = getattr(self, 'readfile', None)
|
||||||
|
@ -71,24 +70,22 @@ class SFTPHandle (object):
|
||||||
|
|
||||||
def read(self, offset, length):
|
def read(self, offset, length):
|
||||||
"""
|
"""
|
||||||
Read up to C{length} bytes from this file, starting at position
|
Read up to ``length`` bytes from this file, starting at position
|
||||||
C{offset}. The offset may be a python long, since SFTP allows it
|
``offset``. The offset may be a Python long, since SFTP allows it
|
||||||
to be 64 bits.
|
to be 64 bits.
|
||||||
|
|
||||||
If the end of the file has been reached, this method may return an
|
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
|
The default implementation checks for an attribute on ``self`` named
|
||||||
C{readfile}, and if present, performs the read operation on the python
|
``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
|
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.
|
:param offset: position in the file to start reading from.
|
||||||
@type offset: int or long
|
:type offset: int or long
|
||||||
@param length: number of bytes to attempt to read.
|
:param int length: number of bytes to attempt to read.
|
||||||
@type length: int
|
:return: data read from the file, or an SFTP error code, as a `str`.
|
||||||
@return: data read from the file, or an SFTP error code.
|
|
||||||
@rtype: str
|
|
||||||
"""
|
"""
|
||||||
readfile = getattr(self, 'readfile', None)
|
readfile = getattr(self, 'readfile', None)
|
||||||
if readfile is None:
|
if readfile is None:
|
||||||
|
@ -108,23 +105,22 @@ class SFTPHandle (object):
|
||||||
|
|
||||||
def write(self, offset, data):
|
def write(self, offset, data):
|
||||||
"""
|
"""
|
||||||
Write C{data} into this file at position C{offset}. Extending the
|
Write ``data`` into this file at position ``offset``. Extending the
|
||||||
file past its original end is expected. Unlike python's normal
|
file past its original end is expected. Unlike Python's normal
|
||||||
C{write()} methods, this method cannot do a partial write: it must
|
``write()`` methods, this method cannot do a partial write: it must
|
||||||
write all of C{data} or else return an error.
|
write all of ``data`` or else return an error.
|
||||||
|
|
||||||
The default implementation checks for an attribute on C{self} named
|
The default implementation checks for an attribute on ``self`` named
|
||||||
C{writefile}, and if present, performs the write operation on the
|
``writefile``, and if present, performs the write operation on the
|
||||||
python file-like object found there. The attribute is named
|
Python file-like object found there. The attribute is named
|
||||||
differently from C{readfile} to make it easy to implement read-only
|
differently from ``readfile`` to make it easy to implement read-only
|
||||||
(or write-only) files, but if both attributes are present, they should
|
(or write-only) files, but if both attributes are present, they should
|
||||||
refer to the same file.
|
refer to the same file.
|
||||||
|
|
||||||
@param offset: position in the file to start reading from.
|
:param offset: position in the file to start reading from.
|
||||||
@type offset: int or long
|
:type offset: int or long
|
||||||
@param data: data to write into the file.
|
:param str data: data to write into the file.
|
||||||
@type data: str
|
:return: an SFTP error code like `.SFTP_OK`.
|
||||||
@return: an SFTP error code like L{SFTP_OK}.
|
|
||||||
"""
|
"""
|
||||||
writefile = getattr(self, 'writefile', None)
|
writefile = getattr(self, 'writefile', None)
|
||||||
if writefile is None:
|
if writefile is None:
|
||||||
|
@ -148,26 +144,25 @@ class SFTPHandle (object):
|
||||||
|
|
||||||
def stat(self):
|
def stat(self):
|
||||||
"""
|
"""
|
||||||
Return an L{SFTPAttributes} object referring to this open file, or an
|
Return an `.SFTPAttributes` object referring to this open file, or an
|
||||||
error code. This is equivalent to L{SFTPServerInterface.stat}, except
|
error code. This is equivalent to `.SFTPServerInterface.stat`, except
|
||||||
it's called on an open file instead of a path.
|
it's called on an open file instead of a path.
|
||||||
|
|
||||||
@return: an attributes object for the given file, or an SFTP error
|
:return:
|
||||||
code (like L{SFTP_PERMISSION_DENIED}).
|
an attributes object for the given file, or an SFTP error code
|
||||||
@rtype: L{SFTPAttributes} I{or error code}
|
(like `.SFTP_PERMISSION_DENIED`).
|
||||||
|
:rtype: `.SFTPAttributes` or error code
|
||||||
"""
|
"""
|
||||||
return SFTP_OP_UNSUPPORTED
|
return SFTP_OP_UNSUPPORTED
|
||||||
|
|
||||||
def chattr(self, attr):
|
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
|
only those fields provided by the client in its request, so you should
|
||||||
check for the presence of fields before using them.
|
check for the presence of fields before using them.
|
||||||
|
|
||||||
@param attr: the attributes to change on this file.
|
:param .SFTPAttributes attr: the attributes to change on this file.
|
||||||
@type attr: L{SFTPAttributes}
|
:return: an `int` error code like `.SFTP_OK`.
|
||||||
@return: an error code like L{SFTP_OK}.
|
|
||||||
@rtype: int
|
|
||||||
"""
|
"""
|
||||||
return SFTP_OP_UNSUPPORTED
|
return SFTP_OP_UNSUPPORTED
|
||||||
|
|
||||||
|
|
|
@ -40,28 +40,25 @@ _hash_class = {
|
||||||
|
|
||||||
class SFTPServer (BaseSFTP, SubsystemHandler):
|
class SFTPServer (BaseSFTP, SubsystemHandler):
|
||||||
"""
|
"""
|
||||||
Server-side SFTP subsystem support. Since this is a L{SubsystemHandler},
|
Server-side SFTP subsystem support. Since this is a `.SubsystemHandler`,
|
||||||
it can be (and is meant to be) set as the handler for C{"sftp"} requests.
|
it can be (and is meant to be) set as the handler for ``"sftp"`` requests.
|
||||||
Use L{Transport.set_subsystem_handler} to activate this class.
|
Use `.Transport.set_subsystem_handler` to activate this class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, channel, name, server, sftp_si=SFTPServerInterface, *largs, **kwargs):
|
def __init__(self, channel, name, server, sftp_si=SFTPServerInterface, *largs, **kwargs):
|
||||||
"""
|
"""
|
||||||
The constructor for SFTPServer is meant to be called from within the
|
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
|
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}.
|
:param .Channel channel: channel passed from the `.Transport`.
|
||||||
@type channel: L{Channel}
|
:param str name: name of the requested subsystem.
|
||||||
@param name: name of the requested subsystem.
|
:param .ServerInterface server:
|
||||||
@type name: str
|
the server object associated with this channel and subsystem
|
||||||
@param server: the server object associated with this channel and
|
:param class sftp_si:
|
||||||
subsystem
|
a subclass of `.SFTPServerInterface` to use for handling individual
|
||||||
@type server: L{ServerInterface}
|
requests.
|
||||||
@param sftp_si: a subclass of L{SFTPServerInterface} to use for handling
|
|
||||||
individual requests.
|
|
||||||
@type sftp_si: class
|
|
||||||
"""
|
"""
|
||||||
BaseSFTP.__init__(self)
|
BaseSFTP.__init__(self)
|
||||||
SubsystemHandler.__init__(self, channel, name, server)
|
SubsystemHandler.__init__(self, channel, name, server)
|
||||||
|
@ -122,14 +119,12 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
|
||||||
|
|
||||||
def convert_errno(e):
|
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
|
standard SFTP result code. This is a convenience function for trapping
|
||||||
exceptions in server code and returning an appropriate result.
|
exceptions in server code and returning an appropriate result.
|
||||||
|
|
||||||
@param e: an errno code, as from C{OSError.errno}.
|
:param int e: an errno code, as from ``OSError.errno``.
|
||||||
@type e: int
|
:return: an `int` SFTP error code like ``SFTP_NO_SUCH_FILE``.
|
||||||
@return: an SFTP error code like L{SFTP_NO_SUCH_FILE}.
|
|
||||||
@rtype: int
|
|
||||||
"""
|
"""
|
||||||
if e == errno.EACCES:
|
if e == errno.EACCES:
|
||||||
# permission denied
|
# permission denied
|
||||||
|
@ -144,18 +139,16 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
|
||||||
def set_file_attr(filename, attr):
|
def set_file_attr(filename, attr):
|
||||||
"""
|
"""
|
||||||
Change a file's attributes on the local filesystem. The contents of
|
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
|
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
|
This is meant to be a handy helper function for translating SFTP file
|
||||||
requests into local file operations.
|
requests into local file operations.
|
||||||
|
|
||||||
@param filename: name of the file to alter (should usually be an
|
:param str filename:
|
||||||
absolute path).
|
name of the file to alter (should usually be an absolute path).
|
||||||
@type filename: str
|
:param .SFTPAttributes attr: attributes to change.
|
||||||
@param attr: attributes to change.
|
|
||||||
@type attr: L{SFTPAttributes}
|
|
||||||
"""
|
"""
|
||||||
if sys.platform != 'win32':
|
if sys.platform != 'win32':
|
||||||
# mode operations are meaningless on win32
|
# mode operations are meaningless on win32
|
||||||
|
@ -296,7 +289,7 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
|
||||||
self._send_packet(CMD_EXTENDED_REPLY, str(msg))
|
self._send_packet(CMD_EXTENDED_REPLY, str(msg))
|
||||||
|
|
||||||
def _convert_pflags(self, pflags):
|
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):
|
if (pflags & SFTP_FLAG_READ) and (pflags & SFTP_FLAG_WRITE):
|
||||||
flags = os.O_RDWR
|
flags = os.O_RDWR
|
||||||
elif pflags & SFTP_FLAG_WRITE:
|
elif pflags & SFTP_FLAG_WRITE:
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
# 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
|
import os
|
||||||
|
@ -29,7 +29,7 @@ from paramiko.sftp import *
|
||||||
class SFTPServerInterface (object):
|
class SFTPServerInterface (object):
|
||||||
"""
|
"""
|
||||||
This class defines an interface for controlling the behavior of paramiko
|
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
|
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
|
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
|
Create a new SFTPServerInterface object. This method does nothing by
|
||||||
default and is meant to be overridden by subclasses.
|
default and is meant to be overridden by subclasses.
|
||||||
|
|
||||||
@param server: the server object associated with this channel and
|
:param .ServerInterface server:
|
||||||
SFTP subsystem
|
the server object associated with this channel and SFTP subsystem
|
||||||
@type server: L{ServerInterface}
|
|
||||||
"""
|
"""
|
||||||
super(SFTPServerInterface, self).__init__(*largs, **kwargs)
|
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
|
The SFTP server session has just ended, either cleanly or via an
|
||||||
exception. This method is meant to be overridden to perform any
|
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.
|
destroyed.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
@ -72,103 +71,105 @@ class SFTPServerInterface (object):
|
||||||
def open(self, path, flags, attr):
|
def open(self, path, flags, attr):
|
||||||
"""
|
"""
|
||||||
Open a file on the server and create a handle for future operations
|
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
|
should be returned. This handle will be used for future operations
|
||||||
on the file (read, write, etc). On failure, an error code such as
|
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,
|
``flags`` contains the requested mode for opening (read-only,
|
||||||
write-append, etc) as a bitset of flags from the C{os} module:
|
write-append, etc) as a bitset of flags from the ``os`` module:
|
||||||
- C{os.O_RDONLY}
|
|
||||||
- C{os.O_WRONLY}
|
- ``os.O_RDONLY``
|
||||||
- C{os.O_RDWR}
|
- ``os.O_WRONLY``
|
||||||
- C{os.O_APPEND}
|
- ``os.O_RDWR``
|
||||||
- C{os.O_CREAT}
|
- ``os.O_APPEND``
|
||||||
- C{os.O_TRUNC}
|
- ``os.O_CREAT``
|
||||||
- C{os.O_EXCL}
|
- ``os.O_TRUNC``
|
||||||
(One of C{os.O_RDONLY}, C{os.O_WRONLY}, or C{os.O_RDWR} will always
|
- ``os.O_EXCL``
|
||||||
|
|
||||||
|
(One of ``os.O_RDONLY``, ``os.O_WRONLY``, or ``os.O_RDWR`` will always
|
||||||
be set.)
|
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
|
has to be created. Some or all attribute fields may be missing if
|
||||||
the client didn't specify them.
|
the client didn't specify them.
|
||||||
|
|
||||||
@note: The SFTP protocol defines all files to be in "binary" mode.
|
.. note:: The SFTP protocol defines all files to be in "binary" mode.
|
||||||
There is no equivalent to python's "text" mode.
|
There is no equivalent to Python's "text" mode.
|
||||||
|
|
||||||
@param path: the requested path (relative or absolute) of the file
|
:param str path:
|
||||||
to be opened.
|
the requested path (relative or absolute) of the file to be opened.
|
||||||
@type path: str
|
:param int flags:
|
||||||
@param flags: flags or'd together from the C{os} module indicating the
|
flags or'd together from the ``os`` module indicating the requested
|
||||||
requested mode for opening the file.
|
mode for opening the file.
|
||||||
@type flags: int
|
:param .SFTPAttributes attr:
|
||||||
@param attr: requested attributes of the file if it is newly created.
|
requested attributes of the file if it is newly created.
|
||||||
@type attr: L{SFTPAttributes}
|
:return: a new `.SFTPHandle` or error code.
|
||||||
@return: a new L{SFTPHandle} I{or error code}.
|
|
||||||
@rtype L{SFTPHandle}
|
|
||||||
"""
|
"""
|
||||||
return SFTP_OP_UNSUPPORTED
|
return SFTP_OP_UNSUPPORTED
|
||||||
|
|
||||||
def list_folder(self, path):
|
def list_folder(self, path):
|
||||||
"""
|
"""
|
||||||
Return a list of files within a given folder. The C{path} will use
|
Return a list of files within a given folder. The ``path`` will use
|
||||||
posix notation (C{"/"} separates folder names) and may be an absolute
|
posix notation (``"/"`` separates folder names) and may be an absolute
|
||||||
or relative path.
|
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
|
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
|
field filled in, since this is important to a directory listing and
|
||||||
not normally present in C{os.stat} results. The method
|
not normally present in ``os.stat`` results. The method
|
||||||
L{SFTPAttributes.from_stat} will usually do what you want.
|
`.SFTPAttributes.from_stat` will usually do what you want.
|
||||||
|
|
||||||
In case of an error, you should return one of the C{SFTP_*} error
|
In case of an error, you should return one of the ``SFTP_*`` error
|
||||||
codes, such as L{SFTP_PERMISSION_DENIED}.
|
codes, such as `.SFTP_PERMISSION_DENIED`.
|
||||||
|
|
||||||
@param path: the requested path (relative or absolute) to be listed.
|
:param str path: the requested path (relative or absolute) to be listed.
|
||||||
@type path: str
|
:return:
|
||||||
@return: a list of the files in the given folder, using
|
a list of the files in the given folder, using `.SFTPAttributes`
|
||||||
L{SFTPAttributes} objects.
|
objects.
|
||||||
@rtype: list of L{SFTPAttributes} I{or error code}
|
|
||||||
|
|
||||||
@note: You should normalize the given C{path} first (see the
|
.. note::
|
||||||
C{os.path} module) and check appropriate permissions before returning
|
You should normalize the given ``path`` first (see the `os.path`
|
||||||
the list of files. Be careful of malicious clients attempting to use
|
module) and check appropriate permissions before returning the list
|
||||||
relative paths to escape restricted folders, if you're doing a direct
|
of files. Be careful of malicious clients attempting to use
|
||||||
translation from the SFTP server path to your local filesystem.
|
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
|
return SFTP_OP_UNSUPPORTED
|
||||||
|
|
||||||
def stat(self, path):
|
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
|
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.)
|
call that doesn't follow symlinks/aliases.)
|
||||||
|
|
||||||
@param path: the requested path (relative or absolute) to fetch
|
:param str path:
|
||||||
file statistics for.
|
the requested path (relative or absolute) to fetch file statistics
|
||||||
@type path: str
|
for.
|
||||||
@return: an attributes object for the given file, or an SFTP error
|
:return:
|
||||||
code (like L{SFTP_PERMISSION_DENIED}).
|
an `.SFTPAttributes` object for the given file, or an SFTP error
|
||||||
@rtype: L{SFTPAttributes} I{or error code}
|
code (like `.SFTP_PERMISSION_DENIED`).
|
||||||
"""
|
"""
|
||||||
return SFTP_OP_UNSUPPORTED
|
return SFTP_OP_UNSUPPORTED
|
||||||
|
|
||||||
def lstat(self, path):
|
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
|
error code. If your server supports symbolic links (also known as
|
||||||
"aliases"), you should I{not} follow them -- instead, you should
|
"aliases"), you should not follow them -- instead, you should
|
||||||
return data on the symlink or alias itself. (L{stat} is the
|
return data on the symlink or alias itself. (`stat` is the
|
||||||
corresponding call that follows symlinks/aliases.)
|
corresponding call that follows symlinks/aliases.)
|
||||||
|
|
||||||
@param path: the requested path (relative or absolute) to fetch
|
:param str path:
|
||||||
file statistics for.
|
the requested path (relative or absolute) to fetch file statistics
|
||||||
@type path: str
|
for.
|
||||||
@return: an attributes object for the given file, or an SFTP error
|
:type path: str
|
||||||
code (like L{SFTP_PERMISSION_DENIED}).
|
:return:
|
||||||
@rtype: L{SFTPAttributes} I{or error code}
|
an `.SFTPAttributes` object for the given file, or an SFTP error
|
||||||
|
code (like `.SFTP_PERMISSION_DENIED`).
|
||||||
"""
|
"""
|
||||||
return SFTP_OP_UNSUPPORTED
|
return SFTP_OP_UNSUPPORTED
|
||||||
|
|
||||||
|
@ -176,11 +177,9 @@ class SFTPServerInterface (object):
|
||||||
"""
|
"""
|
||||||
Delete a file, if possible.
|
Delete a file, if possible.
|
||||||
|
|
||||||
@param path: the requested path (relative or absolute) of the file
|
:param str path:
|
||||||
to delete.
|
the requested path (relative or absolute) of the file to delete.
|
||||||
@type path: str
|
:return: an SFTP error code `int` like `.SFTP_OK`.
|
||||||
@return: an SFTP error code like L{SFTP_OK}.
|
|
||||||
@rtype: int
|
|
||||||
"""
|
"""
|
||||||
return SFTP_OP_UNSUPPORTED
|
return SFTP_OP_UNSUPPORTED
|
||||||
|
|
||||||
|
@ -192,83 +191,74 @@ class SFTPServerInterface (object):
|
||||||
probably a good idea to implement "move" in this method too, even for
|
probably a good idea to implement "move" in this method too, even for
|
||||||
files that cross disk partition boundaries, if at all possible.
|
files that cross disk partition boundaries, if at all possible.
|
||||||
|
|
||||||
@note: You should return an error if a file with the same name as
|
.. note:: You should return an error if a file with the same name as
|
||||||
C{newpath} already exists. (The rename operation should be
|
``newpath`` already exists. (The rename operation should be
|
||||||
non-desctructive.)
|
non-desctructive.)
|
||||||
|
|
||||||
@param oldpath: the requested path (relative or absolute) of the
|
:param str oldpath:
|
||||||
existing file.
|
the requested path (relative or absolute) of the existing file.
|
||||||
@type oldpath: str
|
:param str newpath: the requested new path of the file.
|
||||||
@param newpath: the requested new path of the file.
|
:return: an SFTP error code `int` like `.SFTP_OK`.
|
||||||
@type newpath: str
|
|
||||||
@return: an SFTP error code like L{SFTP_OK}.
|
|
||||||
@rtype: int
|
|
||||||
"""
|
"""
|
||||||
return SFTP_OP_UNSUPPORTED
|
return SFTP_OP_UNSUPPORTED
|
||||||
|
|
||||||
def mkdir(self, path, attr):
|
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.
|
object may be considered a "hint" and ignored.
|
||||||
|
|
||||||
The C{attr} object will contain only those fields provided by the
|
The ``attr`` object will contain only those fields provided by the
|
||||||
client in its request, so you should use C{hasattr} to check for
|
client in its request, so you should use ``hasattr`` to check for
|
||||||
the presense of fields before using them. In some cases, the C{attr}
|
the presense of fields before using them. In some cases, the ``attr``
|
||||||
object may be completely empty.
|
object may be completely empty.
|
||||||
|
|
||||||
@param path: requested path (relative or absolute) of the new
|
:param str path:
|
||||||
folder.
|
requested path (relative or absolute) of the new folder.
|
||||||
@type path: str
|
:param .SFTPAttributes attr: requested attributes of the new folder.
|
||||||
@param attr: requested attributes of the new folder.
|
:return: an SFTP error code `int` like `.SFTP_OK`.
|
||||||
@type attr: L{SFTPAttributes}
|
|
||||||
@return: an SFTP error code like L{SFTP_OK}.
|
|
||||||
@rtype: int
|
|
||||||
"""
|
"""
|
||||||
return SFTP_OP_UNSUPPORTED
|
return SFTP_OP_UNSUPPORTED
|
||||||
|
|
||||||
def rmdir(self, path):
|
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
|
existing, empty folder -- otherwise this method should return an
|
||||||
error.
|
error.
|
||||||
|
|
||||||
@param path: requested path (relative or absolute) of the folder
|
:param str path:
|
||||||
to remove.
|
requested path (relative or absolute) of the folder to remove.
|
||||||
@type path: str
|
:return: an SFTP error code `int` like `.SFTP_OK`.
|
||||||
@return: an SFTP error code like L{SFTP_OK}.
|
|
||||||
@rtype: int
|
|
||||||
"""
|
"""
|
||||||
return SFTP_OP_UNSUPPORTED
|
return SFTP_OP_UNSUPPORTED
|
||||||
|
|
||||||
def chattr(self, path, attr):
|
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
|
only those fields provided by the client in its request, so you
|
||||||
should check for the presence of fields before using them.
|
should check for the presence of fields before using them.
|
||||||
|
|
||||||
@param path: requested path (relative or absolute) of the file to
|
:param str path:
|
||||||
change.
|
requested path (relative or absolute) of the file to change.
|
||||||
@type path: str
|
:param attr:
|
||||||
@param attr: requested attributes to change on the file.
|
requested attributes to change on the file (an `.SFTPAttributes`
|
||||||
@type attr: L{SFTPAttributes}
|
object)
|
||||||
@return: an error code like L{SFTP_OK}.
|
:return: an error code `int` like `.SFTP_OK`.
|
||||||
@rtype: int
|
|
||||||
"""
|
"""
|
||||||
return SFTP_OP_UNSUPPORTED
|
return SFTP_OP_UNSUPPORTED
|
||||||
|
|
||||||
def canonicalize(self, path):
|
def canonicalize(self, path):
|
||||||
"""
|
"""
|
||||||
Return the canonical form of a path on the server. For example,
|
Return the canonical form of a path on the server. For example,
|
||||||
if the server's home folder is C{/home/foo}, the path
|
if the server's home folder is ``/home/foo``, the path
|
||||||
C{"../betty"} would be canonicalized to C{"/home/betty"}. Note
|
``"../betty"`` would be canonicalized to ``"/home/betty"``. Note
|
||||||
the obvious security issues: if you're serving files only from a
|
the obvious security issues: if you're serving files only from a
|
||||||
specific folder, you probably don't want this method to reveal path
|
specific folder, you probably don't want this method to reveal path
|
||||||
names outside that folder.
|
names outside that folder.
|
||||||
|
|
||||||
You may find the python methods in C{os.path} useful, especially
|
You may find the Python methods in ``os.path`` useful, especially
|
||||||
C{os.path.normpath} and C{os.path.realpath}.
|
``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):
|
if os.path.isabs(path):
|
||||||
out = os.path.normpath(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
|
If the specified path doesn't refer to a symbolic link, an error
|
||||||
should be returned.
|
should be returned.
|
||||||
|
|
||||||
@param path: path (relative or absolute) of the symbolic link.
|
:param str path: path (relative or absolute) of the symbolic link.
|
||||||
@type path: str
|
:return:
|
||||||
@return: the target path of the symbolic link, or an error code like
|
the target `str` path of the symbolic link, or an error code like
|
||||||
L{SFTP_NO_SUCH_FILE}.
|
`.SFTP_NO_SUCH_FILE`.
|
||||||
@rtype: str I{or error code}
|
|
||||||
"""
|
"""
|
||||||
return SFTP_OP_UNSUPPORTED
|
return SFTP_OP_UNSUPPORTED
|
||||||
|
|
||||||
def symlink(self, target_path, path):
|
def symlink(self, target_path, path):
|
||||||
"""
|
"""
|
||||||
Create a symbolic link on the server, as new pathname C{path},
|
Create a symbolic link on the server, as new pathname ``path``,
|
||||||
with C{target_path} as the target of the link.
|
with ``target_path`` as the target of the link.
|
||||||
|
|
||||||
@param target_path: path (relative or absolute) of the target for
|
:param str target_path:
|
||||||
this new symbolic link.
|
path (relative or absolute) of the target for this new symbolic
|
||||||
@type target_path: str
|
link.
|
||||||
@param path: path (relative or absolute) of the symbolic link to
|
:param str path:
|
||||||
create.
|
path (relative or absolute) of the symbolic link to create.
|
||||||
@type path: str
|
:return: an error code `int` like ``SFTP_OK``.
|
||||||
@return: an error code like C{SFTP_OK}.
|
|
||||||
@rtype: int
|
|
||||||
"""
|
"""
|
||||||
return SFTP_OP_UNSUPPORTED
|
return SFTP_OP_UNSUPPORTED
|
||||||
|
|
|
@ -16,10 +16,6 @@
|
||||||
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
||||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
"""
|
|
||||||
Exceptions defined by paramiko.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class SSHException (Exception):
|
class SSHException (Exception):
|
||||||
"""
|
"""
|
||||||
|
@ -34,7 +30,7 @@ class AuthenticationException (SSHException):
|
||||||
possible to retry with different credentials. (Other classes specify more
|
possible to retry with different credentials. (Other classes specify more
|
||||||
specific reasons.)
|
specific reasons.)
|
||||||
|
|
||||||
@since: 1.6
|
.. versionadded:: 1.6
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -52,12 +48,11 @@ class BadAuthenticationType (AuthenticationException):
|
||||||
the server isn't allowing that type. (It may only allow public-key, for
|
the server isn't allowing that type. (It may only allow public-key, for
|
||||||
example.)
|
example.)
|
||||||
|
|
||||||
@ivar allowed_types: list of allowed authentication types provided by the
|
:ivar list allowed_types:
|
||||||
server (possible values are: C{"none"}, C{"password"}, and
|
list of allowed authentication types provided by the server (possible
|
||||||
C{"publickey"}).
|
values are: ``"none"``, ``"password"``, and ``"publickey"``).
|
||||||
@type allowed_types: list
|
|
||||||
|
|
||||||
@since: 1.1
|
.. versionadded:: 1.1
|
||||||
"""
|
"""
|
||||||
allowed_types = []
|
allowed_types = []
|
||||||
|
|
||||||
|
@ -82,12 +77,11 @@ class PartialAuthentication (AuthenticationException):
|
||||||
|
|
||||||
class ChannelException (SSHException):
|
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
|
:ivar int code: the error code returned by the server
|
||||||
@type code: int
|
|
||||||
|
|
||||||
@since: 1.6
|
.. versionadded:: 1.6
|
||||||
"""
|
"""
|
||||||
def __init__(self, code, text):
|
def __init__(self, code, text):
|
||||||
SSHException.__init__(self, 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.
|
The host key given by the SSH server did not match what we were expecting.
|
||||||
|
|
||||||
@ivar hostname: the hostname of the SSH server
|
:ivar str hostname: the hostname of the SSH server
|
||||||
@type hostname: str
|
:ivar PKey got_key: the host key presented by the server
|
||||||
@ivar key: the host key presented by the server
|
:ivar PKey expected_key: the host key expected
|
||||||
@type key: L{PKey}
|
|
||||||
@ivar expected_key: the host key expected
|
|
||||||
@type expected_key: L{PKey}
|
|
||||||
|
|
||||||
@since: 1.6
|
.. versionadded:: 1.6
|
||||||
"""
|
"""
|
||||||
def __init__(self, hostname, got_key, expected_key):
|
def __init__(self, hostname, got_key, expected_key):
|
||||||
SSHException.__init__(self, 'Host key for server %s does not match!' % hostname)
|
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.
|
The "ProxyCommand" found in the .ssh/config file returned an error.
|
||||||
|
|
||||||
@ivar command: The command line that is generating this exception.
|
:ivar str command: The command line that is generating this exception.
|
||||||
@type command: str
|
:ivar str error: The error captured from the proxy command output.
|
||||||
@ivar error: The error captured from the proxy command output.
|
|
||||||
@type error: str
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, command, error):
|
def __init__(self, command, error):
|
||||||
SSHException.__init__(self,
|
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
|
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):
|
if sys.version_info < (2,3):
|
||||||
class enumerate:
|
class enumerate:
|
||||||
def __init__ (self, sequence):
|
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
|
through a secure hash into some keyworthy bytes. This specific algorithm
|
||||||
is used for encrypting/decrypting private key files.
|
is used for encrypting/decrypting private key files.
|
||||||
|
|
||||||
@param hashclass: class from L{Crypto.Hash} that can be used as a secure
|
:param class hashclass:
|
||||||
hashing function (like C{MD5} or C{SHA}).
|
class from `Crypto.Hash` that can be used as a secure hashing function
|
||||||
@type hashclass: L{Crypto.Hash}
|
(like ``MD5`` or ``SHA``).
|
||||||
@param salt: data to salt the hash with.
|
:param str salt: data to salt the hash with.
|
||||||
@type salt: string
|
:param str key: human-entered password or passphrase.
|
||||||
@param key: human-entered password or passphrase.
|
:param int nbytes: number of bytes to generate.
|
||||||
@type key: string
|
:return: Key data `str`
|
||||||
@param nbytes: number of bytes to generate.
|
|
||||||
@type nbytes: int
|
|
||||||
@return: key data
|
|
||||||
@rtype: string
|
|
||||||
"""
|
"""
|
||||||
keydata = ''
|
keydata = ''
|
||||||
digest = ''
|
digest = ''
|
||||||
|
@ -185,26 +181,25 @@ def generate_key_bytes(hashclass, salt, key, nbytes):
|
||||||
def load_host_keys(filename):
|
def load_host_keys(filename):
|
||||||
"""
|
"""
|
||||||
Read a file of known SSH host keys, in the format used by openssh, and
|
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>}.
|
return a compound dict of ``hostname -> keytype ->`` `PKey
|
||||||
The hostname may be an IP address or DNS name. The keytype will be either
|
<paramiko.pkey.PKey>`. The hostname may be an IP address or DNS name. The
|
||||||
C{"ssh-rsa"} or C{"ssh-dss"}.
|
keytype will be either ``"ssh-rsa"`` or ``"ssh-dss"``.
|
||||||
|
|
||||||
This type of file unfortunately doesn't exist on Windows, but on posix,
|
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
|
:param str filename: name of the file to read host keys from
|
||||||
@type filename: str
|
:return:
|
||||||
@return: dict of host keys, indexed by hostname and then keytype
|
nested dict of `.PKey` objects, indexed by hostname and then keytype
|
||||||
@rtype: dict(hostname, dict(keytype, L{PKey <paramiko.pkey.PKey>}))
|
|
||||||
"""
|
"""
|
||||||
from paramiko.hostkeys import HostKeys
|
from paramiko.hostkeys import HostKeys
|
||||||
return HostKeys(filename)
|
return HostKeys(filename)
|
||||||
|
|
||||||
def parse_ssh_config(file_obj):
|
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 = SSHConfig()
|
||||||
config.parse(file_obj)
|
config.parse(file_obj)
|
||||||
|
@ -212,12 +207,12 @@ def parse_ssh_config(file_obj):
|
||||||
|
|
||||||
def lookup_ssh_host_config(hostname, config):
|
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)
|
return config.lookup(hostname)
|
||||||
|
|
||||||
def mod_inverse(x, m):
|
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
|
u1, u2, u3 = 1, 0, m
|
||||||
v1, v2, v3 = 0, 1, x
|
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
|
# Obtain shared config values
|
||||||
import os, sys
|
import os, sys
|
||||||
sys.path.append(os.path.abspath('..'))
|
sys.path.append(os.path.abspath('..'))
|
||||||
|
sys.path.append(os.path.abspath('../..'))
|
||||||
from shared_conf import *
|
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!
|
Welcome to Paramiko's documentation!
|
||||||
====================================
|
====================================
|
||||||
|
|
||||||
This site covers Paramiko's usage & API documentation. For basic info on what
|
This site covers Paramiko's usage & API documentation. For basic info on what
|
||||||
Paramiko is, including its public changelog & how the project is maintained,
|
Paramiko is, including its public changelog & how the project is maintained,
|
||||||
please see `the main website <http://paramiko.org>`_.
|
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
|
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
|
* :bug:`-` Use constant-time hash comparison operations where possible, to
|
||||||
protect against `timing-based attacks
|
protect against `timing-based attacks
|
||||||
<http://codahale.com/a-lesson-in-timing-attacks/>`_. Thanks to Alex Gaynor
|
<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')
|
target = join(dirname(__file__), '..', 'docs', '_build')
|
||||||
if os.environ.get('READTHEDOCS') == 'True':
|
if os.environ.get('READTHEDOCS') == 'True':
|
||||||
# TODO: switch to docs.paramiko.org post go-live of sphinx API docs
|
# TODO: switch to docs.paramiko.org post go-live of sphinx API docs
|
||||||
target = 'http://paramiko-docs.readthedocs.org/en/latest/'
|
target = 'http://docs.paramiko.org/en/latest/'
|
||||||
#intersphinx_mapping = {
|
intersphinx_mapping = {
|
||||||
# 'docs': (target, None),
|
'docs': (target, None),
|
||||||
#}
|
}
|
||||||
|
|
||||||
# Sister-site links to API docs
|
# Sister-site links to API docs
|
||||||
html_theme_options['extra_nav_links'] = {
|
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 os.path import join
|
||||||
|
from shutil import rmtree, copytree
|
||||||
|
|
||||||
from invoke import Collection, ctask as task
|
from invoke import Collection, ctask as task
|
||||||
from invocations import docs as _docs
|
from invocations import docs as _docs
|
||||||
|
from invocations.packaging import publish
|
||||||
|
|
||||||
|
|
||||||
d = 'sites'
|
d = 'sites'
|
||||||
|
|
||||||
# Usage doc/API site (published as docs.paramiko.org)
|
# 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={
|
docs = Collection.from_module(_docs, name='docs', config={
|
||||||
'sphinx.source': path,
|
'sphinx.source': docs_path,
|
||||||
'sphinx.target': join(path, '_build'),
|
'sphinx.target': docs_build,
|
||||||
})
|
})
|
||||||
|
|
||||||
# Main/about/changelog site ((www.)?paramiko.org)
|
# Main/about/changelog site ((www.)?paramiko.org)
|
||||||
path = join(d, 'www')
|
www_path = join(d, 'www')
|
||||||
www = Collection.from_module(_docs, name='www', config={
|
www = Collection.from_module(_docs, name='www', config={
|
||||||
'sphinx.source': path,
|
'sphinx.source': www_path,
|
||||||
'sphinx.target': join(path, '_build'),
|
'sphinx.target': join(www_path, '_build'),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,4 +35,15 @@ def coverage(ctx):
|
||||||
ctx.run("coverage run --source=paramiko test.py --verbose")
|
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