compared with
Key
This line was removed.
This word was removed. This word was added.
This line was added.

Changes (4)

View Page History
{zone-template-instance:ZFDEV:Zend Proposal Zone Template}
{note:title=Under Construction}
Proposal withdrawn.
{note}
{zone-data:component-name}
Zend_BitTorrent
{zone-data}

{zone-data:proposer-list}
[Christer Edvartsen|mailto:cogo@starzinger.net]
[~ralph], Zend liaison
{zone-data}

{zone-data:revision}
0.1 - 11 October 2007: Initial proposal made
0.2 - 5 November 2007: Added use cases and some info about Zend_BitTorrent_Torrent
0.3 - 20 December 2007: Changed component name and updated the proposal a bit
{zone-data}

{zone-data:overview}
Zend_BitTorrent is a component that can be used to encode and decode data in the BitTorrent format. Zend_BitTorrent will contain convenient methods for accessing methods in the Zend_BitTorrent_Encoder and Zend_BitTorrent_Decoder classes.

The most common usage of a component such as this one would be to decode .torrent files. The information found in torrent files are the name of the file(s) included in the torrent along with file sizes and more info used by BitTorrent clients when downloading torrents.

The Zend_BitTorrent_Torrent class contain ways to generate .torrent files. It can generate files based on other torrent files, or a path to a file or a directory.
{zone-data}

