/lib/bittorrent/Decoder.php

https://github.com/Optix/ZenTracker · PHP · 200 lines · 92 code · 28 blank · 80 comment · 29 complexity · 4c11e648a76e5628478af5016d45dd19 MD5 · raw file

  1. <?php
  2. /**
  3. * PHP_BitTorrent
  4. *
  5. * Copyright (c) 2011 Christer Edvartsen <cogo@starzinger.net>
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to
  9. * deal in the Software without restriction, including without limitation the
  10. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  11. * sell copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * * The above copyright notice and this permission notice shall be included in
  15. * all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  23. * IN THE SOFTWARE.
  24. *
  25. * @package PHP_BitTorrent
  26. * @author Christer Edvartsen <cogo@starzinger.net>
  27. * @copyright Copyright (c) 2011, Christer Edvartsen
  28. * @license http://www.opensource.org/licenses/mit-license MIT License
  29. */
  30. /**
  31. * Decode bittorrent strings to it's PHP variable counterpart
  32. *
  33. * @package PHP_BitTorrent
  34. * @author Christer Edvartsen <cogo@starzinger.net>
  35. * @copyright Copyright (c) 2011, Christer Edvartsen
  36. * @license http://www.opensource.org/licenses/mit-license MIT License
  37. */
  38. class PHP_BitTorrent_Decoder {
  39. /**
  40. * Decode a file
  41. *
  42. * @param string $file Path to the torrent file we want to decode
  43. * @param boolean $strict If set to true this method will check for certain elements in the dictionary.
  44. * @return array
  45. * @throws PHP_BitTorrent_Decoder_Exception
  46. */
  47. static public function decodeFile($file, $strict = false) {
  48. if (!is_readable($file)) {
  49. throw new PHP_BitTorrent_Decoder_Exception('File ' . $file . ' does not exist or can not be read.');
  50. }
  51. $dictionary = static::decodeDictionary(file_get_contents($file, true));
  52. if ($strict) {
  53. if (!isset($dictionary['announce']) || !is_string($dictionary['announce'])) {
  54. throw new PHP_BitTorrent_Decoder_Exception('Missing "announce" key.');
  55. } else if (!isset($dictionary['info']) || !is_array($dictionary['info'])) {
  56. throw new PHP_BitTorrent_Decoder_Exception('Missing "info" key.');
  57. }
  58. }
  59. return $dictionary;
  60. }
  61. /**
  62. * Decode any bittorrent encoded string
  63. *
  64. * @param string $string
  65. * @return mixed
  66. * @throws PHP_BitTorrent_Decoder_Exception
  67. */
  68. static public function decode($string) {
  69. if ($string[0] === 'i') {
  70. return static::decodeInteger($string);
  71. } else if ($string[0] === 'l') {
  72. return static::decodeList($string);
  73. } else if ($string[0] === 'd') {
  74. return static::decodeDictionary($string);
  75. } else if (preg_match('/^\d+:/', $string)) {
  76. return static::decodeString($string);
  77. }
  78. throw new PHP_BitTorrent_Decoder_Exception('Parameter is not correctly encoded.');
  79. }
  80. /**
  81. * Decode an encoded PHP integer
  82. *
  83. * @param string $integer
  84. * @return int
  85. * @throws PHP_BitTorrent_Decoder_Exception
  86. */
  87. static public function decodeInteger($integer) {
  88. if ($integer[0] !== 'i' || (!$ePos = strpos($integer, 'e'))) {
  89. throw new PHP_BitTorrent_Decoder_Exception('Invalid integer. Inteers must start wth "i" and end with "e".');
  90. }
  91. $int = substr($integer, 1, ($ePos - 1));
  92. $intLen = strlen($int);
  93. if (($int[0] === '0' && $intLen > 1) || ($int[0] === '-' && $int[1] === '0') || !is_numeric($int)) {
  94. throw new PHP_BitTorrent_Decoder_Exception('Invalid integer value.');
  95. }
  96. return (int) $int;
  97. }
  98. /**
  99. * Decode an encoded PHP string
  100. *
  101. * @param string $string
  102. * @return string
  103. * @throws PHP_BitTorrent_Decoder_Exception
  104. */
  105. static public function decodeString($string) {
  106. $stringParts = explode(':', $string, 2);
  107. // The string must have two parts
  108. if (count($stringParts) !== 2) {
  109. throw new PHP_BitTorrent_Decoder_Exception('Invalid string. Strings consist of two parts separated by ":".');
  110. }
  111. $length = (int) $stringParts[0];
  112. $lengthLen = strlen($length);
  113. if (($lengthLen + 1 + $length) > strlen($string)) {
  114. throw new PHP_BitTorrent_Decoder_Exception('The length of the string does not match the prefix of the encoded data.');
  115. }
  116. return substr($string, ($lengthLen + 1), $length);
  117. }
  118. /**
  119. * Decode an encoded PHP array
  120. *
  121. * @param string $list
  122. * @return array
  123. * @throws PHP_BitTorrent_Decoder_Exception
  124. */
  125. static public function decodeList($list) {
  126. if ($list[0] !== 'l') {
  127. throw new PHP_BitTorrent_Decoder_Exception('Parameter is not an encoded list.');
  128. }
  129. $ret = array();
  130. $length = strlen($list);
  131. $i = 1;
  132. while ($i < $length) {
  133. if ($list[$i] === 'e') {
  134. break;
  135. }
  136. $part = substr($list, $i);
  137. $decodedPart = static::decode($part);
  138. $ret[] = $decodedPart;
  139. $i += strlen(PHP_BitTorrent_Encoder::encode($decodedPart));
  140. }
  141. return $ret;
  142. }
  143. /**
  144. * Decode an encoded PHP associative array
  145. *
  146. * @param string $dictionary
  147. * @return array
  148. * @throws PHP_BitTorrent_Decoder_Exception
  149. */
  150. static public function decodeDictionary($dictionary) {
  151. if ($dictionary[0] !== 'd') {
  152. throw new PHP_BitTorrent_Decoder_Exception('Parameter is not an encoded dictionary.');
  153. }
  154. $length = strlen($dictionary);
  155. $ret = array();
  156. $i = 1;
  157. while ($i < $length) {
  158. if ($dictionary[$i] === 'e') {
  159. break;
  160. }
  161. $keyPart = substr($dictionary, $i);
  162. $key = static::decodeString($keyPart);
  163. $keyPartLength = strlen(PHP_BitTorrent_Encoder::encodeString($key));
  164. $valuePart = substr($dictionary, ($i + $keyPartLength));
  165. $value = static::decode($valuePart);
  166. $valuePartLength = strlen(PHP_BitTorrent_Encoder::encode($value));
  167. $ret[$key] = $value;
  168. $i += ($keyPartLength + $valuePartLength);
  169. }
  170. return $ret;
  171. }
  172. }