mirror of
https://github.com/OPSnet/bencode-torrent.git
synced 2026-11-07 22:58:53 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b94cf1056 | ||
|
|
74929fdac8 | ||
|
|
5c26a5ca1f |
No files matched your search
+3
-1
@@ -6,6 +6,7 @@ php:
|
||||
- '7.0'
|
||||
- '7.1'
|
||||
- '7.2'
|
||||
- nightly
|
||||
|
||||
cache:
|
||||
directories:
|
||||
@@ -16,4 +17,5 @@ install:
|
||||
|
||||
script:
|
||||
- vendor/bin/phpunit
|
||||
- vendor/bin/phpcs src -s
|
||||
- vendor/bin/phpcs src
|
||||
- vendor/bin/phpcs tests
|
||||
+124
-116
@@ -46,26 +46,30 @@ class BencodeTorrent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the internal data array
|
||||
* @param array $data
|
||||
* @throws \Exception
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function setData($data) {
|
||||
public function setData(array $data) {
|
||||
$this->data = $data;
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a BEncoded string and decode it
|
||||
* @param string $data
|
||||
* @throws \Exception
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function decodeData(string $data) {
|
||||
public function decodeString(string $data) {
|
||||
$this->data = $this->decode($data);
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a path to a file, decode the contents of it
|
||||
*
|
||||
* @param string $path
|
||||
* @throws \Exception
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function decodeFile(string $path) {
|
||||
$this->data = $this->decode(file_get_contents($path, FILE_BINARY));
|
||||
@@ -73,6 +77,14 @@ class BencodeTorrent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a BEncoded string to the following values:
|
||||
* - Dictionary (starts with d, ends with e)
|
||||
* - List (starts with l, ends with e
|
||||
* - Integer (starts with i, ends with e
|
||||
* - String (starts with number denoting number of characters followed by : and then the string)
|
||||
*
|
||||
* @see https://wiki.theory.org/index.php/BitTorrentSpecification
|
||||
*
|
||||
* @param string $data
|
||||
* @param int $pos
|
||||
* @return array|bool|float|string
|
||||
@@ -89,6 +101,7 @@ class BencodeTorrent {
|
||||
}
|
||||
$return[$key] = $value;
|
||||
}
|
||||
ksort($return);
|
||||
$pos++;
|
||||
}
|
||||
elseif ($data[$pos] === 'l') {
|
||||
@@ -116,16 +129,37 @@ class BencodeTorrent {
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function getData() {
|
||||
/**
|
||||
* Get the internal data array
|
||||
* @return array
|
||||
*/
|
||||
public function getData() : array {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
* Validates that the internal data array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function validate() {
|
||||
if (empty($this->data['info'])) {
|
||||
throw new \Exception("Torrent dictionary doesn't have info key");
|
||||
throw new \RuntimeException("Torrent dictionary doesn't have info key");
|
||||
}
|
||||
if (isset($this->data['info']['files'])) {
|
||||
foreach ($this->data['info']['files'] as $file) {
|
||||
$path_key = isset($file['path.utf-8']) ? 'path.utf-8' : 'path';
|
||||
if (isset($file[$path_key])) {
|
||||
$filter = array_filter(
|
||||
$file[$path_key],
|
||||
function ($element) {
|
||||
return strlen($element) === 0;
|
||||
}
|
||||
);
|
||||
if (count($filter) > 0) {
|
||||
throw new \RuntimeException('Cannot have empty path for a file');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +175,7 @@ class BencodeTorrent {
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getEncode() {
|
||||
public function getEncode() : string {
|
||||
$this->hasData();
|
||||
return $this->encodeVal($this->data);
|
||||
}
|
||||
@@ -150,7 +184,7 @@ class BencodeTorrent {
|
||||
* @param mixed $data
|
||||
* @return string
|
||||
*/
|
||||
private function encodeVal($data) {
|
||||
private function encodeVal($data) : string {
|
||||
if (is_array($data)) {
|
||||
$return = '';
|
||||
$check = -1;
|
||||
@@ -194,7 +228,7 @@ class BencodeTorrent {
|
||||
*
|
||||
* @return bool flag to indicate if we altered the info dictionary
|
||||
*/
|
||||
public function clean() {
|
||||
public function clean() : bool {
|
||||
$this->cleanDataDictionary();
|
||||
return $this->cleanInfoDictionary();
|
||||
}
|
||||
@@ -206,9 +240,9 @@ class BencodeTorrent {
|
||||
*/
|
||||
public function cleanDataDictionary() {
|
||||
$allowed_keys = array('encoding', 'info');
|
||||
foreach ($this->data['info'] as $key => $value) {
|
||||
foreach ($this->data as $key => $value) {
|
||||
if (!in_array($key, $allowed_keys)) {
|
||||
unset($this->data['info'][$key]);
|
||||
unset($this->data[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,7 +261,7 @@ class BencodeTorrent {
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function cleanInfoDictionary() {
|
||||
public function cleanInfoDictionary() : bool {
|
||||
$cleaned = false;
|
||||
$allowed_keys = array('files', 'name', 'piece length', 'pieces', 'private', 'length',
|
||||
'name.utf8', 'name.utf-8', 'md5sum', 'sha1', 'source',
|
||||
@@ -247,7 +281,7 @@ class BencodeTorrent {
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isPrivate() {
|
||||
public function isPrivate() : bool {
|
||||
$this->hasData();
|
||||
return isset($this->data['info']['private']) && $this->data['info']['private'] === 1;
|
||||
}
|
||||
@@ -261,7 +295,7 @@ class BencodeTorrent {
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function makePrivate() {
|
||||
public function makePrivate() : bool {
|
||||
$this->hasData();
|
||||
if ($this->isPrivate()) {
|
||||
return false;
|
||||
@@ -282,58 +316,48 @@ class BencodeTorrent {
|
||||
*
|
||||
* @return bool true if the source was set/changed, false if no change
|
||||
*/
|
||||
public function setSource(string $source) {
|
||||
public function setSource(string $source) : bool {
|
||||
$this->hasData();
|
||||
if (isset($this->data['info']['source']) && $this->data['info']['source'] === $source) {
|
||||
return false;
|
||||
}
|
||||
// Set we've set the source and will require a download, we might as well clean
|
||||
// Since we've set the source and will require a re-download, we might as well clean
|
||||
// these out as well
|
||||
unset($this->data['info']['x_cross_seed']);
|
||||
unset($this->data['info']['unique']);
|
||||
$this->data['info']['source'] = $source;
|
||||
ksort($this->data['info']);
|
||||
$this->setValue(['info.source' => $source]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to allow you set any number of keys and values in the data dictionary.
|
||||
* Function to allow you set any number of keys and values in the data dictionary. You can
|
||||
* set the value in a dictionary by concatenating the keys into a string with a period
|
||||
* separator (ex: info.name will set name field in the info dictionary) so that the rest
|
||||
* of the dictionary is unaffected.
|
||||
*
|
||||
* @param array $array
|
||||
*/
|
||||
public function set(array $array) {
|
||||
public function setValue(array $array) {
|
||||
foreach ($array as $key => $value) {
|
||||
$this->data[$key] = $value;
|
||||
if (is_array($value)) {
|
||||
ksort($this->data[$key]);
|
||||
ksort($value);
|
||||
}
|
||||
$keys = explode('.', $key);
|
||||
$data = &$this->data;
|
||||
for ($i = 0; $i < count($keys); $i++) {
|
||||
$data = &$data[$keys[$i]];
|
||||
}
|
||||
$data = $value;
|
||||
$data = &$this->data;
|
||||
for ($i = 0; $i < count($keys); $i++) {
|
||||
$data = &$data[$keys[$i]];
|
||||
if (is_array($data)) {
|
||||
ksort($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
ksort($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the announce URL for current data. This URL forms the base of the GET request that
|
||||
* the torrent client will send to a tracker so that a client can get peer lists as well as
|
||||
* tell the tracker the stats on the download. The announce URL should probably also end with
|
||||
* /announce which allows for the more efficient scrape to happen on an initial handshake by
|
||||
* the client and when getting just the peer list.
|
||||
*
|
||||
* @see https://wiki.theory.org/index.php/BitTorrentSpecification#Tracker_HTTP.2FHTTPS_Protocol
|
||||
* @see https://wiki.theory.org/index.php/BitTorrentSpecification#Tracker_.27scrape.27_Convention
|
||||
*
|
||||
* @param string $announce_url
|
||||
*/
|
||||
public function setAnnounceUrl(string $announce_url) {
|
||||
$this->hasData();
|
||||
$this->set(['announce' => $announce_url]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the comment string for the current data. This does not affect the info_hash.
|
||||
* @param string $comment
|
||||
*/
|
||||
public function setComment(string $comment) {
|
||||
$this->hasData();
|
||||
$this->set(['comment' => $comment]);
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -346,11 +370,15 @@ class BencodeTorrent {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getInfoHash() {
|
||||
public function getInfoHash() : string {
|
||||
$this->hasData();
|
||||
return sha1($this->encodeVal($this->data['info']));
|
||||
}
|
||||
|
||||
public function getHexInfoHash(): string {
|
||||
return pack('H*', $this->getInfoHash());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -368,7 +396,7 @@ class BencodeTorrent {
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSize() {
|
||||
public function getSize() : int {
|
||||
$cur_size = 0;
|
||||
if (!isset($this->data['info']['files'])) {
|
||||
$cur_size = $this->data['info']['length'];
|
||||
@@ -390,7 +418,7 @@ class BencodeTorrent {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFileList() {
|
||||
public function getFileList() : array {
|
||||
$files = [];
|
||||
if (!isset($this->data['info']['files'])) {
|
||||
// Single-file torrent
|
||||
@@ -398,27 +426,31 @@ class BencodeTorrent {
|
||||
$this->data['info']['name.utf-8'] :
|
||||
$this->data['info']['name']);
|
||||
$size = $this->data['info']['length'];
|
||||
$files[] = array('name' => $name, 'size' => $size);
|
||||
$files[] = ['path' => $name, 'size' => $size];
|
||||
}
|
||||
else {
|
||||
$path_key = isset($this->data['info']['files'][0]['path.utf-8']) ?
|
||||
'path.utf-8' :
|
||||
'path';
|
||||
$size = 0;
|
||||
foreach ($this->data['info']['files'] as $file) {
|
||||
$tmp_path = array();
|
||||
foreach ($file[$path_key] as $sub_path) {
|
||||
$tmp_path[] = $sub_path;
|
||||
}
|
||||
$files[] = array('name' => implode('/', $tmp_path), 'size' => $file['length']);
|
||||
$size += $file['length'];
|
||||
$path_key = isset($file['path.utf-8']) ? 'path.utf-8' : 'path';
|
||||
$files[] = ['path' => implode('/', $file[$path_key]), 'size' => $file['length']];
|
||||
}
|
||||
uasort(
|
||||
usort(
|
||||
$files,
|
||||
function ($a, $b) {
|
||||
return strnatcasecmp($a['name'], $b['name']);
|
||||
return strnatcasecmp($a['path'], $b['path']);
|
||||
}
|
||||
);
|
||||
}
|
||||
return $files;
|
||||
return array('total_size' => $size, 'files' => $files);
|
||||
}
|
||||
|
||||
public function hasFiles(): bool {
|
||||
return isset($this->data['info']['files']);
|
||||
}
|
||||
|
||||
public function hasEncryptedFiles(): bool {
|
||||
return isset($this->data['encrypted_files']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -431,17 +463,17 @@ class BencodeTorrent {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getGazelleFileList() {
|
||||
public function getGazelleFileList() : array {
|
||||
$files = [];
|
||||
foreach ($this->getFileList() as $file) {
|
||||
$name = $file['name'];
|
||||
$size = $file['length'];
|
||||
$name = self::makeUTF8(strtr($name, "\n\r\t", ' '));
|
||||
$ext_pos = strrpos($name, '.');
|
||||
foreach ($this->getFileList()['files'] as $file) {
|
||||
$path = $file['path'];
|
||||
$size = $file['size'];
|
||||
$path = $this->makeUTF8(strtr($path, "\n\r\t", ' '));
|
||||
$ext_pos = strrpos($path, '.');
|
||||
// Should not be $ExtPos !== false. Extension-less files that start with a .
|
||||
// should not get extensions
|
||||
$ext = ($ext_pos ? trim(substr($name, $ext_pos + 1)) : '');
|
||||
$files[] = sprintf("%s s%ds %s %s", ".$ext", $size, $name, self::$utf8_filelist_delim);
|
||||
$ext = ($ext_pos ? trim(substr($path, $ext_pos + 1)) : '');
|
||||
$files[] = sprintf("%s s%ds %s %s", ".$ext", $size, $path, self::$utf8_filelist_delim);
|
||||
}
|
||||
return $files;
|
||||
}
|
||||
@@ -449,52 +481,28 @@ class BencodeTorrent {
|
||||
/**
|
||||
* Given a string, convert it to UTF-8 format, if it's not already in UTF-8.
|
||||
*
|
||||
* @param string $Str input to convert to utf-8 format
|
||||
* @param string $str input to convert to utf-8 format
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function makeUTF8($Str) {
|
||||
if ($Str != '') {
|
||||
if (self::isUTF8($Str)) {
|
||||
$Encoding = 'UTF-8';
|
||||
}
|
||||
if (empty($Encoding)) {
|
||||
$Encoding = mb_detect_encoding($Str, 'UTF-8, ISO-8859-1');
|
||||
}
|
||||
if (empty($Encoding)) {
|
||||
$Encoding = 'ISO-8859-1';
|
||||
}
|
||||
if ($Encoding == 'UTF-8') {
|
||||
return $Str;
|
||||
}
|
||||
else {
|
||||
return @mb_convert_encoding($Str, 'UTF-8', $Encoding);
|
||||
}
|
||||
private function makeUTF8(string $str) : string {
|
||||
if (preg_match('//u', $str)) {
|
||||
$encoding = 'UTF-8';
|
||||
}
|
||||
if (empty($encoding)) {
|
||||
$encoding = mb_detect_encoding($str, 'UTF-8, ISO-8859-1');
|
||||
}
|
||||
// Legacy thing for Gazelle, leaving it in, but not going to bother testing
|
||||
// @codeCoverageIgnoreStart
|
||||
if (empty($encoding)) {
|
||||
$encoding = 'ISO-8859-1';
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
if ($encoding === 'UTF-8') {
|
||||
return $str;
|
||||
}
|
||||
else {
|
||||
return @mb_convert_encoding($str, 'UTF-8', $encoding);
|
||||
}
|
||||
return $Str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string, determine if that string is encoded in UTF-8 via regular expressions,
|
||||
* so that we don't have to rely on mb_detect_encoding which isn't quite as accurate
|
||||
*
|
||||
* @param string $Str input to check encoding of
|
||||
*
|
||||
* @return false|int
|
||||
*/
|
||||
private static function isUTF8($Str) {
|
||||
return preg_match(
|
||||
'%^(?:
|
||||
[\x09\x0A\x0D\x20-\x7E] // ASCII
|
||||
| [\xC2-\xDF][\x80-\xBF] // non-overlong 2-byte
|
||||
| \xE0[\xA0-\xBF][\x80-\xBF] // excluding overlongs
|
||||
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} // straight 3-byte
|
||||
| \xED[\x80-\x9F][\x80-\xBF] // excluding surrogates
|
||||
| \xF0[\x90-\xBF][\x80-\xBF]{2} // planes 1-3
|
||||
| [\xF1-\xF3][\x80-\xBF]{3} // planes 4-15
|
||||
| \xF4[\x80-\x8F][\x80-\xBF]{2} // plane 16
|
||||
)*$%xs',
|
||||
$Str
|
||||
);
|
||||
}
|
||||
}
|
||||
+416
-40
@@ -5,25 +5,31 @@ namespace ApolloRip\BencodeTorrent;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class BencodeTorrentTest extends TestCase {
|
||||
public function testLoadTorrent() {
|
||||
$bencode = new BencodeTorrent();
|
||||
try {
|
||||
$bencode->decodeFile(__DIR__.'/data/test_1.torrent');
|
||||
}
|
||||
catch (\Exception $exc) {
|
||||
$this->fail('Decode should not have thrown exception');
|
||||
}
|
||||
$data = $bencode->getData();
|
||||
$this->assertEquals('https://localhost:34000/4f9587fbcb06fe09165e4f84d35d0403/announce', $data['announce']);
|
||||
$this->assertEquals('https://localhost:8080/torrents.php?id=2&torrentid=2', $data['comment']);
|
||||
$this->assertEquals('uTorrent/3.4.2', $data['created by']);
|
||||
$this->assertEquals(1425699508, $data['creation date']);
|
||||
$this->assertEquals('UTF-8', $data['encoding']);
|
||||
$this->assertArrayHasKey('info', $data);
|
||||
$this->assertCount(11, $data['info']['files']);
|
||||
$files = [
|
||||
[
|
||||
'length' => 12310347,
|
||||
public function testLoadTorrent() {
|
||||
$bencode = new BencodeTorrent();
|
||||
try {
|
||||
$bencode->decodeFile(__DIR__.'/data/test_1.torrent');
|
||||
}
|
||||
catch (\Exception $exc) {
|
||||
$this->fail('Decode should not have thrown exception');
|
||||
}
|
||||
$data = $bencode->getData();
|
||||
$this->assertEquals(
|
||||
'https://localhost:34000/4f9587fbcb06fe09165e4f84d35d0403/announce',
|
||||
$data['announce']
|
||||
);
|
||||
$this->assertEquals(
|
||||
'https://localhost:8080/torrents.php?id=2&torrentid=2',
|
||||
$data['comment']
|
||||
);
|
||||
$this->assertEquals('uTorrent/3.4.2', $data['created by']);
|
||||
$this->assertEquals(1425699508, $data['creation date']);
|
||||
$this->assertEquals('UTF-8', $data['encoding']);
|
||||
$this->assertArrayHasKey('info', $data);
|
||||
$this->assertCount(11, $data['info']['files']);
|
||||
$files = [
|
||||
[
|
||||
'length' => 12310347,
|
||||
'path' => ['02 Should have known better.mp3']
|
||||
],
|
||||
[
|
||||
@@ -67,18 +73,81 @@ class BencodeTorrentTest extends TestCase {
|
||||
'path' => ['05 Eugene.mp3']
|
||||
]
|
||||
];
|
||||
$this->assertEquals($files, $data['info']['files']);
|
||||
$this->assertEquals('Sufjan Stevens - Carrie & Lowell (2015) [MP3 320]', $data['info']['name']);
|
||||
$this->assertEquals('Sufjan Stevens - Carrie & Lowell (2015) [MP3 320]', $bencode->getName());
|
||||
$this->assertEquals(16020, strlen($data['info']['pieces']));
|
||||
$this->assertEquals(1, $data['info']['private']);
|
||||
$this->assertEquals('APL', $data['info']['source']);
|
||||
$this->assertStringEqualsFile(__DIR__.'/data/test_1.torrent', $bencode->getEncode());
|
||||
}
|
||||
$this->assertEquals($files, $data['info']['files']);
|
||||
$this->assertEquals(
|
||||
'Sufjan Stevens - Carrie & Lowell (2015) [MP3 320]',
|
||||
$data['info']['name']
|
||||
);
|
||||
$this->assertEquals(
|
||||
'Sufjan Stevens - Carrie & Lowell (2015) [MP3 320]',
|
||||
$bencode->getName()
|
||||
);
|
||||
$this->assertEquals(16020, strlen($data['info']['pieces']));
|
||||
$this->assertEquals(1, $data['info']['private']);
|
||||
$this->assertEquals('APL', $data['info']['source']);
|
||||
$this->assertStringEqualsFile(__DIR__.'/data/test_1.torrent', $bencode->getEncode());
|
||||
$this->assertEquals(104916260, $bencode->getSize());
|
||||
$this->assertTrue($bencode->isPrivate());
|
||||
$bencode->decodeString($bencode->getEncode());
|
||||
$this->assertStringEqualsFile(__DIR__.'/data/test_1.torrent', $bencode->getEncode());
|
||||
$this->assertEquals('1f830103427029a88dd5fde85be74e622ee07951', $bencode->getInfoHash());
|
||||
$this->assertEquals($bencode->getInfoHash(), unpack('H*', $bencode->getHexInfoHash())[1]);
|
||||
$this->assertFalse($bencode->hasEncryptedFiles());
|
||||
$file_list = [
|
||||
'total_size' => 104916260,
|
||||
'files' => [
|
||||
[
|
||||
'size' => 9584196,
|
||||
'path' => '01 Death with Dignity.mp3'
|
||||
],
|
||||
[
|
||||
'size' => 12310347,
|
||||
'path' => '02 Should have known better.mp3'
|
||||
],
|
||||
[
|
||||
'size' => 8871591,
|
||||
'path' => '03 All of me wants all of you.mp3'
|
||||
],
|
||||
[
|
||||
'size' => 7942661,
|
||||
'path' => '04 Drawn to the Blood.mp3'
|
||||
],
|
||||
[
|
||||
'size' => 5878964,
|
||||
'path' => '05 Eugene.mp3'
|
||||
],
|
||||
[
|
||||
'size' => 11175567,
|
||||
'path' => '06 Fourth of July.mp3'
|
||||
],
|
||||
[
|
||||
'size' => 11367829,
|
||||
'path' => '07 The Only Thing.mp3'
|
||||
],
|
||||
[
|
||||
'size' => 7789055,
|
||||
'path' => '08 Carrie & Lowell.mp3'
|
||||
],
|
||||
[
|
||||
'size' => 12197480,
|
||||
'path' => '09 John My Beloved.mp3'
|
||||
],
|
||||
[
|
||||
'size' => 6438044,
|
||||
'path' => '10 No shade in the shadow of the cross.mp3'
|
||||
],
|
||||
[
|
||||
'size' => 11360526,
|
||||
'path' => '11 Blue Bucket of Gold.mp3'
|
||||
]
|
||||
]
|
||||
];
|
||||
$this->assertEquals($file_list, $bencode->getFileList());
|
||||
}
|
||||
|
||||
public function testSetData() {
|
||||
$data = [
|
||||
'encoding' => 'UTF8',
|
||||
public function testSetData() {
|
||||
$data = [
|
||||
'encoding' => 'UTF8',
|
||||
'info' => [
|
||||
'name' => 'test',
|
||||
'length' => 1213134,
|
||||
@@ -86,15 +155,322 @@ class BencodeTorrentTest extends TestCase {
|
||||
]
|
||||
];
|
||||
|
||||
$bencode = new BencodeTorrent();
|
||||
try {
|
||||
$bencode->setData($data);
|
||||
}
|
||||
catch (\Exception $exc) {
|
||||
$this->fail('Exception should not have been thrown');
|
||||
}
|
||||
$bencode = new BencodeTorrent();
|
||||
$bencode->setData($data);
|
||||
|
||||
$data = $bencode->getData();
|
||||
$this->assertEquals('UTF8', $data['encoding']);
|
||||
}
|
||||
}
|
||||
$this->assertEquals($data, $bencode->getData());
|
||||
$this->assertEquals('test', $bencode->getName());
|
||||
$this->assertFalse($bencode->isPrivate());
|
||||
$this->assertEquals(1213134, $bencode->getSize());
|
||||
$this->assertEquals(
|
||||
[
|
||||
'total_size' => 1213134,
|
||||
'files' => [
|
||||
[
|
||||
'path' => 'test',
|
||||
'size' => 1213134
|
||||
]
|
||||
]
|
||||
],
|
||||
$bencode->getFileList()
|
||||
);
|
||||
}
|
||||
|
||||
public function testEmptyDictionary() {
|
||||
$expected = [
|
||||
'encoding' => 'UTF8',
|
||||
'info' => [
|
||||
'name' => 'test',
|
||||
'length' => 1213134,
|
||||
'pieces' => 'fake pieces string'
|
||||
]
|
||||
];
|
||||
$bencode = new BencodeTorrent();
|
||||
$bencode->decodeString(
|
||||
'd8:encoding4:UTF84:infod4:name4:test6:lengthi1213134e'.
|
||||
'6:pieces18:fake pieces stringe4:test0:e'
|
||||
);
|
||||
$this->assertEquals($expected, $bencode->getData());
|
||||
}
|
||||
|
||||
public function testClean() {
|
||||
$data = [
|
||||
'encoding' => 'UTF8',
|
||||
'info' => [
|
||||
'file-duration' => '',
|
||||
'file-media' => '',
|
||||
'files' => [],
|
||||
'name' => 'test',
|
||||
'name.utf8' => 'test',
|
||||
'name.utf-8' => 'test',
|
||||
'length' => 12345,
|
||||
'md5sum' => 12345,
|
||||
'piece length' => 12345,
|
||||
'pieces' => '',
|
||||
'private' => 1,
|
||||
'profiles' => '',
|
||||
'sha1' => 1,
|
||||
'source' => 'APL',
|
||||
'unique' => 1,
|
||||
'test' => 'delete me',
|
||||
'x_cross_seed' => 1
|
||||
],
|
||||
'test' => 'delete me'
|
||||
];
|
||||
|
||||
$bencode = new BencodeTorrent();
|
||||
$bencode->setData($data);
|
||||
$this->assertTrue($bencode->clean());
|
||||
unset($data['test']);
|
||||
unset($data['info']['test']);
|
||||
$this->assertEquals($data, $bencode->getData());
|
||||
}
|
||||
|
||||
public function testSetPrivate() {
|
||||
$data = [
|
||||
'encoding' => 'UTF8',
|
||||
'info' => [
|
||||
'name' => 'test',
|
||||
'length' => 1213134,
|
||||
'pieces' => 'fake pieces string'
|
||||
]
|
||||
];
|
||||
$bencode = new BencodeTorrent();
|
||||
$bencode->setData($data);
|
||||
$this->assertFalse($bencode->isPrivate());
|
||||
$actual = $bencode->getData();
|
||||
$this->assertNotContains('private', $actual['info']);
|
||||
$this->assertTrue($bencode->makePrivate());
|
||||
$this->assertTrue($bencode->isPrivate());
|
||||
$actual = $bencode->getData();
|
||||
$this->assertEquals(1, $actual['info']['private']);
|
||||
}
|
||||
|
||||
public function testSetAlreadyPrivate() {
|
||||
$data = [
|
||||
'encoding' => 'UTF8',
|
||||
'info' => [
|
||||
'name' => 'test',
|
||||
'private' => 1
|
||||
]
|
||||
];
|
||||
$bencode = new BencodeTorrent();
|
||||
$bencode->setData($data);
|
||||
$this->assertTrue($bencode->isPrivate());
|
||||
$this->assertFalse($bencode->makePrivate());
|
||||
}
|
||||
|
||||
public function testSetSource() {
|
||||
$data = [
|
||||
'encoding' => 'UTF8',
|
||||
'info' => [
|
||||
'name' => 'test',
|
||||
'private' => 1,
|
||||
'unique' => 1,
|
||||
'x_cross_seed' => 'true'
|
||||
]
|
||||
];
|
||||
$bencode = new BencodeTorrent();
|
||||
$bencode->setData($data);
|
||||
$this->assertTrue($bencode->setSource('APL'));
|
||||
$actual = $bencode->getData();
|
||||
$this->assertEquals('APL', $actual['info']['source']);
|
||||
$this->assertNotContains('unique', $actual['info']);
|
||||
$this->assertNotContains('x_cross_seed', $actual['info']);
|
||||
}
|
||||
|
||||
public function testSetAlreadySourced() {
|
||||
$data = [
|
||||
'encoding' => 'UTF8',
|
||||
'info' => [
|
||||
'name' => 'test',
|
||||
'private' => 1,
|
||||
'source' => 'APL',
|
||||
'unique' => 1,
|
||||
'x_cross_seed' => 'true'
|
||||
]
|
||||
];
|
||||
|
||||
$bencode = new BencodeTorrent();
|
||||
$bencode->setData($data);
|
||||
$this->assertFalse($bencode->setSource('APL'));
|
||||
$this->assertEquals($data, $bencode->getData());
|
||||
}
|
||||
|
||||
public function testSetDifferentSource() {
|
||||
$data = [
|
||||
'encoding' => 'UTF8',
|
||||
'info' => [
|
||||
'name' => 'test',
|
||||
'private' => 1,
|
||||
'source' => 'RED',
|
||||
'unique' => 1,
|
||||
'x_cross_seed' => 'true'
|
||||
]
|
||||
];
|
||||
$bencode = new BencodeTorrent();
|
||||
$bencode->setData($data);
|
||||
$this->assertTrue($bencode->setSource('APL'));
|
||||
$actual = $bencode->getData();
|
||||
$this->assertEquals('APL', $actual['info']['source']);
|
||||
$this->assertNotContains('unique', $actual['info']);
|
||||
$this->assertNotContains('x_cross_seed', $actual['info']);
|
||||
}
|
||||
|
||||
public function testSetValue() {
|
||||
$data = [
|
||||
'encoding' => 'UTF8',
|
||||
'info' => [
|
||||
'files' => [
|
||||
0 => [
|
||||
'length' => 1234,
|
||||
'path' => ['01 Test.mp3'],
|
||||
'path.utf-8' => ['01 Test??.mp3']
|
||||
],
|
||||
1 => [
|
||||
'length' => 2345,
|
||||
'path' => ['03 Test!!.mp3'],
|
||||
'path.utf-8' => ['03 Test.mp3']
|
||||
]
|
||||
],
|
||||
'name' => 'test',
|
||||
'private' => 1
|
||||
]
|
||||
];
|
||||
$bencode = new BencodeTorrent();
|
||||
$bencode->setData($data);
|
||||
$set = ['announce' => 'http://localhost:8080/announce',
|
||||
'info.name' => 'test2',
|
||||
'info.private' => 0,
|
||||
'info.files.0.path.0' => '02 Test.mp3',
|
||||
'comment' => 'http://localhost:8080/torrents.php?id=1'];
|
||||
$bencode->setValue($set);
|
||||
$expected = [
|
||||
'announce' => 'http://localhost:8080/announce',
|
||||
'comment' => 'http://localhost:8080/torrents.php?id=1',
|
||||
'encoding' => 'UTF8',
|
||||
'info' => [
|
||||
'files' => [
|
||||
0 => [
|
||||
'length' => 1234,
|
||||
'path' => ['02 Test.mp3'],
|
||||
'path.utf-8' => ['01 Test??.mp3']
|
||||
],
|
||||
1 => [
|
||||
'length' => 2345,
|
||||
'path' => ['03 Test!!.mp3'],
|
||||
'path.utf-8' => ['03 Test.mp3']
|
||||
]
|
||||
],
|
||||
'name' => 'test2',
|
||||
'private' => 0
|
||||
]
|
||||
];
|
||||
$this->assertEquals($expected, $bencode->getData());
|
||||
$this->assertEquals(
|
||||
['.mp3 s1234s 01 Test??.mp3 ÷', '.mp3 s2345s 03 Test.mp3 ÷'],
|
||||
$bencode->getGazelleFileList()
|
||||
);
|
||||
$this->assertTrue($bencode->hasFiles());
|
||||
}
|
||||
|
||||
public function testGetUtf8Name() {
|
||||
$data = [
|
||||
'encoding' => 'UTF8',
|
||||
'info' => [
|
||||
'length' => 12345,
|
||||
'name' => 'test',
|
||||
'name.utf-8' => 'test!!',
|
||||
'private' => 1
|
||||
]
|
||||
];
|
||||
$bencode = new BencodeTorrent();
|
||||
$bencode->setData($data);
|
||||
$this->assertEquals('test!!', $bencode->getName());
|
||||
$this->assertEquals(12345, $bencode->getSize());
|
||||
$this->assertEquals(
|
||||
[
|
||||
'total_size' => 12345,
|
||||
'files' => [
|
||||
[
|
||||
'path' => 'test!!',
|
||||
'size' => 12345
|
||||
]
|
||||
]
|
||||
],
|
||||
$bencode->getFileList()
|
||||
);
|
||||
$this->assertEquals(['. s12345s test!! ÷'], $bencode->getGazelleFileList());
|
||||
}
|
||||
|
||||
public function testIso88591Name() {
|
||||
$data = [
|
||||
'info' => [
|
||||
'length' => 1234,
|
||||
'name' => utf8_decode('(test.,!?)óÉ')
|
||||
]
|
||||
];
|
||||
$bencode = new BencodeTorrent();
|
||||
$bencode->setData($data);
|
||||
$this->assertEquals(['.,!?)óÉ s1234s (test.,!?)óÉ ÷'], $bencode->getGazelleFileList());
|
||||
$this->assertFalse($bencode->hasFiles());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testInvalidSet() {
|
||||
$data = ['encoding' => 'UTF8', 'announce' => 'http://localhost:8080/announce'];
|
||||
$bencode = new BencodeTorrent();
|
||||
$bencode->setData($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testInvalidSetValue() {
|
||||
$data = [
|
||||
'encoding' => 'UTF8',
|
||||
'info' => [
|
||||
'name' => 'test',
|
||||
'length' => 1213134,
|
||||
'pieces' => 'fake pieces string'
|
||||
]
|
||||
];
|
||||
|
||||
$bencode = new BencodeTorrent();
|
||||
$bencode->setData($data);
|
||||
$bencode->setValue(['info' => []]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testGetEncodeNoData() {
|
||||
$bencode = new BencodeTorrent();
|
||||
$bencode->getEncode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testInvalidPath() {
|
||||
$data = [
|
||||
'encoding' => 'UTF8',
|
||||
'info' => [
|
||||
'files' => [
|
||||
0 => [
|
||||
'length' => 1234,
|
||||
'path' => ['']
|
||||
]
|
||||
],
|
||||
'name' => 'test',
|
||||
'length' => 1213134,
|
||||
'pieces' => 'fake pieces string'
|
||||
]
|
||||
];
|
||||
$bencode = new BencodeTorrent();
|
||||
$bencode->setData($data);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user