net-sftp 1.1.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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