net-sftp 0.5.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 (77) hide show
  1. data/doc/LICENSE-BSD +27 -0
  2. data/doc/LICENSE-GPL +280 -0
  3. data/doc/LICENSE-RUBY +56 -0
  4. data/examples/asynchronous.rb +57 -0
  5. data/examples/ssh-service.rb +31 -0
  6. data/examples/synchronous.rb +120 -0
  7. data/lib/net/sftp.rb +39 -0
  8. data/lib/net/sftp/errors.rb +25 -0
  9. data/lib/net/sftp/operations/abstract.rb +103 -0
  10. data/lib/net/sftp/operations/close.rb +31 -0
  11. data/lib/net/sftp/operations/errors.rb +76 -0
  12. data/lib/net/sftp/operations/fsetstat.rb +36 -0
  13. data/lib/net/sftp/operations/fstat.rb +32 -0
  14. data/lib/net/sftp/operations/lstat.rb +31 -0
  15. data/lib/net/sftp/operations/mkdir.rb +33 -0
  16. data/lib/net/sftp/operations/open.rb +32 -0
  17. data/lib/net/sftp/operations/opendir.rb +32 -0
  18. data/lib/net/sftp/operations/read.rb +84 -0
  19. data/lib/net/sftp/operations/readdir.rb +55 -0
  20. data/lib/net/sftp/operations/realpath.rb +37 -0
  21. data/lib/net/sftp/operations/remove.rb +31 -0
  22. data/lib/net/sftp/operations/rename.rb +32 -0
  23. data/lib/net/sftp/operations/rmdir.rb +31 -0
  24. data/lib/net/sftp/operations/services.rb +42 -0
  25. data/lib/net/sftp/operations/setstat.rb +33 -0
  26. data/lib/net/sftp/operations/stat.rb +31 -0
  27. data/lib/net/sftp/operations/write.rb +63 -0
  28. data/lib/net/sftp/protocol/01/attributes.rb +146 -0
  29. data/lib/net/sftp/protocol/01/impl.rb +251 -0
  30. data/lib/net/sftp/protocol/01/packet-assistant.rb +82 -0
  31. data/lib/net/sftp/protocol/01/services.rb +47 -0
  32. data/lib/net/sftp/protocol/02/impl.rb +39 -0
  33. data/lib/net/sftp/protocol/02/packet-assistant.rb +32 -0
  34. data/lib/net/sftp/protocol/02/services.rb +44 -0
  35. data/lib/net/sftp/protocol/03/impl.rb +42 -0
  36. data/lib/net/sftp/protocol/03/packet-assistant.rb +35 -0
  37. data/lib/net/sftp/protocol/03/services.rb +44 -0
  38. data/lib/net/sftp/protocol/04/attributes.rb +227 -0
  39. data/lib/net/sftp/protocol/04/impl.rb +134 -0
  40. data/lib/net/sftp/protocol/04/packet-assistant.rb +51 -0
  41. data/lib/net/sftp/protocol/04/services.rb +44 -0
  42. data/lib/net/sftp/protocol/05/services.rb +44 -0
  43. data/lib/net/sftp/protocol/constants.rb +60 -0
  44. data/lib/net/sftp/protocol/driver.rb +232 -0
  45. data/lib/net/sftp/protocol/packet-assistant.rb +84 -0
  46. data/lib/net/sftp/protocol/services.rb +55 -0
  47. data/lib/net/sftp/session.rb +215 -0
  48. data/lib/net/sftp/version.rb +25 -0
  49. data/test/ALL-TESTS.rb +21 -0
  50. data/test/operations/tc_abstract.rb +124 -0
  51. data/test/operations/tc_close.rb +40 -0
  52. data/test/operations/tc_fsetstat.rb +48 -0
  53. data/test/operations/tc_fstat.rb +40 -0
  54. data/test/operations/tc_lstat.rb +40 -0
  55. data/test/operations/tc_mkdir.rb +48 -0
  56. data/test/operations/tc_open.rb +42 -0
  57. data/test/operations/tc_opendir.rb +40 -0
  58. data/test/operations/tc_read.rb +103 -0
  59. data/test/operations/tc_readdir.rb +88 -0
  60. data/test/operations/tc_realpath.rb +54 -0
  61. data/test/operations/tc_remove.rb +40 -0
  62. data/test/operations/tc_rmdir.rb +40 -0
  63. data/test/operations/tc_setstat.rb +48 -0
  64. data/test/operations/tc_stat.rb +40 -0
  65. data/test/operations/tc_write.rb +91 -0
  66. data/test/protocol/01/tc_attributes.rb +138 -0
  67. data/test/protocol/01/tc_impl.rb +294 -0
  68. data/test/protocol/01/tc_packet_assistant.rb +81 -0
  69. data/test/protocol/02/tc_impl.rb +41 -0
  70. data/test/protocol/02/tc_packet_assistant.rb +31 -0
  71. data/test/protocol/03/tc_impl.rb +48 -0
  72. data/test/protocol/03/tc_packet_assistant.rb +34 -0
  73. data/test/protocol/04/tc_attributes.rb +174 -0
  74. data/test/protocol/04/tc_impl.rb +102 -0
  75. data/test/protocol/04/tc_packet_assistant.rb +41 -0
  76. data/test/protocol/tc_driver.rb +219 -0
  77. metadata +137 -0
