net-sftp 1.1.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. data/CHANGELOG.rdoc +23 -0
  2. data/Manifest +55 -0
  3. data/README.rdoc +96 -0
  4. data/Rakefile +30 -0
  5. data/lib/net/sftp.rb +53 -38
  6. data/lib/net/sftp/constants.rb +187 -0
  7. data/lib/net/sftp/errors.rb +34 -20
  8. data/lib/net/sftp/operations/dir.rb +93 -0
  9. data/lib/net/sftp/operations/download.rb +364 -0
  10. data/lib/net/sftp/operations/file.rb +176 -0
  11. data/lib/net/sftp/operations/file_factory.rb +60 -0
  12. data/lib/net/sftp/operations/upload.rb +387 -0
  13. data/lib/net/sftp/packet.rb +21 -0
  14. data/lib/net/sftp/protocol.rb +32 -0
  15. data/lib/net/sftp/protocol/01/attributes.rb +265 -96
  16. data/lib/net/sftp/protocol/01/base.rb +268 -0
  17. data/lib/net/sftp/protocol/01/name.rb +43 -0
  18. data/lib/net/sftp/protocol/02/base.rb +31 -0
  19. data/lib/net/sftp/protocol/03/base.rb +35 -0
  20. data/lib/net/sftp/protocol/04/attributes.rb +120 -195
  21. data/lib/net/sftp/protocol/04/base.rb +94 -0
  22. data/lib/net/sftp/protocol/04/name.rb +67 -0
  23. data/lib/net/sftp/protocol/05/base.rb +66 -0
  24. data/lib/net/sftp/protocol/06/attributes.rb +107 -0
  25. data/lib/net/sftp/protocol/06/base.rb +63 -0
  26. data/lib/net/sftp/protocol/base.rb +50 -0
  27. data/lib/net/sftp/request.rb +91 -0
  28. data/lib/net/sftp/response.rb +76 -0
  29. data/lib/net/sftp/session.rb +914 -238
  30. data/lib/net/sftp/version.rb +14 -21
  31. data/net-sftp.gemspec +60 -0
  32. data/setup.rb +1331 -0
  33. data/test/common.rb +173 -0
  34. data/test/protocol/01/test_attributes.rb +97 -0
  35. data/test/protocol/01/test_base.rb +210 -0
  36. data/test/protocol/01/test_name.rb +27 -0
  37. data/test/protocol/02/test_base.rb +26 -0
  38. data/test/protocol/03/test_base.rb +27 -0
  39. data/test/protocol/04/test_attributes.rb +148 -0
  40. data/test/protocol/04/test_base.rb +74 -0
  41. data/test/protocol/04/test_name.rb +49 -0
  42. data/test/protocol/05/test_base.rb +62 -0
  43. data/test/protocol/06/test_attributes.rb +124 -0
  44. data/test/protocol/06/test_base.rb +51 -0
  45. data/test/protocol/test_base.rb +42 -0
  46. data/test/test_all.rb +3 -0
  47. data/test/test_dir.rb +47 -0
  48. data/test/test_download.rb +252 -0
  49. data/test/test_file.rb +159 -0
  50. data/test/test_file_factory.rb +48 -0
  51. data/test/test_packet.rb +9 -0
  52. data/test/test_protocol.rb +17 -0
  53. data/test/test_request.rb +71 -0
  54. data/test/test_response.rb +53 -0
  55. data/test/test_session.rb +741 -0
  56. data/test/test_upload.rb +219 -0
  57. metadata +59 -111
  58. data/doc/LICENSE-BSD +0 -27
  59. data/doc/LICENSE-GPL +0 -280
  60. data/doc/LICENSE-RUBY +0 -56
  61. data/doc/faq/faq.html +0 -298
  62. data/doc/faq/faq.rb +0 -154
  63. data/doc/faq/faq.yml +0 -183
  64. data/examples/asynchronous.rb +0 -57
  65. data/examples/get-put.rb +0 -45
  66. data/examples/sftp-open-uri.rb +0 -30
  67. data/examples/ssh-service.rb +0 -30
  68. data/examples/synchronous.rb +0 -131
  69. data/lib/net/sftp/operations/abstract.rb +0 -108
  70. data/lib/net/sftp/operations/close.rb +0 -31
  71. data/lib/net/sftp/operations/errors.rb +0 -76
  72. data/lib/net/sftp/operations/fsetstat.rb +0 -36
  73. data/lib/net/sftp/operations/fstat.rb +0 -32
  74. data/lib/net/sftp/operations/lstat.rb +0 -31
  75. data/lib/net/sftp/operations/mkdir.rb +0 -33
  76. data/lib/net/sftp/operations/open.rb +0 -32
  77. data/lib/net/sftp/operations/opendir.rb +0 -32
  78. data/lib/net/sftp/operations/read.rb +0 -88
  79. data/lib/net/sftp/operations/readdir.rb +0 -55
  80. data/lib/net/sftp/operations/realpath.rb +0 -37
  81. data/lib/net/sftp/operations/remove.rb +0 -31
  82. data/lib/net/sftp/operations/rename.rb +0 -32
  83. data/lib/net/sftp/operations/rmdir.rb +0 -31
  84. data/lib/net/sftp/operations/services.rb +0 -42
  85. data/lib/net/sftp/operations/setstat.rb +0 -33
  86. data/lib/net/sftp/operations/stat.rb +0 -31
  87. data/lib/net/sftp/operations/write.rb +0 -63
  88. data/lib/net/sftp/protocol/01/impl.rb +0 -251
  89. data/lib/net/sftp/protocol/01/packet-assistant.rb +0 -82
  90. data/lib/net/sftp/protocol/01/services.rb +0 -47
  91. data/lib/net/sftp/protocol/02/impl.rb +0 -39
  92. data/lib/net/sftp/protocol/02/packet-assistant.rb +0 -32
  93. data/lib/net/sftp/protocol/02/services.rb +0 -44
  94. data/lib/net/sftp/protocol/03/impl.rb +0 -42
  95. data/lib/net/sftp/protocol/03/packet-assistant.rb +0 -35
  96. data/lib/net/sftp/protocol/03/services.rb +0 -44
  97. data/lib/net/sftp/protocol/04/impl.rb +0 -86
  98. data/lib/net/sftp/protocol/04/packet-assistant.rb +0 -45
  99. data/lib/net/sftp/protocol/04/services.rb +0 -44
  100. data/lib/net/sftp/protocol/05/impl.rb +0 -90
  101. data/lib/net/sftp/protocol/05/packet-assistant.rb +0 -34
  102. data/lib/net/sftp/protocol/05/services.rb +0 -44
  103. data/lib/net/sftp/protocol/constants.rb +0 -60
  104. data/lib/net/sftp/protocol/driver.rb +0 -235
  105. data/lib/net/sftp/protocol/packet-assistant.rb +0 -84
  106. data/lib/net/sftp/protocol/services.rb +0 -55
  107. data/lib/uri/open-sftp.rb +0 -54
  108. data/lib/uri/sftp.rb +0 -42
  109. data/test/ALL-TESTS.rb +0 -23
  110. data/test/operations/tc_abstract.rb +0 -124
  111. data/test/operations/tc_close.rb +0 -40
  112. data/test/operations/tc_fsetstat.rb +0 -48
  113. data/test/operations/tc_fstat.rb +0 -40
  114. data/test/operations/tc_lstat.rb +0 -40
  115. data/test/operations/tc_mkdir.rb +0 -48
  116. data/test/operations/tc_open.rb +0 -42
  117. data/test/operations/tc_opendir.rb +0 -40
  118. data/test/operations/tc_read.rb +0 -103
  119. data/test/operations/tc_readdir.rb +0 -88
  120. data/test/operations/tc_realpath.rb +0 -54
  121. data/test/operations/tc_remove.rb +0 -40
  122. data/test/operations/tc_rmdir.rb +0 -40
  123. data/test/operations/tc_setstat.rb +0 -48
  124. data/test/operations/tc_stat.rb +0 -40
  125. data/test/operations/tc_write.rb +0 -91
  126. data/test/protocol/01/tc_attributes.rb +0 -138
  127. data/test/protocol/01/tc_impl.rb +0 -294
  128. data/test/protocol/01/tc_packet_assistant.rb +0 -81
  129. data/test/protocol/02/tc_impl.rb +0 -41
  130. data/test/protocol/02/tc_packet_assistant.rb +0 -31
  131. data/test/protocol/03/tc_impl.rb +0 -48
  132. data/test/protocol/03/tc_packet_assistant.rb +0 -34
  133. data/test/protocol/04/tc_attributes.rb +0 -174
  134. data/test/protocol/04/tc_impl.rb +0 -91
  135. data/test/protocol/04/tc_packet_assistant.rb +0 -38
  136. data/test/protocol/05/tc_impl.rb +0 -61
  137. data/test/protocol/05/tc_packet_assistant.rb +0 -32
  138. data/test/protocol/tc_driver.rb +0 -219
