I have some troubleshoot with µTorrent and I think you can help me.
My µTorrent doesn't receive any data about seeds and leeches
Here is my scrapement code:
<?php
class scrapement extends Core {
public function renderPage() {
if (!isset($_GET['info_hash']) || (strlen($_GET['info_hash']) != 20))
$this->error('Invalid hash');
$query = $this->query("SELECT `info_hash`, `seeders`, `leechers`, `times_completed` FROM `torrents` WHERE `info_hash` = '".$this->checkValues($_GET['info_hash'], 0)."'");
if(!mysql_num_rows($query)) {
$this->error('No torrent with that hash found');
}
$benc = "d5:files";
while ($row = $this->fetch($query))
{
$benc .= "d20:".str_pad($row["info_hash"], 20)."d8:completei".$row['seeders']."e10:downloadedi".$row['times_completed']."e10:incompletei".$row['leechers']."eee";
}
$benc .= 'ed5:flagsd20:min_request_intervali1800eee';
$this->getLog($benc);
header("Content-Type: text/plain");
header("Pragma: no-cache");
echo $benc;
}
private function error($err) {
header('Content-Type: text/plain; charset=UTF-8');
header('Pragma: no-cache');
exit("d14:failure reason".strlen($err).":{$err}ed5:flagsd20:min_request_intervali1800eeee");
}
}
?>
The logs from scrapement script: ($this->getLog($benc);)
d5:filesd20:êzo¦G{9…NÑ´ò43d8:completei1e10:downloadedi21e10:incompletei0eeeed5:flagsd20:min_request_intervali1800eee
as µTorrent has not any logs to view I tried with Ratio Master ...and... the scrapement it's working
[02:49:10] GET /scrapement?passkey=fe4d2xxxxxx&info_hash=B%c3%2c%e7%be%ec%2a%5c%a1%c4c%f8%c4M35%3f%f3%c6%e8 HTTP/1.1
Host:
User-Agent: uTorrent/1800
Accept-Encoding: gzip
so why the uTorrent doesn't receive any data?
You have an error in your bencoded data format. I recommend you use a proper encoding routine to convert data structures to bencoded format, rather than manually constructing the strings by concatenation. Here is a class which handles both encoding and decoding:
/**
* Encodes and decodes bencode formatted strings
*/
class BEncoder
{
/**
* Encode a value using the bencode format
*
* @param mixed $data The value to encode
*
* @return string The encoded value
*
* @throws \InvalidArgumentException When an unencodable value is encountered
*/
public function encode($data)
{
if (is_resource($data)) {
throw new \InvalidArgumentException('Resources cannot be bencoded');
}
// convert objects to arrays of public properties
// this makes it possible to sort dictionaries as required
if (is_object($data)) {
$data = get_object_vars($data);
}
if (is_string($data)) {
// byte sequence
$result = strlen($data) . ':' . $data;
} else if (is_int($data) || is_float($data) || is_bool($data)) {
// integer
$result = 'i' . round($data, 0) . 'e';
} else if (is_array($data)) {
if (array_values($data) === $data) {
// list
$result = 'l';
foreach ($data as $value) {
$result .= $this->encode($value);
}
} else if (is_array($data)) {
// dictionary
ksort($data);
$result = 'd';
foreach ($data as $key => $value) {
$result .= $this->encode((string) $key) . $this->encode($value);
}
}
$result .= 'e';
}
return $result;
}
/**
* Decode a value using the bencode format
*
* @param string $data The value to decode
*
* @return mixed The decoded value
*
* @throws \InvalidArgumentException When an undecodable value is encountered
*/
public function decode($data)
{
if (!is_string($data)) {
throw new \InvalidArgumentException('Data is not a valid bencoded string');
}
$data = trim($data);
try {
$result = $this->decodeComponent($data, $position);
if ($data !== '') {
throw new \InvalidArgumentException('Data found after end of value');
}
} catch (\InvalidArgumentException $e) {
$err = 'Syntax error at character ' . $position . ' "' . substr($data, 0, 7) . '..."): ' . $e->getMessage();
throw new \InvalidArgumentException($err);
}
return $result;
}
/**
* Move the pointer in the data currently being decoded
*
* @param string $data The data being decoded
* @param int $position The position pointer
* @param int $count The number of bytes to move the pointer
*/
private function movePointer(&$data, &$position, $count)
{
$data = (string) substr($data, $count);
$position += $count;
}
/**
* Recursively decode a structure from a data string
*
* @param string $data The data being decoded
* @param int $position The position pointer
*
* @return mixed The decoded value
*
* @throws \InvalidArgumentException When an undecodable value is encountered
*/
private function decodeComponent(&$data, &$position = 0)
{
switch ($data[0]) {
case 'i':
if (!preg_match('/^i(-?\d+)e/', $data, $matches)) {
throw new \InvalidArgumentException('Invalid integer');
}
$this->movePointer($data, $position, strlen($matches[0]));
return (int) $matches[1];
case 'l':
$this->movePointer($data, $position, 1);
if ($data === '') {
throw new \InvalidArgumentException('Unexpected end of list');
}
$result = array();
while ($data[0] !== 'e') {
$value = $this->decodeComponent($data, $position);
if ($data === '') {
throw new \InvalidArgumentException('Unexpected end of list');
}
$result[] = $value;
}
$this->movePointer($data, $position, 1);
return $result;
case 'd':
$this->movePointer($data, $position, 1);
if ($data === '') {
throw new \InvalidArgumentException('Unexpected end of dictionary');
}
$result = array();
while ($data[0] !== 'e') {
$key = $this->decodeComponent($data, $position);
if ($data === '') {
throw new \InvalidArgumentException('Unexpected end of dictionary');
}
$value = $this->decodeComponent($data, $position);
if ($data === '') {
throw new \InvalidArgumentException('Unexpected end of dictionary');
}
$result[$key] = $value;
}
$this->movePointer($data, $position, 1);
return $result;
default:
if (!preg_match('/^(\d+):/', $data, $matches)) {
throw new \InvalidArgumentException('Unknown data type');
}
$this->movePointer($data, $position, strlen($matches[0]));
if (strlen($data) < $matches[1]) {
$this->movePointer($data, $position, strlen($data));
throw new \InvalidArgumentException('Unexpected end of byte string');
}
$result = substr($data, 0, $matches[1]);
$this->movePointer($data, $position, $matches[1]);
return $result;
}
}
}
To use it with your code you would do something like this:
class scrapement extends Core
{
private $bencoder;
public function __construct($bencoder)
{
// inject an instance of the BEncoder class into this object
$this->bencoder = $bencoder;
}
public function renderPage()
{
if (!isset($_GET['info_hash']) || (strlen($_GET['info_hash']) != 20)) {
$this->error('Invalid hash');
}
$query = $this->query("
SELECT `info_hash`, `seeders`, `leechers`, `times_completed`
FROM `torrents`
WHERE `info_hash` = '" . $this->checkValues($_GET['info_hash'], 0) . "'
");
if (!mysql_num_rows($query)) {
$this->error('No torrent with that hash found');
}
$data = array(
'flags' => array(
'min_request_interval' => 1800
),
'files' => array()
);
while ($row = $this->fetch($query)) {
$hash = str_pad($row["info_hash"], 20);
$data['files'][$hash] = array(
'complete' => $row['seeders'],
'incomplete' => $row['leechers'],
'downloaded' => $row['times_completed']
);
}
$data = $this->bencoder->encode($data);
$this->getLog($data);
header("Content-Type: text/plain");
header("Pragma: no-cache");
echo $data;
}
private function error($err)
{
$data = array(
'flags' => array(
'min_request_interval' => 1800
),
'failure reason' => $err
);
$data = $this->bencoder->encode($data);
$this->getLog($data);
header('Content-Type: text/plain; charset=UTF-8');
header('Pragma: no-cache');
echo $data;
}
}