datacom-net-ldap 0.5.0.datacom

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 (52) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +11 -0
  3. data/.rspec +2 -0
  4. data/Contributors.rdoc +22 -0
  5. data/Hacking.rdoc +68 -0
  6. data/History.rdoc +198 -0
  7. data/License.rdoc +29 -0
  8. data/Manifest.txt +50 -0
  9. data/README.rdoc +49 -0
  10. data/Rakefile +74 -0
  11. data/autotest/discover.rb +1 -0
  12. data/lib/net-ldap.rb +2 -0
  13. data/lib/net/ber.rb +320 -0
  14. data/lib/net/ber/ber_parser.rb +168 -0
  15. data/lib/net/ber/core_ext.rb +62 -0
  16. data/lib/net/ber/core_ext/array.rb +96 -0
  17. data/lib/net/ber/core_ext/bignum.rb +22 -0
  18. data/lib/net/ber/core_ext/false_class.rb +10 -0
  19. data/lib/net/ber/core_ext/fixnum.rb +66 -0
  20. data/lib/net/ber/core_ext/string.rb +78 -0
  21. data/lib/net/ber/core_ext/true_class.rb +12 -0
  22. data/lib/net/ldap.rb +1646 -0
  23. data/lib/net/ldap/dataset.rb +154 -0
  24. data/lib/net/ldap/dn.rb +225 -0
  25. data/lib/net/ldap/entry.rb +185 -0
  26. data/lib/net/ldap/filter.rb +781 -0
  27. data/lib/net/ldap/password.rb +37 -0
  28. data/lib/net/ldap/pdu.rb +273 -0
  29. data/lib/net/ldap/version.rb +5 -0
  30. data/lib/net/snmp.rb +270 -0
  31. data/net-ldap.gemspec +61 -0
  32. data/spec/integration/ssl_ber_spec.rb +36 -0
  33. data/spec/spec.opts +2 -0
  34. data/spec/spec_helper.rb +5 -0
  35. data/spec/unit/ber/ber_spec.rb +141 -0
  36. data/spec/unit/ber/core_ext/string_spec.rb +51 -0
  37. data/spec/unit/ldap/dn_spec.rb +80 -0
  38. data/spec/unit/ldap/entry_spec.rb +51 -0
  39. data/spec/unit/ldap/filter_spec.rb +115 -0
  40. data/spec/unit/ldap_spec.rb +78 -0
  41. data/test/common.rb +3 -0
  42. data/test/test_entry.rb +59 -0
  43. data/test/test_filter.rb +122 -0
  44. data/test/test_ldap_connection.rb +24 -0
  45. data/test/test_ldif.rb +79 -0
  46. data/test/test_password.rb +17 -0
  47. data/test/test_rename.rb +77 -0
  48. data/test/test_snmp.rb +114 -0
  49. data/test/testdata.ldif +101 -0
  50. data/testserver/ldapserver.rb +210 -0
  51. data/testserver/testdata.ldif +101 -0
  52. metadata +213 -0