@@ -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
@@ -1,227 +1,152 @@
1
- #--
2
- # =============================================================================
3
- # Copyright (c) 2004, Jamis Buck (jamis@37signals.com)
4
- # All rights reserved.
5
- #
6
- # This source file is distributed as part of the Net::SFTP Secure FTP Client
7
- # library for Ruby. This file (and the library as a whole) may be used only as
8
- # allowed by either the BSD license, or the Ruby license (or, by association
9
- # with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SFTP
10
- # distribution for the texts of these licenses.
11
- # -----------------------------------------------------------------------------
12
- # net-sftp website: http://net-ssh.rubyforge.org/sftp
13
- # project website : http://rubyforge.org/projects/net-ssh
14
- # =============================================================================
15
- #++
16
-
17
- module Net ; module SFTP ; module Protocol ; module V_04
18
-
19
- # Version 4 of the SFTP protocol made some pretty significant alterations to
20
- # the File Attributes data type. This encapsulates those changes.
21
- class Attributes
22
-
23
- F_SIZE = 0x00000001
24
- F_PERMISSIONS = 0x00000004
1
+ require 'net/sftp/protocol/01/attributes'
2
+
3
+ module Net; module SFTP; module Protocol; module V04
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
+ # This particular class is specific to versions 4 and 5 of the SFTP
8
+ # protocol.
9
+ #
10
+ # To specify new attributes, just pass a hash as the argument to the
11
+ # constructor. The following keys are supported:
12
+ #
13
+ # * :type:: the type of the item (integer, one of the T_ constants)
14
+ # * :size:: the size of the item (integer)
15
+ # * :uid:: the user-id that owns the file (integer)
16
+ # * :gid:: the group-id that owns the file (integer)
17
+ # * :owner:: the name of the user that owns the file (string)
18
+ # * :group:: the name of the group that owns the file (string)
19
+ # * :permissions:: the permissions on the file (integer, e.g. 0755)
20
+ # * :atime:: the access time of the file (integer, seconds since epoch)
21
+ # * :atime_nseconds:: the nanosecond component of atime (integer)
22
+ # * :createtime:: the time at which the file was created (integer, seconds since epoch)
23
+ # * :createtime_nseconds:: the nanosecond component of createtime (integer)
24
+ # * :mtime:: the modification time of the file (integer, seconds since epoch)
25
+ # * :mtime_nseconds:: the nanosecond component of mtime (integer)
26
+ # * :acl:: an array of ACL entries for the item
27
+ # * :extended:: a hash of name/value pairs identifying extended info
28
+ #
29
+ # Likewise, when the server sends an Attributes object, all of the
30
+ # above attributes are exposed as methods (though not all will be set with
31
+ # non-nil values from the server).
32
+ class Attributes < V01::Attributes
33
+
25
34
  F_ACCESSTIME = 0x00000008
