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):
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):
372 """
373 Change the mode (permissions) of a file. The permissions are
374 unix-style and identical to those used by python's C{os.chmod}
375 function.
376
377 @param path: path of the file to change the permissions of
378 @type path: str
379 @param mode: new permissions
380 @type mode: int
381 """
382 path = self._adjust_cwd(path)
383 self._log(DEBUG, 'chmod(%r, %r)' % (path, mode))
384 attr = SFTPAttributes()
385 attr.st_mode = mode
386 self._request(CMD_SETSTAT, path, attr)
387
388 - def chown(self, path, uid, gid):
389 """
390 Change the owner (C{uid}) and group (C{gid}) of a file. As with
391 python's C{os.chown} function, you must pass both arguments, so if you
392 only want to change one, use L{stat} first to retrieve the current
393 owner and group.
394
395 @param path: path of the file to change the owner and group of
396 @type path: str
397 @param uid: new owner's uid
398 @type uid: int
399 @param gid: new group id
400 @type gid: int
401 """
402 path = self._adjust_cwd(path)
403 self._log(DEBUG, 'chown(%r, %r, %r)' % (path, uid, gid))
404 attr = SFTPAttributes()
405 attr.st_uid, attr.st_gid = uid, gid
406 self._request(CMD_SETSTAT, path, attr)
407
408 - def utime(self, path, times):
409 """
410 Set the access and modified times of the file specified by C{path}. If
411 C{times} is C{None}, then the file's access and modified times are set
412 to the current time. Otherwise, C{times} must be a 2-tuple of numbers,
413 of the form C{(atime, mtime)}, which is used to set the access and
414 modified times, respectively. This bizarre API is mimicked from python
415 for the sake of consistency -- I apologize.
416
417 @param path: path of the file to modify
418 @type path: str
419 @param times: C{None} or a tuple of (access time, modified time) in
420 standard internet epoch time (seconds since 01 January 1970 GMT)
421 @type times: tuple(int)
422 """
423 path = self._adjust_cwd(path)
424 if times is None:
425 times = (time.time(), time.time())
426 self._log(DEBUG, 'utime(%r, %r)' % (path, times))
427 attr = SFTPAttributes()
428 attr.st_atime, attr.st_mtime = times
429 self._request(CMD_SETSTAT, path, attr)
430
432 """
433 Change the size of the file specified by C{path}. This usually extends
434 or shrinks the size of the file, just like the C{truncate()} method on
435 python file objects.
436
437 @param path: path of the file to modify
438 @type path: str
439 @param size: the new size of the file
440 @type size: int or long
441 """
442 path = self._adjust_cwd(path)
443 self._log(DEBUG, 'truncate(%r, %r)' % (path, size))
444 attr = SFTPAttributes()
445 attr.st_size = size
446 self._request(CMD_SETSTAT, path, attr)
447
449 """
450 Return the target of a symbolic link (shortcut). You can use
451 L{symlink} to create these. The result may be either an absolute or
452 relative pathname.
453
454 @param path: path of the symbolic link file
455 @type path: str
456 @return: target path
457 @rtype: str
458 """
459 path = self._adjust_cwd(path)
460 self._log(DEBUG, 'readlink(%r)' % path)
461 t, msg = self._request(CMD_READLINK, path)
462 if t != CMD_NAME:
463 raise SFTPError('Expected name response')
464 count = msg.get_int()
465 if count == 0:
466 return None
467 if count != 1:
468 raise SFTPError('Readlink returned %d results' % count)
469 return _to_unicode(msg.get_string())
470
472 """
473 Return the normalized path (on the server) of a given path. This
474 can be used to quickly resolve symbolic links or determine what the
475 server is considering to be the "current folder" (by passing C{'.'}
476 as C{path}).
477
478 @param path: path to be normalized
479 @type path: str
480 @return: normalized form of the given path
481 @rtype: str
482
483 @raise IOError: if the path can't be resolved on the server
484 """
485 path = self._adjust_cwd(path)
486 self._log(DEBUG, 'normalize(%r)' % path)
487 t, msg = self._request(CMD_REALPATH, path)
488 if t != CMD_NAME:
489 raise SFTPError('Expected name response')
490 count = msg.get_int()
491 if count != 1:
492 raise SFTPError('Realpath returned %d results' % count)
493 return _to_unicode(msg.get_string())
494
496 """
497 Change the "current directory" of this SFTP session. Since SFTP
498 doesn't really have the concept of a current working directory, this
499 is emulated by paramiko. Once you use this method to set a working
500 directory, all operations on this SFTPClient object will be relative
501 to that path.
502
503 @param path: new current working directory
504 @type path: str
505
506 @raise IOError: if the requested path doesn't exist on the server
507
508 @since: 1.4
509 """
510 self._cwd = self.normalize(path)
511
513 """
514 Return the "current working directory" for this SFTP session, as
515 emulated by paramiko. If no directory has been set with L{chdir},
516 this method will return C{None}.
517
518 @return: the current working directory on the server, or C{None}
519 @rtype: str
520
521 @since: 1.4
522 """
523 return self._cwd
524
525 - def put(self, localpath, remotepath):
526 """
527 Copy a local file (C{localpath}) to the SFTP server as C{remotepath}.
528 Any exception raised by operations will be passed through. This
529 method is primarily provided as a convenience.
530
531 The SFTP operations use pipelining for speed.
532
533 @param localpath: the local file to copy
534 @type localpath: str
535 @param remotepath: the destination path on the SFTP server
536 @type remotepath: str
537
538 @since: 1.4
539 """
540 fl = file(localpath, 'rb')
541 fr = self.file(remotepath, 'wb')
542 fr.set_pipelined(True)
543 size = 0
544 while True:
545 data = fl.read(32768)
546 if len(data) == 0:
547 break
548 fr.write(data)
549 size += len(data)
550 fl.close()
551 fr.close()
552 s = self.stat(remotepath)
553 if s.st_size != size:
554 raise IOError('size mismatch in put! %d != %d' % (s.st_size, size))
555
556 - def get(self, remotepath, localpath):
557 """
558 Copy a remote file (C{remotepath}) from the SFTP server to the local
559 host as C{localpath}. Any exception raised by operations will be
560 passed through. This method is primarily provided as a convenience.
561
562 @param remotepath: the remote file to copy
563 @type remotepath: str
564 @param localpath: the destination path on the local host
565 @type localpath: str
566
567 @since: 1.4
568 """
569 fr = self.file(remotepath, 'rb')
570 fr.prefetch()
571 fl = file(localpath, 'wb')
572 size = 0
573 while True:
574 data = fr.read(32768)
575 if len(data) == 0:
576 break
577 fl.write(data)
578 size += len(data)
579 fl.close()
580 fr.close()
581 s = os.stat(localpath)
582 if s.st_size != size:
583 raise IOError('size mismatch in get! %d != %d' % (s.st_size, size))
584
585
586
587
588
590 num = self._async_request(type(None), t, *arg)
591 return self._read_response(num)
592
594
595 self._lock.acquire()
596 try:
597 msg = Message()
598 msg.add_int(self.request_number)
599 for item in arg:
600 if type(item) is int:
601 msg.add_int(item)
602 elif type(item) is long:
603 msg.add_int64(item)
604 elif type(item) is str:
605 msg.add_string(item)
606 elif type(item) is SFTPAttributes:
607 item._pack(msg)
608 else:
609 raise Exception('unknown type for %r type %r' % (item, type(item)))
610 num = self.request_number
611 self._expecting[num] = fileobj
612 self._send_packet(t, str(msg))
613 self.request_number += 1
614 finally:
615 self._lock.release()
616 return num
617
619 while True:
620 try:
621 t, data = self._read_packet()
622 except EOFError, e:
623 raise SSHException('Server connection dropped: %s' % (str(e),))
624 msg = Message(data)
625 num = msg.get_int()
626 if num not in self._expecting:
627
628 self._log(DEBUG, 'Unexpected response #%d' % (num,))
629 if waitfor is None:
630
631 break
632 continue
633 fileobj = self._expecting[num]
634 del self._expecting[num]
635 if num == waitfor:
636
637 if t == CMD_STATUS:
638 self._convert_status(msg)
639 return t, msg
640 if fileobj is not type(None):
641 fileobj._async_response(t, msg)
642 if waitfor is None:
643
644 break
645 return (None, None)
646
648 while fileobj in self._expecting.values():
649 self._read_response()
650 fileobj._check_exception()
651
653 """
654 Raises EOFError or IOError on error status; otherwise does nothing.
655 """
656 code = msg.get_int()
657 text = msg.get_string()
658 if code == SFTP_OK:
659 return
660 elif code == SFTP_EOF:
661 raise EOFError(text)
662 elif code == SFTP_NO_SUCH_FILE:
663
664 raise IOError(errno.ENOENT, text)
665 elif code == SFTP_PERMISSION_DENIED:
666 raise IOError(errno.EACCES, text)
667 else:
668 raise IOError(text)
669
671 """
672 Return an adjusted path if we're emulating a "current working
673 directory" for the server.
674 """
675 if type(path) is unicode:
676 path = path.encode('utf-8')
677 if self._cwd is None:
678 return path
679 if (len(path) > 0) and (path[0] == '/'):
680
681 return path
682 if self._cwd == '/':
683 return self._cwd + path
684 return self._cwd + '/' + path
685
686
687 -class SFTP (SFTPClient):
688 "an alias for L{SFTPClient} for backwards compatability"
689 pass
690