net-sftp-backports 4.0.0.backports

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,315 @@
1
+ require 'net/ssh/buffer'
2
+
3
+ module Net; module SFTP; module Protocol; module V01
4
+
5
+ # A class representing the attributes of a file or directory on the server.
6
+ # It may be used to specify new attributes, or to query existing attributes.
7
+ #
8
+ # To specify new attributes, just pass a hash as the argument to the
9
+ # constructor. The following keys are supported:
10
+ #
11
+ # * :size:: the size of the file
12
+ # * :uid:: the user-id that owns the file (integer)
13
+ # * :gid:: the group-id that owns the file (integer)
14
+ # * :owner:: the name of the user that owns the file (string)
15
+ # * :group:: the name of the group that owns the file (string)
16
+ # * :permissions:: the permissions on the file (integer, e.g. 0755)
17
+ # * :atime:: the access time of the file (integer, seconds since epoch)
18
+ # * :mtime:: the modification time of the file (integer, seconds since epoch)
19
+ # * :extended:: a hash of name/value pairs identifying extended info
20
+ #
21
+ # Likewise, when the server sends an Attributes object, all of the
22
+ # above attributes are exposed as methods (though not all will be set with
23
+ # non-nil values from the server).
24
+ class Attributes
25
+
26
+ F_SIZE = 0x00000001
27
+ F_UIDGID = 0x00000002
28
+ F_PERMISSIONS = 0x00000004
29
+ F_ACMODTIME = 0x00000008
30
+ F_EXTENDED = 0x80000000
31
+
32
+ T_REGULAR = 1
33
+ T_DIRECTORY = 2
34
+ T_SYMLINK = 3
35
+ T_SPECIAL = 4
36
+ T_UNKNOWN = 5
37
+ T_SOCKET = 6
38
+ T_CHAR_DEVICE = 7
39
+ T_BLOCK_DEVICE = 8
40
+ T_FIFO = 9
41
+
42
+ class <<self
43
+ # Returns the array of attribute meta-data that defines the structure of
44
+ # the attributes packet as described by this version of the protocol.
45
+ def elements #:nodoc:
46
+ @elements ||= [
47
+ [:size, :int64, F_SIZE],
48
+ [:uid, :long, F_UIDGID],
49
+ [:gid, :long, F_UIDGID],
50
+ [:permissions, :long, F_PERMISSIONS],
51
+ [:atime, :long, F_ACMODTIME],
52
+ [:mtime, :long, F_ACMODTIME],
53
+ [:extended, :special, F_EXTENDED]
54
+ ]
55
+ end
56
+
57
+ # Parses the given buffer and returns an Attributes object compsed from
58
+ # the data extracted from it.
59
+ def from_buffer(buffer)
60
+ flags = buffer.read_long
61
+ data = {}
62
+
63
+ elements.each do |name, type, condition|
64
+ if flags & condition == condition
65
+ if type == :special
66
+ data[name] = send("parse_#{name}", buffer)
67
+ else
68
+ data[name] = buffer.send("read_#{type}")
69
+ end
70
+ end
71
+ end
72
+
73
+ new(data)
74
+ end
75
+
76
+ # A convenience method for defining methods that expose specific
77
+ # attributes. This redefines the standard attr_accessor (an admittedly
78
+ # bad practice) because (1) I don't need any "regular" accessors, and
79
+ # (2) because rdoc will automatically pick up and note methods defined
80
+ # via attr_accessor.
81
+ def attr_accessor(name) #:nodoc:
82
+ class_eval <<-CODE
83
+ def #{name}
84
+ attributes[:#{name}]
85
+ end
86
+ CODE
87
+
88
+ attr_writer(name)
89
+ end
90
+
91
+ # A convenience method for defining methods that expose specific
92
+ # attributes. This redefines the standard attr_writer (an admittedly
93
+ # bad practice) because (1) I don't need any "regular" accessors, and
94
+ # (2) because rdoc will automatically pick up and note methods defined
95
+ # via attr_writer.
96
+ def attr_writer(name) #:nodoc:
97
+ class_eval <<-CODE
98
+ def #{name}=(value)
99
+ attributes[:#{name}] = value
100
+ end
101
+ CODE
102
+ end
103
+
104
+ private
105
+
106
+ # Parse the hash of extended data from the buffer.
107
+ def parse_extended(buffer)
108
+ extended = Hash.new
109
+ buffer.read_long.times do
110
+ extended[buffer.read_string] = buffer.read_string
111
+ end
112
+ extended
113
+ end
114
+ end
115
+
116
+ # The hash of name/value pairs that backs this Attributes instance
117
+ attr_reader :attributes
118
+
119
+ # The size of the file.
120
+ attr_accessor :size
121
+
122
+ # The user-id of the user that owns the file
123
+ attr_writer :uid
124
+
125
+ # The group-id of the user that owns the file
126
+ attr_writer :gid
127
+
128
+ # The permissions on the file
129
+ attr_accessor :permissions
130
+
131
+ # The last access time of the file
132
+ attr_accessor :atime
133
+
134
+ # The modification time of the file
135
+ attr_accessor :mtime
136
+
137
+ # The hash of name/value pairs identifying extended information about the file
138
+ attr_accessor :extended
139
+
140
+ # Create a new Attributes instance with the given attributes. The
141
+ # following keys are supported:
142
+ #
143
+ # * :size:: the size of the file
144
+ # * :uid:: the user-id that owns the file (integer)
145
+ # * :gid:: the group-id that owns the file (integer)
146
+ # * :owner:: the name of the user that owns the file (string)
147
+ # * :group:: the name of the group that owns the file (string)
148
+ # * :permissions:: the permissions on the file (integer, e.g. 0755)
149
+ # * :atime:: the access time of the file (integer, seconds since epoch)
150
+ # * :mtime:: the modification time of the file (integer, seconds since epoch)
151
+ # * :extended:: a hash of name/value pairs identifying extended info
152
+ def initialize(attributes={})
153
+ @attributes = attributes
154
+ end
155
+
156
+ # Returns the user-id of the user that owns the file, or +nil+ if that
157
+ # information is not available. If an :owner key exists, but not a :uid
158
+ # key, the Etc module will be used to reverse lookup the id from the name.
159
+ # This might fail on some systems (e.g., Windows).
160
+ def uid
161
+ if attributes[:owner] && !attributes.key?(:uid)
162
+ require 'etc'
163
+ attributes[:uid] = Etc.getpwnam(attributes[:owner]).uid
164
+ end
165
+ attributes[:uid]
166
+ end
167
+
168
+ # Returns the group-id of the group that owns the file, or +nil+ if that
169
+ # information is not available. If a :group key exists, but not a :gid
170
+ # key, the Etc module will be used to reverse lookup the id from the name.
171
+ # This might fail on some systems (e.g., Windows).
172
+ def gid
173
+ if attributes[:group] && !attributes.key?(:gid)
174
+ require 'etc'
175
+ attributes[:gid] = Etc.getgrnam(attributes[:group]).gid
176
+ end
177
+ attributes[:gid]
178
+ end
179
+
180
+ # Returns the username of the user that owns the file, or +nil+ if that
181
+ # information is not available. If the :uid is given, but not the :owner,
182
+ # the Etc module will be used to lookup the name from the id. This might
183
+ # fail on some systems (e.g. Windows).
184
+ def owner
185
+ if attributes[:uid] && !attributes[:owner]
186
+ require 'etc'
187
+ attributes[:owner] = Etc.getpwuid(attributes[:uid].to_i).name
188
+ end
189
+ attributes[:owner]
190
+ end
191
+
192
+ # Returns the group name of the group that owns the file, or +nil+ if that
193
+ # information is not available. If the :gid is given, but not the :group,
194
+ # the Etc module will be used to lookup the name from the id. This might
195
+ # fail on some systems (e.g. Windows).
196
+ def group
197
+ if attributes[:gid] && !attributes[:group]
198
+ require 'etc'
199
+ attributes[:group] = Etc.getgrgid(attributes[:gid].to_i).name
200
+ end
201
+ attributes[:group]
202
+ end
203
+
204
+ # Inspects the permissions bits to determine what type of entity this
205
+ # attributes object represents. If will return one of the T_ constants.
206
+ def type
207
+ if permissions & 0140000 == 0140000 then
208
+ T_SOCKET
209
+ elsif permissions & 0120000 == 0120000 then
210
+ T_SYMLINK
211
+ elsif permissions & 0100000 == 0100000 then
212
+ T_REGULAR
213
+ elsif permissions & 060000 == 060000 then
214
+ T_BLOCK_DEVICE
215
+ elsif permissions & 040000 == 040000 then
216
+ T_DIRECTORY
217
+ elsif permissions & 020000 == 020000 then
218
+ T_CHAR_DEVICE
219
+ elsif permissions & 010000 == 010000 then
220
+ T_FIFO
221
+ else
222
+ T_UNKNOWN
223
+ end
224
+ end
225
+
226
+ # Returns the type as a symbol, rather than an integer, for easier use in
227
+ # Ruby programs.
228
+ def symbolic_type
229
+ case type
230
+ when T_SOCKET then :socket
231
+ when T_SYMLINK then :symlink
232
+ when T_REGULAR then :regular
233
+ when T_BLOCK_DEVICE then :block_device
234
+ when T_DIRECTORY then :directory
235
+ when T_CHAR_DEVICE then :char_device
236
+ when T_FIFO then :fifo
237
+ when T_SPECIAL then :special
238
+ when T_UNKNOWN then :unknown
239
+ else raise NotImplementedError, "unknown file type #{type} (bug?)"
240
+ end
241
+ end
242
+
243
+ # Returns true if these attributes appear to describe a directory.
244
+ def directory?
245
+ case type
246
+ when T_DIRECTORY then true
247
+ when T_UNKNOWN then nil
248
+ else false
249
+ end
250
+ end
251
+
252
+ # Returns true if these attributes appear to describe a symlink.
253
+ def symlink?
254
+ case type
255
+ when T_SYMLINK then true
256
+ when T_UNKNOWN then nil
257
+ else false
258
+ end
259
+ end
260
+
261
+ # Returns true if these attributes appear to describe a regular file.
262
+ def file?
263
+ case type
264
+ when T_REGULAR then true
265
+ when T_UNKNOWN then nil
266
+ else false
267
+ end
268
+ end
269
+
270
+ # Convert the object to a string suitable for passing in an SFTP
271
+ # packet. This is the raw representation of the attribute packet payload,
272
+ # and is not intended to be human readable.
273
+ def to_s
274
+ prepare_serialization!
275
+
276
+ flags = 0
277
+
278
+ self.class.elements.each do |name, type, condition|
279
+ flags |= condition if attributes[name]
280
+ end
281
+
282
+ buffer = Net::SSH::Buffer.from(:long, flags)
283
+ self.class.elements.each do |name, type, condition|
284
+ if flags & condition == condition
285
+ if type == :special
286
+ send("encode_#{name}", buffer)
287
+ else
288
+ buffer.send("write_#{type}", attributes[name])
289
+ end
290
+ end
291
+ end
292
+
293
+ buffer.to_s
294
+ end
295
+
296
+ private
297
+
298
+ # Perform protocol-version-specific preparations for serialization.
299
+ def prepare_serialization!
300
+ # force the uid/gid to be translated from owner/group, if those keys
301
+ # were given on instantiation
302
+ uid
303
+ gid
304
+ end
305
+
306
+ # Encodes information about the extended info onto the end of the given
307
+ # buffer.
308
+ def encode_extended(buffer)
309
+ buffer.write_long extended.size
310
+ extended.each { |k,v| buffer.write_string k, v }
311
+ end
312
+
313
+ end
314
+
315
+ end ; end ; end ; end
@@ -0,0 +1,268 @@
1
+ require 'net/ssh/loggable'
2
+ require 'net/sftp/constants'
3
+ require 'net/sftp/packet'
4
+ require 'net/sftp/protocol/base'
5
+ require 'net/sftp/protocol/01/attributes'
6
+ require 'net/sftp/protocol/01/name'
7
+
8
+ module Net; module SFTP; module Protocol; module V01
9
+
10
+ # Wraps the low-level SFTP calls for version 1 of the SFTP protocol. Also
11
+ # implements the packet parsing as defined by version 1 of the protocol.
12
+ #
13
+ # None of these protocol methods block--all of them return immediately,
14
+ # requiring the SSH event loop to be run while the server response is
15
+ # pending.
16
+ #
17
+ # You will almost certainly never need to use this driver directly. Please
18
+ # see Net::SFTP::Session for the recommended interface.
19
+ class Base < Protocol::Base
20
+ include Net::SFTP::Constants::OpenFlags
21
+
22
+ # Returns the protocol version implemented by this driver. (1, in this
23
+ # case)
24
+ def version
25
+ 1
26
+ end
27
+
28
+ # Parses the given FXP_HANDLE packet and returns a hash with one key,
29
+ # :handle, which references the handle.
30
+ def parse_handle_packet(packet)
31
+ { :handle => packet.read_string }
32
+ end
33
+
34
+ # Parses the given FXP_STATUS packet and returns a hash with one key,
35
+ # :code, which references the status code returned by the server.
36
+ def parse_status_packet(packet)
37
+ { :code => packet.read_long }
38
+ end
39
+
40
+ # Parses the given FXP_DATA packet and returns a hash with one key,
41
+ # :data, which references the data returned in the packet.
42
+ def parse_data_packet(packet)
43
+ { :data => packet.read_string }
44
+ end
45
+
46
+ # Parses the given FXP_ATTRS packet and returns a hash with one key,
47
+ # :attrs, which references an Attributes object.
48
+ def parse_attrs_packet(packet)
49
+ { :attrs => attribute_factory.from_buffer(packet) }
50
+ end
51
+
52
+ # Parses the given FXP_NAME packet and returns a hash with one key, :names,
53
+ # which references an array of Name objects.
54
+ def parse_name_packet(packet)
55
+ names = []
56
+
57
+ packet.read_long.times do
58
+ filename = packet.read_string
59
+ longname = packet.read_string
60
+ attrs = attribute_factory.from_buffer(packet)
61
+ names << name_factory.new(filename, longname, attrs)
62
+ end
63
+
64
+ { :names => names }
65
+ end
66
+
67
+ # Sends a FXP_OPEN packet to the server and returns the packet identifier.
68
+ # The +flags+ parameter is either an integer (in which case it must be
69
+ # a combination of the IO constants) or a string (in which case it must
70
+ # be one of the mode strings that IO::open accepts). The +options+
71
+ # parameter is a hash that is used to construct a new Attribute object,
72
+ # to pass as part of the FXP_OPEN request.
73
+ def open(path, flags, options)
74
+ flags = normalize_open_flags(flags)
75
+
76
+ if flags & (IO::WRONLY | IO::RDWR) != 0
77
+ sftp_flags = FV1::WRITE
78
+ sftp_flags |= FV1::READ if flags & IO::RDWR != 0
79
+ sftp_flags |= FV1::APPEND if flags & IO::APPEND != 0
80
+ else
81
+ sftp_flags = FV1::READ
82
+ end
83
+
84
+ sftp_flags |= FV1::CREAT if flags & IO::CREAT != 0
85
+ sftp_flags |= FV1::TRUNC if flags & IO::TRUNC != 0
86
+ sftp_flags |= FV1::EXCL if flags & IO::EXCL != 0
87
+
88
+ attributes = attribute_factory.new(options)
89
+
90
+ send_request(FXP_OPEN, :string, path, :long, sftp_flags, :raw, attributes.to_s)
91
+ end
92
+
93
+ # Sends a FXP_CLOSE packet to the server for the given +handle+ (such as
94
+ # would be returned via a FXP_HANDLE packet). Returns the new packet id.
95
+ def close(handle)
96
+ send_request(FXP_CLOSE, :string, handle)
97
+ end
98
+
99
+ # Sends a FXP_READ packet to the server, requesting that +length+ bytes
100
+ # be read from the file identified by +handle+, starting at +offset+ bytes
101
+ # within the file. The handle must be one that was returned via a
102
+ # FXP_HANDLE packet. Returns the new packet id.
103
+ def read(handle, offset, length)
104
+ send_request(FXP_READ, :string, handle, :int64, offset, :long, length)
105
+ end
106
+
107
+ # Sends a FXP_WRITE packet to the server, requesting that +data+ (a string),
108
+ # be written to the file identified by +handle+, starting at +offset+ bytes
109
+ # from the beginning of the file. The handle must be one that was returned
110
+ # via a FXP_HANDLE packet. Returns the new packet id.
111
+ def write(handle, offset, data)
112
+ send_request(FXP_WRITE, :string, handle, :int64, offset, :string, data)
113
+ end
114
+
115
+ # Sends a FXP_LSTAT packet to the server, requesting a FXP_ATTR response
116
+ # for the file at the given remote +path+ (a string). The +flags+ parameter
117
+ # is ignored in this version of the protocol. #lstat will not follow
118
+ # symbolic links; see #stat for a version that will.
119
+ def lstat(path, flags=nil)
120
+ send_request(FXP_LSTAT, :string, path)
121
+ end
122
+
123
+ # Sends a FXP_FSTAT packet to the server, requesting a FXP_ATTR response
124
+ # for the file represented by the given +handle+ (which must have been
125
+ # obtained from a FXP_HANDLE packet). The +flags+ parameter is ignored in
126
+ # this version of the protocol.
127
+ def fstat(handle, flags=nil)
128
+ send_request(FXP_FSTAT, :string, handle)
129
+ end
130
+
131
+ # Sends a FXP_SETSTAT packet to the server, to update the attributes for
132
+ # the file at the given remote +path+ (a string). The +attrs+ parameter is
133
+ # a hash that defines the attributes to set.
134
+ def setstat(path, attrs)
135
+ send_request(FXP_SETSTAT, :string, path, :raw, attribute_factory.new(attrs).to_s)
136
+ end
137
+
138
+ # Sends a FXP_FSETSTAT packet to the server, to update the attributes for
139
+ # the file represented by the given +handle+ (which must have been obtained
140
+ # from a FXP_HANDLE packet). The +attrs+ parameter is a hash that defines
141
+ # the attributes to set.
142
+ def fsetstat(handle, attrs)
143
+ send_request(FXP_FSETSTAT, :string, handle, :raw, attribute_factory.new(attrs).to_s)
144
+ end
145
+
146
+ # Sends a FXP_OPENDIR packet to the server, to request a handle for
147
+ # manipulating the directory at the given remote +path+.
148
+ def opendir(path)
149
+ send_request(FXP_OPENDIR, :string, path)
150
+ end
151
+
152
+ # Sends a FXP_READDIR packet to the server, to request a batch of
153
+ # directory name entries in the directory identified by +handle+ (which
154
+ # must have been obtained via a FXP_OPENDIR request).
155
+ def readdir(handle)
156
+ send_request(FXP_READDIR, :string, handle)
157
+ end
158
+
159
+ # Sends a FXP_REMOTE packet to the server, to request that the given
160
+ # file be deleted from the remote server.
161
+ def remove(filename)
162
+ send_request(FXP_REMOVE, :string, filename)
163
+ end
164
+
165
+ # Sends a FXP_MKDIR packet to the server, to request that a new directory
166
+ # at +path+ on the remote server be created, and with +attrs+ (a hash)
167
+ # describing the attributes of the new directory.
168
+ def mkdir(path, attrs)
169
+ send_request(FXP_MKDIR, :string, path, :raw, attribute_factory.new(attrs).to_s)
170
+ end
171
+
172
+ # Sends a FXP_RMDIR packet to the server, to request that the directory
173
+ # at +path+ on the remote server be deleted.
174
+ def rmdir(path)
175
+ send_request(FXP_RMDIR, :string, path)
176
+ end
177
+
178
+ # Sends a FXP_REALPATH packet to the server, to request that the given
179
+ # +path+ be canonicalized, taking into account path segments like "..".
180
+ def realpath(path)
181
+ send_request(FXP_REALPATH, :string, path)
182
+ end
183
+
184
+ # Sends a FXP_STAT packet to the server, requesting a FXP_ATTR response
185
+ # for the file at the given remote +path+ (a string). The +flags+ parameter
186
+ # is ignored in this version of the protocol. #stat will follow
187
+ # symbolic links; see #lstat for a version that will not.
188
+ def stat(path, flags=nil)
189
+ send_request(FXP_STAT, :string, path)
190
+ end
191
+
192
+ # Not implemented in version 1 of the SFTP protocol. Raises a
193
+ # NotImplementedError if called.
194
+ def rename(name, new_name, flags=nil)
195
+ not_implemented! :rename
196
+ end
197
+
198
+ # Not implemented in version 1 of the SFTP protocol. Raises a
199
+ # NotImplementedError if called.
200
+ def readlink(path)
201
+ not_implemented! :readlink
202
+ end
203
+
204
+ # Not implemented in version 1 of the SFTP protocol. Raises a
205
+ # NotImplementedError if called.
206
+ def symlink(path, target)
207
+ not_implemented! :symlink
208
+ end
209
+
210
+ # Not implemented in version 1 of the SFTP protocol. Raises a
211
+ # NotImplementedError if called.
212
+ def link(*args)
213
+ not_implemented! :link
214
+ end
215
+
216
+ # Not implemented in version 1 of the SFTP protocol. Raises a
217
+ # NotImplementedError if called.
218
+ def block(handle, offset, length, mask)
219
+ not_implemented! :block
220
+ end
221
+
222
+ # Not implemented in version 1 of the SFTP protocol. Raises a
223
+ # NotImplementedError if called.
224
+ def unblock(handle, offset, length)
225
+ not_implemented! :unblock
226
+ end
227
+
228
+ protected
229
+
230
+ # A helper method for implementing wrappers for operations that are
231
+ # not implemented by the current SFTP protocol version. Simply raises
232
+ # NotImplementedError with a message based on the given operation name.
233
+ def not_implemented!(operation)
234
+ raise NotImplementedError, "the #{operation} operation is not available in the version of the SFTP protocol supported by your server"
235
+ end
236
+
237
+ # Normalizes the given flags parameter, converting it into a combination
238
+ # of IO constants.
239
+ def normalize_open_flags(flags)
240
+ if String === flags
241
+ case flags.tr("b", "")
242
+ when "r" then IO::RDONLY
243
+ when "r+" then IO::RDWR
244
+ when "w" then IO::WRONLY | IO::TRUNC | IO::CREAT
245
+ when "w+" then IO::RDWR | IO::TRUNC | IO::CREAT
246
+ when "a" then IO::APPEND | IO::CREAT | IO::WRONLY
247
+ when "a+" then IO::APPEND | IO::CREAT | IO::RDWR
248
+ else raise ArgumentError, "unsupported flags: #{flags.inspect}"
249
+ end
250
+ else
251
+ flags.to_i
252
+ end
253
+ end
254
+
255
+ # Returns the Attributes class used by this version of the protocol
256
+ # (Net::SFTP::Protocol::V01::Attributes, in this case)
257
+ def attribute_factory
258
+ V01::Attributes
259
+ end
260
+
261
+ # Returns the Name class used by this version of the protocol
262
+ # (Net::SFTP::Protocol::V01::Name, in this case)
263
+ def name_factory
264
+ V01::Name
265
+ end
266
+ end
267
+
268
+ end; end; end; end
@@ -0,0 +1,43 @@
1
+ module Net; module SFTP; module Protocol; module V01
2
+
3
+ # Represents a single named item on the remote server. This includes the
4
+ # name, attributes about the item, and the "longname", which is intended
5
+ # for use when displaying directory data, and has no specified format.
6
+ class Name
7
+ # The name of the item on the remote server.
8
+ attr_reader :name
9
+
10
+ # The display-ready name of the item, possibly with other attributes.
11
+ attr_reader :longname
12
+
13
+ # The Attributes object describing this item.
14
+ attr_reader :attributes
15
+
16
+ # Create a new Name object with the given name, longname, and attributes.
17
+ def initialize(name, longname, attributes)
18
+ @name, @longname, @attributes = name, longname, attributes
19
+ end
20
+
21
+ # Returns +true+ if the item appears to be a directory. It does this by
22
+ # examining the attributes. If there is insufficient information in the
23
+ # attributes, this will return nil, rather than a boolean.
24
+ def directory?
25
+ attributes.directory?
26
+ end
27
+
28
+ # Returns +true+ if the item appears to be a symlink. It does this by
29
+ # examining the attributes. If there is insufficient information in the
30
+ # attributes, this will return nil, rather than a boolean.
31
+ def symlink?
32
+ attributes.symlink?
33
+ end
34
+
35
+ # Returns +true+ if the item appears to be a regular file. It does this by
36
+ # examining the attributes. If there is insufficient information in the
37
+ # attributes, this will return nil, rather than a boolean.
38
+ def file?
39
+ attributes.file?
40
+ end
41
+ end
42
+
43
+ end; end; end; end
@@ -0,0 +1,31 @@
1
+ require 'net/sftp/protocol/01/base'
2
+
3
+ module Net; module SFTP; module Protocol; module V02
4
+
5
+ # Wraps the low-level SFTP calls for version 2 of the SFTP protocol.
6
+ #
7
+ # None of these protocol methods block--all of them return immediately,
8
+ # requiring the SSH event loop to be run while the server response is
9
+ # pending.
10
+ #
11
+ # You will almost certainly never need to use this driver directly. Please
12
+ # see Net::SFTP::Session for the recommended interface.
13
+ class Base < V01::Base
14
+
15
+ # Returns the protocol version implemented by this driver. (2, in this
16
+ # case)
17
+ def version
18
+ 2
19
+ end
20
+
21
+ # Sends a FXP_RENAME packet to the server to request that the file or
22
+ # directory with the given +name+ (must be a full path) be changed to
23
+ # +new_name+ (which must also be a path). The +flags+ parameter is
24
+ # ignored in this version of the protocol.
25
+ def rename(name, new_name, flags=nil)
26
+ send_request(FXP_RENAME, :string, name, :string, new_name)
27
+ end
28
+
29
+ end
30
+
31
+ end; end; end; end
@@ -0,0 +1,35 @@
1
+ require 'net/sftp/protocol/02/base'
2
+
3
+ module Net; module SFTP; module Protocol; module V03
4
+
5
+ # Wraps the low-level SFTP calls for version 3 of the SFTP protocol.
6
+ #
7
+ # None of these protocol methods block--all of them return immediately,
8
+ # requiring the SSH event loop to be run while the server response is
9
+ # pending.
10
+ #
11
+ # You will almost certainly never need to use this driver directly. Please
12
+ # see Net::SFTP::Session for the recommended interface.
13
+ class Base < V02::Base
14
+
15
+ # Returns the protocol version implemented by this driver. (3, in this
16
+ # case)
17
+ def version
18
+ 3
19
+ end
20
+
21
+ # Sends a FXP_READLINK packet to the server to request that the target of
22
+ # the given symlink on the remote host (+path+) be returned.
23
+ def readlink(path)
24
+ send_request(FXP_READLINK, :string, path)
25
+ end
26
+
27
+ # Sends a FXP_SYMLINK packet to the server to request that a symlink at the
28
+ # given +path+ be created, pointing at +target+..
29
+ def symlink(path, target)
30
+ send_request(FXP_SYMLINK, :string, path, :string, target)
31
+ end
32
+
33
+ end
34
+
35
+ end; end; end; end