line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Copyright (c) 1999-2004 Graham Barr and |
2
|
|
|
|
|
|
|
# Norbert Klasen All Rights Reserved. |
3
|
|
|
|
|
|
|
# This program is free software; you can redistribute it and/or modify |
4
|
|
|
|
|
|
|
# it under the same terms as Perl itself. |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Net::LDAP::Util; |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 NAME |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
Net::LDAP::Util - Utility functions |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 SYNOPSIS |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
use Net::LDAP::Util qw(ldap_error_text |
15
|
|
|
|
|
|
|
ldap_error_name |
16
|
|
|
|
|
|
|
ldap_error_desc |
17
|
|
|
|
|
|
|
); |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
$mesg = $ldap->search( .... ); |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
die "Error ",ldap_error_name($mesg) if $mesg->code; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 DESCRIPTION |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
B is a collection of utility functions for use with |
26
|
|
|
|
|
|
|
the L modules. |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=head1 FUNCTIONS |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=over 4 |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=cut |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
require Exporter; |
35
|
|
|
|
|
|
|
require Net::LDAP::Constant; |
36
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
37
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
38
|
|
|
|
|
|
|
ldap_error_name |
39
|
|
|
|
|
|
|
ldap_error_text |
40
|
|
|
|
|
|
|
ldap_error_desc |
41
|
|
|
|
|
|
|
canonical_dn |
42
|
|
|
|
|
|
|
ldap_explode_dn |
43
|
|
|
|
|
|
|
escape_filter_value |
44
|
|
|
|
|
|
|
unescape_filter_value |
45
|
|
|
|
|
|
|
escape_dn_value |
46
|
|
|
|
|
|
|
unescape_dn_value |
47
|
|
|
|
|
|
|
ldap_url_parse |
48
|
|
|
|
|
|
|
generalizedTime_to_time |
49
|
|
|
|
|
|
|
time_to_generalizedTime |
50
|
|
|
|
|
|
|
); |
51
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( |
52
|
|
|
|
|
|
|
error => [ qw(ldap_error_name ldap_error_text ldap_error_desc) ], |
53
|
|
|
|
|
|
|
filter => [ qw(escape_filter_value unescape_filter_value) ], |
54
|
|
|
|
|
|
|
dn => [ qw(canonical_dn ldap_explode_dn |
55
|
|
|
|
|
|
|
escape_dn_value unescape_dn_value) ], |
56
|
|
|
|
|
|
|
escape => [ qw(escape_filter_value unescape_filter_value |
57
|
|
|
|
|
|
|
escape_dn_value unescape_dn_value) ], |
58
|
|
|
|
|
|
|
url => [ qw(ldap_url_parse) ], |
59
|
|
|
|
|
|
|
time => [ qw(generalizedTime_to_time time_to_generalizedTime) ], |
60
|
|
|
|
|
|
|
); |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
our $VERSION = '0.20'; |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=item ldap_error_name ( ERR ) |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
Returns the name corresponding with ERR. ERR can either be an LDAP |
67
|
|
|
|
|
|
|
error number, or a C object containing an error |
68
|
|
|
|
|
|
|
code. If the error is not known the a string in the form C<"LDAP error |
69
|
|
|
|
|
|
|
code %d(0x%02X)"> is returned. |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=cut |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# Defined in Constant.pm |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
=item ldap_error_text ( ERR ) |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
Returns the text from the POD description for the given error. ERR can |
78
|
|
|
|
|
|
|
either be an LDAP error code, or a C object |
79
|
|
|
|
|
|
|
containing an LDAP error code. If the error code given is unknown then |
80
|
|
|
|
|
|
|
C is returned. |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=cut |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# Defined in Constant.pm |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=item ldap_error_desc ( ERR ) |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
Returns a short text description of the error. ERR can either be an |
89
|
|
|
|
|
|
|
LDAP error code or a C object containing an LDAP |
90
|
|
|
|
|
|
|
error code. |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=cut |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
my @err2desc = ( |
95
|
|
|
|
|
|
|
'Success', # 0x00 LDAP_SUCCESS |
96
|
|
|
|
|
|
|
'Operations error', # 0x01 LDAP_OPERATIONS_ERROR |
97
|
|
|
|
|
|
|
'Protocol error', # 0x02 LDAP_PROTOCOL_ERROR |
98
|
|
|
|
|
|
|
'Timelimit exceeded', # 0x03 LDAP_TIMELIMIT_EXCEEDED |
99
|
|
|
|
|
|
|
'Sizelimit exceeded', # 0x04 LDAP_SIZELIMIT_EXCEEDED |
100
|
|
|
|
|
|
|
'Compare false', # 0x05 LDAP_COMPARE_FALSE |
101
|
|
|
|
|
|
|
'Compare true', # 0x06 LDAP_COMPARE_TRUE |
102
|
|
|
|
|
|
|
'Strong authentication not supported', # 0x07 LDAP_STRONG_AUTH_NOT_SUPPORTED |
103
|
|
|
|
|
|
|
'Strong authentication required', # 0x08 LDAP_STRONG_AUTH_REQUIRED |
104
|
|
|
|
|
|
|
'Partial results and referral received', # 0x09 LDAP_PARTIAL_RESULTS |
105
|
|
|
|
|
|
|
'Referral received', # 0x0a LDAP_REFERRAL |
106
|
|
|
|
|
|
|
'Admin limit exceeded', # 0x0b LDAP_ADMIN_LIMIT_EXCEEDED |
107
|
|
|
|
|
|
|
'Critical extension not available', # 0x0c LDAP_UNAVAILABLE_CRITICAL_EXT |
108
|
|
|
|
|
|
|
'Confidentiality required', # 0x0d LDAP_CONFIDENTIALITY_REQUIRED |
109
|
|
|
|
|
|
|
'SASL bind in progress', # 0x0e LDAP_SASL_BIND_IN_PROGRESS |
110
|
|
|
|
|
|
|
undef, |
111
|
|
|
|
|
|
|
'No such attribute', # 0x10 LDAP_NO_SUCH_ATTRIBUTE |
112
|
|
|
|
|
|
|
'Undefined attribute type', # 0x11 LDAP_UNDEFINED_TYPE |
113
|
|
|
|
|
|
|
'Inappropriate matching', # 0x12 LDAP_INAPPROPRIATE_MATCHING |
114
|
|
|
|
|
|
|
'Constraint violation', # 0x13 LDAP_CONSTRAINT_VIOLATION |
115
|
|
|
|
|
|
|
'Type or value exists', # 0x14 LDAP_TYPE_OR_VALUE_EXISTS |
116
|
|
|
|
|
|
|
'Invalid syntax', # 0x15 LDAP_INVALID_SYNTAX |
117
|
|
|
|
|
|
|
undef, |
118
|
|
|
|
|
|
|
undef, |
119
|
|
|
|
|
|
|
undef, |
120
|
|
|
|
|
|
|
undef, |
121
|
|
|
|
|
|
|
undef, |
122
|
|
|
|
|
|
|
undef, |
123
|
|
|
|
|
|
|
undef, |
124
|
|
|
|
|
|
|
undef, |
125
|
|
|
|
|
|
|
undef, |
126
|
|
|
|
|
|
|
undef, |
127
|
|
|
|
|
|
|
'No such object', # 0x20 LDAP_NO_SUCH_OBJECT |
128
|
|
|
|
|
|
|
'Alias problem', # 0x21 LDAP_ALIAS_PROBLEM |
129
|
|
|
|
|
|
|
'Invalid DN syntax', # 0x22 LDAP_INVALID_DN_SYNTAX |
130
|
|
|
|
|
|
|
'Object is a leaf', # 0x23 LDAP_IS_LEAF |
131
|
|
|
|
|
|
|
'Alias dereferencing problem', # 0x24 LDAP_ALIAS_DEREF_PROBLEM |
132
|
|
|
|
|
|
|
undef, |
133
|
|
|
|
|
|
|
undef, |
134
|
|
|
|
|
|
|
undef, |
135
|
|
|
|
|
|
|
undef, |
136
|
|
|
|
|
|
|
undef, |
137
|
|
|
|
|
|
|
undef, |
138
|
|
|
|
|
|
|
undef, |
139
|
|
|
|
|
|
|
undef, |
140
|
|
|
|
|
|
|
undef, |
141
|
|
|
|
|
|
|
undef, |
142
|
|
|
|
|
|
|
'Proxy authorization failure', # 0x2F LDAP_PROXY_AUTHZ_FAILURE |
143
|
|
|
|
|
|
|
'Inappropriate authentication', # 0x30 LDAP_INAPPROPRIATE_AUTH |
144
|
|
|
|
|
|
|
'Invalid credentials', # 0x31 LDAP_INVALID_CREDENTIALS |
145
|
|
|
|
|
|
|
'Insufficient access', # 0x32 LDAP_INSUFFICIENT_ACCESS |
146
|
|
|
|
|
|
|
'DSA is busy', # 0x33 LDAP_BUSY |
147
|
|
|
|
|
|
|
'DSA is unavailable', # 0x34 LDAP_UNAVAILABLE |
148
|
|
|
|
|
|
|
'DSA is unwilling to perform', # 0x35 LDAP_UNWILLING_TO_PERFORM |
149
|
|
|
|
|
|
|
'Loop detected', # 0x36 LDAP_LOOP_DETECT |
150
|
|
|
|
|
|
|
undef, |
151
|
|
|
|
|
|
|
undef, |
152
|
|
|
|
|
|
|
undef, |
153
|
|
|
|
|
|
|
undef, |
154
|
|
|
|
|
|
|
undef, |
155
|
|
|
|
|
|
|
'Sort control missing', # 0x3C LDAP_SORT_CONTROL_MISSING |
156
|
|
|
|
|
|
|
'Index range error', # 0x3D LDAP_INDEX_RANGE_ERROR |
157
|
|
|
|
|
|
|
undef, |
158
|
|
|
|
|
|
|
undef, |
159
|
|
|
|
|
|
|
'Naming violation', # 0x40 LDAP_NAMING_VIOLATION |
160
|
|
|
|
|
|
|
'Object class violation', # 0x41 LDAP_OBJECT_CLASS_VIOLATION |
161
|
|
|
|
|
|
|
'Operation not allowed on non-leaf', # 0x42 LDAP_NOT_ALLOWED_ON_NONLEAF |
162
|
|
|
|
|
|
|
'Operation not allowed on RDN', # 0x43 LDAP_NOT_ALLOWED_ON_RDN |
163
|
|
|
|
|
|
|
'Already exists', # 0x44 LDAP_ALREADY_EXISTS |
164
|
|
|
|
|
|
|
'Cannot modify object class', # 0x45 LDAP_NO_OBJECT_CLASS_MODS |
165
|
|
|
|
|
|
|
'Results too large', # 0x46 LDAP_RESULTS_TOO_LARGE |
166
|
|
|
|
|
|
|
'Affects multiple servers', # 0x47 LDAP_AFFECTS_MULTIPLE_DSAS |
167
|
|
|
|
|
|
|
undef, |
168
|
|
|
|
|
|
|
undef, |
169
|
|
|
|
|
|
|
undef, |
170
|
|
|
|
|
|
|
undef, |
171
|
|
|
|
|
|
|
'VLV error', # 0x4C LDAP_VLV_ERROR |
172
|
|
|
|
|
|
|
undef, |
173
|
|
|
|
|
|
|
undef, |
174
|
|
|
|
|
|
|
undef, |
175
|
|
|
|
|
|
|
'Unknown error', # 0x50 LDAP_OTHER |
176
|
|
|
|
|
|
|
'Can\'t contact LDAP server', # 0x51 LDAP_SERVER_DOWN |
177
|
|
|
|
|
|
|
'Local error', # 0x52 LDAP_LOCAL_ERROR |
178
|
|
|
|
|
|
|
'Encoding error', # 0x53 LDAP_ENCODING_ERROR |
179
|
|
|
|
|
|
|
'Decoding error', # 0x54 LDAP_DECODING_ERROR |
180
|
|
|
|
|
|
|
'Timed out', # 0x55 LDAP_TIMEOUT |
181
|
|
|
|
|
|
|
'Unknown authentication method', # 0x56 LDAP_AUTH_UNKNOWN |
182
|
|
|
|
|
|
|
'Bad search filter', # 0x57 LDAP_FILTER_ERROR |
183
|
|
|
|
|
|
|
'Canceled', # 0x58 LDAP_USER_CANCELED |
184
|
|
|
|
|
|
|
'Bad parameter to an ldap routine', # 0x59 LDAP_PARAM_ERROR |
185
|
|
|
|
|
|
|
'Out of memory', # 0x5a LDAP_NO_MEMORY |
186
|
|
|
|
|
|
|
'Can\'t connect to the LDAP server', # 0x5b LDAP_CONNECT_ERROR |
187
|
|
|
|
|
|
|
'Not supported by this version of the LDAP protocol', # 0x5c LDAP_NOT_SUPPORTED |
188
|
|
|
|
|
|
|
'Requested LDAP control not found', # 0x5d LDAP_CONTROL_NOT_FOUND |
189
|
|
|
|
|
|
|
'No results returned', # 0x5e LDAP_NO_RESULTS_RETURNED |
190
|
|
|
|
|
|
|
'More results to return', # 0x5f LDAP_MORE_RESULTS_TO_RETURN |
191
|
|
|
|
|
|
|
'Client detected loop', # 0x60 LDAP_CLIENT_LOOP |
192
|
|
|
|
|
|
|
'Referral hop limit exceeded', # 0x61 LDAP_REFERRAL_LIMIT_EXCEEDED |
193
|
|
|
|
|
|
|
); |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub ldap_error_desc { |
196
|
0
|
0
|
|
0
|
1
|
0
|
my $code = (ref($_[0]) ? $_[0]->code : $_[0]); |
197
|
0
|
0
|
|
|
|
0
|
$err2desc[$code] || sprintf('LDAP error code %d(0x%02X)', $code, $code); |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=item canonical_dn ( DN [ , OPTIONS ] ) |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
Returns the given B in a canonical form. Returns undef if B is |
207
|
|
|
|
|
|
|
not a valid Distinguished Name. (Note: The empty string "" is a valid DN.) |
208
|
|
|
|
|
|
|
B can either be a string or reference to an array of hashes as returned by |
209
|
|
|
|
|
|
|
ldap_explode_dn, which is useful when constructing a DN. |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
It performs the following operations on the given B: |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=over 4 |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=item * |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
Removes the leading 'OID.' characters if the type is an OID instead |
218
|
|
|
|
|
|
|
of a name. |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=item * |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
Escapes all RFC 4514 special characters (",", "+", """, "\", "E", |
223
|
|
|
|
|
|
|
"E", ";", "#", "=", " "), slashes ("/"), and any other character |
224
|
|
|
|
|
|
|
where the ASCII code is E 32 as \hexpair. |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=item * |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
Converts all leading and trailing spaces in values to be \20. |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=item * |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
If an RDN contains multiple parts, the parts are re-ordered so that |
233
|
|
|
|
|
|
|
the attribute type names are in alphabetical order. |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
=back |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
B is a list of name/value pairs, valid options are: |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
=over 4 |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
=item casefold |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
Controls case folding of attribute type names. Attribute values are not |
244
|
|
|
|
|
|
|
affected by this option. The default is to uppercase. Valid values are: |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=over 4 |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
=item lower |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
Lowercase attribute type names. |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=item upper |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
Uppercase attribute type names. This is the default. |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
=item none |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
Do not change attribute type names. |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
=back |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
=item mbcescape |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
If TRUE, characters that are encoded as a multi-octet UTF-8 sequence |
265
|
|
|
|
|
|
|
will be escaped as \(hexpair){2,*}. |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
=item reverse |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
If TRUE, the RDN sequence is reversed. |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=item separator |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
Separator to use between RDNs. Defaults to comma (','). |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=back |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=cut |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
sub canonical_dn($%) { |
280
|
73
|
|
|
73
|
1
|
22063
|
my ($dn, %opt) = @_; |
281
|
|
|
|
|
|
|
|
282
|
73
|
100
|
66
|
|
|
329
|
return $dn unless defined $dn and $dn ne ''; |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
# create array of hash representation |
285
|
|
|
|
|
|
|
my $rdns = ref($dn) eq 'ARRAY' |
286
|
|
|
|
|
|
|
? $dn |
287
|
72
|
50
|
50
|
|
|
298
|
: ldap_explode_dn( $dn, casefold => $opt{casefold} || 'upper') |
|
|
100
|
|
|
|
|
|
288
|
|
|
|
|
|
|
or return undef; #error condition |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
# assign specified or default separator value |
291
|
54
|
|
50
|
|
|
170
|
my $separator = $opt{separator} || ','; |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
# flatten all RDNs into strings |
294
|
|
|
|
|
|
|
my @flatrdns = |
295
|
|
|
|
|
|
|
map { |
296
|
54
|
|
|
|
|
113
|
my $rdn = $_; |
|
175
|
|
|
|
|
248
|
|
297
|
175
|
|
|
|
|
471
|
my @types = sort keys %$rdn; |
298
|
|
|
|
|
|
|
join('+', |
299
|
|
|
|
|
|
|
map { |
300
|
175
|
|
|
|
|
273
|
my $val = $rdn->{$_}; |
|
191
|
|
|
|
|
313
|
|
301
|
|
|
|
|
|
|
|
302
|
191
|
100
|
|
|
|
279
|
if ( ref($val) ) { |
303
|
4
|
|
|
|
|
19
|
$val = '#' . unpack('H*', $$val); |
304
|
|
|
|
|
|
|
} else { |
305
|
|
|
|
|
|
|
#escape insecure characters and optionally MBCs |
306
|
187
|
50
|
|
|
|
278
|
if ( $opt{mbcescape} ) { |
307
|
0
|
|
|
|
|
0
|
$val =~ s/([\x00-\x1f\/\\",=+<>#;\x7f-\xff])/ |
308
|
0
|
|
|
|
|
0
|
sprintf('\\%02x', ord($1))/xeg; |
309
|
|
|
|
|
|
|
} else { |
310
|
187
|
|
|
|
|
350
|
$val =~ s/([\x00-\x1f\/\\",=+<>#;])/ |
311
|
28
|
|
|
|
|
154
|
sprintf('\\%02x', ord($1))/xeg; |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
#escape leading and trailing whitespace |
314
|
187
|
|
|
|
|
606
|
$val =~ s/(^\s+|\s+$)/ |
315
|
13
|
|
|
|
|
50
|
'\\20' x length $1/xeg; |
316
|
|
|
|
|
|
|
#compact multiple spaces |
317
|
187
|
|
|
|
|
393
|
$val =~ s/\s+/ /g; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
# case fold attribute type and create return value |
321
|
191
|
50
|
33
|
|
|
422
|
if ( !$opt{casefold} || $opt{casefold} eq 'upper' ) { |
|
|
0
|
|
|
|
|
|
322
|
191
|
|
|
|
|
734
|
(uc $_)."=$val"; |
323
|
|
|
|
|
|
|
} elsif ( $opt{casefold} eq 'lower' ) { |
324
|
0
|
|
|
|
|
0
|
(lc $_)."=$val"; |
325
|
|
|
|
|
|
|
} else { |
326
|
0
|
|
|
|
|
0
|
"$_=$val"; |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
} @types); |
329
|
|
|
|
|
|
|
} @$rdns; |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
# join RDNs into string, optionally reversing order |
332
|
|
|
|
|
|
|
$opt{reverse} |
333
|
54
|
50
|
|
|
|
318
|
? join($separator, reverse @flatrdns) |
334
|
|
|
|
|
|
|
: join($separator, @flatrdns); |
335
|
|
|
|
|
|
|
} |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
=item ldap_explode_dn ( DN [ , OPTIONS ] ) |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
Explodes the given B into an array of hashes and returns a reference to this |
341
|
|
|
|
|
|
|
array. Returns undef if B is not a valid Distinguished Name. |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
A Distinguished Name is a sequence of Relative Distinguished Names (RDNs), which |
344
|
|
|
|
|
|
|
themselves are sets of Attributes. For each RDN a hash is constructed with the |
345
|
|
|
|
|
|
|
attribute type names as keys and the attribute values as corresponding values. |
346
|
|
|
|
|
|
|
These hashes are then stored in an array in the order in which they appear |
347
|
|
|
|
|
|
|
in the DN. |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
For example, the DN 'OU=Sales+CN=J. Smith,DC=example,DC=net' is exploded to: |
350
|
|
|
|
|
|
|
[ |
351
|
|
|
|
|
|
|
{ |
352
|
|
|
|
|
|
|
'OU' =E 'Sales', |
353
|
|
|
|
|
|
|
'CN' =E 'J. Smith' |
354
|
|
|
|
|
|
|
}, |
355
|
|
|
|
|
|
|
{ |
356
|
|
|
|
|
|
|
'DC' =E 'example' |
357
|
|
|
|
|
|
|
}, |
358
|
|
|
|
|
|
|
{ |
359
|
|
|
|
|
|
|
'DC' =E 'net' |
360
|
|
|
|
|
|
|
} |
361
|
|
|
|
|
|
|
] |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
(RFC4514 string) DNs might also contain values, which are the bytes of the |
364
|
|
|
|
|
|
|
BER encoding of the X.500 AttributeValue rather than some LDAP string syntax. |
365
|
|
|
|
|
|
|
These values are hex-encoded and prefixed with a #. To distinguish such BER |
366
|
|
|
|
|
|
|
values, ldap_explode_dn uses references to the actual values, |
367
|
|
|
|
|
|
|
e.g. '1.3.6.1.4.1.1466.0=#04024869,DC=example,DC=com' is exploded to: |
368
|
|
|
|
|
|
|
[ |
369
|
|
|
|
|
|
|
{ |
370
|
|
|
|
|
|
|
'1.3.6.1.4.1.1466.0' =E "\004\002Hi" |
371
|
|
|
|
|
|
|
}, |
372
|
|
|
|
|
|
|
{ |
373
|
|
|
|
|
|
|
'DC' =E 'example' |
374
|
|
|
|
|
|
|
}, |
375
|
|
|
|
|
|
|
{ |
376
|
|
|
|
|
|
|
'DC' =E 'com' |
377
|
|
|
|
|
|
|
} |
378
|
|
|
|
|
|
|
]; |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
It also performs the following operations on the given DN: |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
=over 4 |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=item * |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
Unescape "\" followed by ",", "+", """, "\", "E", "E", ";", |
387
|
|
|
|
|
|
|
"#", "=", " ", or a hexpair and strings beginning with "#". |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
=item * |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
Removes the leading 'OID.' characters if the type is an OID instead |
392
|
|
|
|
|
|
|
of a name. |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
=back |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
B is a list of name/value pairs, valid options are: |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
=over 4 |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
=item casefold |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
Controls case folding of attribute types names. Attribute values are not |
403
|
|
|
|
|
|
|
affected by this option. The default is to uppercase. Valid values are: |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
=over 4 |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
=item lower |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
Lowercase attribute types names. |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
=item upper |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
Uppercase attribute type names. This is the default. |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
=item none |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
Do not change attribute type names. |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
=back |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
=item reverse |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
If TRUE, the RDN sequence is reversed. |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
=back |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
=cut |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
sub ldap_explode_dn($%) { |
430
|
76
|
|
|
76
|
1
|
183
|
my ($dn, %opt) = @_; |
431
|
76
|
50
|
|
|
|
148
|
return undef unless defined $dn; |
432
|
76
|
50
|
|
|
|
146
|
return [] if $dn eq ''; |
433
|
|
|
|
|
|
|
|
434
|
76
|
|
|
|
|
260
|
my $pair = qr/\\(?:[\\"+,;<> #=]|[0-9A-F]{2})/i; |
435
|
|
|
|
|
|
|
|
436
|
76
|
|
|
|
|
119
|
my (@dn, %rdn); |
437
|
76
|
|
|
|
|
1582
|
while ( |
438
|
|
|
|
|
|
|
$dn =~ /\G(?: |
439
|
|
|
|
|
|
|
\s* |
440
|
|
|
|
|
|
|
((?i)[A-Z][-A-Z0-9]*|(?:oid\.)?\d+(?:\.\d+)*) # attribute type |
441
|
|
|
|
|
|
|
\s* |
442
|
|
|
|
|
|
|
= |
443
|
|
|
|
|
|
|
[ ]* |
444
|
|
|
|
|
|
|
( # attribute value |
445
|
|
|
|
|
|
|
(?:(?:[^\x00 "\#+,;<>\\\x80-\xBF]|$pair) # string |
446
|
|
|
|
|
|
|
(?:(?:[^\x00"+,;<>\\]|$pair)* |
447
|
|
|
|
|
|
|
(?:[^\x00 "+,;<>\\]|$pair))?)? |
448
|
|
|
|
|
|
|
| |
449
|
|
|
|
|
|
|
\#(?:[0-9a-fA-F]{2})+ # hex string |
450
|
|
|
|
|
|
|
| |
451
|
|
|
|
|
|
|
"(?:[^\\"]+|$pair)*" # "-quoted string, only for v2 |
452
|
|
|
|
|
|
|
) |
453
|
|
|
|
|
|
|
[ ]* |
454
|
|
|
|
|
|
|
(?:([;,+])\s*(?=\S)|$) # separator |
455
|
|
|
|
|
|
|
)\s*/gcx) |
456
|
|
|
|
|
|
|
{ |
457
|
217
|
|
|
|
|
776
|
my($type, $val, $sep) = ($1, $2, $3); |
458
|
|
|
|
|
|
|
|
459
|
217
|
|
|
|
|
310
|
$type =~ s/^oid\.//i; #remove leading "oid." |
460
|
|
|
|
|
|
|
|
461
|
217
|
100
|
66
|
|
|
728
|
if ( !$opt{casefold} || $opt{casefold} eq 'upper' ) { |
|
|
50
|
|
|
|
|
|
462
|
197
|
|
|
|
|
336
|
$type = uc $type; |
463
|
|
|
|
|
|
|
} elsif ( $opt{casefold} eq 'lower' ) { |
464
|
20
|
|
|
|
|
35
|
$type = lc($type); |
465
|
|
|
|
|
|
|
} |
466
|
|
|
|
|
|
|
|
467
|
217
|
100
|
|
|
|
396
|
if ( $val =~ s/^#// ) { |
468
|
|
|
|
|
|
|
# decode hex-encoded BER value |
469
|
4
|
|
|
|
|
19
|
my $tmp = pack('H*', $val); |
470
|
4
|
|
|
|
|
8
|
$val = \$tmp; |
471
|
|
|
|
|
|
|
} else { |
472
|
|
|
|
|
|
|
# remove quotes |
473
|
213
|
|
|
|
|
307
|
$val =~ s/^"(.*)"$/$1/; |
474
|
|
|
|
|
|
|
# unescape characters |
475
|
213
|
|
|
|
|
360
|
$val =~ s/\\([\\ ",=+<>#;]|[0-9a-fA-F]{2}) |
476
|
44
|
100
|
|
|
|
208
|
/length($1)==1 ? $1 : chr(hex($1)) |
477
|
|
|
|
|
|
|
/xeg; |
478
|
|
|
|
|
|
|
} |
479
|
|
|
|
|
|
|
|
480
|
217
|
|
|
|
|
433
|
$rdn{$type} = $val; |
481
|
|
|
|
|
|
|
|
482
|
217
|
100
|
100
|
|
|
810
|
unless (defined $sep and $sep eq '+') { |
483
|
199
|
50
|
|
|
|
1420
|
if ( $opt{reverse} ) { |
484
|
0
|
|
|
|
|
0
|
unshift @dn, { %rdn }; |
485
|
|
|
|
|
|
|
} else { |
486
|
199
|
|
|
|
|
571
|
push @dn, { %rdn }; |
487
|
|
|
|
|
|
|
} |
488
|
199
|
|
|
|
|
1878
|
%rdn = (); |
489
|
|
|
|
|
|
|
} |
490
|
|
|
|
|
|
|
} |
491
|
|
|
|
|
|
|
|
492
|
76
|
100
|
100
|
|
|
462
|
length($dn) == (pos($dn)||0) |
493
|
|
|
|
|
|
|
? \@dn |
494
|
|
|
|
|
|
|
: undef; |
495
|
|
|
|
|
|
|
} |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
=item escape_filter_value ( VALUES ) |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
Escapes the given B according to RFC 4515 so that they |
501
|
|
|
|
|
|
|
can be safely used in LDAP filters. |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
Any control characters with an ASCII code E 32 as well as the |
504
|
|
|
|
|
|
|
characters with special meaning in LDAP filters "*", "(", ")", |
505
|
|
|
|
|
|
|
and "\" the backslash are converted into the representation |
506
|
|
|
|
|
|
|
of a backslash followed by two hex digits representing the |
507
|
|
|
|
|
|
|
hexadecimal value of the character. |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
Returns the converted list in list mode and the first element |
510
|
|
|
|
|
|
|
in scalar mode. |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=cut |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
## convert a list of values into its LDAP filter encoding ## |
515
|
|
|
|
|
|
|
# Synopsis: @escaped = escape_filter_value(@values) |
516
|
|
|
|
|
|
|
sub escape_filter_value(@) |
517
|
|
|
|
|
|
|
{ |
518
|
0
|
|
|
0
|
1
|
0
|
my @values = @_; |
519
|
|
|
|
|
|
|
|
520
|
0
|
|
|
|
|
0
|
map { $_ =~ s/([\x00-\x1F\*\(\)\\])/'\\'.unpack('H2', $1)/oge; } @values; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
521
|
|
|
|
|
|
|
|
522
|
0
|
0
|
|
|
|
0
|
return(wantarray ? @values : $values[0]); |
523
|
|
|
|
|
|
|
} |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=item unescape_filter_value ( VALUES ) |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
Undoes the conversion done by B. |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
Converts any sequences of a backslash followed by two hex digits |
531
|
|
|
|
|
|
|
into the corresponding character. |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
Returns the converted list in list mode and the first element |
534
|
|
|
|
|
|
|
in scalar mode. |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
=cut |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
## convert a list of values from its LDAP filter encoding ## |
539
|
|
|
|
|
|
|
# Synopsis: @values = unescape_filter_value(@escaped) |
540
|
|
|
|
|
|
|
sub unescape_filter_value(@) |
541
|
|
|
|
|
|
|
{ |
542
|
0
|
|
|
0
|
1
|
0
|
my @values = @_; |
543
|
|
|
|
|
|
|
|
544
|
0
|
|
|
|
|
0
|
map { $_ =~ s/\\([0-9a-fA-F]{2})/pack('H2', $1)/oge; } @values; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
545
|
|
|
|
|
|
|
|
546
|
0
|
0
|
|
|
|
0
|
return(wantarray ? @values : $values[0]); |
547
|
|
|
|
|
|
|
} |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
=item escape_dn_value ( VALUES ) |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
Escapes the given B according to RFC 4514 so that they |
553
|
|
|
|
|
|
|
can be safely used in LDAP DNs. |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
The characters ",", "+", """, "\", "E", "E", ";", "#", "=" with |
556
|
|
|
|
|
|
|
a special meaning in section 2.4 of RFC 4514 are preceded by a backslash. |
557
|
|
|
|
|
|
|
Control characters with an ASCII code E 32 are represented |
558
|
|
|
|
|
|
|
as \hexpair. |
559
|
|
|
|
|
|
|
Finally all leading and trailing spaces are converted to |
560
|
|
|
|
|
|
|
sequences of \20. |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
Returns the converted list in list mode and the first element |
563
|
|
|
|
|
|
|
in scalar mode. |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
=cut |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
## convert a list of values into its DN encoding ## |
568
|
|
|
|
|
|
|
# Synopsis: @escaped = escape_dn_value(@values) |
569
|
|
|
|
|
|
|
sub escape_dn_value(@) |
570
|
|
|
|
|
|
|
{ |
571
|
0
|
|
|
0
|
1
|
0
|
my @values = @_; |
572
|
|
|
|
|
|
|
|
573
|
0
|
|
|
|
|
0
|
map { $_ =~ s/([\\",=+<>#;])/\\$1/og; |
|
0
|
|
|
|
|
0
|
|
574
|
0
|
|
|
|
|
0
|
$_ =~ s/([\x00-\x1F])/'\\'.unpack('H2', $1)/oge; |
|
0
|
|
|
|
|
0
|
|
575
|
0
|
|
|
|
|
0
|
$_ =~ s/(^ +| +$)/'\\20' x length($1)/oge; } @values; |
|
0
|
|
|
|
|
0
|
|
576
|
|
|
|
|
|
|
|
577
|
0
|
0
|
|
|
|
0
|
return(wantarray ? @values : $values[0]); |
578
|
|
|
|
|
|
|
} |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
=item unescape_dn_value ( VALUES ) |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
Undoes the conversion done by B. |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
Any escape sequence starting with a backslash - hexpair or |
586
|
|
|
|
|
|
|
special character - will be transformed back to the |
587
|
|
|
|
|
|
|
corresponding character. |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
Returns the converted list in list mode and the first element |
590
|
|
|
|
|
|
|
in scalar mode. |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=cut |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
## convert a list of values from its LDAP filter encoding ## |
595
|
|
|
|
|
|
|
# Synopsis: @values = unescape_dn_value(@escaped) |
596
|
|
|
|
|
|
|
sub unescape_dn_value(@) |
597
|
|
|
|
|
|
|
{ |
598
|
0
|
|
|
0
|
1
|
0
|
my @values = @_; |
599
|
|
|
|
|
|
|
|
600
|
0
|
|
|
|
|
0
|
map { $_ =~ s/\\([\\",=+<>#;]|[0-9a-fA-F]{2}) |
|
0
|
|
|
|
|
0
|
|
601
|
0
|
0
|
|
|
|
0
|
/(length($1)==1) ? $1 : pack('H2', $1) |
602
|
|
|
|
|
|
|
/ogex; } @values; |
603
|
|
|
|
|
|
|
|
604
|
0
|
0
|
|
|
|
0
|
return(wantarray ? @values : $values[0]); |
605
|
|
|
|
|
|
|
} |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
=item ldap_url_parse ( LDAP-URL [, OPTIONS ] ) |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
Parse an B conforming to RFC 4516 into a hash containing its elements. |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
For easy cooperation with LDAP queries, the hash keys for the elements |
613
|
|
|
|
|
|
|
used in LDAP search operations are named after the parameters to |
614
|
|
|
|
|
|
|
L. |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
In extension to RFC 4516, the socket path for URLs with the scheme C |
617
|
|
|
|
|
|
|
will be stored in the hash key named C. |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
If any element is omitted, the result depends on the setting of the option |
620
|
|
|
|
|
|
|
C. |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
B is a list of key/value pairs with the following keys recognized: |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
=over 4 |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
=item defaults |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
A Boolean option that determines whether default values according to RFC 4516 |
629
|
|
|
|
|
|
|
shall be returned for missing URL elements. |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
If set to TRUE, default values are returned, with C |
632
|
|
|
|
|
|
|
using the following defaults in extension to RFC 4516. |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
=over 4 |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
=item * |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
The default port for C URLs is C<636>. |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
=item * |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
The default path for C URLs is the contents of the environment variable |
643
|
|
|
|
|
|
|
C. If that is not defined or empty, then C is used. |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
This is consistent with the behaviour of L. |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
=item * |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
The default C name for C and C URLs is C. |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
=back |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
When set to FALSE, no default values are used. |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
This leaves all keys in the resulting hash undefined where the corresponding |
656
|
|
|
|
|
|
|
URL element is empty. |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
To distinguish between an empty base DN and an undefined base DN, |
659
|
|
|
|
|
|
|
C uses the slash between the host:port resp. path |
660
|
|
|
|
|
|
|
part of the URL and the base DN part of the URL. |
661
|
|
|
|
|
|
|
With the slash present, the hash key C is set to the empty string, |
662
|
|
|
|
|
|
|
without it, it is left undefined. |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
Leaving away the C option entirely is equivalent to setting it to TRUE. |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
=back |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
Returns the hash in list mode, or the reference to the hash in scalar mode. |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
=cut |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
## parse an LDAP URL into its various elements |
673
|
|
|
|
|
|
|
# Synopsis: {$elementref,%elements} = ldap_url_parse($url) |
674
|
|
|
|
|
|
|
sub ldap_url_parse($@) |
675
|
|
|
|
|
|
|
{ |
676
|
0
|
|
|
0
|
1
|
0
|
my $url = shift; |
677
|
0
|
|
|
|
|
0
|
my %opt = @_; |
678
|
|
|
|
|
|
|
|
679
|
0
|
|
|
|
|
0
|
eval { require URI }; |
|
0
|
|
|
|
|
0
|
|
680
|
0
|
0
|
|
|
|
0
|
return if ($@); |
681
|
|
|
|
|
|
|
|
682
|
0
|
|
|
|
|
0
|
my $uri = URI->new($url); |
683
|
0
|
0
|
0
|
|
|
0
|
return unless ($uri && ref($uri) =~ /^URI::ldap[is]?$/); |
684
|
|
|
|
|
|
|
|
685
|
0
|
0
|
|
|
|
0
|
$opt{defaults} = 1 unless (exists($opt{defaults})); |
686
|
|
|
|
|
|
|
|
687
|
0
|
|
|
|
|
0
|
my %elements = ( scheme => $uri->scheme ); |
688
|
|
|
|
|
|
|
|
689
|
0
|
|
|
|
|
0
|
$uri = $uri->canonical; # canonical form |
690
|
0
|
|
|
|
|
0
|
$url = $uri->as_string; # normalize |
691
|
|
|
|
|
|
|
|
692
|
0
|
0
|
|
|
|
0
|
if ($elements{scheme} eq 'ldapi') { |
693
|
|
|
|
|
|
|
$elements{path} = $uri->un_path || $ENV{LDAPI_SOCK} || '/var/run/ldapi' |
694
|
0
|
0
|
0
|
|
|
0
|
if ($opt{defaults} || $uri->un_path); |
|
|
|
0
|
|
|
|
|
695
|
|
|
|
|
|
|
} |
696
|
|
|
|
|
|
|
else { |
697
|
|
|
|
|
|
|
$elements{host} = $uri->host || 'localhost' |
698
|
0
|
0
|
0
|
|
|
0
|
if ($opt{defaults} || $uri->host); |
|
|
|
0
|
|
|
|
|
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
$elements{port} = $uri->port || ($elements{scheme} eq 'ldaps' ? 636 : 389) |
701
|
0
|
0
|
0
|
|
|
0
|
if ($opt{defaults} || $uri->port); |
|
|
|
0
|
|
|
|
|
702
|
|
|
|
|
|
|
} |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
$elements{base} = $uri->dn |
705
|
0
|
0
|
0
|
|
|
0
|
if ($opt{defaults} || $uri->dn || $url =~ m{^ldap[is]?://[^/]*/}); |
|
|
|
0
|
|
|
|
|
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
$elements{attrs} = [ $uri->attributes ] |
708
|
0
|
0
|
0
|
|
|
0
|
if ($opt{defaults} || $uri->attributes); |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
$elements{scope} = $uri->scope |
711
|
0
|
0
|
0
|
|
|
0
|
if ($opt{defaults} || $uri->_scope); |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
$elements{filter} = $uri->filter |
714
|
0
|
0
|
0
|
|
|
0
|
if ($opt{defaults} || $uri->_filter); |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
$elements{extensions} = [ $uri->extensions ] |
717
|
0
|
0
|
0
|
|
|
0
|
if ($opt{defaults} || $uri->extensions); |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
#return _error($ldap, $mesg, LDAP_LOCAL_ERROR, "unhandled critical URL extension") |
720
|
|
|
|
|
|
|
# if (grep(/^!/, keys(%extns))); |
721
|
|
|
|
|
|
|
|
722
|
0
|
0
|
|
|
|
0
|
return wantarray ? %elements : \%elements; |
723
|
|
|
|
|
|
|
} |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
=item generalizedTime_to_time ( GENERALIZEDTIME ) |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
Convert the generalizedTime string B, which is expected |
729
|
|
|
|
|
|
|
to match the template C |
730
|
|
|
|
|
|
|
to a floating point number compatible with UNIX time |
731
|
|
|
|
|
|
|
(i.e. the integral part of the number is a UNIX time). |
732
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
Returns an extended UNIX time or C on error. |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
Times in years smaller than 1000 will lead to C being returned. |
736
|
|
|
|
|
|
|
This restriction is a direct effect of the year value interpretation rules |
737
|
|
|
|
|
|
|
in Time::Local. |
738
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
B this function depends on Perl's implementation of time and Time::Local. |
740
|
|
|
|
|
|
|
See L, L, and |
741
|
|
|
|
|
|
|
L for restrictions in older versions of Perl. |
742
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
=cut |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
sub generalizedTime_to_time($) |
746
|
|
|
|
|
|
|
{ |
747
|
32
|
|
|
32
|
1
|
18115
|
my $generalizedTime = shift; |
748
|
|
|
|
|
|
|
|
749
|
32
|
100
|
|
|
|
231
|
if ($generalizedTime =~ /^\s*(\d{4})(\d{2})(\d{2}) |
750
|
|
|
|
|
|
|
(\d{2})(?:(\d{2})(\d{2})?)? |
751
|
|
|
|
|
|
|
(?:[.,](\d+))?\s*(Z|[+-]\d{2}(?:\d{2})?)\s*$/x) { |
752
|
24
|
|
|
|
|
115
|
my ($year,$month,$day,$hour,$min,$sec,$dec,$offset) = ($1,$2,$3,$4,$5,$6,$7,$8); |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
# Time::Local's timegm() interpret years strangely |
755
|
24
|
100
|
|
|
|
71
|
if ($year >= 1000) { |
756
|
22
|
100
|
|
|
|
54
|
$dec = defined($dec) ? "0.$dec" : 0; |
757
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
# decimals in case of missing minutes / seconds - see RFC 4517 |
759
|
22
|
100
|
|
|
|
48
|
if (!defined($min)) { |
760
|
3
|
|
|
|
|
6
|
$min = 0; |
761
|
|
|
|
|
|
|
|
762
|
3
|
50
|
|
|
|
5
|
if ($dec) { |
763
|
0
|
|
|
|
|
0
|
$min = int(60 * $dec); |
764
|
0
|
|
|
|
|
0
|
$dec = sprintf('%.4f', 60 * $dec - $min); |
765
|
|
|
|
|
|
|
} |
766
|
|
|
|
|
|
|
} |
767
|
22
|
100
|
|
|
|
40
|
if (!defined($sec)) { |
768
|
3
|
|
|
|
|
4
|
$sec = 0; |
769
|
|
|
|
|
|
|
|
770
|
3
|
50
|
|
|
|
6
|
if ($dec) { |
771
|
0
|
|
|
|
|
0
|
$sec = int(60 * $dec); |
772
|
0
|
|
|
|
|
0
|
$dec = sprintf('%.2f', 60 * $dec - $sec); |
773
|
|
|
|
|
|
|
} |
774
|
|
|
|
|
|
|
} |
775
|
|
|
|
|
|
|
|
776
|
22
|
|
|
|
|
32
|
eval { require Time::Local; }; |
|
22
|
|
|
|
|
717
|
|
777
|
22
|
50
|
|
|
|
2555
|
unless ($@) { |
778
|
22
|
|
|
|
|
26
|
my $time; |
779
|
|
|
|
|
|
|
|
780
|
22
|
|
|
|
|
31
|
eval { $time = Time::Local::timegm($sec,$min,$hour,$day,$month-1,$year); }; |
|
22
|
|
|
|
|
88
|
|
781
|
22
|
100
|
|
|
|
1615
|
unless ($@) { |
782
|
14
|
100
|
|
|
|
39
|
if ($offset =~ /^([+-])(\d{2})(\d{2})?$/) { |
783
|
4
|
|
|
|
|
14
|
my ($direction,$hourdelta,$mindelta) = ($1,$2,$3); |
784
|
|
|
|
|
|
|
|
785
|
4
|
100
|
|
|
|
10
|
$mindelta = 0 if (!$mindelta); |
786
|
4
|
100
|
|
|
|
19
|
$time += ($direction eq '-') |
787
|
|
|
|
|
|
|
? 3600 * $hourdelta + 60 * $mindelta |
788
|
|
|
|
|
|
|
: -3600 * $hourdelta - 60 * $mindelta; |
789
|
|
|
|
|
|
|
} |
790
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
# make decimal part directional |
792
|
14
|
100
|
|
|
|
34
|
if ($dec != 0) { |
793
|
4
|
|
|
|
|
9
|
my $sign = ''; |
794
|
|
|
|
|
|
|
|
795
|
4
|
100
|
|
|
|
7
|
if ($time < 0) { |
796
|
2
|
|
|
|
|
5
|
$dec = 1 - $dec; |
797
|
2
|
|
|
|
|
2
|
$time++; |
798
|
2
|
100
|
|
|
|
6
|
$sign = '-' if ($time == 0); |
799
|
|
|
|
|
|
|
} |
800
|
4
|
|
|
|
|
34
|
$dec =~ s/^0\.//; |
801
|
4
|
|
|
|
|
13
|
$time = "${sign}${time}.${dec}"; |
802
|
|
|
|
|
|
|
} |
803
|
|
|
|
|
|
|
|
804
|
14
|
|
|
|
|
97
|
return $time; |
805
|
|
|
|
|
|
|
} |
806
|
|
|
|
|
|
|
} |
807
|
|
|
|
|
|
|
} |
808
|
|
|
|
|
|
|
} |
809
|
|
|
|
|
|
|
|
810
|
18
|
|
|
|
|
47
|
return undef; |
811
|
|
|
|
|
|
|
} |
812
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
=item time_to_generalizedTime ( TIME [, OPTIONS ] ) |
815
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
Convert the UNIX time B |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
In extension to UNIX times, B |
819
|
|
|
|
|
|
|
the decimal part will be used for the resulting generalizedTime. |
820
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
B is a list of key/value pairs. The following keys are recognized: |
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
=over 4 |
824
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
=item AD |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
Take care of an ActiveDirectory peculiarity to always require decimals. |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
=back |
830
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
Returns the generalizedTime string, or C on error. |
832
|
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
Times before BC or after year 9999 result in C |
834
|
|
|
|
|
|
|
as they cannot be represented in the generalizedTime format. |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
B this function depends on Perl's implementation of gmtime. |
837
|
|
|
|
|
|
|
See L, L, and |
838
|
|
|
|
|
|
|
L for restrictions in older versions of Perl. |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
=cut |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
sub time_to_generalizedTime($;@) |
843
|
|
|
|
|
|
|
{ |
844
|
8
|
|
|
8
|
1
|
5377
|
my $arg = shift; |
845
|
8
|
|
|
|
|
18
|
my %opt = @_; |
846
|
|
|
|
|
|
|
|
847
|
8
|
50
|
|
|
|
55
|
if ($arg =~ /^(\-?)(\d*)(?:[.,](\d*))?$/) { |
848
|
8
|
|
|
|
|
29
|
my ($sign, $time, $dec) = ($1, $2, $3); |
849
|
|
|
|
|
|
|
|
850
|
8
|
100
|
|
|
|
24
|
$dec = defined($dec) ? "0.$dec" : 0; |
851
|
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
# decimal part of time is directional: make sure to have it positive |
853
|
8
|
100
|
|
|
|
17
|
if ($sign) { |
854
|
4
|
100
|
|
|
|
14
|
if ($dec != 0) { |
855
|
2
|
|
|
|
|
4
|
$time++; |
856
|
2
|
|
|
|
|
4
|
$dec = 1 - $dec; |
857
|
|
|
|
|
|
|
} |
858
|
4
|
|
|
|
|
9
|
$time = -$time; |
859
|
|
|
|
|
|
|
} |
860
|
|
|
|
|
|
|
|
861
|
8
|
|
|
|
|
50
|
my ($sec,$min,$hour,$mday,$month,$year,$wday,$yday,$isdst) = gmtime(int($time)); |
862
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
# generalizedTime requires 4-digit year without sign |
864
|
8
|
50
|
33
|
|
|
41
|
return undef if ($year < -1900 || $year > 8099); |
865
|
|
|
|
|
|
|
|
866
|
8
|
|
|
|
|
114
|
$dec =~ s/^0?\.(\d*?)0*$/$1/; |
867
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
return sprintf("%04d%02d%02d%02d%02d%02d%sZ", |
869
|
|
|
|
|
|
|
$year+1900, $month+1, $mday, $hour, $min, $sec, |
870
|
|
|
|
|
|
|
# AD peculiarity: if there are no decimals, add .0 as decimals |
871
|
8
|
50
|
|
|
|
71
|
($dec ? ('.'.$dec) : ($opt{AD} ? '.0' : ''))); |
|
|
100
|
|
|
|
|
|
872
|
|
|
|
|
|
|
} |
873
|
|
|
|
|
|
|
|
874
|
0
|
|
|
|
|
|
return undef; |
875
|
|
|
|
|
|
|
} |
876
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
=back |
879
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
=head1 AUTHOR |
882
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
Graham Barr Egbarr@pobox.comE |
884
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
=head1 COPYRIGHT |
886
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
Copyright (c) 1999-2004 Graham Barr. All rights reserved. This program is |
888
|
|
|
|
|
|
|
free software; you can redistribute it and/or modify it under the same |
889
|
|
|
|
|
|
|
terms as Perl itself. |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
ldap_explode_dn and canonical_dn also |
892
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
(c) 2002 Norbert Klasen, norbert.klasen@daasi.de, All Rights Reserved. |
894
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
=cut |
896
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
1; |