{zone-data:references}
* [Wikipedia - BitTorrent|http://en.wikipedia.org/wiki/BitTorrent_%28protocol%29]
* [Bittorrent.org - For developers|http://www.bittorrent.org/beps/bep_0003.html]
{zone-data}

{zone-data:requirements}

* This component *will* encode any encodable PHP variable (int, string, array).
* This component *will* decode a BitTorrent encoded string back to the original PHP counterpart.
* This component *will* decode entire BitTorrent files (typically .torrent)
* This component *will* generate .torrent files based on a path to a file or a directory
* This component *will* mimic the official btmakemetafile script when generating .torrent files.
{zone-data}

{zone-data:dependencies}
* Zend_Exception
{zone-data}

{zone-data:operation}
When decoding it will first figure out what kind of variable the BitTorrent encoded string represents and then iterate through the string decoding all nested parts (if any). When encoding it will follow the simple rules of the BitTorrent specification.
{zone-data}

{zone-data:milestones}
* Milestone 1: [DONE] Initial design of the classes
* Milestone 2: [DONE] Working prototype
* Milestone 3: [DONE] Unit tests exist and work
* Milestone 4: Prototype and tests are checked into SVN
* Milestone 5: Initial documentation exists.
{zone-data}

{zone-data:class-list}
* Zend_BitTorrent
* Zend_BitTorrent_Encoder
* Zend_BitTorrent_Encoder_Exception
* Zend_BitTorrent_Decoder
* Zend_BitTorrent_Decoder_Exception
* Zend_BitTorrent_Torrent
* Zend_BitTorrent_Torrent_Exception
{zone-data}

{zone-data:use-cases}
||UC-01||
Encode a string using the Zend_BitTorrent class:
{code}
$string = "My string";
$encodedString = Zend_BitTorrent::encode($string);
{code}
||UC-02||
Encode a string using the Zend_BitTorrent_Encoder class:
{code}
$string = "My string";
$encodedString = Zend_BitTorrent_Encoder::encodeString($string);
{code}
||UC-03||
Decode a torrent file using the Zend_BitTorrent class:
{code}
$pathToFile = '/path/to/file.torrent';
$data = Zend_BitTorrent::decode($pathToFile, true); // second parameter informs the class that the argument is a path to a file.
{code}
||UC-04||
Decode a torrent file using the Zend_BitTorrent_Decoder class:
{code}
$pathToFile = '/path/to/file.torrent';
$data = Zend_BitTorrent_Decoder::decodeFile($pathToFile);
{code}
||UC-05||
Use the factory to generate a new .torrent file
{code}
$torrent = Zend_BitTorrent_Torrent::factory(Zend_BitTorrent_Torrent::CREATE_NEW);

$torrent->addPath('/path/to/files')
->addAnnounce('http://tracker/announce')
->save('my.torrent');
{code}
||UC-06||
Use the factory to generate a .torrent file from a path
{code}
$torrent = Zend_BitTorrent_Torrent::factory(Zend_BitTorrent_Torrent::CREATE_FROM_PATH, array(
'path' => '/path/to/files',
'announce' => 'http://tracker/announce'));
$torrent->setComment('My comment');
$torrent->save('my.torrent');
{code}
{zone-data}

{zone-data:skeletons}
{composition-setup}
{deck:id=Skeletons}







{card:label=Zend_BitTorrent}
{code}
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_BitTorrent
* @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

/** @see Zend_BitTorrent_Encoder */
require_once 'Zend/BitTorrent/Encoder.php';

/** @see Zend_BitTorrent_Decoder */
require_once 'Zend/BitTorrent/Decoder.php';

/**
* Convenient class that uses the Zend_BitTorrent_Encoder and Zend_BitTorrent_Decoder classes.
*
* @category Zend
* @package Zend_BitTorrent
* @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_BitTorrent
{
/**
* Encode a variable
*
* @param mixed $var
* @return string
* @uses Zend_BitTorrent_Encoder::encode()
*/
public static function encode($var)
{
return Zend_BitTorrent_Encoder::encode($var);
}

/**
* Decode a string
*
* @param string $var
* @param boolean $isFilePath If $var is a path to a file, set this to true to decode the file.
* @return integer|string|array
* @uses Zend_BitTorrent_Decoder::decodeFile()
* @uses Zend_BitTorrent_Decoder::decode()
*/
public static function decode($var, $isFilePath = false)
{
if ($isFilePath) {
return Zend_BitTorrent_Decoder::decodeFile($var);
}

return Zend_BitTorrent_Decoder::decode($var);
}
}
{code}
{card}
{card:label=Zend_BitTorrent_Encoder}
{code}
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_BitTorrent
* @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

/**
* Encode encodable PHP variables to the BitTorrent counterpart
*
* @category Zend
* @package Zend_BitTorrent
* @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_BitTorrent_Encoder
{
/**
* Encode any encodable variable
*
* @param mixed $var
* @return string
* @uses Zend_BitTorrent_Encoder::encodeInteger()
* @uses Zend_BitTorrent_Encoder::encodeString()
* @uses Zend_BitTorrent_Encoder::encodeList()
* @uses Zend_BitTorrent_Encoder::encodeDictionary()
* @throws Zend_BitTorrent_Encoder_Exception
*/
public static function encode($var)
{
switch (gettype($var)) {
case 'integer':
return self::encodeInteger($var);
case 'string':
return self::encodeString($var);
case 'array':
$indexed = true;
$size = count($var);

for ($i = 0; $i < $size; $i++) {
if (!isset($var[$i])) {
return self::encodeDictionary($var);
}
}

return self::encodeList($var);
}

/** @see Zend_BitTorrent_Encoder_Exception */
require_once 'Zend/BitTorrent/Encoder/Exception.php';

throw new Zend_BitTorrent_Encoder_Exception('Variables of type ' . gettype($var) . ' can not be encoded.');
}

/**
* Encode an integer
*
* @param int $integer
* @return string
* @throws Zend_BitTorrent_Encoder_Exception
*/
public static function encodeInteger($integer)
{
if (!is_int($integer)) {
/** @see Zend_BitTorrent_Encoder_Exception */
require_once 'Zend/BitTorrent/Encoder/Exception.php';

throw new Zend_BitTorrent_Encoder_Exception('Expected integer, got: ' . gettype($integer) . '.');
}

return 'i' . $integer . 'e';
}

/**
* Encode a string
*
* @param string $string
* @return string
* @throws Zend_BitTorrent_Encoder_Exception
*/
public static function encodeString($string)
{
if (!is_string($string)) {
/** @see Zend_BitTorrent_Encoder_Exception */
require_once 'Zend/BitTorrent/Encoder/Exception.php';

throw new Zend_BitTorrent_Encoder_Exception('Expected string, got: ' . gettype($string) . '.');
}

return strlen($string) . ':' . $string;
}

/**
* Encode a list (regular PHP array)
*
* @param array $list
* @return string
* @throws Zend_BitTorrent_Encoder_Exception
* @uses Zend_BitTorrent_Encode::encode()
*/
public static function encodeList($list)
{
if (!is_array($list)) {
/** @see Zend_BitTorrent_Encoder_Exception */
require_once 'Zend/BitTorrent/Encoder/Exception.php';

throw new Zend_BitTorrent_Encoder_Exception('Expected array, got: ' . gettype($list) . '.');
}

$ret = 'l';

foreach ($list as $value) {
$ret .= self::encode($value);
}

return $ret . 'e';
}

/**
* Encode a dictionary (associative PHP array)
*
* @param array $dictionary
* @return string
* @throws Zend_BitTorrent_Encoder_Exception
* @uses Zend_BitTorrent_Encode::encodeString()
* @uses Zend_BitTorrent_Encode::encode()
*/
public static function encodeDictionary($dictionary)
{
if (!is_array($dictionary)) {
/** @see Zend_BitTorrent_Encoder_Exception */
require_once 'Zend/BitTorrent/Encoder/Exception.php';

throw new Zend_BitTorrent_Encoder_Exception('Expected array, got: ' . gettype($dictionary) . '.');
}

$ret = 'd';

foreach ($dictionary as $key => $value) {
$ret .= self::encodeString($key) . self::encode($value);
}

return $ret . 'e';
}
}
{code}
{card}
{card:label=Zend_BitTorrent_Encoder_Exception}
{code}
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_BitTorrent
* @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