26
35
  F_CREATETIME = 0x00000010
27
36
  F_MODIFYTIME = 0x00000020
28
37
  F_ACL = 0x00000040
29
38
  F_OWNERGROUP = 0x00000080
30
39
  F_SUBSECOND_TIMES = 0x00000100
31
- F_EXTENDED = 0x80000000
32
40
 
33
- attr_accessor :type
34
- attr_accessor :size
35
- attr_accessor :owner
36
- attr_accessor :group
37
- attr_accessor :permissions
38
- attr_accessor :atime
39
- attr_accessor :atime_nseconds
40
- attr_accessor :ctime
41
- attr_accessor :ctime_nseconds
42
- attr_accessor :mtime
43
- attr_accessor :mtime_nseconds
44
- attr_accessor :acl
45
- attr_accessor :extended
41
+ # A simple struct for representing a single entry in an Access Control
42
+ # List. (See Net::SFTP::Constants::ACE)
43
+ ACL = Struct.new(:type, :flag, :mask, :who)
44
+
45
+ class <<self
46
+ # The list of supported elements in the attributes structure as defined
47
+ # by v4 of the sftp protocol.
48
+ def elements #:nodoc:
49
+ @elements ||= [
50
+ [:type, :byte, 0],
51
+ [:size, :int64, V01::Attributes::F_SIZE],
52
+ [:owner, :string, F_OWNERGROUP],
53
+ [:group, :string, F_OWNERGROUP],
54
+ [:permissions, :long, V01::Attributes::F_PERMISSIONS],
55
+ [:atime, :int64, F_ACCESSTIME],
56
+ [:atime_nseconds, :long, F_ACCESSTIME | F_SUBSECOND_TIMES],
57
+ [:createtime, :int64, F_CREATETIME],
58
+ [:createtime_nseconds, :long, F_CREATETIME | F_SUBSECOND_TIMES],
59
+ [:mtime, :int64, F_MODIFYTIME],
60
+ [:mtime_nseconds, :long, F_MODIFYTIME | F_SUBSECOND_TIMES],
61
+ [:acl, :special, F_ACL],
62
+ [:extended, :special, V01::Attributes::F_EXTENDED]
63
+ ]
64
+ end
46
65
 