@@ -0,0 +1,33 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
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
+ require 'net/sftp/operations/abstract'
18
+
19
+ module Net ; module SFTP ; module Operations
20
+
21
+ # Implements the +setstat+ operation.
22
+ class Setstat < Abstract
23
+
24
+ # Perform the operation. The +hash+ parameter is a hash of attributes that
25
+ # should be set on the file or directory indicated by the +path+.
26
+ def perform( path, hash )
27
+ attrs = @driver.attr_factory.from_hash( hash )
28
+ @driver.setstat( nil, path, attrs )
29
+ end
30
+
31
+ end
32
+
33
+ end ; end ; end
@@ -0,0 +1,31 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
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
+ require 'net/sftp/operations/abstract'
18
+
19
+ module Net ; module SFTP ; module Operations
20
+
21
+ # Implements the +stat+ operation.
22
+ class Stat < Abstract
23
+
24
+ # Perform the operation.
25
+ def perform( path )
26
+ @driver.stat( nil, path )
27
+ end
28
+
29
+ end
30
+
31
+ end ; end ; end
@@ -0,0 +1,63 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
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
+ require 'net/sftp/operations/abstract'
18
+
19
+ module Net ; module SFTP ; module Operations
20
+
21
+ # Implements the +write+ operation. Handles, automatically, the looping
22
+ # necessary to write a large data set.
23
+ class Write < Abstract
24
+
25
+ # The maximum size of data that will be written at one time.
26
+ CHUNK_SIZE = 64 * 1024
27
+
28
+ # Perform the operation. Only CHUNK_SIZE portions of the +data+ parameter
29
+ # will be written at a time, with subsequent chunks being writteni
30
+ # automatically when prior chunks complete.
31
+ def perform( handle, data, offset=0 )
32
+ @handle = handle
33
+ @offset = offset
34
+ @data = data
35
+ @pos = 0
36
+
37
+ @driver.write( nil, handle, offset, data[0,CHUNK_SIZE] )
38
+ end
39
+
40
+ # Invoked when the server sends a status packet. If the status is FX_OK,
41
+ # then the callback is invoked (if all data has been written), or the
42
+ # next chunk is written to the server (if more data remains). Other
43
+ # status codes are handled by the superclass.
44
+ def do_status( code, message, language )
45
+ if code == FX_OK
46
+ @log.debug "[#{@id}] chunk written" if @log.debug?
47
+ @pos += CHUNK_SIZE
48
+
49
+ if @pos > @data.length
50
+ @callback[ OK ]
51
+ return
52
+ end
53
+
54
+ @driver.write @id, @handle, @offset + @pos, @data[@pos,CHUNK_SIZE]
55
+ @session.register @id, self
56
+ else
57
+ super
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ end ; end ; end
@@ -0,0 +1,146 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
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_01
18
+
19
+ # A class representing the attributes of a file or directory on the server.
20
+ # It may be used to specify new attributes, or to query existing attributes.
21
+ class Attributes
22
+
23
+ F_SIZE = 0x00000001
24
+ F_UIDGID = 0x00000002
25
+ F_PERMISSIONS = 0x00000004
26
+ F_ACMODTIME = 0x00000008
27
+ F_EXTENDED = 0x80000000
28
+
29
+ attr_accessor :size
30
+ attr_accessor :uid
31
+ attr_accessor :gid
32
+ attr_accessor :permissions
33
+ attr_accessor :atime
34
+ attr_accessor :mtime
35
+ attr_accessor :extended
36
+
37
+ # An initialization routine, to grant the class (factory) access to a
38
+ # buffer factory. The buffer factory is used by the class' #to_s
39
+ # method to encode the object's attributes.
40
+ #
41
+ # This returns +self+, making it suitable for chaining.
42
+ def self.init( buffers )
43
+ @buffers = buffers
44
+ self
45
+ end
46
+
47
+ # Returns the buffer factory for this class.
48
+ def self.buffers
49
+ @buffers
50
+ end
51
+
52
+ # Returns the buffer factory for the object's class.
53
+ def buffers
54
+ self.class.buffers
55
+ end
56
+
57
+ # Create a new, empty Attributes object.
58
+ def self.empty
59
+ new
60
+ end
61
+
62
+ # Create a new Attributes object, initialized from the given buffer.
63
+ def self.from_buffer( buffer )
64
+ flags = buffer.read_long
65
+
66
+ size = buffer.read_int64 if ( flags & F_SIZE ) != 0
67
+ uid = buffer.read_long if ( flags & F_UIDGID ) != 0
68
+ gid = buffer.read_long if ( flags & F_UIDGID ) != 0
69
+ permissions = buffer.read_long if ( flags & F_PERMISSIONS ) != 0
70
+ atime = buffer.read_long if ( flags & F_ACMODTIME ) != 0
71
+ mtime = buffer.read_long if ( flags & F_ACMODTIME ) != 0
72
+
73
+ if ( flags & F_EXTENDED ) != 0
74
+ extended = Hash.new
75
+ buffer.read_long.times do
76
+ extended[ buffer.read_string ] = buffer.read_string
77
+ end
78
+ end
79
+
80
+ new( size, uid, gid, permissions, atime, mtime, extended )
81
+ end
82
+
83
+ # Create a new attributes object, initialized from the given hash. The
84
+ # :owner and :group attributes are treated specially; they are not actually
85
+ # supported by this version of the protocol, but are instead converted
86
+ # by this method to their corresponding id numbers, and assigned
87
+ # (respectively) to :uid and :gid.
88
+ def self.from_hash( hash )
89
+ if hash[:owner]
90
+ require 'etc'
91
+ hash[:uid] = Etc.getpwnam( hash[:owner] ).uid
92
+ end
93
+
94
+ if hash[:group]
95
+ require 'etc'
96
+ hash[:gid] = Etc.getgrnam( hash[:group] ).gid
97
+ end
98
+
99
+ new hash[:size], hash[:uid], hash[:gid], hash[:permissions],
100
+ hash[:atime], hash[:mtime], hash[:extended]
101
+ end
102
+
103
+ private_class_method :new
104
+
105
+ # Create a new Attributes with the given attributes.
106
+ def initialize( size=nil, uid=nil, gid=nil, permissions=nil,
107
+ atime=nil, mtime=nil, extended=nil )
108
+ # begin
109
+ @size = size
110
+ @uid = uid
111
+ @gid = gid
112
+ @permissions = permissions
113
+ @atime = atime
114
+ @mtime = mtime
115
+ @extended = extended
116
+ end
117
+
118
+ # Convert the object to a string suitable for passing in an SFTP
119
+ # packet.
120
+ def to_s
121
+ flags = 0
122
+
123
+ flags |= F_SIZE if @size
124
+ flags |= F_UIDGID if @uid && @gid
125
+ flags |= F_PERMISSIONS if @permissions
126
+ flags |= F_ACMODTIME if @atime && @mtime
127
+ flags |= F_EXTENDED if @extended
128
+
129
+ buffer = buffers.writer
130
+ buffer.write_long flags
131
+ buffer.write_int64 @size if @size
132
+ buffer.write_long @uid, @gid if @uid && @gid
133
+ buffer.write_long @permissions if @permissions
134
+ buffer.write_long @atime, @mtime if @atime && @mtime
135
+
136
+ if @extended
137
+ buffer.write_long @extended.size
138
+ @extended.each { |k,v| buffer.write_string k, v }
139
+ end
140
+
141
+ buffer.to_s
142
+ end
143
+
144
+ end
145
+
146
+ end ; end ; end ; end
@@ -0,0 +1,251 @@
1
+ #--
2
+ # =============================================================================
3
+ # Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
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
+ require 'net/sftp/errors'
18
+ require 'net/sftp/protocol/constants'
19
+
20
+ module Net ; module SFTP ; module Protocol ; module V_01
21
+
22
+ # The implementing class for version 1 of the SFTP protocol. It
23
+ # implements the various operations and callbacks available to this
24
+ # level of the protocol. Other protocol versions will typically
25
+ # extend this class, adding (or modifying) methods as needed to bring
26
+ # the implementation into sync with the needed version.
27
+ class Impl
28
+ include Constants
29
+
30
+ Name = Struct.new( :filename, :longname, :attributes )
31
+
32
+ # The protocol driver that drives this dispatcher.
33
+ attr_reader :driver
34
+
35
+ # The attribute-factory used by this dispatcher.
36
+ attr_reader :attr_factory
37
+
38
+ # The protocol extensions specified when the protocol version was
39
+ # negotiated.
40
+ attr_accessor :extensions
41
+
42
+ # Create a new instance using the given protocol driver, packet
43
+ # assistant, and attributes factory.
44
+ def initialize( buffers, driver, assistant, attr_factory )
45
+ @buffers = buffers
46
+ @driver = driver
47
+ @assistant = assistant
48
+ @attr_factory = attr_factory
49
+ @on_status = nil
50
+ @on_handle = nil
51
+ @on_data = nil
52
+ @on_name = nil
53
+ @on_attrs = nil
54
+ end
55
+
56
+ # A helper method for defining new operations supported by this
57
+ # implementation. This will create one method for each named operation.
58
+ # By default, the method simply formats the packet (using the packet
59
+ # assistant), and then sends the data via the driver. It will then
60
+ # return the request id used for this operation.
61
+ def self.operation( *names )
62
+ names.each do |name|
63
+ const = "FXP_#{name.to_s.upcase}"
64
+ class_eval <<-EOF, __FILE__, __LINE__+1
65
+ def #{name}( id, *args )
66
+ id, packet = @assistant.#{name}( id, *args )
67
+ @driver.send_data #{const}, packet
68
+ id
69
+ end
70
+ EOF
71
+ end
72
+ end
73
+
74
+ # A helper method for registering new callbacks. Each callback will
75
+ # cause three new methods to be created, <tt>on_<em>name</em></tt>,
76
+ # <tt>has_on_<em>name</em>?</tt>, and <tt>call_on_<em>name</em></tt>.
77
+ # The <tt>on_<em>name</em></tt> method may be used to register a block
78
+ # for the corresponding callback.
79
+ def self.callback( *names )
80
+ names.each do |name|
81
+ class_eval <<-EOF, __FILE__, __LINE__+1
82
+ def on_#{name}( &block )
83
+ @on_#{name} = block
84
+ end
85
+
86
+ def has_on_#{name}?
87
+ not @on_#{name}.nil?
88
+ end
89
+
90
+ def call_on_#{name}( *args )
91
+ return unless @on_#{name}
92
+ @on_#{name}.call( *args )
93
+ end
94
+
95
+ public :on_#{name}
96
+ protected :has_on_#{name}?, :call_on_#{name}
97
+ EOF
98
+ end
99
+ end
100
+
101
+ operation :open,
102
+ :close,
103
+ :read,
104
+ :write,
105
+ :opendir,
106
+ :readdir,
107
+ :remove,
108
+ :stat,
109
+ :lstat,
110
+ :fstat,
111
+ :setstat,
112
+ :fsetstat,
113
+ :mkdir,
114
+ :rmdir,
115
+ :realpath
116
+
117
+ callback :status,
118
+ :handle,
119
+ :data,
120
+ :name,
121
+ :attrs
122
+
123
+ alias :open_raw :open
124
+ alias :stat_raw :stat
125
+ alias :lstat_raw :lstat
126
+ alias :fstat_raw :fstat
127
+
128
+ alias :close_handle :close
129
+
130
+ F_READ = 0x00000001
131
+ F_WRITE = 0x00000002
132
+ F_APPEND = 0x00000004
133
+ F_CREAT = 0x00000008
134
+ F_TRUNC = 0x00000010
135
+ F_EXCL = 0x00000020
136
+
137
+ # The open operation is special, since it protects the caller from the
138
+ # specific flags and options required by SFTP. Instead, the caller simply
139
+ # specifies a combination of IO flags, and an appropriate posix mode, and
140
+ # they are translated into the correct SFTP flags.
141
+ def open( id, path, flags, mode=0660 )
142
+ sftp_flags = case
143
+ when ( flags & IO::WRONLY ) != 0 then F_WRITE
144
+ when ( flags & IO::RDWR ) != 0 then F_READ | F_WRITE
145
+ when ( flags & IO::APPEND ) != 0 then F_APPEND
146
+ else F_READ
147
+ end
148
+
149
+ sftp_flags |= F_CREAT if ( flags & IO::CREAT ) != 0
150
+ sftp_flags |= F_TRUNC if ( flags & IO::TRUNC ) != 0
151
+ sftp_flags |= F_EXCL if ( flags & IO::EXCL ) != 0
152
+
153
+ attributes = @attr_factory.empty
154
+ attributes.permissions = mode
155
+
156
+ open_raw id, path, sftp_flags, attributes
157
+ end
158
+
159
+ # The stat operation is special, since later versions of the protocol add
160
+ # support for 'flags'. These flags are ignored in this version, but the
161
+ # parameter exists to allow a program written for one version of the
162
+ # protocol to work with later versions.
163
+ def stat( id, filename, flags=nil )
164
+ stat_raw id, filename
165
+ end
166
+
167
+ # The lstat operation is special, since later versions of the protocol add
168
+ # support for 'flags'. These flags are ignored in this version, but the
169
+ # parameter exists to allow a program written for one version of the
170
+ # protocol to work with later versions.
171
+ def lstat( id, filename, flags=nil )
172
+ lstat_raw id, filename
173
+ end
174
+
175
+ # The fstat operation is special, since later versions of the protocol add
176
+ # support for 'flags'. These flags are ignored in this version, but the
177
+ # parameter exists to allow a program written for one version of the
178
+ # protocol to work with later versions.
179
+ def fstat( id, handle, flags=nil )
180
+ fstat_raw id, handle
181
+ end
182
+
183
+ # Dispatches the given packet type to the appropriate handler method.
184
+ # If a new protocol version adds a new packet type, it should override
185
+ # this method, performing its own checking first, followed by calling
186
+ # +super+.
187
+ def dispatch( channel, type, content )
188
+ case type
189
+ when FXP_STATUS then do_status( channel, content )
190
+ when FXP_HANDLE then do_handle( channel, content )
191
+ when FXP_DATA then do_data( channel, content )
192
+ when FXP_NAME then do_name( channel, content )
193
+ when FXP_ATTRS then do_attrs( channel, content )
194
+ else
195
+ raise Net::SFTP::Exception,
196
+ "unsupported SFTP packet type #{type} (#{content.to_s.inspect})"
197
+ end
198
+ end
199
+
200
+ # Used internally to handle +status+ packets. The +on_status+ callback is
201
+ # invoked, if registered, with the driver, id, and code.
202
+ def do_status( channel, content )
203
+ return unless has_on_status?
204
+ id = content.read_long
205
+ code = content.read_long
206
+ call_on_status( driver, id, code, nil, nil )
207
+ end
208
+
209
+ # Used internally to handle +handle+ packets. The +on_handle+ callback is
210
+ # invoked, if registered, with the driver, id, and handle.
211
+ def do_handle( channel, content )
212
+ return unless has_on_handle?
213
+ id = content.read_long
214
+ handle = content.read_string
215
+ call_on_handle( driver, id, handle )
216
+ end
217
+
218
+ # Used internally to handle +data+ packets. The +on_data+ callback is
219
+ # invoked, if registered, with the driver, id, and data (as a buffer).
220
+ def do_data( channel, content )
221
+ return unless has_on_data?
222
+ id = content.read_long
223
+ data = content.read_string
224
+ call_on_data( driver, id, data )
225
+ end
226
+
227
+ # Used internally to handle +name+ packets. The +on_name+ callback is
228
+ # invoked, if registered, with the driver, id, and array of items.
229
+ def do_name( channel, content )
230
+ return unless has_on_name?
231
+ id = content.read_long
232
+ items = []
233
+ content.read_long.times do
234
+ items.push( Name.new( content.read_string, content.read_string,
235
+ @attr_factory.from_buffer( content ) ) )
236
+ end
237
+ call_on_name( driver, id, items )
238
+ end
239
+
240
+ # Used internally to handle +attrs+ packets. The +on_attrs+ callback is
241
+ # invoked, if registered, with the driver, id, and attribute object.
242
+ def do_attrs( channel, content )
243
+ return unless has_on_attrs?
244
+ id = content.read_long
245
+ attrs = @attr_factory.from_buffer( content )
246
+ call_on_attrs( driver, id, attrs )
247
+ end
248
+
249
+ end
250
+
251
+ end ; end ; end ; end