/** @see Zend_Exception */
require_once 'Zend/Exception.php';

/**
* @category Zend
* @package Zend_BitTorrent
* @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_BitTorrent_Encoder_Exception extends Zend_Exception
{}
{code}
{card}
{card:label=Zend_BitTorrent_Decoder}
{code}
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_BitTorrent
* @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

/** @see Zend_BitTorrent_Encoder */
require_once 'Zend/BitTorrent/Encoder.php';

/**
* Decode bittorrent strings to it's PHP variable counterpart
*
* @category Zend
* @package Zend_BitTorrent
* @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_BitTorrent_Decoder
{
/**
* Decode a file
*
* @param string $file
* @param boolean $strict If set to true this method will check for certain elements in the dictionary.
* @return array
* @throws Zend_BitTorrent_Decoder_Exception
* @uses Zend_BitTorrent_Decoder::decodeDictionary()
*/
public static function decodeFile($file, $strict = false)
{
if (!is_readable($file)) {
/** @see Zend_BitTorrent_Decoder_Exception */
require_once 'Zend/BitTorrent/Decoder/Exception.php';

throw new Zend_BitTorrent_Decoder_Exception('File ' . $file . ' does not exist or can not be read.');
}

$dictionary = self::decodeDictionary(file_get_contents($file, true));

if ($strict) {
if (!isset($dictionary['announce']) || !is_string($dictionary['announce'])) {
/** @see Zend_BitTorrent_Decoder_Exception */
require_once 'Zend/BitTorrent/Decoder/Exception.php';

throw new Zend_BitTorrent_Decoder_Exception('Missing "announce" key.');
} else if (!isset($dictionary['info']) || !is_array($dictionary['info'])) {
/** @see Zend_BitTorrent_Decoder_Exception */
require_once 'Zend/BitTorrent/Decoder/Exception.php';

throw new Zend_BitTorrent_Decoder_Exception('Missing "info" key.');
}
}

return $dictionary;
}

/**
* Decode any bittorrent encoded string
*
* @param string $string
* @return mixed
* @uses Zend_BitTorrent_Decoder::decodeInteger()
* @uses Zend_BitTorrent_Decoder::decodeString()
* @uses Zend_BitTorrent_Decoder::decodeList()
* @uses Zend_BitTorrent_Decoder::decodeDictionary()
* @throws Zend_BitTorrent_Decoder_Exception
*/
public static function decode($string)
{
if ($string[0] === 'i') {
return self::decodeInteger($string);
} else if ($string[0] === 'l') {
return self::decodeList($string);
} else if ($string[0] === 'd') {
return self::decodeDictionary($string);
} else if (preg_match('/^\d+:/', $string)) {
return self::decodeString($string);
}

/** @see Zend_BitTorrent_Decoder_Exception */
require_once 'Zend/BitTorrent/Decoder/Exception.php';

throw new Zend_BitTorrent_Decoder_Exception('Parameter is not correctly encoded.');
}

/**
* Decode an encoded PHP integer
*
* @param string $integer
* @return int
* @throws Zend_BitTorrent_Decoder_Exception
*/
public static function decodeInteger($integer)
{
if ($integer[0] !== 'i' || (!$ePos = strpos($integer, 'e'))) {
/** @see Zend_BitTorrent_Decoder_Exception */
require_once 'Zend/BitTorrent/Decoder/Exception.php';

throw new Zend_BitTorrent_Decoder_Exception('Invalid integer. Inteers must start wth "i" and end with "e".');
}

$int = substr($integer, 1, ($ePos - 1));
$intLen = strlen($int);

if (($int[0] === '0' && $intLen > 1) || ($int[0] === '-' && $int[1] === '0') || !is_numeric($int)) {
/** @see Zend_BitTorrent_Decoder_Exception */
require_once 'Zend/BitTorrent/Decoder/Exception.php';

throw new Zend_BitTorrent_Decoder_Exception('Invalid integer value.');
}

return (int) $int;
}