47
- ACL = Struct.new( :type, :flag, :mask, :who )
66
+ private
48
67
 
49
- # An initializer for specifying the buffer factory that should be used by
50
- # instances of this class.
51
- def self.init( buffers )
52
- @buffers = buffers
53
- self
68
+ # A helper method for parsing the ACL entry in an Attributes struct.
69
+ def parse_acl(buffer)
70
+ acl_buf = Net::SSH::Buffer.new(buffer.read_string)
71
+ acl = []
72
+ acl_buf.read_long.times do
73
+ acl << ACL.new(acl_buf.read_long, acl_buf.read_long, acl_buf.read_long, acl_buf.read_string)
74
+ end
75
+ acl
76
+ end
54
77
  end
55
78
 
56
- # Return a reference to the buffer factory in use by this class.
57
- def self.buffers
58
- @buffers
59
- end
79
+ # The type of the item on the remote server. Must be one of the T_* constants.
80
+ attr_accessor :type
60
81
 
61
- # A convenience for obtaining a reference to the buffer factory used by
62
- # this instance's class.
63
- def buffers
64
- self.class.buffers
65
- end
82
+ # The owner of the item on the remote server, as a string.
83
+ attr_writer :owner
66
84
 
67
- # Return an empty Attributes instance.
68
- def self.empty
69
- new
70
- end
85
+ # The group of the item on the remote server, as a string.
86
+ attr_writer :group
71
87
 
