net-ldap 0.0.5

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.

Potentially problematic release.


This version of net-ldap might be problematic. Click here for more details.

@@ -0,0 +1,62 @@
1
+ = Net::LDAP for Ruby
2
+
3
+ * http://rubyforge.org/projects/net-ldap
4
+
5
+ == DESCRIPTION:
6
+
7
+ Pure Ruby LDAP library.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ The Lightweight Directory Access Protocol (LDAP) is an Internet protocol
12
+ for accessing distributed directory services.
13
+
14
+ Net::LDAP is an LDAP support library written in pure Ruby. It supports
15
+ most LDAP client features and a subset of server features as well.
16
+
17
+ * Standards-based (going for RFC 4511)
18
+ * Portable: 100% Ruby
19
+
20
+ == SYNOPSIS:
21
+
22
+ See Net::LDAP for documentation and usage samples.
23
+
24
+ == REQUIREMENTS:
25
+
26
+ Net::LDAP requires Ruby 1.8.2 or better.
27
+
28
+ == INSTALL:
29
+
30
+ Net::LDAP is a pure Ruby library. It does not require any external
31
+ libraries.
32
+
33
+ You can install the RubyGems version of Net::LDAP available from the
34
+ usual sources.
35
+
36
+ * gem install net-ldap
37
+
38
+ If using the packaged (.tgz) version; it can be installed with:
39
+
40
+ * ruby setup.rb
41
+
42
+ == CREDITS:
43
+
44
+ Net::LDAP was originally developed by:
45
+
46
+ * Francis Cianfrocca <garbagecat10@gmail.com>
47
+
48
+ Contributions since:
49
+
50
+ * Austin Ziegler <halostatue@gmail.com>
51
+ * Emiel van de Laar <gemiel@gmail.com>
52
+
53
+ == LICENSE:
54
+
55
+ Copyright (C) 2006 by Francis Cianfrocca
56
+
57
+ Please read the file LICENSE for licensing restrictions on this library. In
58
+ the simplest terms, this library is available under the same terms as Ruby
59
+ itself.
60
+
61
+ Available under the same terms as Ruby. See LICENSE in the main
62
+ distribution for full licensing information.
@@ -0,0 +1,18 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ # Add 'lib' to load path.
7
+ $LOAD_PATH.unshift( "#{File.dirname(__FILE__)}/lib" )
8
+
9
+ # Pull in local 'net/ldap' as opposed to an installed version.
10
+ require 'net/ldap'
11
+
12
+ Hoe.new('net-ldap', Net::LDAP::VERSION) do |p|
13
+ p.rubyforge_name = 'net-ldap'
14
+ p.developer('Francis Cianfrocca', 'garbagecat10@gmail.com')
15
+ p.developer('Emiel van de Laar', 'gemiel@gmail.com')
16
+ end
17
+
18
+ # vim: syntax=Ruby
@@ -0,0 +1,95 @@
1
+ We're pleased to announce version 0.0.4 of Net::LDAP, the pure-Ruby LDAP library.
2
+
3
+ This version adds an implementation of Net::LDAP#bind_as, which allows
4
+ you to authenticate users who log into your applications using simple
5
+ identifiers like email addresses and simple usernames. Thanks to Simon Claret
6
+ for suggesting the original implementation in his page on the Rails-Wiki,
7
+ and for valuable comments and help with testing.
8
+
9
+ We have un-deprecated Net::LDAP#modify, which can be useful with
10
+ LDAP servers that observe non-standard transactional and concurrency
11
+ semantics in LDAP Modify operations. Note: this is a documentation change,
12
+ not a code change. Thanks to Justin Forder for providing the rationale
13
+ for this change.
14
+
15
+ We added a larger set of special characters which may appear in RFC-2254
16
+ standard search filters. Thanks to Andre Nathan for this patch.
17
+
18
+ We fixed a bug that was preventing Net::LDAP#open from being called more
19
+ than once on the same object.
20
+
21
+
22
+ Net::LDAP is a feature-complete LDAP client which can access as much as
23
+ possible of the functionality of the most-used LDAP server implementations.
24
+ This library does not wrap any existing native-code LDAP libraries, creates no
25
+ Ruby extensions, and has no dependencies external to Ruby.
26
+
27
+ If anyone wants to contribute suggestions, insights or (especially)
28
+ code, please email me at garbagecat10 .. .. gmail.com.
29
+
30
+ = What is Net::LDAP for Ruby?
31
+ This library provides a pure-Ruby implementation of an LDAP client.
32
+ It can be used to access any server which implements the LDAP protocol.
33
+
34
+ Net::LDAP is intended to provide full LDAP functionality while hiding
35
+ the more arcane aspects of the LDAP protocol itself, so as to make the
36
+ programming interface as Ruby-like as possible.
37
+
38
+ In particular, this means that there is no direct dependence on the
39
+ structure of the various "traditional" LDAP clients. This is a ground-up
40
+ rethinking of the LDAP API.
41
+
42
+ Net::LDAP is based on RFC-2251, which specifies the Lightweight Directory
43
+ Access Protocol, as amended and extended by subsequent RFCs and by the more
44
+ widely-used directory implementations.
45
+
46
+ Homepage:: http://rubyforge.org/projects/net-ldap/
47
+ Download:: http://rubyforge.org/frs/?group_id=143
48
+ Copyright:: 2006 by Francis Cianfrocca
49
+
50
+ == LICENCE NOTES
51
+ Please read the file LICENCE for licensing restrictions on this library. In
52
+ the simplest terms, this library is available under the same terms as Ruby
53
+ itself.
54
+
55
+ == Requirements and Installation
56
+ Net::LDAP requires Ruby 1.8.2 or better.
57
+
58
+ Net::LDAP can be installed with:
59
+
60
+ % ruby setup.rb
61
+
62
+ Alternatively, you can use the RubyGems version of Net::LDAP available
63
+ as ruby-net-ldap-0.0.2.gem from the usual sources.
64
+
65
+ == Whet your appetite:
66
+ require 'net/ldap'
67
+
68
+ ldap = Net::LDAP.new :host => server_ip_address,
69
+ :port => 389,
70
+ :auth => {
71
+ :method => :simple,
72
+ :username => "cn=manager,dc=example,dc=com",
73
+ :password => "opensesame"
74
+ }
75
+
76
+ filter = Net::LDAP::Filter.eq( "cn", "George*" )
77
+ treebase = "dc=example,dc=com"
78
+
79
+ ldap.search( :base => treebase, :filter => filter ) do |entry|
80
+ puts "DN: #{entry.dn}"
81
+ entry.each do |attribute, values|
82
+ puts " #{attribute}:"
83
+ values.each do |value|
84
+ puts " --->#{value}"
85
+ end
86
+ end
87
+ end
88
+
89
+ p ldap.get_operation_result
90
+
91
+ == Net::LDAP 0.0.2: May 3, 2006
92
+ * Fixed malformation in distro tarball and gem.
93
+ * Improved documentation.
94
+ * Supported "paged search control."
95
+
@@ -0,0 +1,557 @@
1
+ # $Id$
2
+ #
3
+ # NET::BER
4
+ # Mixes ASN.1/BER convenience methods into several standard classes.
5
+ # Also provides BER parsing functionality.
6
+ #
7
+ #----------------------------------------------------------------------------
8
+ #
9
+ # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
10
+ #
11
+ # Gmail: garbagecat10
12
+ #
13
+ # This program is free software; you can redistribute it and/or modify
14
+ # it under the terms of the GNU General Public License as published by
15
+ # the Free Software Foundation; either version 2 of the License, or
16
+ # (at your option) any later version.
17
+ #
18
+ # This program is distributed in the hope that it will be useful,
19
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
+ # GNU General Public License for more details.
22
+ #
23
+ # You should have received a copy of the GNU General Public License
24
+ # along with this program; if not, write to the Free Software
25
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26
+ #
27
+ #---------------------------------------------------------------------------
28
+ #
29
+ #
30
+
31
+
32
+ module Net
33
+
34
+ module BER
35
+
36
+ class BerError < StandardError; end
37
+
38
+
39
+ class BerIdentifiedString < String
40
+ attr_accessor :ber_identifier
41
+ def initialize args
42
+ super args
43
+ end
44
+ end
45
+
46
+ class BerIdentifiedArray < Array
47
+ attr_accessor :ber_identifier
48
+ def initialize
49
+ super
50
+ end
51
+ end
52
+
53
+ class BerIdentifiedNull
54
+ attr_accessor :ber_identifier
55
+ def to_ber
56
+ "\005\000"
57
+ end
58
+ end
59
+
60
+ class BerIdentifiedOid
61
+ attr_accessor :ber_identifier
62
+ def initialize oid
63
+ if oid.is_a?(String)
64
+ oid = oid.split(/\./).map {|s| s.to_i }
65
+ end
66
+ @value = oid
67
+ end
68
+ def to_ber
69
+ # Provisional implementation.
70
+ # We ASSUME that our incoming value is an array, and we
71
+ # use the Array#to_ber_oid method defined below.
72
+ # We probably should obsolete that method, actually, in
73
+ # and move the code here.
74
+ # WE ARE NOT CURRENTLY ENCODING THE BER-IDENTIFIER.
75
+ # This implementation currently hardcodes 6, the universal OID tag.
76
+ ary = @value.dup
77
+ first = ary.shift
78
+ raise Net::BER::BerError.new(" invalid OID" ) unless [0,1,2].include?(first)
79
+ first = first * 40 + ary.shift
80
+ ary.unshift first
81
+ oid = ary.pack("w*")
82
+ [6, oid.length].pack("CC") + oid
83
+ end
84
+ end
85
+
86
+ #--
87
+ # This condenses our nicely self-documenting ASN hashes down
88
+ # to an array for fast lookups.
89
+ # Scoped to be called as a module method, but not intended for
90
+ # user code to call.
91
+ #
92
+ def self.compile_syntax syn
93
+ out = [nil] * 256
94
+ syn.each {|tclass,tclasses|
95
+ tagclass = {:universal=>0, :application=>64, :context_specific=>128, :private=>192} [tclass]
96
+ tclasses.each {|codingtype,codings|
97
+ encoding = {:primitive=>0, :constructed=>32} [codingtype]
98
+ codings.each {|tag,objtype|
99
+ out[tagclass + encoding + tag] = objtype
100
+ }
101
+ }
102
+ }
103
+ out
104
+ end
105
+
106
+ # This module is for mixing into IO and IO-like objects.
107
+ module BERParser
108
+
109
+ # The order of these follows the class-codes in BER.
110
+ # Maybe this should have been a hash.
111
+ TagClasses = [:universal, :application, :context_specific, :private]
112
+
113
+ BuiltinSyntax = BER.compile_syntax( {
114
+ :universal => {
115
+ :primitive => {
116
+ 1 => :boolean,
117
+ 2 => :integer,
118
+ 4 => :string,
119
+ 5 => :null,
120
+ 6 => :oid,
121
+ 10 => :integer,
122
+ 13 => :string # (relative OID)
123
+ },
124
+ :constructed => {
125
+ 16 => :array,
126
+ 17 => :array
127
+ }
128
+ },
129
+ :context_specific => {
130
+ :primitive => {
131
+ 10 => :integer
132
+ }
133
+ }
134
+ })
135
+
136
+ #
137
+ # read_ber
138
+ # TODO: clean this up so it works properly with partial
139
+ # packets coming from streams that don't block when
140
+ # we ask for more data (like StringIOs). At it is,
141
+ # this can throw TypeErrors and other nasties.
142
+ #--
143
+ # BEWARE, this violates DRY and is largely equal in functionality to
144
+ # read_ber_from_string. Eventually that method may subsume the functionality
145
+ # of this one.
146
+ #
147
+ def read_ber syntax=nil
148
+ # don't bother with this line, since IO#getbyte by definition returns nil on eof.
149
+ #return nil if eof?
150
+
151
+ id = getbyte or return nil # don't trash this value, we'll use it later
152
+ #tag = id & 31
153
+ #tag < 31 or raise BerError.new( "unsupported tag encoding: #{id}" )
154
+ #tagclass = TagClasses[ id >> 6 ]
155
+ #encoding = (id & 0x20 != 0) ? :constructed : :primitive
156
+
157
+ n = getbyte
158
+ lengthlength,contentlength = if n <= 127
159
+ [1,n]
160
+ else
161
+ # Replaced the inject because it profiles hot.
162
+ #j = (0...(n & 127)).inject(0) {|mem,x| mem = (mem << 8) + getbyte}
163
+ j = 0
164
+ read( n & 127 ).each_byte {|n1| j = (j << 8) + n1}
165
+ [1 + (n & 127), j]
166
+ end
167
+
168
+ newobj = read contentlength
169
+
170
+ # This exceptionally clever and clear bit of code is verrrry slow.
171
+ objtype = (syntax && syntax[id]) || BuiltinSyntax[id]
172
+
173
+
174
+ # == is expensive so sort this if/else so the common cases are at the top.
175
+ obj = if objtype == :string
176
+ #(newobj || "").dup
177
+ s = BerIdentifiedString.new( newobj || "" )
178
+ s.ber_identifier = id
179
+ s
180
+ elsif objtype == :integer
181
+ j = 0
182
+ newobj.each_byte {|b| j = (j << 8) + b}
183
+ j
184
+ elsif objtype == :oid
185
+ # cf X.690 pgh 8.19 for an explanation of this algorithm.
186
+ # Potentially not good enough. We may need a BerIdentifiedOid
187
+ # as a subclass of BerIdentifiedArray, to get the ber identifier
188
+ # and also a to_s method that produces the familiar dotted notation.
189
+ oid = newobj.unpack("w*")
190
+ f = oid.shift
191
+ g = if f < 40
192
+ [0, f]
193
+ elsif f < 80
194
+ [1, f-40]
195
+ else
196
+ [2, f-80] # f-80 can easily be > 80. What a weird optimization.
197
+ end
198
+ oid.unshift g.last
199
+ oid.unshift g.first
200
+ oid
201
+ elsif objtype == :array
202
+ #seq = []
203
+ seq = BerIdentifiedArray.new
204
+ seq.ber_identifier = id
205
+ sio = StringIO.new( newobj || "" )
206
+ # Interpret the subobject, but note how the loop
207
+ # is built: nil ends the loop, but false (a valid
208
+ # BER value) does not!
209
+ while (e = sio.read_ber(syntax)) != nil
210
+ seq << e
211
+ end
212
+ seq
213
+ elsif objtype == :boolean
214
+ newobj != "\000"
215
+ elsif objtype == :null
216
+ n = BerIdentifiedNull.new
217
+ n.ber_identifier = id
218
+ n
219
+ else
220
+ #raise BerError.new( "unsupported object type: class=#{tagclass}, encoding=#{encoding}, tag=#{tag}" )
221
+ raise BerError.new( "unsupported object type: id=#{id}" )
222
+ end
223
+
224
+ # Add the identifier bits into the object if it's a String or an Array.
225
+ # We can't add extra stuff to Fixnums and booleans, not that it makes much sense anyway.
226
+ # Replaced this mechanism with subclasses because the instance_eval profiled too hot.
227
+ #obj and ([String,Array].include? obj.class) and obj.instance_eval "def ber_identifier; #{id}; end"
228
+ #obj.ber_identifier = id if obj.respond_to?(:ber_identifier)
229
+ obj
230
+
231
+ end
232
+
233
+ #--
234
+ # Violates DRY! This replicates the functionality of #read_ber.
235
+ # Eventually this method may replace that one.
236
+ # This version of #read_ber behaves properly in the face of incomplete
237
+ # data packets. If a full BER object is detected, we return an array containing
238
+ # the detected object and the number of bytes consumed from the string.
239
+ # If we don't detect a complete packet, return nil.
240
+ #
241
+ # Observe that weirdly we recursively call the original #read_ber in here.
242
+ # That needs to be fixed if we ever obsolete the original method in favor of this one.
243
+ def read_ber_from_string str, syntax=nil
244
+ id = str[0] or return nil
245
+ n = str[1] or return nil
246
+ n_consumed = 2
247
+ lengthlength,contentlength = if n <= 127
248
+ [1,n]
249
+ else
250
+ n1 = n & 127
251
+ return nil unless str.length >= (n_consumed + n1)
252
+ j = 0
253
+ n1.times {
254
+ j = (j << 8) + str[n_consumed]
255
+ n_consumed += 1
256
+ }
257
+ [1 + (n1), j]
258
+ end
259
+
260
+ return nil unless str.length >= (n_consumed + contentlength)
261
+ newobj = str[n_consumed...(n_consumed + contentlength)]
262
+ n_consumed += contentlength
263
+
264
+ objtype = (syntax && syntax[id]) || BuiltinSyntax[id]
265
+
266
+ # == is expensive so sort this if/else so the common cases are at the top.
267
+ obj = if objtype == :array
268
+ seq = BerIdentifiedArray.new
269
+ seq.ber_identifier = id
270
+ sio = StringIO.new( newobj || "" )
271
+ # Interpret the subobject, but note how the loop
272
+ # is built: nil ends the loop, but false (a valid
273
+ # BER value) does not!
274
+ # Also, we can use the standard read_ber method because
275
+ # we know for sure we have enough data. (Although this
276
+ # might be faster than the standard method.)
277
+ while (e = sio.read_ber(syntax)) != nil
278
+ seq << e
279
+ end
280
+ seq
281
+ elsif objtype == :string
282
+ s = BerIdentifiedString.new( newobj || "" )
283
+ s.ber_identifier = id
284
+ s
285
+ elsif objtype == :integer
286
+ j = 0
287
+ newobj.each_byte {|b| j = (j << 8) + b}
288
+ j
289
+ elsif objtype == :oid
290
+ # cf X.690 pgh 8.19 for an explanation of this algorithm.
291
+ # Potentially not good enough. We may need a BerIdentifiedOid
292
+ # as a subclass of BerIdentifiedArray, to get the ber identifier
293
+ # and also a to_s method that produces the familiar dotted notation.
294
+ oid = newobj.unpack("w*")
295
+ f = oid.shift
296
+ g = if f < 40
297
+ [0,f]
298
+ elsif f < 80
299
+ [1, f-40]
300
+ else
301
+ [2, f-80] # f-80 can easily be > 80. What a weird optimization.
302
+ end
303
+ oid.unshift g.last
304
+ oid.unshift g.first
305
+ oid
306
+ elsif objtype == :boolean
307
+ newobj != "\000"
308
+ elsif objtype == :null
309
+ n = BerIdentifiedNull.new
310
+ n.ber_identifier = id
311
+ n
312
+ else
313
+ raise BerError.new( "unsupported object type: id=#{id}" )
314
+ end
315
+
316
+ [obj, n_consumed]
317
+ end
318
+
319
+ end # module BERParser
320
+ end # module BER
321
+
322
+ end # module Net
323
+
324
+
325
+ class IO
326
+ include Net::BER::BERParser
327
+ end
328
+
329
+ require "stringio"
330
+ class StringIO
331
+ include Net::BER::BERParser
332
+ end
333
+
334
+ begin
335
+ require 'openssl'
336
+ class OpenSSL::SSL::SSLSocket
337
+ include Net::BER::BERParser
338
+ end
339
+ rescue LoadError
340
+ # Ignore LoadError.
341
+ # DON'T ignore NameError, which means the SSLSocket class
342
+ # is somehow unavailable on this implementation of Ruby's openssl.
343
+ # This may be WRONG, however, because we don't yet know how Ruby's
344
+ # openssl behaves on machines with no OpenSSL library. I suppose
345
+ # it's possible they do not fail to require 'openssl' but do not
346
+ # create the classes. So this code is provisional.
347
+ # Also, you might think that OpenSSL::SSL::SSLSocket inherits from
348
+ # IO so we'd pick it up above. But you'd be wrong.
349
+ end
350
+
351
+
352
+
353
+ class String
354
+ include Net::BER::BERParser
355
+ def read_ber syntax=nil
356
+ StringIO.new(self).read_ber(syntax)
357
+ end
358
+ def read_ber! syntax=nil
359
+ obj,n_consumed = read_ber_from_string(self, syntax)
360
+ if n_consumed
361
+ self.slice!(0...n_consumed)
362
+ obj
363
+ else
364
+ nil
365
+ end
366
+ end
367
+ end
368
+
369
+ #----------------------------------------------
370
+
371
+
372
+ class FalseClass
373
+ #
374
+ # to_ber
375
+ #
376
+ def to_ber
377
+ "\001\001\000"
378
+ end
379
+ end
380
+
381
+
382
+ class TrueClass
383
+ #
384
+ # to_ber
385
+ #
386
+ def to_ber
387
+ "\001\001\001"
388
+ end
389
+ end
390
+
391
+
392
+
393
+ class Fixnum
394
+ #
395
+ # to_ber
396
+ #
397
+ def to_ber
398
+ "\002" + to_ber_internal
399
+ end
400
+
401
+ #
402
+ # to_ber_enumerated
403
+ #
404
+ def to_ber_enumerated
405
+ "\012" + to_ber_internal
406
+ end
407
+
408
+ #
409
+ # to_ber_length_encoding
410
+ #
411
+ def to_ber_length_encoding
412
+ if self <= 127
413
+ [self].pack('C')
414
+ else
415
+ i = [self].pack('N').sub(/^[\0]+/,"")
416
+ [0x80 + i.length].pack('C') + i
417
+ end
418
+ end
419
+
420
+ # Generate a BER-encoding for an application-defined INTEGER.
421
+ # Example: SNMP's Counter, Gauge, and TimeTick types.
422
+ #
423
+ def to_ber_application tag
424
+ [0x40 + tag].pack("C") + to_ber_internal
425
+ end
426
+
427
+ #--
428
+ # Called internally to BER-encode the length and content bytes of a Fixnum.
429
+ # The caller will prepend the tag byte.
430
+ def to_ber_internal
431
+ # PLEASE optimize this code path. It's awfully ugly and probably slow.
432
+ # It also doesn't understand negative numbers yet.
433
+ raise Net::BER::BerError.new( "range error in fixnum" ) unless self >= 0
434
+ z = [self].pack("N")
435
+ zlen = if self < 0x80
436
+ 1
437
+ elsif self < 0x8000
438
+ 2
439
+ elsif self < 0x800000
440
+ 3
441
+ else
442
+ 4
443
+ end
444
+ [zlen].pack("C") + z[0-zlen,zlen]
445
+ end
446
+ private :to_ber_internal
447
+
448
+ end # class Fixnum
449
+
450
+
451
+ class Bignum
452
+
453
+ def to_ber
454
+ #i = [self].pack('w')
455
+ #i.length > 126 and raise Net::BER::BerError.new( "range error in bignum" )
456
+ #[2, i.length].pack("CC") + i
457
+
458
+ # Ruby represents Bignums as two's-complement numbers so we may actually be
459
+ # good as far as representing negatives goes.
460
+ # I'm sure this implementation can be improved performance-wise if necessary.
461
+ # Ruby's Bignum#size returns the number of bytes in the internal representation
462
+ # of the number, but it can and will include leading zero bytes on at least
463
+ # some implementations. Evidently Ruby stores these as sets of quadbytes.
464
+ # It's not illegal in BER to encode all of the leading zeroes but let's strip
465
+ # them out anyway.
466
+ #
467
+ sz = self.size
468
+ out = "\000" * sz
469
+ (sz*8).times {|bit|
470
+ if self[bit] == 1
471
+ out[bit/8] += (1 << (bit % 8))
472
+ end
473
+ }
474
+
475
+ while out.length > 1 and out[-1] == 0
476
+ out.slice!(-1,1)
477
+ end
478
+
479
+ [2, out.length].pack("CC") + out.reverse
480
+ end
481
+
482
+ end
483
+
484
+
485
+
486
+ class String
487
+ #
488
+ # to_ber
489
+ # A universal octet-string is tag number 4,
490
+ # but others are possible depending on the context, so we
491
+ # let the caller give us one.
492
+ # The preferred way to do this in user code is via to_ber_application_sring
493
+ # and to_ber_contextspecific.
494
+ #
495
+ def to_ber code = 4
496
+ [code].pack('C') + length.to_ber_length_encoding + self
497
+ end
498
+
499
+ #
500
+ # to_ber_application_string
501
+ #
502
+ def to_ber_application_string code
503
+ to_ber( 0x40 + code )
504
+ end
505
+
506
+ #
507
+ # to_ber_contextspecific
508
+ #
509
+ def to_ber_contextspecific code
510
+ to_ber( 0x80 + code )
511
+ end
512
+
513
+ end # class String
514
+
515
+
516
+
517
+ class Array
518
+ #
519
+ # to_ber_appsequence
520
+ # An application-specific sequence usually gets assigned
521
+ # a tag that is meaningful to the particular protocol being used.
522
+ # This is different from the universal sequence, which usually
523
+ # gets a tag value of 16.
524
+ # Now here's an interesting thing: We're adding the X.690
525
+ # "application constructed" code at the top of the tag byte (0x60),
526
+ # but some clients, notably ldapsearch, send "context-specific
527
+ # constructed" (0xA0). The latter would appear to violate RFC-1777,
528
+ # but what do I know? We may need to change this.
529
+ #
530
+
531
+ def to_ber id = 0; to_ber_seq_internal( 0x30 + id ); end
532
+ def to_ber_set id = 0; to_ber_seq_internal( 0x31 + id ); end
533
+ def to_ber_sequence id = 0; to_ber_seq_internal( 0x30 + id ); end
534
+ def to_ber_appsequence id = 0; to_ber_seq_internal( 0x60 + id ); end
535
+ def to_ber_contextspecific id = 0; to_ber_seq_internal( 0xA0 + id ); end
536
+
537
+ def to_ber_oid
538
+ ary = self.dup
539
+ first = ary.shift
540
+ raise Net::BER::BerError.new( "invalid OID" ) unless [0,1,2].include?(first)
541
+ first = first * 40 + ary.shift
542
+ ary.unshift first
543
+ oid = ary.pack("w*")
544
+ [6, oid.length].pack("CC") + oid
545
+ end
546
+
547
+ private
548
+ def to_ber_seq_internal code
549
+ s = ''
550
+ self.each{|x| s = s + x}
551
+ [code].pack('C') + s.length.to_ber_length_encoding + s
552
+ end
553
+
554
+
555
+ end # class Array
556
+
557
+