/**
* Decode an encoded PHP string
*
* @param string $string
* @return string
* @throws Zend_BitTorrent_Decoder_Exception
*/
public static function decodeString($string)
{
$stringParts = explode(':', $string, 2);

/* The string must have two parts */
if (count($stringParts) !== 2) {
/** @see Zend_BitTorrent_Decoder_Exception */
require_once 'Zend/BitTorrent/Decoder/Exception.php';

throw new Zend_BitTorrent_Decoder_Exception('Invalid string. Strings consist of two parts separated by ":".');
}

$length = (int) $stringParts[0];
$lengthLen = strlen($length);

if (($lengthLen + 1 + $length) > strlen($string)) {
/** @see Zend_BitTorrent_Decoder_Exception */
require_once 'Zend/BitTorrent/Decoder/Exception.php';

throw new Zend_BitTorrent_Decoder_Exception('The length of the string does not match the prefix of the encoded data.');
}

return substr($string, ($lengthLen + 1), $length);
}

/**
* Decode an encoded PHP array
*
* @param string $list
* @return array
* @throws Zend_BitTorrent_Decoder_Exception
* @uses Zend_BitTorrent_Decoder::decode()
* @uses Zend_BitTorrent_Encoder::encode()
*/
public static function decodeList($list)
{
if ($list[0] !== 'l') {
/** @see Zend_BitTorrent_Decoder_Exception */
require_once 'Zend/BitTorrent/Decoder/Exception.php';

throw new Zend_BitTorrent_Decoder_Exception('Parameter is not an encoded list.');
}

$ret = array();

$length = strlen($list);
$i = 1;

while ($i < $length) {
if ($list[$i] === 'e') {
break;
}

$part = substr($list, $i);
$decodedPart = self::decode($part);
$ret[] = $decodedPart;
$i += strlen(Zend_BitTorrent_Encoder::encode($decodedPart));
}

return $ret;
}

/**
* Decode an encoded PHP associative array
*
* @param string $dictionary
* @return array
* @uses Zend_BitTorrent_Decoder::decodeString()
* @uses Zend_BitTorrent_Decoder::decode()
* @throws Zend_BitTorrent_Decoder_Exception
*/
public static function decodeDictionary($dictionary)
{
if ($dictionary[0] !== 'd') {
/** @see Zend_BitTorrent_Decoder_Exception */
require_once 'Zend/BitTorrent/Decoder/Exception.php';

throw new Zend_BitTorrent_Decoder_Exception('Parameter is not an encoded dictionary.');
}

$length = strlen($dictionary);
$ret = array();
$i = 1;

while ($i < $length) {
if ($dictionary[$i] === 'e') {
break;
}

$keyPart = substr($dictionary, $i);
$key = self::decodeString($keyPart);
$keyPartLength = strlen(Zend_BitTorrent_Encoder::encodeString($key));

$valuePart = substr($dictionary, ($i + $keyPartLength));
$value = self::decode($valuePart);
$valuePartLength = strlen(Zend_BitTorrent_Encoder::encode($value));

$ret[$key] = $value;
$i += ($keyPartLength + $valuePartLength);
}

return $ret;
}
}
{code}
{card}
{card:label=Zend_BitTorrent_Decoder_Exception}
{code}
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_BitTorrent
* @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

/** @see Zend_Exception */
require_once 'Zend/Exception.php';

/**
* @category Zend
* @package Zend_BitTorrent
* @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_BitTorrent_Decoder_Exception extends Zend_Exception
{}
{code}
{card}
{card:label=Zend_BitTorrent_Torrent}
{code}
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_BitTorrent
* @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

/** @see Zend_BitTorrent_Decoder */
require_once 'Zend/BitTorrent/Decoder.php';

/** @see Zend_BitTorrent_Encoder */
require_once 'Zend/BitTorrent/Encoder.php';

/**
* A class that represents a single torrent file
*
* @category Zend
* @package Zend_BitTorrent
* @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_BitTorrent_Torrent
{
/**
* Constant used in the factory method
*
* @var string
*/
const CREATE_FROM_FILE = 'fromFile';

/**
* Constant used in the factory method
*
* @var string
*/
const CREATE_FROM_PATH = 'fromPath';

/**
* Constant used in the factory method
*
* @var string
*/
const CREATE_NEW = 'new';

/**
* Constant used with the $path
*
* @var int
*/
const FILE_PATH = 1;

/**
* Constant used with the $path
*
* @var int
*/
const DIR_PATH = 2;

/**
* The exponent to use when making the pieces
*
* @var int
*/
protected $pieceLengthExp = 18;