72
- # Return an Attributes instance initialized from the given buffer.
73
- def self.from_buffer( buffer )
74
- flags = buffer.read_long
75
-
76
- type = buffer.read_byte
77
- size = buffer.read_int64 if ( flags & F_SIZE ) != 0
78
- owner = buffer.read_string if ( flags & F_OWNERGROUP ) != 0
79
- group = buffer.read_string if ( flags & F_OWNERGROUP ) != 0
80
- permissions = buffer.read_long if ( flags & F_PERMISSIONS ) != 0
81
- if ( flags & F_ACCESSTIME ) != 0
82
- atime = buffer.read_int64
83
- atime_nseconds = buffer.read_long if ( flags & F_SUBSECOND_TIMES ) != 0
84
- end
85
- if ( flags & F_CREATETIME ) != 0
86
- ctime = buffer.read_int64
87
- ctime_nseconds = buffer.read_long if ( flags & F_SUBSECOND_TIMES ) != 0
88
- end
89
- if ( flags & F_MODIFYTIME ) != 0
90
- mtime = buffer.read_int64
91
- mtime_nseconds = buffer.read_long if ( flags & F_SUBSECOND_TIMES ) != 0
92
- end
93
- if ( flags & F_ACL ) != 0
94
- acl_buf = buffers.reader( buffer.read_string )
95
- acl = []
96
- acl_buf.read_long.times do
97
- acl << ACL.new( acl_buf.read_long,
98
- acl_buf.read_long,
99
- acl_buf.read_long,
100
- acl_buf.read_string )
101
- end
102
- end
88
+ # The nanosecond component of the access time.
89
+ attr_accessor :atime_nseconds
103
90
 
104
- if ( flags & F_EXTENDED ) != 0
105
- extended = Hash.new
106
- buffer.read_long.times do
107
- extended[ buffer.read_string ] = buffer.read_string
108
- end
109
- end
91
+ # The creation time of the remote item, in seconds since the epoch.
92
+ attr_accessor :createtime
110
93
 
111
- new( type, size, owner, group, permissions, atime, atime_nseconds,
112
- ctime, ctime_nseconds, mtime, mtime_nseconds, acl, extended )
113
- end
94
+ # The nanosecond component of the creation time.
95
+ attr_accessor :createtime_nseconds
114
96
 
115
- # Create a new attributes object, initialized from the given hash. The
116
- # :uid and :gid attributes are treated specially; they are not actually
117
- # supported by this version of the protocol, but are instead converted
118
- # by this method to their corresponding names, and assigned (respectively)
119
- # to :owner and :group.
120
- def self.from_hash( hash )
121
- if hash.has_key?(:uid)
122
- require 'etc'
123
- hash[:owner] = Etc.getpwuid( hash[:uid] ).name
124
- end
97
+ # The nanosecond component of the modification time.
98
+ attr_accessor :mtime_nseconds
125
99
 
126
- if hash.has_key?(:gid)
127
- require 'etc'
128
- hash[:group] = Etc.getgrgid( hash[:gid] ).name
129
- end
100
+ # The array of access control entries for this item.
101
+ attr_accessor :acl
130
102
 
131
- new hash[:type] || T_REGULAR, hash[:size], hash[:owner], hash[:group],
132
- hash[:permissions], hash[:atime], hash[:atime_nseconds],
133
- hash[:ctime], hash[:ctime_nseconds], hash[:mtime],
134
- hash[:mtime_nseconds], hash[:acl], hash[:extended]
103
+ # Create a new Attributes instance with the given attributes. The
104
+ # following keys are supported:
105
+ #
106
+ # * :type:: the type of the item (integer, one of the T_ constants)
107
+ # * :size:: the size of the item (integer)
108
+ # * :uid:: the user-id that owns the file (integer)
109
+ # * :gid:: the group-id that owns the file (integer)
110
+ # * :owner:: the name of the user that owns the file (string)
111
+ # * :group:: the name of the group that owns the file (string)
112
+ # * :permissions:: the permissions on the file (integer, e.g. 0755)
113
+ # * :atime:: the access time of the file (integer, seconds since epoch)
114
+ # * :atime_nseconds:: the nanosecond component of atime (integer)
115
+ # * :createtime:: the time at which the file was created (integer, seconds since epoch)
116
+ # * :createtime_nseconds:: the nanosecond component of createtime (integer)
117
+ # * :mtime:: the modification time of the file (integer, seconds since epoch)
118
+ # * :mtime_nseconds:: the nanosecond component of mtime (integer)
119
+ # * :acl:: an array of ACL entries for the item
120
+ # * :extended:: a hash of name/value pairs identifying extended info
121
+ #
122
+ # All of them default to +nil+ if omitted, except for +type+, which defaults
123
+ # to T_REGULAR.
124
+ def initialize(attributes={})
125
+ super
126
+ attributes[:type] ||= T_REGULAR
135
127
  end
