Coverage for /pythoncovmergedfiles/medio/medio/src/paramiko/paramiko/sftp.py: 35%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
2#
3# This file is part of paramiko.
4#
5# Paramiko is free software; you can redistribute it and/or modify it under the
6# terms of the GNU Lesser General Public License as published by the Free
7# Software Foundation; either version 2.1 of the License, or (at your option)
8# any later version.
9#
10# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
11# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
13# details.
14#
15# You should have received a copy of the GNU Lesser General Public License
16# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
17# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19import select
20import socket
21import struct
23from paramiko import util
24from paramiko.common import DEBUG, byte_chr, byte_ord
25from paramiko.message import Message
28(
29 CMD_INIT,
30 CMD_VERSION,
31 CMD_OPEN,
32 CMD_CLOSE,
33 CMD_READ,
34 CMD_WRITE,
35 CMD_LSTAT,
36 CMD_FSTAT,
37 CMD_SETSTAT,
38 CMD_FSETSTAT,
39 CMD_OPENDIR,
40 CMD_READDIR,
41 CMD_REMOVE,
42 CMD_MKDIR,
43 CMD_RMDIR,
44 CMD_REALPATH,
45 CMD_STAT,
46 CMD_RENAME,
47 CMD_READLINK,
48 CMD_SYMLINK,
49) = range(1, 21)
50(CMD_STATUS, CMD_HANDLE, CMD_DATA, CMD_NAME, CMD_ATTRS) = range(101, 106)
51(CMD_EXTENDED, CMD_EXTENDED_REPLY) = range(200, 202)
53SFTP_OK = 0
54(
55 SFTP_EOF,
56 SFTP_NO_SUCH_FILE,
57 SFTP_PERMISSION_DENIED,
58 SFTP_FAILURE,
59 SFTP_BAD_MESSAGE,
60 SFTP_NO_CONNECTION,
61 SFTP_CONNECTION_LOST,
62 SFTP_OP_UNSUPPORTED,
63) = range(1, 9)
65SFTP_DESC = [
66 "Success",
67 "End of file",
68 "No such file",
69 "Permission denied",
70 "Failure",
71 "Bad message",
72 "No connection",
73 "Connection lost",
74 "Operation unsupported",
75]
77SFTP_FLAG_READ = 0x1
78SFTP_FLAG_WRITE = 0x2
79SFTP_FLAG_APPEND = 0x4
80SFTP_FLAG_CREATE = 0x8
81SFTP_FLAG_TRUNC = 0x10
82SFTP_FLAG_EXCL = 0x20
84_VERSION = 3
87# for debugging
88CMD_NAMES = {
89 CMD_INIT: "init",
90 CMD_VERSION: "version",
91 CMD_OPEN: "open",
92 CMD_CLOSE: "close",
93 CMD_READ: "read",
94 CMD_WRITE: "write",
95 CMD_LSTAT: "lstat",
96 CMD_FSTAT: "fstat",
97 CMD_SETSTAT: "setstat",
98 CMD_FSETSTAT: "fsetstat",
99 CMD_OPENDIR: "opendir",
100 CMD_READDIR: "readdir",
101 CMD_REMOVE: "remove",
102 CMD_MKDIR: "mkdir",
103 CMD_RMDIR: "rmdir",
104 CMD_REALPATH: "realpath",
105 CMD_STAT: "stat",
106 CMD_RENAME: "rename",
107 CMD_READLINK: "readlink",
108 CMD_SYMLINK: "symlink",
109 CMD_STATUS: "status",
110 CMD_HANDLE: "handle",
111 CMD_DATA: "data",
112 CMD_NAME: "name",
113 CMD_ATTRS: "attrs",
114 CMD_EXTENDED: "extended",
115 CMD_EXTENDED_REPLY: "extended_reply",
116}
119# TODO: rewrite SFTP file/server modules' overly-flexible "make a request with
120# xyz components" so we don't need this very silly method of signaling whether
121# a given Python integer should be 32- or 64-bit.
122# NOTE: this only became an issue when dropping Python 2 support; prior to
123# doing so, we had to support actual-longs, which served as that signal. This
124# is simply recreating that structure in a more tightly scoped fashion.
125class int64(int):
126 pass
129class SFTPError(Exception):
130 pass
133class BaseSFTP:
134 def __init__(self):
135 self.logger = util.get_logger("paramiko.sftp")
136 self.sock = None
137 self.ultra_debug = False
139 # ...internals...
141 def _send_version(self):
142 m = Message()
143 m.add_int(_VERSION)
144 self._send_packet(CMD_INIT, m)
145 t, data = self._read_packet()
146 if t != CMD_VERSION:
147 raise SFTPError("Incompatible sftp protocol")
148 version = struct.unpack(">I", data[:4])[0]
149 # if version != _VERSION:
150 # raise SFTPError('Incompatible sftp protocol')
151 return version
153 def _send_server_version(self):
154 # winscp will freak out if the server sends version info before the
155 # client finishes sending INIT.
156 t, data = self._read_packet()
157 if t != CMD_INIT:
158 raise SFTPError("Incompatible sftp protocol")
159 version = struct.unpack(">I", data[:4])[0]
160 # advertise that we support "check-file"
161 extension_pairs = ["check-file", "md5,sha1"]
162 msg = Message()
163 msg.add_int(_VERSION)
164 msg.add(*extension_pairs)
165 self._send_packet(CMD_VERSION, msg)
166 return version
168 def _log(self, level, msg, *args):
169 self.logger.log(level, msg, *args)
171 def _write_all(self, out):
172 while len(out) > 0:
173 n = self.sock.send(out)
174 if n <= 0:
175 raise EOFError()
176 if n == len(out):
177 return
178 out = out[n:]
179 return
181 def _read_all(self, n):
182 out = bytes()
183 while n > 0:
184 if isinstance(self.sock, socket.socket):
185 # sometimes sftp is used directly over a socket instead of
186 # through a paramiko channel. in this case, check periodically
187 # if the socket is closed. (for some reason, recv() won't ever
188 # return or raise an exception, but calling select on a closed
189 # socket will.)
190 while True:
191 read, write, err = select.select([self.sock], [], [], 0.1)
192 if len(read) > 0:
193 x = self.sock.recv(n)
194 break
195 else:
196 x = self.sock.recv(n)
198 if len(x) == 0:
199 raise EOFError()
200 out += x
201 n -= len(x)
202 return out
204 def _send_packet(self, t, packet):
205 packet = packet.asbytes()
206 out = struct.pack(">I", len(packet) + 1) + byte_chr(t) + packet
207 if self.ultra_debug:
208 self._log(DEBUG, util.format_binary(out, "OUT: "))
209 self._write_all(out)
211 def _read_packet(self):
212 x = self._read_all(4)
213 # most sftp servers won't accept packets larger than about 32k, so
214 # anything with the high byte set (> 16MB) is just garbage.
215 if byte_ord(x[0]):
216 raise SFTPError("Garbage packet received")
217 size = struct.unpack(">I", x)[0]
218 data = self._read_all(size)
219 if self.ultra_debug:
220 self._log(DEBUG, util.format_binary(data, "IN: "))
221 if size > 0:
222 t = byte_ord(data[0])
223 return t, data[1:]
224 return 0, bytes()