/**
* The announce url
*
* @var string
*/
protected $announce = null;

/**
* Optional comment
*
* @var string
*/
protected $comment = null;

/**
* Optional string that informs clients who or what created this torrent
*
* @var string
*/
protected $createdBy = 'Zend Framework BitTorrent component';

/**
* The unix timestamp of when the torrent was created
*
* @var int
*/
protected $creationDate = null;

/**
* Info about the file(s) in the torrent
*
* @var array
*/
protected $info = null;

/**
* The path to the file or directory we want to make a torrent of.
*
* @var string
*/
protected $path = null;

/**
* Variable telling us the type of the $path
*
* @var int
*/
protected $pathType = null;

/**
* Flag telling us if the torrent is built
*
* @var boolean
*/
protected $isBuilt = false;

/**
* Path to the torrent file we might want to build from
*
* @var string
*/
protected $torrentFilePath = null;

/**
* Class constructor
*
* The constructor can not be called by the user. The factory method will use this if someone
* wants to create a torrent from blank using the CREATE_BLANK constant.
*
*/
protected function __construct()
{}

/**
* See if the instance of the object is ready to be built
*
* @return boolean
*/
public function isReadyToBeBuilt()
{
if (
($this->getTorrentFilePath() === null && $this->getPath() === null) ||
$this->getAnnounce() === null
) {
return false;
}

return true;
}

/**
* Convenient method to use when building an instance of the torrent object
*
* This method will see what build* method we need to use to build the object. If the $path
* property is set we will use the buildFromPath method and if the $torrentFilePath property is
* set we will use the buildFromTorrent method.
*
* @return Zend_BitTorrent_Torrent
* @throws Zend_BitTorrent_Torrent_Exception
*/
public function build()
{
/* See if the torrent is already built */
if ($this->isBuilt === true) {
/** @see Zend_BitTorrent_Torrent_Exception */
require_once 'Zend/BitTorrent/Torrent/Exception.php';

throw new Zend_BitTorrent_Torrent_Exception('Torrent is already built.');
}

/* See if the torrent is ready to be built */
if ($this->isReadyToBeBuilt() === false) {
/** @see Zend_BitTorrent_Torrent_Exception */
require_once 'Zend/BitTorrent/Torrent/Exception.php';

throw new Zend_BitTorrent_Torrent_Exception('The torrent is not ready to be built.');
}

/* Decide how to build the torrent */
if ($this->torrentFilePath !== null) {
$this->buildFromTorrent();
} else if ($this->path !== null) {
$this->buildFromPath();
}

/* Set the creation date property if it does not exist */
if ($this->getCreationDate() === null) {
$this->setCreationDate(time());
}

/* Update the isBuilt flag so we can't build twice with the same object */
$this->isBuilt = true;

return $this;
}

/**
* Create a torrent object from a torrent file
*
* @return Zend_BitTorrent_Torrent
*/
protected function buildFromTorrent()
{
/* Decode the file */
$decodedFile = Zend_BitTorrent_Decoder::decodeFile($this->getTorrentFilePath());

/* Populate the object with data from the file */
if (isset($decodedFile['announce'])) {
$this->setAnnounce($decodedFile['announce']);
}

if (isset($decodedFile['comment'])) {
$this->setComment($decodedFile['comment']);
}

if (isset($decodedFile['created by'])) {
$this->setCreatedBy($decodedFile['created by']);
}

if (isset($decodedFile['creation date'])) {
$this->setCreationDate($decodedFile['creation date']);
}

if (isset($decodedFile['info'])) {
$this->setInfo($decodedFile['info']);
}

return $this;
}