136
128
 
137
- private_class_method :new
138
-
139
- T_REGULAR = 1
140
- T_DIRECTORY = 2
141
- T_SYMLINK = 3
142
- T_SPECIAL = 4
143
- T_UNKNOWN = 5
144
- T_SOCKET = 6
145
- T_CHAR_DEVICE = 7
146
- T_BLOCK_DEVICE = 8
147
- T_FIFO = 9
148
-
149
- # Create a new Attributes instance with the given values.
150
- def initialize( type=T_REGULAR, size=nil, owner=nil, group=nil,
151
- permissions=nil, atime=nil, atime_nseconds=nil, ctime=nil,
152
- ctime_nseconds=nil, mtime=nil, mtime_nseconds=nil, acl=nil,
153
- extended=nil )
154
- # begin
155
- @type = type
156
- @size = size
157
- @owner = owner
158
- @group = group
159
- @permissions = permissions
160
- @atime = atime
161
- @atime_nseconds = atime_nseconds
162
- @ctime = ctime
163
- @ctime_nseconds = ctime_nseconds
164
- @mtime = mtime
165
- @mtime_nseconds = mtime_nseconds
166
- @acl = acl
167
- @extended = extended
168
- end
129
+ private
169
130
 
170
- # Convert this object to a string, suitable for inclusion in an SFTP
171
- # packet (protocol version 4+).
172
- def to_s
173
- flags = 0
174
-
175
- flags |= F_SIZE if @size
176
- flags |= F_OWNERGROUP if @owner && @group
177
- flags |= F_PERMISSIONS if @permissions
178
- flags |= F_ACCESSTIME if @atime
179
- flags |= F_CREATETIME if @ctime
180
- flags |= F_MODIFYTIME if @mtime
181
- if @atime_nseconds && @ctime_nseconds && @mtime_nseconds
182
- flags |= F_SUBSECOND_TIMES
183
- end
184
- flags |= F_ACL if @acl
185
- flags |= F_EXTENDED if @extended
186
-
187
- buffer = buffers.writer
188
- buffer.write_long flags
189
- buffer.write_byte @type
190
- buffer.write_int64 @size if @size
191
- buffer.write_string @owner, @group if @owner && @group
192
- buffer.write_long @permissions if @permissions
193
-
194
- if @atime
195
- buffer.write_int64 @atime
196
- buffer.write_long @atime_nseconds if ( flags & F_SUBSECOND_TIMES != 0 )
197
- end
198
- if @ctime
199
- buffer.write_int64 @ctime
200
- buffer.write_long @ctime_nseconds if ( flags & F_SUBSECOND_TIMES != 0 )
201
- end
202
- if @mtime
203
- buffer.write_int64 @mtime
204
- buffer.write_long @mtime_nseconds if ( flags & F_SUBSECOND_TIMES != 0 )
131
+ # Perform protocol-version-specific preparations for serialization.
132
+ def prepare_serialization!
133
+ # force the group/owner to be translated from uid/gid, if those keys
134
+ # were given on instantiation
135
+ owner
136
+ group
205
137
  end
206
138
 
207
- if @acl
208
- acl_buf = buffers.writer
209
- acl_buf.write_long @acl.length
210
- @acl.each do |item|
139
+ # Performs protocol-version-specific encoding of the access control
140
+ # list, if one exists.
141
+ def encode_acl(buffer)
142
+ acl_buf = Net::SSH::Buffer.from(:long, acl.length)
143
+ acl.each do |item|
211
144
  acl_buf.write_long item.type, item.flag, item.mask
212
145
  acl_buf.write_string item.who
213
146
  end
214
- buffer.write_string acl_buf.to_s
147
+ buffer.write_string(acl_buf.to_s)
215
148
  end
216
149
 
217
- if @extended
218
- buffer.write_long @extended.length
219
- @extended.each { |k,v| buffer.write_string k, v }
220
- end
221
-
222
- buffer.to_s
223
- end
224
-
225
150
  end
226
151
 
227
152
  end ; end ; end ; end