PageRenderTime 56ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/Ldap/Ldif/Encoder.php

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