/**
* Build a torrent from a path
*
* Some of the code in this method is ported directly from the official btmakemetafile script
* by Bram Cohen.
*
* @return Zend_BitTorrent_Torrent
*/
protected function buildFromPath()
{
$path = $this->getPath();

/* Generate an absolute path */
$absolutePath = realpath($path);

/* An array of the files to include in the torrent */
$files = array();

/* If the $absolutePath is a single file we're all set to go */
if ($this->getPathType() === self::FILE_PATH) {
$files[] = basename($absolutePath);
} else if ($this->getPathType() === self::DIR_PATH) {
/*
We have a directory. Lets find all files in that directory and put them in the
$files array. Realpath also remove a possible trailing '/'.
*/
$dirList = array(realpath($absolutePath));

/* Loop as long as we have directories in our list */
while ($dirList) {
$currentDir = array_pop($dirList);

/* Open the directory we are currently in */
$dh = opendir($currentDir);

/* Loop throght the contents of the directory */
while (($file = readdir($dh)) !== false) {
if ($file === '.' || $file === '..') {
continue;
}

$currentFile = $currentDir . DIRECTORY_SEPARATOR . $file;

/* If we have a file, add it to the list. If we have a directory, add it to the list so the loop will continue */
if (is_file($currentFile)) {
/* Remove the prepending path to make a torrent exactly like btmakemetafile does. */
$files[] = str_replace($absolutePath . DIRECTORY_SEPARATOR, '', $currentFile);
} else if (is_dir($currentFile)) {
$dirList[] = $currentFile;
}
}

/* Close the directory handle */
closedir($dh);
}
}

/* Initialize the info part of the torrent */
$info = array();

$info['piece length'] = pow(2, $this->getPieceLengthExp());

/* If we only have a single file, get the size of the file and set the name property */
if ($this->getPathType() === self::FILE_PATH) {
/* Regenerate the path to the file */
$filePath = dirname($absolutePath);

/* The name of the file in the torrent */
$info['name'] = $files[0];

/* The size of the file */
$info['length'] = filesize($filePath . DIRECTORY_SEPARATOR . $files[0]);

/* Initialize the pieces */
$pieces = array();

/* The current position in the file */
$position = 0;

/* Open the file */
$fp = fopen($filePath . DIRECTORY_SEPARATOR . $files[0], 'rb');

while ($position < $info['length']) {
$part = fread($fp, min($info['piece length'], $info['length'] - $position));
$pieces[] = sha1($part, true);

$position += $info['piece length'];

if ($position > $info['length']) {
$position = $info['length'];
}
}

/* Close the file handle */
fclose($fp);

$pieces = implode('', $pieces);
} else {
/* Initialize the pieces array */
$pieces = array();

/* The name of the directory in the torrent */
$info['name'] = basename($absolutePath);

/* Sort the filelist to mimic btmakemetafile */
sort($files);

/* Initialize some helper variables for the hashing or the parts of each file */
$part = '';
$done = 0;

/*
Loop through all the files in the $files array to generate the pieces and the other
stuff in the info part of the torrent. Note that two files may be part of the same
piece since btmakemetafile uses cyclic buffers.
*/
foreach ($files as $file) {
$filesize = filesize($absolutePath . DIRECTORY_SEPARATOR . $file);

$info['files'][] = array(
'length' => $filesize,
'path' => explode(DIRECTORY_SEPARATOR, $file));

/* Reset the position in the current file */
$position = 0;

/* Open the current file */
$fp = fopen($absolutePath . DIRECTORY_SEPARATOR . $file, 'rb');

/* Loop through the file */
while ($position < $filesize) {
$bytes = min(($filesize - $position), ($info['piece length'] - $done));
$part .= fread($fp, $bytes);

$done += $bytes;
$position += $bytes;

/* We have a piece. Add it to the array and reset the helper variables */
if ($done === $info['piece length']) {
$pieces[] = sha1($part, true);
$part = '';
$done = 0;
}
}

/* Close the file handle */
fclose($fp);
}

/* If there is a part still not hashed then add it to the pieces array */
if ($done > 0) {
$pieces[] = sha1($part, true);
}

/* Make a string of the pieces */
$pieces = implode('', $pieces);
}

/* Store the pieces in the $info array */
$info['pieces'] = $pieces;

/* Sort the info array */
ksort($info);

/* Set the info */
$this->setInfo($info);

return $this;
}

/**
* Factory method to use when creating a new torrent object
*
* @param string $type
* @param array $params
* @return Zend_BitTorrent_Torrent
* @throws Zend_BitTorrent_Torrent_Exception
*/
public static function factory($type, $params = array())
{
if ($params !== null && !is_array($params)) {
/** @see Zend_BitTorrent_Torrent_Exception */
require_once 'Zend/BitTorrent/Torrent/Exception.php';

throw new Zend_BitTorrent_Torrent_Exception('$params must either be null or an array.');
}

$torrent = new self();

if ($type === self::CREATE_FROM_FILE) {
if (!isset($params['file'])) {
/** @see Zend_BitTorrent_Torrent_Exception */
require_once 'Zend/BitTorrent/Torrent/Exception.php';

throw new Zend_BitTorrent_Torrent_Exception('Missing "file" parameter.');
}

$torrent->setTorrentFilePath($params['file']);
$torrent->buildFromTorrent();
} else if ($type === self::CREATE_FROM_PATH) {
if (!isset($params['path']) || !isset($params['announce'])) {
/** @see Zend_BitTorrent_Torrent_Exception */
require_once 'Zend/BitTorrent/Torrent/Exception.php';

throw new Zend_BitTorrent_Torrent_Exception('Missing "path" and/or "announce" parameters.');
}

$torrent->setPath($params['path'])
->setAnnounce($params['announce'])
->buildFromPath();
} else if ($type === self::CREATE_NEW) {
/* Do nothing */
} else {
/** @see Zend_BitTorrent_Torrent_Exception */
require_once 'Zend/BitTorrent/Torrent/Exception.php';

throw new Zend_BitTorrent_Torrent_Exception('Invalid type.');
}

return $torrent;
}