@@ -0,0 +1,74 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+
3
+ require "rubygems"
4
+ require 'hoe'
5
+
6
+ Hoe.plugin :doofus
7
+ Hoe.plugin :git
8
+ Hoe.plugin :gemspec
9
+
10
+ Hoe.spec 'net-ldap' do |spec|
11
+ spec.rubyforge_name = spec.name
12
+
13
+ spec.developer("Francis Cianfrocca", "blackhedd@rubyforge.org")
14
+ spec.developer("Emiel van de Laar", "gemiel@gmail.com")
15
+ spec.developer("Rory O'Connell", "rory.ocon@gmail.com")
16
+ spec.developer("Kaspar Schiess", "kaspar.schiess@absurd.li")
17
+ spec.developer("Austin Ziegler", "austin@rubyforge.org")
18
+
19
+ spec.remote_rdoc_dir = ''
20
+ spec.rsync_args << ' --exclude=statsvn/'
21
+
22
+ spec.url = %W(http://rubyldap.com/ https://github.com/ruby-ldap/ruby-net-ldap)
23
+
24
+ spec.history_file = 'History.rdoc'
25
+ spec.readme_file = 'README.rdoc'
26
+
27
+ spec.extra_rdoc_files = FileList["*.rdoc"].to_a
28
+
29
+ spec.extra_dev_deps << [ "hoe-git", "~> 1" ]
30
+ spec.extra_dev_deps << [ "hoe-gemspec", "~> 1" ]
31
+ spec.extra_dev_deps << [ "metaid", "~> 1" ]
32
+ spec.extra_dev_deps << [ "flexmock", "~> 0.9.0" ]
33
+ spec.extra_dev_deps << [ "rspec", "~> 2.0" ]
34
+
35
+ spec.clean_globs << "coverage"
36
+
37
+ spec.spec_extras[:required_ruby_version] = ">= 1.8.7"
38
+ spec.multiruby_skip << "1.8.6"
39
+ spec.multiruby_skip << "1_8_6"
40
+
41
+ spec.need_tar = true
42
+ end
43
+
44
+ # I'm not quite ready to get rid of this, but I think "rake git:manifest" is
45
+ # sufficient.
46
+ namespace :old do
47
+ desc "Build the manifest file from the current set of files."
48
+ task :build_manifest do |t|
49
+ require 'find'
50
+
51
+ paths = []
52
+ Find.find(".") do |path|
53
+ next if File.directory?(path)
54
+ next if path =~ /\.svn/
55
+ next if path =~ /\.git/
56
+ next if path =~ /\.hoerc/
57
+ next if path =~ /\.swp$/
58
+ next if path =~ %r{coverage/}
59
+ next if path =~ /~$/
60
+ paths << path.sub(%r{^\./}, '')
61
+ end
62
+
63
+ File.open("Manifest.txt", "w") do |f|
64
+ f.puts paths.sort.join("\n")
65
+ end
66
+
67
+ puts paths.sort.join("\n")
68
+ end
69
+ end
70
+
71
+ desc "Run a full set of integration and unit tests"
72
+ task :cruise => [:test, :spec]
73
+
74
+ # vim: syntax=ruby
@@ -0,0 +1 @@
1
+ Autotest.add_discovery { "rspec2" }
@@ -0,0 +1,2 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+ require 'net/ldap'
@@ -0,0 +1,320 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+ require 'net/ldap/version'
3
+
4
+ module Net # :nodoc:
5
+ ##
6
+ # == Basic Encoding Rules (BER) Support Module
7
+ #
8
+ # Much of the text below is cribbed from Wikipedia:
9
+ # http://en.wikipedia.org/wiki/Basic_Encoding_Rules
10
+ #
11
+ # The ITU Specification is also worthwhile reading:
12
+ # http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
13
+ #
14
+ # The Basic Encoding Rules were the original rules laid out by the ASN.1
15
+ # standard for encoding abstract information into a concrete data stream.
16
+ # The rules, collectively referred to as a transfer syntax in ASN.1
17
+ # parlance, specify the exact octet sequences which are used to encode a
18
+ # given data item. The syntax defines such elements as: the
19
+ # representations for basic data types, the structure of length
20
+ # information, and the means for defining complex or compound types based
21
+ # on more primitive types. The BER syntax, along with two subsets of BER
22
+ # (the Canonical Encoding Rules and the Distinguished Encoding Rules), are
23
+ # defined by the ITU-T's X.690 standards document, which is part of the
24
+ # ASN.1 document series.
25
+ #
26
+ # == Encoding
27
+ # The BER format specifies a self-describing and self-delimiting format
28
+ # for encoding ASN.1 data structures. Each data element is encoded as a
29
+ # type identifier, a length description, the actual data elements, and
30
+ # where necessary, an end-of-content marker. This format allows a receiver
31
+ # to decode the ASN.1 information from an incomplete stream, without
32
+ # requiring any pre-knowledge of the size, content, or semantic meaning of
33
+ # the data.
34
+ #
35
+ # <Type | Length | Value [| End-of-Content]>
36
+ #
37
+ # == Protocol Data Units (PDU)
38
+ # Protocols are defined with schema represented in BER, such that a PDU
39
+ # consists of cascaded type-length-value encodings.
40
+ #
41
+ # === Type Tags
42
+ # BER type tags are represented as single octets (bytes). The lower five
43
+ # bits of the octet are tag identifier numbers and the upper three bits of
44
+ # the octet are used to distinguish the type as native to ASN.1,
45
+ # application-specific, context-specific, or private. See
46
+ # Net::BER::TAG_CLASS and Net::BER::ENCODING_TYPE for more information.
47
+ #
48
+ # If Class is set to Universal (0b00______), the value is of a type native
49
+ # to ASN.1 (e.g. INTEGER). The Application class (0b01______) is only
50
+ # valid for one specific application. Context_specific (0b10______)
51
+ # depends on the context and private (0b11_______) can be defined in
52
+ # private specifications
53
+ #
54
+ # If the primitive/constructed bit is zero (0b__0_____), it specifies that
55
+ # the value is primitive like an INTEGER. If it is one (0b__1_____), the
56
+ # value is a constructed value that contains type-length-value encoded
57
+ # types like a SET or a SEQUENCE.
58
+ #
59
+ # === Defined Universal (ASN.1 Native) Types
60
+ # There are a number of pre-defined universal (native) types.
61
+ #
62
+ # <table>
63
+ # <tr><th>Name</th><th>Primitive<br />Constructed</th><th>Number</th></tr>
64
+ # <tr><th>EOC (End-of-Content)</th><th>P</th><td>0: 0 (0x0, 0b00000000)</td></tr>
65
+ # <tr><th>BOOLEAN</th><th>P</th><td>1: 1 (0x01, 0b00000001)</td></tr>
66
+ # <tr><th>INTEGER</th><th>P</th><td>2: 2 (0x02, 0b00000010)</td></tr>
67
+ # <tr><th>BIT STRING</th><th>P</th><td>3: 3 (0x03, 0b00000011)</td></tr>
68
+ # <tr><th>BIT STRING</th><th>C</th><td>3: 35 (0x23, 0b00100011)</td></tr>
69
+ # <tr><th>OCTET STRING</th><th>P</th><td>4: 4 (0x04, 0b00000100)</td></tr>
70
+ # <tr><th>OCTET STRING</th><th>C</th><td>4: 36 (0x24, 0b00100100)</td></tr>
71
+ # <tr><th>NULL</th><th>P</th><td>5: 5 (0x05, 0b00000101)</td></tr>
72
+ # <tr><th>OBJECT IDENTIFIER</th><th>P</th><td>6: 6 (0x06, 0b00000110)</td></tr>
73
+ # <tr><th>Object Descriptor</th><th>P</th><td>7: 7 (0x07, 0b00000111)</td></tr>
74
+ # <tr><th>EXTERNAL</th><th>C</th><td>8: 40 (0x28, 0b00101000)</td></tr>
75
+ # <tr><th>REAL (float)</th><th>P</th><td>9: 9 (0x09, 0b00001001)</td></tr>
76
+ # <tr><th>ENUMERATED</th><th>P</th><td>10: 10 (0x0a, 0b00001010)</td></tr>
77
+ # <tr><th>EMBEDDED PDV</th><th>C</th><td>11: 43 (0x2b, 0b00101011)</td></tr>
78
+ # <tr><th>UTF8String</th><th>P</th><td>12: 12 (0x0c, 0b00001100)</td></tr>
79
+ # <tr><th>UTF8String</th><th>C</th><td>12: 44 (0x2c, 0b00101100)</td></tr>
80
+ # <tr><th>RELATIVE-OID</th><th>P</th><td>13: 13 (0x0d, 0b00001101)</td></tr>
81
+ # <tr><th>SEQUENCE and SEQUENCE OF</th><th>C</th><td>16: 48 (0x30, 0b00110000)</td></tr>
82
+ # <tr><th>SET and SET OF</th><th>C</th><td>17: 49 (0x31, 0b00110001)</td></tr>
83
+ # <tr><th>NumericString</th><th>P</th><td>18: 18 (0x12, 0b00010010)</td></tr>
84
+ # <tr><th>NumericString</th><th>C</th><td>18: 50 (0x32, 0b00110010)</td></tr>
85
+ # <tr><th>PrintableString</th><th>P</th><td>19: 19 (0x13, 0b00010011)</td></tr>
86
+ # <tr><th>PrintableString</th><th>C</th><td>19: 51 (0x33, 0b00110011)</td></tr>
87
+ # <tr><th>T61String</th><th>P</th><td>20: 20 (0x14, 0b00010100)</td></tr>
88
+ # <tr><th>T61String</th><th>C</th><td>20: 52 (0x34, 0b00110100)</td></tr>
89
+ # <tr><th>VideotexString</th><th>P</th><td>21: 21 (0x15, 0b00010101)</td></tr>
90
+ # <tr><th>VideotexString</th><th>C</th><td>21: 53 (0x35, 0b00110101)</td></tr>
91
+ # <tr><th>IA5String</th><th>P</th><td>22: 22 (0x16, 0b00010110)</td></tr>
92
+ # <tr><th>IA5String</th><th>C</th><td>22: 54 (0x36, 0b00110110)</td></tr>
93
+ # <tr><th>UTCTime</th><th>P</th><td>23: 23 (0x17, 0b00010111)</td></tr>
94
+ # <tr><th>UTCTime</th><th>C</th><td>23: 55 (0x37, 0b00110111)</td></tr>
95
+ # <tr><th>GeneralizedTime</th><th>P</th><td>24: 24 (0x18, 0b00011000)</td></tr>
96
+ # <tr><th>GeneralizedTime</th><th>C</th><td>24: 56 (0x38, 0b00111000)</td></tr>
97
+ # <tr><th>GraphicString</th><th>P</th><td>25: 25 (0x19, 0b00011001)</td></tr>
98
+ # <tr><th>GraphicString</th><th>C</th><td>25: 57 (0x39, 0b00111001)</td></tr>
99
+ # <tr><th>VisibleString</th><th>P</th><td>26: 26 (0x1a, 0b00011010)</td></tr>
100
+ # <tr><th>VisibleString</th><th>C</th><td>26: 58 (0x3a, 0b00111010)</td></tr>
101
+ # <tr><th>GeneralString</th><th>P</th><td>27: 27 (0x1b, 0b00011011)</td></tr>
102
+ # <tr><th>GeneralString</th><th>C</th><td>27: 59 (0x3b, 0b00111011)</td></tr>
103
+ # <tr><th>UniversalString</th><th>P</th><td>28: 28 (0x1c, 0b00011100)</td></tr>
104
+ # <tr><th>UniversalString</th><th>C</th><td>28: 60 (0x3c, 0b00111100)</td></tr>
105
+ # <tr><th>CHARACTER STRING</th><th>P</th><td>29: 29 (0x1d, 0b00011101)</td></tr>
106
+ # <tr><th>CHARACTER STRING</th><th>C</th><td>29: 61 (0x3d, 0b00111101)</td></tr>
107
+ # <tr><th>BMPString</th><th>P</th><td>30: 30 (0x1e, 0b00011110)</td></tr>
108
+ # <tr><th>BMPString</th><th>C</th><td>30: 62 (0x3e, 0b00111110)</td></tr>
109
+ # </table>
110
+ module BER
111
+ VERSION = Net::LDAP::VERSION
112
+
113
+ ##
114
+ # Used for BER-encoding the length and content bytes of a Fixnum integer
115
+ # values.
116
+ MAX_FIXNUM_SIZE = 0.size
117
+
118
+ ##
119
+ # BER tag classes are kept in bits seven and eight of the tag type
120
+ # octet.
121
+ #
122
+ # <table>
123
+ # <tr><th>Bitmask</th><th>Definition</th></tr>
124
+ # <tr><th><tt>0b00______</tt></th><td>Universal (ASN.1 Native) Types</td></tr>
125
+ # <tr><th><tt>0b01______</tt></th><td>Application Types</td></tr>
126
+ # <tr><th><tt>0b10______</tt></th><td>Context-Specific Types</td></tr>
127
+ # <tr><th><tt>0b11______</tt></th><td>Private Types</td></tr>
128
+ # </table>
129
+ TAG_CLASS = {
130
+ :universal => 0b00000000, # 0
131
+ :application => 0b01000000, # 64
132
+ :context_specific => 0b10000000, # 128
133
+ :private => 0b11000000, # 192
134
+ }
135
+
136
+ ##
137
+ # BER encoding type is kept in bit 6 of the tag type octet.
138
+ #
139
+ # <table>
140
+ # <tr><th>Bitmask</th><th>Definition</th></tr>
141
+ # <tr><th><tt>0b__0_____</tt></th><td>Primitive</td></tr>
142
+ # <tr><th><tt>0b__1_____</tt></th><td>Constructed</td></tr>
143
+ # </table>
144
+ ENCODING_TYPE = {
145
+ :primitive => 0b00000000, # 0
146
+ :constructed => 0b00100000, # 32
147
+ }
148
+
149
+ ##
150
+ # Accepts a hash of hashes describing a BER syntax and converts it into
151
+ # a byte-keyed object for fast BER conversion lookup. The resulting
152
+ # "compiled" syntax is used by Net::BER::BERParser.
153
+ #
154
+ # This method should be called only by client classes of Net::BER (e.g.,
155
+ # Net::LDAP and Net::SNMP) and not by clients of those classes.
156
+ #
157
+ # The hash-based syntax uses TAG_CLASS keys that contain hashes of
158
+ # ENCODING_TYPE keys that contain tag numbers with object type markers.
159
+ #
160
+ # :<TAG_CLASS> => {
161
+ # :<ENCODING_TYPE> => {
162
+ # <number> => <object-type>
163
+ # },
164
+ # },
165
+ #
166
+ # === Permitted Object Types
167
+ # <tt>:string</tt>:: A string value, represented as BerIdentifiedString.
168
+ # <tt>:integer</tt>:: An integer value, represented with Fixnum.
169
+ # <tt>:oid</tt>:: An Object Identifier value; see X.690 section
170
+ # 8.19. Currently represented with a standard array,
171
+ # but may be better represented as a
172
+ # BerIdentifiedOID object.
173
+ # <tt>:array</tt>:: A sequence, represented as BerIdentifiedArray.
174
+ # <tt>:boolean</tt>:: A boolean value, represented as +true+ or +false+.
175
+ # <tt>:null</tt>:: A null value, represented as BerIdentifiedNull.
176
+ #
177
+ # === Example
178
+ # Net::LDAP defines its ASN.1 BER syntax something like this:
179
+ #
180
+ # class Net::LDAP
181
+ # AsnSyntax = Net::BER.compile_syntax({
182
+ # :application => {
183
+ # :primitive => {
184
+ # 2 => :null,
185
+ # },
186
+ # :constructed => {
187
+ # 0 => :array,
188
+ # # ...
189
+ # },
190
+ # },
191
+ # :context_specific => {
192
+ # :primitive => {
193
+ # 0 => :string,
194
+ # # ...
195
+ # },
196
+ # :constructed => {
197
+ # 0 => :array,
198
+ # # ...
199
+ # },
200
+ # }
201
+ # })
202
+ # end
203
+ #
204
+ # NOTE:: For readability and formatting purposes, Net::LDAP and its
205
+ # siblings actually construct their syntaxes more deliberately,
206
+ # as shown below. Since a hash is passed in the end in any case,
207
+ # the format does not matter.
208
+ #
209
+ # primitive = { 2 => :null }
210
+ # constructed = {
211
+ # 0 => :array,
212
+ # # ...
213
+ # }
214
+ # application = {
215
+ # :primitive => primitive,
216
+ # :constructed => constructed
217
+ # }
218
+ #
219
+ # primitive = {
220
+ # 0 => :string,
221
+ # # ...
222
+ # }
223
+ # constructed = {
224
+ # 0 => :array,
225
+ # # ...
226
+ # }
227
+ # context_specific = {
228
+ # :primitive => primitive,
229
+ # :constructed => constructed
230
+ # }
231
+ # AsnSyntax = Net::BER.compile_syntax(:application => application,
232
+ # :context_specific => context_specific)
233
+ def self.compile_syntax(syntax)
234
+ # TODO 20100327 AZ: Should we be allocating an array of 256 values
235
+ # that will either be +nil+ or an object type symbol, or should we
236
+ # allocate an empty Hash since unknown values return +nil+ anyway?
237
+ out = [ nil ] * 256
238
+ syntax.each do |tag_class_id, encodings|
239
+ tag_class = TAG_CLASS[tag_class_id]
240
+ encodings.each do |encoding_id, classes|
241
+ encoding = ENCODING_TYPE[encoding_id]
242
+ object_class = tag_class + encoding
243
+ classes.each do |number, object_type|
244
+ out[object_class + number] = object_type
245
+ end
246
+ end
247
+ end
248
+ out
249
+ end
250
+ end
251
+ end
252
+
253
+ class Net::BER::BerError < RuntimeError; end
254
+
255
+ ##
256
+ # An Array object with a BER identifier attached.
257
+ class Net::BER::BerIdentifiedArray < Array
258
+ attr_accessor :ber_identifier
259
+
260
+ def initialize(*args)
261
+ super
262
+ end
263
+ end
264
+
265
+ ##
266
+ # A BER object identifier.
267
+ class Net::BER::BerIdentifiedOid
268
+ attr_accessor :ber_identifier
269
+
270
+ def initialize(oid)
271
+ if oid.is_a?(String)
272
+ oid = oid.split(/\./).map {|s| s.to_i }
273
+ end
274
+ @value = oid
275
+ end
276
+
277
+ def to_ber
278
+ to_ber_oid
279
+ end
280
+
281
+ def to_ber_oid
282
+ @value.to_ber_oid
283
+ end
284
+
285
+ def to_s
286
+ @value.join(".")
287
+ end
288
+
289
+ def to_arr
290
+ @value.dup
291
+ end
292
+ end
293
+
294
+ ##
295
+ # A String object with a BER identifier attached.
296
+ class Net::BER::BerIdentifiedString < String
297
+ attr_accessor :ber_identifier
298
+ def initialize args
299
+ super args
300
+ # LDAP uses UTF-8 encoded strings
301
+ self.encode('UTF-8') if self.respond_to?(:encoding) rescue self
302
+ end
303
+ end
304
+
305
+ module Net::BER
306
+ ##
307
+ # A BER null object.
308
+ class BerIdentifiedNull
309
+ attr_accessor :ber_identifier
310
+ def to_ber
311
+ "\005\000"
312
+ end
313
+ end
314
+
315
+ ##
316
+ # The default BerIdentifiedNull object.
317
+ Null = Net::BER::BerIdentifiedNull.new
318
+ end
319
+
320
+ require 'net/ber/core_ext'
@@ -0,0 +1,168 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+ require 'stringio'
3
+
4
+ # Implements Basic Encoding Rules parsing to be mixed into types as needed.
5
+ module Net::BER::BERParser
6
+ primitive = {
7
+ 1 => :boolean,
8
+ 2 => :integer,
9
+ 4 => :string,
10
+ 5 => :null,
11
+ 6 => :oid,
12
+ 10 => :integer,
13
+ 13 => :string # (relative OID)
14
+ }
15
+ constructed = {
16
+ 16 => :array,
17
+ 17 => :array
18
+ }
19
+ universal = { :primitive => primitive, :constructed => constructed }
20
+
21
+ primitive = { 10 => :integer }
22
+ context = { :primitive => primitive }
23
+
24
+ # The universal, built-in ASN.1 BER syntax.
25
+ BuiltinSyntax = Net::BER.compile_syntax(:universal => universal,
26
+ :context_specific => context)
27
+
28
+ ##
29
+ # This is an extract of our BER object parsing to simplify our
30
+ # understanding of how we parse basic BER object types.
31
+ def parse_ber_object(syntax, id, data)
32
+ # Find the object type from either the provided syntax lookup table or
33
+ # the built-in syntax lookup table.
34
+ #
35
+ # This exceptionally clever bit of code is verrrry slow.
36
+ object_type = (syntax && syntax[id]) || BuiltinSyntax[id]
37
+
38
+ # == is expensive so sort this so the common cases are at the top.
39
+ if object_type == :string
40
+ s = Net::BER::BerIdentifiedString.new(data || "")
41
+ s.ber_identifier = id
42
+ s
43
+ elsif object_type == :integer
44
+ j = 0
45
+ data.each_byte { |b| j = (j << 8) + b }
46
+ j
47
+ elsif object_type == :oid
48
+ # See X.690 pgh 8.19 for an explanation of this algorithm.
49
+ # This is potentially not good enough. We may need a
50
+ # BerIdentifiedOid as a subclass of BerIdentifiedArray, to
51
+ # get the ber identifier and also a to_s method that produces
52
+ # the familiar dotted notation.
53
+ oid = data.unpack("w*")
54
+ f = oid.shift
55
+ g = if f < 40
56
+ [0, f]
57
+ elsif f < 80
58
+ [1, f - 40]
59
+ else
60
+ # f - 80 can easily be > 80. What a weird optimization.
61
+ [2, f - 80]
62
+ end
63
+ oid.unshift g.last
64
+ oid.unshift g.first
65
+ # Net::BER::BerIdentifiedOid.new(oid)
66
+ oid
67
+ elsif object_type == :array
68
+ seq = Net::BER::BerIdentifiedArray.new
69
+ seq.ber_identifier = id
70
+ sio = StringIO.new(data || "")
71
+ # Interpret the subobject, but note how the loop is built:
72
+ # nil ends the loop, but false (a valid BER value) does not!
73
+ while (e = sio.read_ber(syntax)) != nil
74
+ seq << e
75
+ end
76
+ seq
77
+ elsif object_type == :boolean
78
+ data != "\000"
79
+ elsif object_type == :null
80
+ n = Net::BER::BerIdentifiedNull.new
81
+ n.ber_identifier = id
82
+ n
83
+ else
84
+ raise Net::BER::BerError, "Unsupported object type: id=#{id}"
85
+ end
86
+ end
87
+ private :parse_ber_object
88
+
89
+ ##
90
+ # This is an extract of how our BER object length parsing is done to
91
+ # simplify the primary call. This is defined in X.690 section 8.1.3.
92
+ #
93
+ # The BER length will either be a single byte or up to 126 bytes in
94
+ # length. There is a special case of a BER length indicating that the
95
+ # content-length is undefined and will be identified by the presence of
96
+ # two null values (0x00 0x00).
97
+ #
98
+ # <table>
99
+ # <tr>
100
+ # <th>Range</th>
101
+ # <th>Length</th>
102
+ # </tr>
103
+ # <tr>
104
+ # <th>0x00 -- 0x7f<br />0b00000000 -- 0b01111111</th>
105
+ # <td>0 - 127 bytes</td>
106
+ # </tr>
107
+ # <tr>
108
+ # <th>0x80<br />0b10000000</th>
109
+ # <td>Indeterminate (end-of-content marker required)</td>
110
+ # </tr>
111
+ # <tr>
112
+ # <th>0x81 -- 0xfe<br />0b10000001 -- 0b11111110</th>
113
+ # <td>1 - 126 bytes of length as an integer value</td>
114
+ # </tr>
115
+ # <tr>
116
+ # <th>0xff<br />0b11111111</th>
117
+ # <td>Illegal (reserved for future expansion)</td>
118
+ # </tr>
119
+ # </table>
120
+ #
121
+ #--
122
+ # This has been modified from the version that was previously inside
123
+ # #read_ber to handle both the indeterminate terminator case and the
124
+ # invalid BER length case. Because the "lengthlength" value was not used
125
+ # inside of #read_ber, we no longer return it.
126
+ def read_ber_length
127
+ n = getbyte
128
+
129
+ if n <= 0x7f
130
+ n
131
+ elsif n == 0x80
132
+ -1
133
+ elsif n == 0xff
134
+ raise Net::BER::BerError, "Invalid BER length 0xFF detected."
135
+ else
136
+ v = 0
137
+ read(n & 0x7f).each_byte do |b|
138
+ v = (v << 8) + b
139
+ end
140
+
141
+ v
142
+ end
143
+ end
144
+ private :read_ber_length
145
+
146
+ ##
147
+ # Reads a BER object from the including object. Requires that #getbyte is
148
+ # implemented on the including object and that it returns a Fixnum value.
149
+ # Also requires #read(bytes) to work.
150
+ #
151
+ # This does not work with non-blocking I/O.
152
+ def read_ber(syntax = nil)
153
+ # TODO: clean this up so it works properly with partial packets coming
154
+ # from streams that don't block when we ask for more data (like
155
+ # StringIOs). At it is, this can throw TypeErrors and other nasties.
156
+
157
+ id = getbyte or return nil # don't trash this value, we'll use it later
158
+ content_length = read_ber_length
159
+
160
+ if -1 == content_length
161
+ raise Net::BER::BerError, "Indeterminite BER content length not implemented."
162
+ else
163
+ data = read(content_length)
164
+ end
165
+
166
+ parse_ber_object(syntax, id, data)
167
+ end
168
+ end