net-ldap 0.0.5

Sign up to get free protection for your applications and to get access to all the features.

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
+