/**
* Set the piece length exponent
*
* @param int $pieceLengthExp
* @return Zend_BitTorrent_Torrent
* @throws Zend_BitTorrent_Torrent_Exception
*/
public function setPieceLengthExp($pieceLengthExp)
{
if (!is_int($pieceLengthExp)) {
/** @see Zend_BitTorrent_Torrent_Exception */
require_once 'Zend/BitTorrent/Torrent/Exception.php';

throw new Zend_BitTorrent_Torrent_Exception('Expected int, got: ' . gettype($pieceLengthExp));
}

$this->pieceLengthExp = $pieceLengthExp;

return $this;
}

/**
* Get the piece length exponent
*
* @return int
*/
public function getPieceLengthExp()
{
return $this->pieceLengthExp;
}

/**
* Set the announce url
*
* @param string $announceUrl
* @return Zend_BitTorrent_Torrent
*/
public function setAnnounce($announceUrl)
{
$this->announce = $announceUrl;

return $this;
}

/**
* Get the announce url
*
* @return mixed Returns null if the announce url is not set or a string otherwise
*/
public function getAnnounce()
{
return $this->announce;
}

/**
* Set the comment
*
* @param string $comment
* @return Zend_BitTorrent_Torrent
*/
public function setComment($comment)
{
$this->comment = $comment;

return $this;
}

/**
* Get the comment
*

* @return mixed Returns null if the comment is not set or a string otherwise
*/
public function getComment()
{
return $this->comment;
}

/**
* Set the created by property
*
* @param string $createdBy
* @return Zend_BitTorrent_Torrent
*/
public function setCreatedBy($createdBy)
{
$this->createdBy = $createdBy;

return $this;
}

/**
* Get the created by property
*
* @return string
*/
public function getCreatedBy()
{
return $this->createdBy;
}

/**
* Set the creation date property
*
* @param int $creationDate
* @return Zend_BitTorrent_Torrent
* @throws Zend_BitTorrent_Torrent_Exception
*/
public function setCreationDate($creationDate)
{
if (!is_numeric($creationDate)) {
/** @see Zend_BitTorrent_Torrent_Exception */
require_once 'Zend/BitTorrent/Torrent/Exception.php';

throw new Zend_BitTorrent_Torrent_Exception('Expected numeric value (unix timestamp), got: ' . gettype($creationDate));
}

$this->creationDate = $creationDate;

return $this;
}

/**
* Get the creation date property
*
* @return int Returns a unix timestamp
*/
public function getCreationDate()
{
return $this->creationDate;
}

/**
* Set the info part of the torrent
*
* @param array $info
* @return Zend_BitTorrent_Torrent
* @throws Zend_BitTorrent_Torrent_Exception
*/
public function setInfo($info)
{
if (!is_array($info)) {
/** @see Zend_BitTorrent_Torrent_Exception */
require_once 'Zend/BitTorrent/Torrent/Exception.php';

throw new Zend_BitTorrent_Torrent_Exception('Expected array, got: ' . gettype($info));
}

$this->info = $info;

return $this;
}

/**
* Get the info part
*
* @return array
*/
public function getInfo()
{
return $this->info;
}

/**
* Set the path
*
* @param string $path
* @return Zend_BitTorrent_Torrent
* @throws Zend_BitTorrent_Torrent_Exception
*/
public function setPath($path)
{
if (is_file($path)) {
$this->pathType = self::FILE_PATH;
} else if (is_dir($path)) {
$this->pathType = self::DIR_PATH;
} else {
/** @see Zend_BitTorrent_Torrent_Exception */
require_once 'Zend/BitTorrent/Torrent/Exception.php';

throw new Zend_BitTorrent_Torrent_Exception('$path must lead to a file or a directory.');
}

$this->path = $path;

return $this;
}

/**
* Get the path
*
* @return string
*/
public function getPath()
{
return $this->path;
}

/**
* Get the path type
*
* @return int The return value maps to one of the *_PATH constants.
*/
public function getPathType()
{
return $this->pathType;
}

