Package paramiko :: Module sftp_client
[frames] | no frames]

Source Code for Module paramiko.sftp_client

  1  # Copyright (C) 2003-2007  Robey Pointer <robey@lag.net> 
  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 distrubuted 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  # 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA. 
 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   
36 -def _to_unicode(s):
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
51 -class SFTPClient (BaseSFTP):
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
57 - def __init__(self, sock):
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 # lock for request_number 76 self._lock = threading.Lock() 77 self._cwd = None 78 # request # -> SFTPFile 79 self._expecting = weakref.WeakValueDictionary() 80 if type(sock) is Channel: 81 # override default logger 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
91 - def from_transport(cls, t):
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
111 - def close(self):
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
120 - def get_channel(self):
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
132 - def listdir(self, path='.'):
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
147 - def listdir_attr(self, path='.'):
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 # done with handle 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 # python continues to vacillate about "open" vs "file"... 248 file = open 249
250 - def remove(self, path):
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
300 - def rmdir(self, path):
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
337 - def lstat(self, path):
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 370
371 - def chmod(self, path, mode):
372 """