| 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; |