PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Ldap/Ldif/Encoder.php

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