/**
* Set the path to the torrent file we might want to build from
*
* @param string $path
* @return Zend_BitTorrent_Torrent
* @throws Zend_BitTorrent_Torrent_Exception
*/
public function setTorrentFilePath($path)
{
if (!is_file($path)) {
/** @see Zend_BitTorrent_Torrent_Exception */
require_once 'Zend/BitTorrent/Torrent/Exception.php';

throw new Zend_BitTorrent_Torrent_Exception('File does not exist: ' . $path);
}

$this->torrentFilePath = $path;

return $this;
}

/**
* Get the path to the torrent file we might want to build from
*
* @return string
*/
public function getTorrentFilePath()
{
return $this->torrentFilePath;
}

/**
* Save the current torrent object to the filename specified
*
* This method will save the current object to a file. If the file specified exists it will be overwritten.
*
* @param string $filename
* @throws Zend_BitTorrent_Torrent_Exception
* @return Zend_BitTorrent_Torrent
*/
public function save($filename)
{
/* Build the torrent if it is not yet built */
if (!$this->isBuilt) {
$this->build();
}

/* Open the file if it is writeable */
if (!is_writeable($filename)) {
/** @see Zend_BitTorrent_Torrent_Exception */
require_once 'Zend/BitTorrent/Torrent/Exception.php';

throw new Zend_BitTorrent_Torrent_Exception('Could not open file "' . $filename . '" for writing.');
}

/* Open file for writing */
$fp = fopen($filename, 'wb');

$torrent = array(
'announce' => $this->getAnnounce(),
'creation date' => $this->getCreationDate(),
'info' => $this->getInfo()
);

if (($comment = $this->getComment()) !== null) {
$torrent['comment'] = $comment;
}

if (($createdBy = $this->getCreatedBy()) !== null) {
$torrent['created by'] = $createdBy;
}

/* Create the encoded dictionary */
$dictionary = Zend_BitTorrent_Encoder::encodeDictionary($torrent);

/* Write the encoded data to the file */
fwrite($fp, $dictionary, strlen($dictionary));

/* Close the file handle */
fclose($fp);

return $this;
}

/**
* Get the files listed in the torrent
*
* If the torrent is a multifile torrent, return the files array. If it contains a single file,
* return the name element from the info array.
*
* @return mixed Returns a string if the torrent only contains one file or an array of files otherwise.
* @throws Zend_BitTorrent_Torrent_Exception
*/
public function getFileList()
{
if (($info = $this->getInfo()) === null) {
/** @see Zend_BitTorrent_Torrent_Exception */
require_once 'Zend/BitTorrent/Torrent/Exception.php';

throw new Zend_BitTorrent_Torrent_Exception('The info part of the torrent is not set.');
}

if (isset($info['length'])) {
return $info['name'];
}

return $info['files'];
}

/**
* Get the size of the files in the torrent
*
* @return int
* @throws Zend_BitTorrent_Torrent_Exception
*/
public function getSize()
{
if (($info = $this->getInfo()) === null) {
/** @see Zend_BitTorrent_Torrent_Exception */
require_once 'Zend/BitTorrent/Torrent/Exception.php';

throw new Zend_BitTorrent_Torrent_Exception('The info part of the torrent is not set.');
}

/* If the length element is set, return that one. If not, loop through the files and generate the total */
if (isset($info['length'])) {
return $info['length'];
}

$files = $this->getFileList();
$size = 0;

foreach ($files as $file) {
$size += $file['length'];
}

return $size;
}

/**
* Get the name that the content will be saved as
*
*
* @return string
* @throws Zend_BitTorrent_Torrent_Exception
*/
public function getName()
{
if (($info = $this->getInfo()) === null) {
/** @see Zend_BitTorrent_Torrent_Exception */
require_once 'Zend/BitTorrent/Torrent/Exception.php';

throw new Zend_BitTorrent_Torrent_Exception('The info part of the torrent is not set.');
}

return $info['name'];
}
}
{code}
{card}
{card:label=Zend_BitTorrent_Torrent_Exception}
{code}
/**
* Zend Framework
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://framework.zend.com/license/new-bsd
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email


* to license@zend.com so we can send you a copy immediately.
*
* @category Zend
* @package Zend_BitTorrent
* @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/

/** @see Zend_Exception */
require_once 'Zend/Exception.php';

/**
* @category Zend
* @package Zend_BitTorrent
* @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
class Zend_BitTorrent_Torrent_Exception extends Zend_Exception
{}
{code}
{card}
{deck}

{zone-data}

{zone-template-instance}