1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 """
20 Client-mode SFTP support.
21 """
22
23 from binascii import hexlify
24 import errno
25 import os
26 import threading
27 import time
28 import weakref
29
30 from paramiko.sftp import *
31 from paramiko.sftp_attr import SFTPAttributes
32 from paramiko.ssh_exception import SSHException
33 from paramiko.sftp_file import SFTPFile
34
35
37 """
38 decode a string as ascii or utf8 if possible (as required by the sftp
39 protocol). if neither works, just return a byte string because the server
40 probably doesn't know the filename's encoding.
41 """
42 try:
43 return s.encode('ascii')
44 except UnicodeError:
45 try:
46 return s.decode('utf-8')
47 except UnicodeError:
48 return s
49
50
52 """
53 SFTP client object. C{SFTPClient} is used to open an sftp session across
54 an open ssh L{Transport} and do remote file operations.
55 """
56
58 """
59 Create an SFTP client from an existing L{Channel}. The channel
60 should already have requested the C{"sftp"} subsystem.
61
62 An alternate way to create an SFTP client context is by using
63 L{from_transport}.
64
65 @param sock: an open L{Channel} using the C{"sftp"} subsystem
66 @type sock: L{Channel}
67
68 @raise SSHException: if there's an exception while negotiating
69 sftp
70 """
71 BaseSFTP.__init__(self)
72 self.sock = sock
73 self.ultra_debug = False
74 self.request_number = 1
75
76 self._lock = threading.Lock()
77 self._cwd = None
78
79 self._expecting = weakref.WeakValueDictionary()
80 if type(sock) is Channel:
81
82 transport = self.sock.get_transport()
83 self.logger = util.get_logger(transport.get_log_channel() + '.sftp')
84 self.ultra_debug = transport.get_hexdump()
85 try:
86 server_version = self._send_version()
87 except EOFError, x:
88 raise SSHException('EOF during negotiation')
89 self._log(INFO, 'Opened sftp connection (server version %d)' % server_version)
90
92 """
93 Create an SFTP client channel from an open L{Transport}.
94
95 @param t: an open L{Transport} which is already authenticated
96 @type t: L{Transport}
97 @return: a new L{SFTPClient} object, referring to an sftp session
98 (channel) across the transport
99 @rtype: L{SFTPClient}
100 """
101 chan = t.open_session()
102 if chan is None:
103 return None
104 chan.invoke_subsystem('sftp')
105 return cls(chan)
106 from_transport = classmethod(from_transport)
107
108 - def _log(self, level, msg, *args):
109 super(SFTPClient, self)._log(level, "[chan %s] " + msg, *([ self.sock.get_name() ] + list(args)))
110
112 """
113 Close the SFTP session and its underlying channel.
114
115 @since: 1.4
116 """
117 self._log(INFO, 'sftp session closed.')
118 self.sock.close()
119
121 """
122 Return the underlying L{Channel} object for this SFTP session. This
123 might be useful for doing things like setting a timeout on the channel.
124
125 @return: the SSH channel
126 @rtype: L{Channel}
127
128 @since: 1.7.1
129 """
130 return self.sock
131
133 """
134 Return a list containing the names of the entries in the given C{path}.
135 The list is in arbitrary order. It does not include the special
136 entries C{'.'} and C{'..'} even if they are present in the folder.
137 This method is meant to mirror C{os.listdir} as closely as possible.
138 For a list of full L{SFTPAttributes} objects, see L{listdir_attr}.
139
140 @param path: path to list (defaults to C{'.'})
141 @type path: str
142 @return: list of filenames
143 @rtype: list of str
144 """
145 return [f.filename for f in self.listdir_attr(path)]
146
148 """
149 Return a list containing L{SFTPAttributes} objects corresponding to
150 files in the given C{path}. The list is in arbitrary order. It does
151 not include the special entries C{'.'} and C{'..'} even if they are
152 present in the folder.
153
154 The returned L{SFTPAttributes} objects will each have an additional
155 field: C{longname}, which may contain a formatted string of the file's
156 attributes, in unix format. The content of this string will probably
157 depend on the SFTP server implementation.
158
159 @param path: path to list (defaults to C{'.'})
160 @type path: str
161 @return: list of attributes
162 @rtype: list of L{SFTPAttributes}
163
164 @since: 1.2
165 """
166 path = self._adjust_cwd(path)
167 self._log(DEBUG, 'listdir(%r)' % path)
168 t, msg = self._request(CMD_OPENDIR, path)
169 if t != CMD_HANDLE:
170 raise SFTPError('Expected handle')
171 handle = msg.get_string()
172 filelist = []
173 while True:
174 try:
175 t, msg = self._request(CMD_READDIR, handle)
176 except EOFError, e:
177
178 break
179 if t != CMD_NAME:
180 raise SFTPError('Expected name response')
181 count = msg.get_int()
182 for i in range(count):
183 filename = _to_unicode(msg.get_string())
184 longname = _to_unicode(msg.get_string())
185 attr = SFTPAttributes._from_msg(msg, filename, longname)
186 if (filename != '.') and (filename != '..'):
187 filelist.append(attr)
188 self._request(CMD_CLOSE, handle)
189 return filelist
190
191 - def open(self, filename, mode='r', bufsize=-1):
192 """
193 Open a file on the remote server. The arguments are the same as for
194 python's built-in C{file} (aka C{open}). A file-like object is
195 returned, which closely mimics the behavior of a normal python file
196 object.
197
198 The mode indicates how the file is to be opened: C{'r'} for reading,
199 C{'w'} for writing (truncating an existing file), C{'a'} for appending,
200 C{'r+'} for reading/writing, C{'w+'} for reading/writing (truncating an
201 existing file), C{'a+'} for reading/appending. The python C{'b'} flag
202 is ignored, since SSH treats all files as binary. The C{'U'} flag is
203 supported in a compatible way.
204
205 Since 1.5.2, an C{'x'} flag indicates that the operation should only
206 succeed if the file was created and did not previously exist. This has
207 no direct mapping to python's file flags, but is commonly known as the
208 C{O_EXCL} flag in posix.
209
210 The file will be buffered in standard python style by default, but
211 can be altered with the C{bufsize} parameter. C{0} turns off
212 buffering, C{1} uses line buffering, and any number greater than 1
213 (C{>1}) uses that specific buffer size.
214
215 @param filename: name of the file to open
216 @type filename: str
217 @param mode: mode (python-style) to open in
218 @type mode: str
219 @param bufsize: desired buffering (-1 = default buffer size)
220 @type bufsize: int
221 @return: a file object representing the open file
222 @rtype: SFTPFile
223
224 @raise IOError: if the file could not be opened.
225 """
226 filename = self._adjust_cwd(filename)
227 self._log(DEBUG, 'open(%r, %r)' % (filename, mode))
228 imode = 0
229 if ('r' in mode) or ('+' in mode):
230 imode |= SFTP_FLAG_READ
231 if ('w' in mode) or ('+' in mode) or ('a' in mode):
232 imode |= SFTP_FLAG_WRITE
233 if ('w' in mode):
234 imode |= SFTP_FLAG_CREATE | SFTP_FLAG_TRUNC
235 if ('a' in mode):
236 imode |= SFTP_FLAG_CREATE | SFTP_FLAG_APPEND
237 if ('x' in mode):
238 imode |= SFTP_FLAG_CREATE | SFTP_FLAG_EXCL
239 attrblock = SFTPAttributes()
240 t, msg = self._request(CMD_OPEN, filename, imode, attrblock)
241 if t != CMD_HANDLE:
242 raise SFTPError('Expected handle')
243 handle = msg.get_string()
244 self._log(DEBUG, 'open(%r, %r) -> %s' % (filename, mode, hexlify(handle)))
245 return SFTPFile(self, handle, mode, bufsize)
246
247
248 file = open
249
251 """
252 Remove the file at the given path. This only works on files; for
253 removing folders (directories), use L{rmdir}.
254
255 @param path: path (absolute or relative) of the file to remove
256 @type path: str
257
258 @raise IOError: if the path refers to a folder (directory)
259 """
260 path = self._adjust_cwd(path)
261 self._log(DEBUG, 'remove(%r)' % path)
262 self._request(CMD_REMOVE, path)
263
264 unlink = remove
265
266 - def rename(self, oldpath, newpath):
267 """
268 Rename a file or folder from C{oldpath} to C{newpath}.
269
270 @param oldpath: existing name of the file or folder
271 @type oldpath: str
272 @param newpath: new name for the file or folder
273 @type newpath: str
274
275 @raise IOError: if C{newpath} is a folder, or something else goes
276 wrong
277 """
278 oldpath = self._adjust_cwd(oldpath)
279 newpath = self._adjust_cwd(newpath)
280 self._log(DEBUG, 'rename(%r, %r)' % (oldpath, newpath))
281 self._request(CMD_RENAME, oldpath, newpath)
282
283 - def mkdir(self, path, mode=0777):
284 """
285 Create a folder (directory) named C{path} with numeric mode C{mode}.
286 The default mode is 0777 (octal). On some systems, mode is ignored.
287 Where it is used, the current umask value is first masked out.
288
289 @param path: name of the folder to create
290 @type path: str
291 @param mode: permissions (posix-style) for the newly-created folder
292 @type mode: int
293 """
294 path = self._adjust_cwd(path)
295 self._log(DEBUG, 'mkdir(%r, %r)' % (path, mode))
296 attr = SFTPAttributes()
297 attr.st_mode = mode
298 self._request(CMD_MKDIR, path, attr)
299
301 """
302 Remove the folder named C{path}.
303
304 @param path: name of the folder to remove
305 @type path: str
306 """
307 path = self._adjust_cwd(path)
308 self._log(DEBUG, 'rmdir(%r)' % path)
309 self._request(CMD_RMDIR, path)
310
311 - def stat(self, path):
312 """
313 Retrieve information about a file on the remote system. The return
314 value is an object whose attributes correspond to the attributes of
315 python's C{stat} structure as returned by C{os.stat}, except that it
316 contains fewer fields. An SFTP server may return as much or as little
317 info as it wants, so the results may vary from server to server.
318
319 Unlike a python C{stat} object, the result may not be accessed as a
320 tuple. This is mostly due to the author's slack factor.
321
322 The fields supported are: C{st_mode}, C{st_size}, C{st_uid}, C{st_gid},
323 C{st_atime}, and C{st_mtime}.
324
325 @param path: the filename to stat
326 @type path: str
327 @return: an object containing attributes about the given file
328 @rtype: SFTPAttributes
329 """
330 path = self._adjust_cwd(path)
331 self._log(DEBUG, 'stat(%r)' % path)
332 t, msg = self._request(CMD_STAT, path)
333 if t != CMD_ATTRS:
334 raise SFTPError('Expected attributes')
335 return SFTPAttributes._from_msg(msg)
336
338 """
339 Retrieve information about a file on the remote system, without
340 following symbolic links (shortcuts). This otherwise behaves exactly
341 the same as L{stat}.
342
343 @param path: the filename to stat
344 @type path: str
345 @return: an object containing attributes about the given file
346 @rtype: SFTPAttributes
347 """
348 path = self._adjust_cwd(path)
349 self._log(DEBUG, 'lstat(%r)' % path)
350 t, msg = self._request(CMD_LSTAT, path)
351 if t != CMD_ATTRS:
352 raise SFTPError('Expected attributes')
353 return SFTPAttributes._from_msg(msg)
354
356 """
357 Create a symbolic link (shortcut) of the C{source} path at
358 C{destination}.
359
360 @param source: path of the original file
361 @type source: str
362 @param dest: path of the newly created symlink
363 @type dest: str
364 """
365 dest = self._adjust_cwd(dest)
366 self._log(DEBUG, 'symlink(%r, %r)' % (source, dest))
367 if type(source) is unicode:
368 source = source.encode('utf-8')
369 self._request(CMD_SYMLINK, source, dest)
370
371 - def chmod(self, path, mode):