Advertisement
Guest User

PHP Bencode/bdecode

a guest
Aug 31st, 2011
1,012
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 10.12 KB | None | 0 0
  1. <?php
  2. /*********************************************************************
  3.  *  Author:     Andris Causs
  4.  *  Email:      cypher[at]inbox[dot]lv
  5.  *  Date:       July 07, 2006
  6.  *  Purpose:    Parse binary encoded (torrent) files into nested array.
  7.  *
  8.  *  You are free to use and modify the code. Just do not copy it and post
  9.  *  somewhere as your own.
  10.  *  Therefore I reserve all rights to this code in modified or unmodified form.
  11.  *
  12.  *  -- Here is a list of some of the most used properties:
  13.  *    $torrent->result['announce']                    // string
  14.  *    $torrent->result['announce-list']               // array
  15.  *    $torrent->result['comment']                     // string
  16.  *    $torrent->result['created by']                  // string
  17.  *    $torrent->result['creation date']               // unix timestamp
  18.  *    $torrent->result['encoding']                    // string
  19.  *    $torrent->result['info']['files']               // array
  20.  *    $torrent->result['info']['files'][?]['length']  // integer
  21.  *    $torrent->result['info']['files'][?]['path']    // string
  22.  *    $torrent->result['info']['name']                // string
  23.  *    $torrent->result['info']['piece length']        // integer
  24.  *    $torrent->result['info']['pieces']              // string
  25.  *    $torrent->result['info']['private']             // integer
  26.  *    $torrent->result['modified-by']                 // array
  27.  *
  28.  *  See http://wiki.theory.org/BitTorrentSpecification for bittorrent specification
  29.  */
  30.  
  31.     final class BDecode {
  32.         private $content;            // string containing contents of file
  33.         private $pointer = 0;        // current position pointer in content
  34.         public $result = array();    // result array containing all decoded elements
  35.  
  36.  
  37.         /**************************************************************************
  38.          * Info: Parses bencoded file into array.
  39.          * Args: {string} filepath: full or relative path to bencoded file
  40.          **************************************************************************/
  41.         function __construct() {
  42.         }
  43.  
  44.     function bdecode($filepath) {
  45.             $this->content = @file_get_contents($filepath);
  46.  
  47.             if (!$this->content) {
  48.                 $this->throwException('File does not exist!');
  49.             } else {
  50.                 if (!isset($this->content)) {
  51.                     $this->throwException('Error opening file!');
  52.                 } else {
  53.                     $this->result = $this->processElement();
  54.                 }
  55.             }
  56.             unset($this->content);
  57.        
  58.         return $this->result;
  59.     }
  60.  
  61.         /**************************************************************************
  62.          * Info: Clear class variables.
  63.          * Args: none
  64.          **************************************************************************/
  65.         function __destruct() {
  66.             unset($this->content);
  67.             unset($this->result);
  68.         }
  69.  
  70.  
  71.         /**************************************************************************
  72.          * Info: Terminates decoding process and returns error.
  73.          * Args: {string} error [optional] - error description
  74.          **************************************************************************/
  75.         private function throwException($error = 'error parsing file') {
  76.                 $this->result = array();
  77.                 $this->result['error'] = $error;
  78.         }
  79.  
  80.         /**************************************************************************
  81.          * Info: Processes element depending on its type.
  82.          *       Results in error if no valid identifier is found.
  83.          * Args: none
  84.          **************************************************************************/
  85.         private function processElement() {
  86.             switch($this->content[$this->pointer]) {
  87.             case 'd':
  88.                 return $this->processDictionary();
  89.                 break;
  90.             case 'l':
  91.                 return $this->processList();
  92.                 break;
  93.             case 'i':
  94.                 return $this->processInteger();
  95.                 break;
  96.             default:
  97.                 if (is_numeric($this->content[$this->pointer])) {
  98.                     return $this->processString();
  99.                 } else {
  100.                     $this->throwException('Unknown BEncode element');
  101.                 }
  102.                 break;
  103.             }
  104.         }
  105.  
  106.         /**************************************************************************
  107.          * Info: Processes dictionary entries.
  108.          *       Returns array of dictionary entries.
  109.          * Args: none
  110.          **************************************************************************/
  111.         private function processDictionary() {
  112.             if (!$this->isOfType('d'))
  113.                 $this->throwException();
  114.  
  115.             $res = array();
  116.             $this->pointer++;
  117.  
  118.             while (!$this->isOfType('e')) {
  119.                 $elemkey = $this->processString();
  120.  
  121.                 switch($this->content[$this->pointer]) {
  122.                 case 'd':
  123.                     $res[$elemkey] = $this->processDictionary();
  124.                     break;
  125.                 case 'l':
  126.                     $res[$elemkey] = $this->processList();
  127.                     break;
  128.                 case 'i':
  129.                     $res[$elemkey] = $this->processInteger();
  130.                     break;
  131.                 default:
  132.                     if (is_numeric($this->content[$this->pointer])) {
  133.                         $res[$elemkey] = $this->processString();
  134.                     } else {
  135.                         $this->throwException('Unknown BEncode element!');
  136.                     }
  137.                     break;
  138.                 }
  139.             }
  140.  
  141.             $this->pointer++;
  142.             return $res;
  143.         }
  144.  
  145.         /**************************************************************************
  146.          * Info: Processes list entries.
  147.          *       Returns array of list entries found between 'l' and 'e' identifiers.
  148.          * Args: none
  149.          **************************************************************************/
  150.         private function processList() {
  151.             if (!$this->isOfType('l'))
  152.                 $this->throwException();
  153.  
  154.             $res = array();
  155.             $this->pointer++;
  156.  
  157.             while (!$this->isOfType('e'))
  158.                 $res[] = $this->processElement();
  159.  
  160.             $this->pointer++;
  161.             return $res;
  162.         }
  163.  
  164.         /**************************************************************************
  165.          * Info: Processes integer value.
  166.          *       Returns integer value found between 'i' and 'e' identifiers.
  167.          * Args: none
  168.          **************************************************************************/
  169.         private function processInteger() {
  170.             if (!$this->isOfType('e'))
  171.                 $this->throwException();
  172.  
  173.             $this->pointer++;
  174.  
  175.             $delim_pos = strpos($this->content, 'e', $this->pointer);
  176.             $integer = substr($this->content, $this->pointer, $delim_pos - $this->pointer);
  177.             if (($integer == '-0') || ((substr($integer, 0, 1) == '0') && (strlen($integer) > 1)))
  178.                 $this->throwException();
  179.  
  180.             $integer = abs(intval($integer));
  181.             $this->pointer = $delim_pos + 1;
  182.             return $integer;
  183.         }
  184.  
  185.         /**************************************************************************
  186.          * Info: Processes string value.
  187.          *       Returns string value found after '%:' identifier, where '%' is any
  188.          *       valid integer.
  189.          * Args: none
  190.          **************************************************************************/
  191.         private function processString() {
  192.             if (!is_numeric($this->content[$this->pointer])) {
  193.                 $this->throwException();
  194.             }
  195.  
  196.             $delim_pos = strpos($this->content, ':', $this->pointer);
  197.             $elem_len = intval(substr($this->content, $this->pointer, $delim_pos - $this->pointer));
  198.             $this->pointer = $delim_pos + 1;
  199.  
  200.             $elem_name = substr($this->content, $this->pointer, $elem_len);
  201.  
  202.             $this->pointer += $elem_len;
  203.             return $elem_name;
  204.         }
  205.  
  206.         /**************************************************************************
  207.          * Info: Checks if identifier at current pointer is of supplied type.
  208.          * Args: {char} type - character denoting required type.
  209.          *   Usually one of [d,l,i,e].
  210.          **************************************************************************/
  211.         private function isOfType($type) {
  212.             return ($this->content[$this->pointer] == $type);
  213.         }
  214.  
  215.         /**************************************************************************
  216.          * Info: BEncodes the array of data
  217.          * Args: {array} type - Array of data in the torrent.
  218.          **************************************************************************/
  219.     function bencode($element) {
  220.         $out = "";
  221.             if (is_int($element)) {
  222.                 $out = 'i'.$element.'e';
  223.             } else if (is_string($element)) {
  224.                 $out = strlen($element).':'.$element;
  225.             } else if (is_array($element)) {
  226.                 ksort($element);
  227.                 if (is_string(key($element))) {
  228.                     $out ='d';
  229.                     foreach($element as $key => $val)
  230.                         $out .= self::bencode($key) . self::bencode($val);
  231.                     $out .= 'e';
  232.                 } else {
  233.                     $out ='l';
  234.                     foreach($element as $val)
  235.                         $out .= self::bencode($val);
  236.                         $out .= 'e';
  237.                 }
  238.             } else {
  239.                 print("$element");
  240.                 trigger_error("unknown element type: '".
  241.                 gettype($element)."'", E_USER_ERROR);
  242.                 exit();
  243.             }
  244.             return $out;
  245.         }
  246.    
  247.     }
  248.  
  249.  
  250. $bd = new BDecode();
  251. $bd->bdecode("/path/to/torrent.torrent");
  252. print( sha1( $bd->bencode($bd->result['info']) ) );
  253.  
  254. ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement