|
1 <?php |
|
2 /** |
|
3 * Zend Framework |
|
4 * |
|
5 * LICENSE |
|
6 * |
|
7 * This source file is subject to the new BSD license that is bundled |
|
8 * with this package in the file LICENSE.txt. |
|
9 * It is also available through the world-wide-web at this URL: |
|
10 * http://framework.zend.com/license/new-bsd |
|
11 * If you did not receive a copy of the license and are unable to |
|
12 * obtain it through the world-wide-web, please send an email |
|
13 * to license@zend.com so we can send you a copy immediately. |
|
14 * |
|
15 * @category Zend |
|
16 * @package Zend_Ldap |
|
17 * @subpackage Ldif |
|
18 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
19 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
20 * @version $Id: Encoder.php 21005 2010-02-09 13:16:26Z sgehrig $ |
|
21 */ |
|
22 |
|
23 /** |
|
24 * Zend_Ldap_Ldif_Encoder provides methods to encode and decode LDAP data into/from LDIF. |
|
25 * |
|
26 * @category Zend |
|
27 * @package Zend_Ldap |
|
28 * @subpackage Ldif |
|
29 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
30 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
31 */ |
|
32 class Zend_Ldap_Ldif_Encoder |
|
33 { |
|
34 /** |
|
35 * Additional options used during encoding |
|
36 * |
|
37 * @var array |
|
38 */ |
|
39 protected $_options = array( |
|
40 'sort' => true, |
|
41 'version' => 1, |
|
42 'wrap' => 78 |
|
43 ); |
|
44 |
|
45 /** |
|
46 * @var boolean |
|
47 */ |
|
48 protected $_versionWritten = false; |
|
49 |
|
50 /** |
|
51 * Constructor. |
|
52 * |
|
53 * @param array $options Additional options used during encoding |
|
54 * @return void |
|
55 */ |
|
56 protected function __construct(array $options = array()) |
|
57 { |
|
58 $this->_options = array_merge($this->_options, $options); |
|
59 } |
|
60 |
|
61 /** |
|
62 * Decodes the string $string into an array of LDIF items |
|
63 * |
|
64 * @param string $string |
|
65 * @return array |
|
66 */ |
|
67 public static function decode($string) |
|
68 { |
|
69 $encoder = new self(array()); |
|
70 return $encoder->_decode($string); |
|
71 } |
|
72 |
|
73 /** |
|
74 * Decodes the string $string into an array of LDIF items |
|
75 * |
|
76 * @param string $string |
|
77 * @return array |
|
78 */ |
|
79 protected function _decode($string) |
|
80 { |
|
81 $items = array(); |
|
82 $item = array(); |
|
83 $last = null; |
|
84 foreach (explode("\n", $string) as $line) { |
|
85 $line = rtrim($line, "\x09\x0A\x0D\x00\x0B"); |
|
86 $matches = array(); |
|
87 if (substr($line, 0, 1) === ' ' && $last !== null) { |
|
88 $last[2] .= substr($line, 1); |
|
89 } else if (substr($line, 0, 1) === '#') { |
|
90 continue; |
|
91 } else if (preg_match('/^([a-z0-9;-]+)(:[:<]?\s*)([^:<]*)$/i', $line, $matches)) { |
|
92 $name = strtolower($matches[1]); |
|
93 $type = trim($matches[2]); |
|
94 $value = $matches[3]; |
|
95 if ($last !== null) { |
|
96 $this->_pushAttribute($last, $item); |
|
97 } |
|
98 if ($name === 'version') { |
|
99 continue; |
|
100 } else if (count($item) > 0 && $name === 'dn') { |
|
101 $items[] = $item; |
|
102 $item = array(); |
|
103 $last = null; |
|
104 } |
|
105 $last = array($name, $type, $value); |
|
106 } else if (trim($line) === '') { |
|
107 continue; |
|
108 } |
|
109 } |
|
110 if ($last !== null) { |
|
111 $this->_pushAttribute($last, $item); |
|
112 } |
|
113 $items[] = $item; |
|
114 return (count($items)>1) ? $items : $items[0]; |
|
115 } |
|
116 |
|
117 /** |
|
118 * Pushes a decoded attribute to the stack |
|
119 * |
|
120 * @param array $attribute |
|
121 * @param array $entry |
|
122 */ |
|
123 protected function _pushAttribute(array $attribute, array &$entry) |
|
124 { |
|
125 $name = $attribute[0]; |
|
126 $type = $attribute[1]; |
|
127 $value = $attribute[2]; |
|
128 if ($type === '::') { |
|
129 $value = base64_decode($value); |
|
130 } |
|
131 if ($name === 'dn') { |
|
132 $entry[$name] = $value; |
|
133 } else if (isset($entry[$name]) && $value !== '') { |
|
134 $entry[$name][] = $value; |
|
135 } else { |
|
136 $entry[$name] = ($value !== '') ? array($value) : array(); |
|
137 } |
|
138 } |
|
139 |
|
140 /** |
|
141 * Encode $value into a LDIF representation |
|
142 * |
|
143 * @param mixed $value The value to be encoded |
|
144 * @param array $options Additional options used during encoding |
|
145 * @return string The encoded value |
|
146 */ |
|
147 public static function encode($value, array $options = array()) |
|
148 { |
|
149 $encoder = new self($options); |
|
150 return $encoder->_encode($value); |
|
151 } |
|
152 |
|
153 /** |
|
154 * Recursive driver which determines the type of value to be encoded |
|
155 * and then dispatches to the appropriate method. |
|
156 * |
|
157 * @param mixed $value The value to be encoded |
|
158 * @return string Encoded value |
|
159 */ |
|
160 protected function _encode($value) |
|
161 { |
|
162 if (is_scalar($value)) { |
|
163 return $this->_encodeString($value); |
|
164 } else if (is_array($value)) { |
|
165 return $this->_encodeAttributes($value); |
|
166 } else if ($value instanceof Zend_Ldap_Node) { |
|
167 return $value->toLdif($this->_options); |
|
168 } |
|
169 return null; |
|
170 } |
|
171 |
|
172 /** |
|
173 * Encodes $string according to RFC2849 |
|
174 * |
|
175 * @link http://www.faqs.org/rfcs/rfc2849.html |
|
176 * |
|
177 * @param string $string |
|
178 * @param boolen $base64 |
|
179 * @return string |
|
180 */ |
|
181 protected function _encodeString($string, &$base64 = null) |
|
182 { |
|
183 $string = (string)$string; |
|
184 if (!is_numeric($string) && empty($string)) { |
|
185 return ''; |
|
186 } |
|
187 |
|
188 /* |
|
189 * SAFE-INIT-CHAR = %x01-09 / %x0B-0C / %x0E-1F / |
|
190 * %x21-39 / %x3B / %x3D-7F |
|
191 * ; any value <= 127 except NUL, LF, CR, |
|
192 * ; SPACE, colon (":", ASCII 58 decimal) |
|
193 * ; and less-than ("<" , ASCII 60 decimal) |
|
194 * |
|
195 */ |
|
196 $unsafe_init_char = array(0, 10, 13, 32, 58, 60); |
|
197 /* |
|
198 * SAFE-CHAR = %x01-09 / %x0B-0C / %x0E-7F |
|
199 * ; any value <= 127 decimal except NUL, LF, |
|
200 * ; and CR |
|
201 */ |
|
202 $unsafe_char = array(0, 10, 13); |
|
203 |
|
204 $base64 = false; |
|
205 for ($i = 0; $i < strlen($string); $i++) { |
|
206 $char = ord(substr($string, $i, 1)); |
|
207 if ($char >= 127) { |
|
208 $base64 = true; |
|
209 break; |
|
210 } else if ($i === 0 && in_array($char, $unsafe_init_char)) { |
|
211 $base64 = true; |
|
212 break; |
|
213 } else if (in_array($char, $unsafe_char)) { |
|
214 $base64 = true; |
|
215 break; |
|
216 } |
|
217 } |
|
218 // Test for ending space |
|
219 if (substr($string, -1) == ' ') { |
|
220 $base64 = true; |
|
221 } |
|
222 |
|
223 if ($base64 === true) { |
|
224 $string = base64_encode($string); |
|
225 } |
|
226 |
|
227 return $string; |
|
228 } |
|
229 |
|
230 /** |
|
231 * Encodes an attribute with $name and $value according to RFC2849 |
|
232 * |
|
233 * @link http://www.faqs.org/rfcs/rfc2849.html |
|
234 * |
|
235 * @param string $name |
|
236 * @param array|string $value |
|
237 * @return string |
|
238 */ |
|
239 protected function _encodeAttribute($name, $value) |
|
240 { |
|
241 if (!is_array($value)) { |
|
242 $value = array($value); |
|
243 } |
|
244 |
|
245 $output = ''; |
|
246 |
|
247 if (count($value) < 1) { |
|
248 return $name . ': '; |
|
249 } |
|
250 |
|
251 foreach ($value as $v) { |
|
252 $base64 = null; |
|
253 $v = $this->_encodeString($v, $base64); |
|
254 $attribute = $name . ':'; |
|
255 if ($base64 === true) { |
|
256 $attribute .= ': ' . $v; |
|
257 } else { |
|
258 $attribute .= ' ' . $v; |
|
259 } |
|
260 if (isset($this->_options['wrap']) && strlen($attribute) > $this->_options['wrap']) { |
|
261 $attribute = trim(chunk_split($attribute, $this->_options['wrap'], PHP_EOL . ' ')); |
|
262 } |
|
263 $output .= $attribute . PHP_EOL; |
|
264 } |
|
265 return trim($output, PHP_EOL); |
|
266 } |
|
267 |
|
268 /** |
|
269 * Encodes a collection of attributes according to RFC2849 |
|
270 * |
|
271 * @link http://www.faqs.org/rfcs/rfc2849.html |
|
272 * |
|
273 * @param array $attributes |
|
274 * @return string |
|
275 */ |
|
276 protected function _encodeAttributes(array $attributes) |
|
277 { |
|
278 $string = ''; |
|
279 $attributes = array_change_key_case($attributes, CASE_LOWER); |
|
280 if (!$this->_versionWritten && array_key_exists('dn', $attributes) && isset($this->_options['version']) |
|
281 && array_key_exists('objectclass', $attributes)) { |
|
282 $string .= sprintf('version: %d', $this->_options['version']) . PHP_EOL; |
|
283 $this->_versionWritten = true; |
|
284 } |
|
285 |
|
286 if (isset($this->_options['sort']) && $this->_options['sort'] === true) { |
|
287 ksort($attributes, SORT_STRING); |
|
288 if (array_key_exists('objectclass', $attributes)) { |
|
289 $oc = $attributes['objectclass']; |
|
290 unset($attributes['objectclass']); |
|
291 $attributes = array_merge(array('objectclass' => $oc), $attributes); |
|
292 } |
|
293 if (array_key_exists('dn', $attributes)) { |
|
294 $dn = $attributes['dn']; |
|
295 unset($attributes['dn']); |
|
296 $attributes = array_merge(array('dn' => $dn), $attributes); |
|
297 } |
|
298 } |
|
299 foreach ($attributes as $key => $value) { |
|
300 $string .= $this->_encodeAttribute($key, $value) . PHP_EOL; |
|
301 } |
|
302 return trim($string, PHP_EOL); |
|
303 } |
|
304 } |