diff --git a/src/Bhutanio/BEncode/Bencode.php b/src/Bhutanio/BEncode/Bencode.php index ac57a36..4892bf1 100644 --- a/src/Bhutanio/BEncode/Bencode.php +++ b/src/Bhutanio/BEncode/Bencode.php @@ -5,9 +5,253 @@ * A BEncode Wrapper for Laravel * Original src - https://wiki.theory.org/Decoding_encoding_bencoded_data_with_PHP * + * GIT :: https://github.com/bhutanio/laravel-bencode + * * @package laravel-bencode * @author Abi Xalmon */ class BENCODE { + + //public $announce = 'http://abi.io/announce'; + public $announce = null; + // Torrent Comment + public $comment = null; + // Created by Program + public $created_by = null; + + function __construct() { + // Here you can load default announce URL, comment and created_by from your configuration file. + } + + // Set torrent data + /** + * Data Setter + * @param array $data [array of public variables] + * eg: + * + * BENCODE::set([ + * 'announce'=>'http://www.torrentsite.com', + * 'comment'=>'Downloaded from torrentsite.com', + * 'created_by'=>'TorrentSite v1.0' + * ]); + */ + public function set($data=array()) + { + if ( is_array($data) ) { + foreach ($data as $key => $value) { + $this->$key = $value; + } + } + } + + /** + * Decode a torrent file into Bencoded data + * @param string $s [link to torrent file] + * @param integer $pos [file position pointer] + * @return array/null [Array of Bencoded data] + * eg: + * $torrent = BENCODE::bdecode( File::get('MyMovieTorrent.torrent')); + * var_dump($torrent); + */ + public function bdecode($s, &$pos=0) + { + if($pos>=strlen($s)) { + return null; + } + switch($s[$pos]){ + case 'd': + $pos++; + $retval=array(); + while ($s[$pos]!='e'){ + $key=BENCODE::bdecode($s, $pos); + $val=BENCODE::bdecode($s, $pos); + if ($key===null || $val===null) + break; + $retval[$key]=$val; + } + $retval["isDct"]=true; + $pos++; + return $retval; + + case 'l': + $pos++; + $retval=array(); + while ($s[$pos]!='e'){ + $val=BENCODE::bdecode($s, $pos); + if ($val===null) + break; + $retval[]=$val; + } + $pos++; + return $retval; + + case 'i': + $pos++; + $digits=strpos($s, 'e', $pos)-$pos; + $val=round((float)substr($s, $pos, $digits)); + $pos+=$digits+1; + return $val; + + // case "0": case "1": case "2": case "3": case "4": + // case "5": case "6": case "7": case "8": case "9": + default: + $digits=strpos($s, ':', $pos)-$pos; + if ($digits<0 || $digits >20) + return null; + $len=(int)substr($s, $pos, $digits); + $pos+=$digits+1; + $str=substr($s, $pos, $len); + $pos+=$len; + //echo "pos: $pos str: [$str] len: $len digits: $digits\n"; + return (string)$str; + } + return null; + } + + /** + * Created Torrent file from Bencoded data + * @param array $d [array data of a decoded torrent file] + * @return string [data can be downloaded as torrent] + */ + public function bencode(&$d) + { + if(is_array($d)){ + $ret="l"; + $isDict = false; + if( isset($d["isDct"]) && $d["isDct"]===true ){ + $isDict=1; + $ret="d"; + // this is required by the specs, and BitTornado actualy chokes on unsorted dictionaries + ksort($d, SORT_STRING); + } + foreach($d as $key=>$value) { + if($isDict){ + // skip the isDct element, only if it's set by us + if($key=="isDct" and is_bool($value)) continue; + $ret.=strlen($key).":".$key; + } + if (is_int($value) || is_float($value)){ + $ret.="i${value}e"; + }else if (is_string($value)) { + $ret.=strlen($value).":".$value; + } else { + $ret.=BENCODE::bencode ($value); + } + } + return $ret."e"; + } elseif (is_string($d)) // fallback if we're given a single bencoded string or int + return strlen($d).":".$d; + elseif (is_int($d) || is_float($d)) + return "i${d}e"; + else + return null; + } + + /** + * Decode a torrent file into Bencoded data + * @param string $filename [File Path] + * @return array/null [Array of Bencoded data] + */ + public function bdecode_file($filename) + { + if ( is_file($filename) ) { + $f=file_get_contents($filename, FILE_BINARY); + return BENCODE::bdecode($f); + } + return null; + } + + /** + * Generate list of files in a torrent + * @param array $data [array data of a decoded torrent file] + * @return array [list of files in an array] + */ + public function filelist($data) + { + $FileCount = 0; + $FileList = array(); + if(!isset($data['info']['files']) ) // Single file mode + { + $FileCount = 1; + $TotalSize = $data['info']['length']; + $FileList[]= array($data['info']['length'], $data['info']['name']); + } else { // Multiple file mode + $FileNames = array(); + $TotalSize = 0; + $Files = $data['info']['files']; + foreach($Files as $File) { + $FileCount++; + $TotalSize+=$File['length']; + $FileSize = $File['length']; + + $FileName = ltrim(implode('/',$File['path']), '/'); + + $FileList[] = array('size'=>$FileSize, 'name'=>$FileName); + $FileNames[] = $FileName; + } + array_multisort($FileNames, $FileList); + } + return array('file_count'=>$FileCount, 'total_size'=>$TotalSize, 'files'=>$FileList); + } + + /** + * Replace array data on Decoded torrent data so that it can be bencoded into a private torrent file. + * Provide the custom data using BENCODE::set(); + * @param array $data [array data of a decoded torrent file] + * @return array [array data for torrent file] + */ + public function make_private($data) + { + // Remove announce + // announce-list is an unofficial extension to the protocol + // that allows for multiple trackers per torrent + unset($data['announce']); + unset($data['announce-list']); + + // Bitcomet & Azureus cache peers in here + unset($data['nodes']); + + // Azureus stores the dht_backup_enable flag here + unset($data['azureus_properties']); + + // Remove web-seeds + unset($data['url-list']); + + // Remove libtorrent resume info + unset($data['libtorrent_resume']); + + // Remove profiles / Media Infos + unset($data['info']['profiles']); + unset($data['info']['file-duration']); + unset($data['info']['file-media']); + + // Add Announce URL + if ( is_array($this->announce) ) { + $data['announce'] = reset($this->announce); + $data["announce-list"] = array(); + $announce_list = array(); + foreach ($this->announce as $announceUri) { + $announce_list[] = $announceUri; + } + $data["announce-list"] = $announce_list; + } else { + $data['announce'] = $this->announce; + } + + // Add Comment + $data['comment'] = $this->comment; + + // Created by and Created on + $data['created by'] = $this->created_by; + $data['creation date'] = time(); + + // Make Private + $data['info']['private'] = 1; + + // Sort by key to respect spec + ksort($data['info']); + ksort($data); + return $data; + } }