Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(142)

Side by Side Diff: third_party/twisted_8_1/twisted/conch/ssh/filetransfer.py

Issue 12261012: Remove third_party/twisted_8_1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # -*- test-case-name: twisted.conch.test.test_filetransfer -*-
2 #
3 # Copyright (c) 2001-2008 Twisted Matrix Laboratories.
4 # See LICENSE for details.
5
6
7 import struct, errno
8
9 from twisted.internet import defer, protocol
10 from twisted.python import failure, log
11
12 from common import NS, getNS
13 from twisted.conch.interfaces import ISFTPServer, ISFTPFile
14
15 from zope import interface
16
17
18
19 class FileTransferBase(protocol.Protocol):
20
21 versions = (3, )
22
23 packetTypes = {}
24
25 def __init__(self):
26 self.buf = ''
27 self.otherVersion = None # this gets set
28
29 def sendPacket(self, kind, data):
30 self.transport.write(struct.pack('!LB', len(data)+1, kind) + data)
31
32 def dataReceived(self, data):
33 self.buf += data
34 while len(self.buf) > 5:
35 length, kind = struct.unpack('!LB', self.buf[:5])
36 if len(self.buf) < 4 + length:
37 return
38 data, self.buf = self.buf[5:4+length], self.buf[4+length:]
39 packetType = self.packetTypes.get(kind, None)
40 if not packetType:
41 log.msg('no packet type for', kind)
42 continue
43 f = getattr(self, 'packet_%s' % packetType, None)
44 if not f:
45 log.msg('not implemented: %s' % packetType)
46 log.msg(repr(data[4:]))
47 reqId, = struct.unpack('!L', data[:4])
48 self._sendStatus(reqId, FX_OP_UNSUPPORTED,
49 "don't understand %s" % packetType)
50 #XXX not implemented
51 continue
52 try:
53 f(data)
54 except:
55 log.err()
56 continue
57 reqId ,= struct.unpack('!L', data[:4])
58 self._ebStatus(failure.Failure(e), reqId)
59
60 def _parseAttributes(self, data):
61 flags ,= struct.unpack('!L', data[:4])
62 attrs = {}
63 data = data[4:]
64 if flags & FILEXFER_ATTR_SIZE == FILEXFER_ATTR_SIZE:
65 size ,= struct.unpack('!Q', data[:8])
66 attrs['size'] = size
67 data = data[8:]
68 if flags & FILEXFER_ATTR_OWNERGROUP == FILEXFER_ATTR_OWNERGROUP:
69 uid, gid = struct.unpack('!2L', data[:8])
70 attrs['uid'] = uid
71 attrs['gid'] = gid
72 data = data[8:]
73 if flags & FILEXFER_ATTR_PERMISSIONS == FILEXFER_ATTR_PERMISSIONS:
74 perms ,= struct.unpack('!L', data[:4])
75 attrs['permissions'] = perms
76 data = data[4:]
77 if flags & FILEXFER_ATTR_ACMODTIME == FILEXFER_ATTR_ACMODTIME:
78 atime, mtime = struct.unpack('!2L', data[:8])
79 attrs['atime'] = atime
80 attrs['mtime'] = mtime
81 data = data[8:]
82 if flags & FILEXFER_ATTR_EXTENDED == FILEXFER_ATTR_EXTENDED:
83 extended_count ,= struct.unpack('!L', data[:4])
84 data = data[4:]
85 for i in xrange(extended_count):
86 extended_type, data = getNS(data)
87 extended_data, data = getNS(data)
88 attrs['ext_%s' % extended_type] = extended_data
89 return attrs, data
90
91 def _packAttributes(self, attrs):
92 flags = 0
93 data = ''
94 if 'size' in attrs:
95 data += struct.pack('!Q', attrs['size'])
96 flags |= FILEXFER_ATTR_SIZE
97 if 'uid' in attrs and 'gid' in attrs:
98 data += struct.pack('!2L', attrs['uid'], attrs['gid'])
99 flags |= FILEXFER_ATTR_OWNERGROUP
100 if 'permissions' in attrs:
101 data += struct.pack('!L', attrs['permissions'])
102 flags |= FILEXFER_ATTR_PERMISSIONS
103 if 'atime' in attrs and 'mtime' in attrs:
104 data += struct.pack('!2L', attrs['atime'], attrs['mtime'])
105 flags |= FILEXFER_ATTR_ACMODTIME
106 extended = []
107 for k in attrs:
108 if k.startswith('ext_'):
109 ext_type = NS(k[4:])
110 ext_data = NS(attrs[k])
111 extended.append(ext_type+ext_data)
112 if extended:
113 data += struct.pack('!L', len(extended))
114 data += ''.join(extended)
115 flags |= FILEXFER_ATTR_EXTENDED
116 return struct.pack('!L', flags) + data
117
118 class FileTransferServer(FileTransferBase):
119
120 def __init__(self, data=None, avatar=None):
121 FileTransferBase.__init__(self)
122 self.client = ISFTPServer(avatar) # yay interfaces
123 self.openFiles = {}
124 self.openDirs = {}
125
126 def packet_INIT(self, data):
127 version ,= struct.unpack('!L', data[:4])
128 self.version = min(list(self.versions) + [version])
129 data = data[4:]
130 ext = {}
131 while data:
132 ext_name, data = getNS(data)
133 ext_data, data = getNS(data)
134 ext[ext_name] = ext_data
135 our_ext = self.client.gotVersion(version, ext)
136 our_ext_data = ""
137 for (k,v) in our_ext.items():
138 our_ext_data += NS(k) + NS(v)
139 self.sendPacket(FXP_VERSION, struct.pack('!L', self.version) + \
140 our_ext_data)
141
142 def packet_OPEN(self, data):
143 requestId = data[:4]
144 data = data[4:]
145 filename, data = getNS(data)
146 flags ,= struct.unpack('!L', data[:4])
147 data = data[4:]
148 attrs, data = self._parseAttributes(data)
149 assert data == '', 'still have data in OPEN: %s' % repr(data)
150 d = defer.maybeDeferred(self.client.openFile, filename, flags, attrs)
151 d.addCallback(self._cbOpenFile, requestId)
152 d.addErrback(self._ebStatus, requestId, "open failed")
153
154 def _cbOpenFile(self, fileObj, requestId):
155 fileId = str(hash(fileObj))
156 if fileId in self.openFiles:
157 raise KeyError, 'id already open'
158 self.openFiles[fileId] = fileObj
159 self.sendPacket(FXP_HANDLE, requestId + NS(fileId))
160
161 def packet_CLOSE(self, data):
162 requestId = data[:4]
163 data = data[4:]
164 handle, data = getNS(data)
165 assert data == '', 'still have data in CLOSE: %s' % repr(data)
166 if handle in self.openFiles:
167 fileObj = self.openFiles[handle]
168 d = defer.maybeDeferred(fileObj.close)
169 d.addCallback(self._cbClose, handle, requestId)
170 d.addErrback(self._ebStatus, requestId, "close failed")
171 elif handle in self.openDirs:
172 dirObj = self.openDirs[handle][0]
173 d = defer.maybeDeferred(dirObj.close)
174 d.addCallback(self._cbClose, handle, requestId, 1)
175 d.addErrback(self._ebStatus, requestId, "close failed")
176 else:
177 self._ebClose(failure.Failure(KeyError()), requestId)
178
179 def _cbClose(self, result, handle, requestId, isDir = 0):
180 if isDir:
181 del self.openDirs[handle]
182 else:
183 del self.openFiles[handle]
184 self._sendStatus(requestId, FX_OK, 'file closed')
185
186 def packet_READ(self, data):
187 requestId = data[:4]
188 data = data[4:]
189 handle, data = getNS(data)
190 (offset, length), data = struct.unpack('!QL', data[:12]), data[12:]
191 assert data == '', 'still have data in READ: %s' % repr(data)
192 if handle not in self.openFiles:
193 self._ebRead(failure.Failure(KeyError()), requestId)
194 else:
195 fileObj = self.openFiles[handle]
196 d = defer.maybeDeferred(fileObj.readChunk, offset, length)
197 d.addCallback(self._cbRead, requestId)
198 d.addErrback(self._ebStatus, requestId, "read failed")
199
200 def _cbRead(self, result, requestId):
201 if result == '': # python's read will return this for EOF
202 raise EOFError()
203 self.sendPacket(FXP_DATA, requestId + NS(result))
204
205 def packet_WRITE(self, data):
206 requestId = data[:4]
207 data = data[4:]
208 handle, data = getNS(data)
209 offset, = struct.unpack('!Q', data[:8])
210 data = data[8:]
211 writeData, data = getNS(data)
212 assert data == '', 'still have data in WRITE: %s' % repr(data)
213 if handle not in self.openFiles:
214 self._ebWrite(failure.Failure(KeyError()), requestId)
215 else:
216 fileObj = self.openFiles[handle]
217 d = defer.maybeDeferred(fileObj.writeChunk, offset, writeData)
218 d.addCallback(self._cbStatus, requestId, "write succeeded")
219 d.addErrback(self._ebStatus, requestId, "write failed")
220
221 def packet_REMOVE(self, data):
222 requestId = data[:4]
223 data = data[4:]
224 filename, data = getNS(data)
225 assert data == '', 'still have data in REMOVE: %s' % repr(data)
226 d = defer.maybeDeferred(self.client.removeFile, filename)
227 d.addCallback(self._cbStatus, requestId, "remove succeeded")
228 d.addErrback(self._ebStatus, requestId, "remove failed")
229
230 def packet_RENAME(self, data):
231 requestId = data[:4]
232 data = data[4:]
233 oldPath, data = getNS(data)
234 newPath, data = getNS(data)
235 assert data == '', 'still have data in RENAME: %s' % repr(data)
236 d = defer.maybeDeferred(self.client.renameFile, oldPath, newPath)
237 d.addCallback(self._cbStatus, requestId, "rename succeeded")
238 d.addErrback(self._ebStatus, requestId, "rename failed")
239
240 def packet_MKDIR(self, data):
241 requestId = data[:4]
242 data = data[4:]
243 path, data = getNS(data)
244 attrs, data = self._parseAttributes(data)
245 assert data == '', 'still have data in MKDIR: %s' % repr(data)
246 d = defer.maybeDeferred(self.client.makeDirectory, path, attrs)
247 d.addCallback(self._cbStatus, requestId, "mkdir succeeded")
248 d.addErrback(self._ebStatus, requestId, "mkdir failed")
249
250 def packet_RMDIR(self, data):
251 requestId = data[:4]
252 data = data[4:]
253 path, data = getNS(data)
254 assert data == '', 'still have data in RMDIR: %s' % repr(data)
255 d = defer.maybeDeferred(self.client.removeDirectory, path)
256 d.addCallback(self._cbStatus, requestId, "rmdir succeeded")
257 d.addErrback(self._ebStatus, requestId, "rmdir failed")
258
259 def packet_OPENDIR(self, data):
260 requestId = data[:4]
261 data = data[4:]
262 path, data = getNS(data)
263 assert data == '', 'still have data in OPENDIR: %s' % repr(data)
264 d = defer.maybeDeferred(self.client.openDirectory, path)
265 d.addCallback(self._cbOpenDirectory, requestId)
266 d.addErrback(self._ebStatus, requestId, "opendir failed")
267
268 def _cbOpenDirectory(self, dirObj, requestId):
269 handle = str(hash(dirObj))
270 if handle in self.openDirs:
271 raise KeyError, "already opened this directory"
272 self.openDirs[handle] = [dirObj, iter(dirObj)]
273 self.sendPacket(FXP_HANDLE, requestId + NS(handle))
274
275 def packet_READDIR(self, data):
276 requestId = data[:4]
277 data = data[4:]
278 handle, data = getNS(data)
279 assert data == '', 'still have data in READDIR: %s' % repr(data)
280 if handle not in self.openDirs:
281 self._ebStatus(failure.Failure(KeyError()), requestId)
282 else:
283 dirObj, dirIter = self.openDirs[handle]
284 d = defer.maybeDeferred(self._scanDirectory, dirIter, [])
285 d.addCallback(self._cbSendDirectory, requestId)
286 d.addErrback(self._ebStatus, requestId, "scan directory failed")
287
288 def _scanDirectory(self, dirIter, f):
289 while len(f) < 250:
290 try:
291 info = dirIter.next()
292 except StopIteration:
293 if not f:
294 raise EOFError
295 return f
296 if isinstance(info, defer.Deferred):
297 info.addCallback(self._cbScanDirectory, dirIter, f)
298 return
299 else:
300 f.append(info)
301 return f
302
303 def _cbScanDirectory(self, result, dirIter, f):
304 f.append(result)
305 return self._scanDirectory(dirIter, f)
306
307 def _cbSendDirectory(self, result, requestId):
308 data = ''
309 for (filename, longname, attrs) in result:
310 data += NS(filename)
311 data += NS(longname)
312 data += self._packAttributes(attrs)
313 self.sendPacket(FXP_NAME, requestId +
314 struct.pack('!L', len(result))+data)
315
316 def packet_STAT(self, data, followLinks = 1):
317 requestId = data[:4]
318 data = data[4:]
319 path, data = getNS(data)
320 assert data == '', 'still have data in STAT/LSTAT: %s' % repr(data)
321 d = defer.maybeDeferred(self.client.getAttrs, path, followLinks)
322 d.addCallback(self._cbStat, requestId)
323 d.addErrback(self._ebStatus, requestId, 'stat/lstat failed')
324
325 def packet_LSTAT(self, data):
326 self.packet_STAT(data, 0)
327
328 def packet_FSTAT(self, data):
329 requestId = data[:4]
330 data = data[4:]
331 handle, data = getNS(data)
332 assert data == '', 'still have data in FSTAT: %s' % repr(data)
333 if handle not in self.openFiles:
334 self._ebStatus(failure.Failure(KeyError('%s not in self.openFiles'
335 % handle)), requestId)
336 else:
337 fileObj = self.openFiles[handle]
338 d = defer.maybeDeferred(fileObj.getAttrs)
339 d.addCallback(self._cbStat, requestId)
340 d.addErrback(self._ebStatus, requestId, 'fstat failed')
341
342 def _cbStat(self, result, requestId):
343 data = requestId + self._packAttributes(result)
344 self.sendPacket(FXP_ATTRS, data)
345
346 def packet_SETSTAT(self, data):
347 requestId = data[:4]
348 data = data[4:]
349 path, data = getNS(data)
350 attrs, data = self._parseAttributes(data)
351 if data != '':
352 log.msg('WARN: still have data in SETSTAT: %s' % repr(data))
353 d = defer.maybeDeferred(self.client.setAttrs, path, attrs)
354 d.addCallback(self._cbStatus, requestId, 'setstat succeeded')
355 d.addErrback(self._ebStatus, requestId, 'setstat failed')
356
357 def packet_FSETSTAT(self, data):
358 requestId = data[:4]
359 data = data[4:]
360 handle, data = getNS(data)
361 attrs, data = self._parseAttributes(data)
362 assert data == '', 'still have data in FSETSTAT: %s' % repr(data)
363 if handle not in self.openFiles:
364 self._ebStatus(failure.Failure(KeyError()), requestId)
365 else:
366 fileObj = self.openFiles[handle]
367 d = defer.maybeDeferred(fileObj.setAttrs, attrs)
368 d.addCallback(self._cbStatus, requestId, 'fsetstat succeeded')
369 d.addErrback(self._ebStatus, requestId, 'fsetstat failed')
370
371 def packet_READLINK(self, data):
372 requestId = data[:4]
373 data = data[4:]
374 path, data = getNS(data)
375 assert data == '', 'still have data in READLINK: %s' % repr(data)
376 d = defer.maybeDeferred(self.client.readLink, path)
377 d.addCallback(self._cbReadLink, requestId)
378 d.addErrback(self._ebStatus, requestId, 'readlink failed')
379
380 def _cbReadLink(self, result, requestId):
381 self._cbSendDirectory([(result, '', {})], requestId)
382
383 def packet_SYMLINK(self, data):
384 requestId = data[:4]
385 data = data[4:]
386 linkPath, data = getNS(data)
387 targetPath, data = getNS(data)
388 d = defer.maybeDeferred(self.client.makeLink, linkPath, targetPath)
389 d.addCallback(self._cbStatus, requestId, 'symlink succeeded')
390 d.addErrback(self._ebStatus, requestId, 'symlink failed')
391
392 def packet_REALPATH(self, data):
393 requestId = data[:4]
394 data = data[4:]
395 path, data = getNS(data)
396 assert data == '', 'still have data in REALPATH: %s' % repr(data)
397 d = defer.maybeDeferred(self.client.realPath, path)
398 d.addCallback(self._cbReadLink, requestId) # same return format
399 d.addErrback(self._ebStatus, requestId, 'realpath failed')
400
401 def packet_EXTENDED(self, data):
402 requestId = data[:4]
403 data = data[4:]
404 extName, extData = getNS(data)
405 d = defer.maybeDeferred(self.client.extendedRequest, extName, extData)
406 d.addCallback(self._cbExtended, requestId)
407 d.addErrback(self._ebStatus, requestId, 'extended %s failed' % extName)
408
409 def _cbExtended(self, data, requestId):
410 self.sendPacket(FXP_EXTENDED_REPLY, requestId + data)
411
412 def _cbStatus(self, result, requestId, msg = "request succeeded"):
413 self._sendStatus(requestId, FX_OK, msg)
414
415 def _ebStatus(self, reason, requestId, msg = "request failed"):
416 code = FX_FAILURE
417 message = msg
418 if reason.type in (IOError, OSError):
419 if reason.value.errno == errno.ENOENT: # no such file
420 code = FX_NO_SUCH_FILE
421 message = reason.value.strerror
422 elif reason.value.errno == errno.EACCES: # permission denied
423 code = FX_PERMISSION_DENIED
424 message = reason.value.strerror
425 else:
426 log.err(reason)
427 elif reason.type == EOFError: # EOF
428 code = FX_EOF
429 if reason.value.args:
430 message = reason.value.args[0]
431 elif reason.type == NotImplementedError:
432 code = FX_OP_UNSUPPORTED
433 if reason.value.args:
434 message = reason.value.args[0]
435 elif reason.type == SFTPError:
436 code = reason.value.code
437 message = reason.value.message
438 else:
439 log.err(reason)
440 self._sendStatus(requestId, code, message)
441
442 def _sendStatus(self, requestId, code, message, lang = ''):
443 """
444 Helper method to send a FXP_STATUS message.
445 """
446 data = requestId + struct.pack('!L', code)
447 data += NS(message)
448 data += NS(lang)
449 self.sendPacket(FXP_STATUS, data)
450
451 class FileTransferClient(FileTransferBase):
452
453 def __init__(self, extData = {}):
454 """
455 @param extData: a dict of extended_name : extended_data items
456 to be sent to the server.
457 """
458 FileTransferBase.__init__(self)
459 self.extData = {}
460 self.counter = 0
461 self.openRequests = {} # id -> Deferred
462 self.wasAFile = {} # Deferred -> 1 TERRIBLE HACK
463
464 def connectionMade(self):
465 data = struct.pack('!L', max(self.versions))
466 for k,v in self.extData.itervalues():
467 data += NS(k) + NS(v)
468 self.sendPacket(FXP_INIT, data)
469
470 def _sendRequest(self, msg, data):
471 data = struct.pack('!L', self.counter) + data
472 d = defer.Deferred()
473 self.openRequests[self.counter] = d
474 self.counter += 1
475 self.sendPacket(msg, data)
476 return d
477
478 def _parseRequest(self, data):
479 (id,) = struct.unpack('!L', data[:4])
480 d = self.openRequests[id]
481 del self.openRequests[id]
482 return d, data[4:]
483
484 def openFile(self, filename, flags, attrs):
485 """
486 Open a file.
487
488 This method returns a L{Deferred} that is called back with an object
489 that provides the L{ISFTPFile} interface.
490
491 @param filename: a string representing the file to open.
492
493 @param flags: a integer of the flags to open the file with, ORed togethe r.
494 The flags and their values are listed at the bottom of this file.
495
496 @param attrs: a list of attributes to open the file with. It is a
497 dictionary, consisting of 0 or more keys. The possible keys are::
498
499 size: the size of the file in bytes
500 uid: the user ID of the file as an integer
501 gid: the group ID of the file as an integer
502 permissions: the permissions of the file with as an integer.
503 the bit representation of this field is defined by POSIX.
504 atime: the access time of the file as seconds since the epoch.
505 mtime: the modification time of the file as seconds since the epoch.
506 ext_*: extended attributes. The server is not required to
507 understand this, but it may.
508
509 NOTE: there is no way to indicate text or binary files. it is up
510 to the SFTP client to deal with this.
511 """
512 data = NS(filename) + struct.pack('!L', flags) + self._packAttributes(at trs)
513 d = self._sendRequest(FXP_OPEN, data)
514 self.wasAFile[d] = (1, filename) # HACK
515 return d
516
517 def removeFile(self, filename):
518 """
519 Remove the given file.
520
521 This method returns a Deferred that is called back when it succeeds.
522
523 @param filename: the name of the file as a string.
524 """
525 return self._sendRequest(FXP_REMOVE, NS(filename))
526
527 def renameFile(self, oldpath, newpath):
528 """
529 Rename the given file.
530
531 This method returns a Deferred that is called back when it succeeds.
532
533 @param oldpath: the current location of the file.
534 @param newpath: the new file name.
535 """
536 return self._sendRequest(FXP_RENAME, NS(oldpath)+NS(newpath))
537
538 def makeDirectory(self, path, attrs):
539 """
540 Make a directory.
541
542 This method returns a Deferred that is called back when it is
543 created.
544
545 @param path: the name of the directory to create as a string.
546
547 @param attrs: a dictionary of attributes to create the directory
548 with. Its meaning is the same as the attrs in the openFile method.
549 """
550 return self._sendRequest(FXP_MKDIR, NS(path)+self._packAttributes(attrs) )
551
552 def removeDirectory(self, path):
553 """
554 Remove a directory (non-recursively)
555
556 It is an error to remove a directory that has files or directories in
557 it.
558
559 This method returns a Deferred that is called back when it is removed.
560
561 @param path: the directory to remove.
562 """
563 return self._sendRequest(FXP_RMDIR, NS(path))
564
565 def openDirectory(self, path):
566 """
567 Open a directory for scanning.
568
569 This method returns a Deferred that is called back with an iterable
570 object that has a close() method.
571
572 The close() method is called when the client is finished reading
573 from the directory. At this point, the iterable will no longer
574 be used.
575
576 The iterable returns triples of the form (filename, longname, attrs)
577 or a Deferred that returns the same. The sequence must support
578 __getitem__, but otherwise may be any 'sequence-like' object.
579
580 filename is the name of the file relative to the directory.
581 logname is an expanded format of the filename. The recommended format
582 is:
583 -rwxr-xr-x 1 mjos staff 348911 Mar 25 14:29 t-filexfer
584 1234567890 123 12345678 12345678 12345678 123456789012
585
586 The first line is sample output, the second is the length of the field.
587 The fields are: permissions, link count, user owner, group owner,
588 size in bytes, modification time.
589
590 attrs is a dictionary in the format of the attrs argument to openFile.
591
592 @param path: the directory to open.
593 """
594 d = self._sendRequest(FXP_OPENDIR, NS(path))
595 self.wasAFile[d] = (0, path)
596 return d
597
598 def getAttrs(self, path, followLinks=0):
599 """
600 Return the attributes for the given path.
601
602 This method returns a dictionary in the same format as the attrs
603 argument to openFile or a Deferred that is called back with same.
604
605 @param path: the path to return attributes for as a string.
606 @param followLinks: a boolean. if it is True, follow symbolic links
607 and return attributes for the real path at the base. if it is False,
608 return attributes for the specified path.
609 """
610 if followLinks: m = FXP_STAT
611 else: m = FXP_LSTAT
612 return self._sendRequest(m, NS(path))
613
614 def setAttrs(self, path, attrs):
615 """
616 Set the attributes for the path.
617
618 This method returns when the attributes are set or a Deferred that is
619 called back when they are.
620
621 @param path: the path to set attributes for as a string.
622 @param attrs: a dictionary in the same format as the attrs argument to
623 openFile.
624 """
625 data = NS(path) + self._packAttributes(attrs)
626 return self._sendRequest(FXP_SETSTAT, data)
627
628 def readLink(self, path):
629 """
630 Find the root of a set of symbolic links.
631
632 This method returns the target of the link, or a Deferred that
633 returns the same.
634
635 @param path: the path of the symlink to read.
636 """
637 d = self._sendRequest(FXP_READLINK, NS(path))
638 return d.addCallback(self._cbRealPath)
639
640 def makeLink(self, linkPath, targetPath):
641 """
642 Create a symbolic link.
643
644 This method returns when the link is made, or a Deferred that
645 returns the same.
646
647 @param linkPath: the pathname of the symlink as a string
648 @param targetPath: the path of the target of the link as a string.
649 """
650 return self._sendRequest(FXP_SYMLINK, NS(linkPath)+NS(targetPath))
651
652 def realPath(self, path):
653 """
654 Convert any path to an absolute path.
655
656 This method returns the absolute path as a string, or a Deferred
657 that returns the same.
658
659 @param path: the path to convert as a string.
660 """
661 d = self._sendRequest(FXP_REALPATH, NS(path))
662 return d.addCallback(self._cbRealPath)
663
664 def _cbRealPath(self, result):
665 name, longname, attrs = result[0]
666 return name
667
668 def extendedRequest(self, request, data):
669 """
670 Make an extended request of the server.
671
672 The method returns a Deferred that is called back with
673 the result of the extended request.
674
675 @param request: the name of the extended request to make.
676 @param data: any other data that goes along with the request.
677 """
678 return self._sendRequest(FXP_EXTENDED, NS(request) + data)
679
680 def packet_VERSION(self, data):
681 version, = struct.unpack('!L', data[:4])
682 data = data[4:]
683 d = {}
684 while data:
685 k, data = getNS(data)
686 v, data = getNS(data)
687 d[k]=v
688 self.version = version
689 self.gotServerVersion(version, d)
690
691 def packet_STATUS(self, data):
692 d, data = self._parseRequest(data)
693 code, = struct.unpack('!L', data[:4])
694 data = data[4:]
695 msg, data = getNS(data)
696 lang = getNS(data)
697 if code == FX_OK:
698 d.callback((msg, lang))
699 elif code == FX_EOF:
700 d.errback(EOFError(msg))
701 elif code == FX_OP_UNSUPPORTED:
702 d.errback(NotImplementedError(msg))
703 else:
704 d.errback(SFTPError(code, msg, lang))
705
706 def packet_HANDLE(self, data):
707 d, data = self._parseRequest(data)
708 isFile, name = self.wasAFile.pop(d)
709 if isFile:
710 cb = ClientFile(self, getNS(data)[0])
711 else:
712 cb = ClientDirectory(self, getNS(data)[0])
713 cb.name = name
714 d.callback(cb)
715
716 def packet_DATA(self, data):
717 d, data = self._parseRequest(data)
718 d.callback(getNS(data)[0])
719
720 def packet_NAME(self, data):
721 d, data = self._parseRequest(data)
722 count, = struct.unpack('!L', data[:4])
723 data = data[4:]
724 files = []
725 for i in range(count):
726 filename, data = getNS(data)
727 longname, data = getNS(data)
728 attrs, data = self._parseAttributes(data)
729 files.append((filename, longname, attrs))
730 d.callback(files)
731
732 def packet_ATTRS(self, data):
733 d, data = self._parseRequest(data)
734 d.callback(self._parseAttributes(data)[0])
735
736 def packet_EXTENDED_REPLY(self, data):
737 d, data = self._parseRequest(data)
738 d.callback(data)
739
740 def gotServerVersion(self, serverVersion, extData):
741 """
742 Called when the client sends their version info.
743
744 @param otherVersion: an integer representing the version of the SFTP
745 protocol they are claiming.
746 @param extData: a dictionary of extended_name : extended_data items.
747 These items are sent by the client to indicate additional features.
748 """
749
750 class ClientFile:
751
752 interface.implements(ISFTPFile)
753
754 def __init__(self, parent, handle):
755 self.parent = parent
756 self.handle = NS(handle)
757
758 def close(self):
759 return self.parent._sendRequest(FXP_CLOSE, self.handle)
760
761 def readChunk(self, offset, length):
762 data = self.handle + struct.pack("!QL", offset, length)
763 return self.parent._sendRequest(FXP_READ, data)
764
765 def writeChunk(self, offset, chunk):
766 data = self.handle + struct.pack("!Q", offset) + NS(chunk)
767 return self.parent._sendRequest(FXP_WRITE, data)
768
769 def getAttrs(self):
770 return self.parent._sendRequest(FXP_FSTAT, self.handle)
771
772 def setAttrs(self, attrs):
773 data = self.handle + self.parent._packAttributes(attrs)
774 return self.parent._sendRequest(FXP_FSTAT, data)
775
776 class ClientDirectory:
777
778 def __init__(self, parent, handle):
779 self.parent = parent
780 self.handle = NS(handle)
781 self.filesCache = []
782
783 def read(self):
784 d = self.parent._sendRequest(FXP_READDIR, self.handle)
785 return d
786
787 def close(self):
788 return self.parent._sendRequest(FXP_CLOSE, self.handle)
789
790 def __iter__(self):
791 return self
792
793 def next(self):
794 if self.filesCache:
795 return self.filesCache.pop(0)
796 d = self.read()
797 d.addCallback(self._cbReadDir)
798 d.addErrback(self._ebReadDir)
799 return d
800
801 def _cbReadDir(self, names):
802 self.filesCache = names[1:]
803 return names[0]
804
805 def _ebReadDir(self, reason):
806 reason.trap(EOFError)
807 def _():
808 raise StopIteration
809 self.next = _
810 return reason
811
812
813 class SFTPError(Exception):
814
815 def __init__(self, errorCode, errorMessage, lang = ''):
816 Exception.__init__(self)
817 self.code = errorCode
818 self.message = errorMessage
819 self.lang = lang
820
821 def __str__(self):
822 return 'SFTPError %s: %s' % (self.code, self.message)
823
824 FXP_INIT = 1
825 FXP_VERSION = 2
826 FXP_OPEN = 3
827 FXP_CLOSE = 4
828 FXP_READ = 5
829 FXP_WRITE = 6
830 FXP_LSTAT = 7
831 FXP_FSTAT = 8
832 FXP_SETSTAT = 9
833 FXP_FSETSTAT = 10
834 FXP_OPENDIR = 11
835 FXP_READDIR = 12
836 FXP_REMOVE = 13
837 FXP_MKDIR = 14
838 FXP_RMDIR = 15
839 FXP_REALPATH = 16
840 FXP_STAT = 17
841 FXP_RENAME = 18
842 FXP_READLINK = 19
843 FXP_SYMLINK = 20
844 FXP_STATUS = 101
845 FXP_HANDLE = 102
846 FXP_DATA = 103
847 FXP_NAME = 104
848 FXP_ATTRS = 105
849 FXP_EXTENDED = 200
850 FXP_EXTENDED_REPLY = 201
851
852 FILEXFER_ATTR_SIZE = 0x00000001
853 FILEXFER_ATTR_OWNERGROUP = 0x00000002
854 FILEXFER_ATTR_PERMISSIONS = 0x00000004
855 FILEXFER_ATTR_ACMODTIME = 0x00000009
856 FILEXFER_ATTR_EXTENDED = 0x80000000L
857
858 FILEXFER_TYPE_REGULAR = 1
859 FILEXFER_TYPE_DIRECTORY = 2
860 FILEXFER_TYPE_SYMLINK = 3
861 FILEXFER_TYPE_SPECIAL = 4
862 FILEXFER_TYPE_UNKNOWN = 5
863
864 FXF_READ = 0x00000001
865 FXF_WRITE = 0x00000002
866 FXF_APPEND = 0x00000004
867 FXF_CREAT = 0x00000008
868 FXF_TRUNC = 0x00000010
869 FXF_EXCL = 0x00000020
870 FXF_TEXT = 0x00000040
871
872 FX_OK = 0
873 FX_EOF = 1
874 FX_NO_SUCH_FILE = 2
875 FX_PERMISSION_DENIED = 3
876 FX_FAILURE = 4
877 FX_BAD_MESSAGE = 5
878 FX_NO_CONNECTION = 6
879 FX_CONNECTION_LOST = 7
880 FX_OP_UNSUPPORTED = 8
881 # http://www.ietf.org/internet-drafts/draft-ietf-secsh-filexfer-12.txt defines
882 # more useful error codes, but so far OpenSSH doesn't implement them. We use
883 # them internally for clarity, but for now define them all as FX_FAILURE to be
884 # compatible with existing software.
885 FX_FILE_ALREADY_EXISTS = FX_FAILURE
886 FX_NOT_A_DIRECTORY = FX_FAILURE
887 FX_FILE_IS_A_DIRECTORY = FX_FAILURE
888
889
890 # initialize FileTransferBase.packetTypes:
891 g = globals()
892 for name in g.keys():
893 if name.startswith('FXP_'):
894 value = g[name]
895 FileTransferBase.packetTypes[value] = name[4:]
896 del g, name, value
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/conch/ssh/factory.py ('k') | third_party/twisted_8_1/twisted/conch/ssh/forwarding.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698