Просьба потестить сабж: http://p2p.toom.su/greygen Примеры использования:
http://p2p.toom.su/greygen/mydc.ru/ — самый простой URL
http://p2p.toom.su/greygen/dchub/mydc.ru/ — можно добавить dchub или adc
http://p2p.toom.su/greygen/dchub/mydc.ru/411/ — ещё можно добавить порт
http://p2p.toom.su/greygen/dchub/mydc.ru/411/greylink4mydc.exe — ещё можно дать название файлу
http://p2p.toom.su/greygen/dchub/mydc.ru/411/greylink4mydc — можно без .exe, .exe всё равно добавится
http://p2p.toom.su/greygen/411/mydc.ru/dchub/greylink4mydc.exe — порядок dchub и номера порта не важны
http://p2p.toom.su/greygen/411/dchub/mydc.ru/greylink4mydc.exe — так тоже будет работать
http://p2p.toom.su/greygen/411/mydc.ru/greylink4mydc.exe — любой компонент можно убрать, если он дефолтный
http://p2p.toom.su/greygen/mydc.ru/greylink4mydc — снова минимальный URL
http://p2p.toom.su/greygen/adc/dchub/adc/dchub/mydc.ru/greylink4mydc — победит dchub, т. к. самый последний
http://p2p.toom.su/greygen/adc/babylon.aab21pro.org/412/ — а это ADC хаб
http://p2p.toom.su/greygen....lon.exe
http://p2p.toom.su/greygen....babylon
http://p2p.toom.su/greygen....adc — адрес хаба в виде URL тоже принимается
Я реализовал продолжение прерванных закачек, но не тестил.
Самая интересная часть делается на PascalScript в Inno Setup, позже опубликую, если интересно.
Исходники собственно greygen.php
Code
<?php
$glversion = trim(file_get_contents("version.txt"));
$directory = "";
$file1 = "greylink.$glversion.metro.ttk.exe";
$file1patchoffset = (int) (file_get_contents("greylink.$glversion.metro.ttk.txt"));
$patchsize = 2048;
$patch = '';
$mime_type = "application/octet-stream";
$file = $directory.$file1;
// URL processing
$configString = $_SERVER['QUERY_STRING']; // edit this line !
// example of $configString :
// "mydc.ru/greylink.setup.exe"
// "dchub/mydc.ru/greylink.setup.exe"
// "greylink.exe" // unmodified
// "dchub/mydc.ru/411/greylink.setup.exe"
// "adc/localhost/greylink.setup.exe"
// "adc/localhost/"
// ""
$predefinedOn = FALSE;
$adc = FALSE;
$hub = '';
$port = 0;
$greyname = '';
$configSplit = explode('/', $configString);
if (FALSE !== $configSplit) {
if (count($configSplit) >= 1) {
if (0 < strlen(end($configSplit))) {
$greyname = end($configSplit);
}
for ($i = 0; $i + 1 < count($configSplit); $i++) {
$configChunk = $configSplit[$i];
if ("dchub" === strtolower($configChunk)) {
$adc = FALSE;
} else if ("dchub:" === strtolower($configChunk)) {
$adc = FALSE;
} else if ("adc" === strtolower($configChunk)) {
$adc = TRUE;
} else if ("adc:" === strtolower($configChunk)) {
$adc = TRUE;
} else if (ctype_digit($configChunk)) {
$port = (int) $configChunk;
} else if (FALSE !== preg_match('/^[-a-zA-Z0-9._]+$/', $configChunk)) {
$predefinedOn = TRUE;
$hub = $configChunk;
} else if (FALSE !== preg_match('/^[-a-zA-Z0-9._]+:[0-9]+$/', $configChunk)) {
$predefinedOn = TRUE;
$configChunkExploded = explode(':', $configChunk);
$hub = $configChunkExploded[0];
$port = (int) ($configChunkExploded[1]);
} else {
header("404 Not found");
echo("<html><head><title>404 Not found</title></head><body><h1>404 Not found</h1>Invalid URL chunk: ".htmlspecialchars($configChunk)."</body></html>");
exit;
}
}
}
}
if (strlen($greyname) >= 4 && strtolower(substr($greyname, strlen($greyname) - 4, 4)) == ".exe") {
$greyname = substr($greyname, 0, strlen($greyname) - 4);
}
if ('' === $greyname) {
$greyname = 'greylink.setup';
if ($predefinedOn) {
$greyname .= '.' . $hub;
} else {
$greyname .= '.openmod';
}
}
$greyname .= ".$glversion.exe";
if ($predefinedOn) {
$hubUrl = $hub;
if ($adc) {
$hubUrl = 'adc://' . $hubUrl;
}
if (0 != $port && 411 != $port) {
$hubUrl .= ':' . $port;
}
$patch = "OpenModDisable=True\r\n".
"PredefinedHub=$hubUrl\r\n".
"PredefinedHubEnable=True\r\n".
"-- ";
}
if (strlen($patch) > $patchsize) {
header("404 Not found");
echo "<html><head><title>404 Not found</title></head><body><h1>404 Not found</h1>URL is too long, failed to generate</body></html>";
exit;
}
if (is_file($file))
{
header("Content-Type: $mime_type");
header("Content-Disposition: attachment; filename=\x22$greyname\x22");
//if (isset($_SERVER['HTTP_RANGE']))
// {
rangeDownload($file);
// }
//else
// {
// header("Content-Length: ".filesize($file));
// readfile($file);
// }
}
else
{
// some error...
header("500 Not found");
echo "<html><head><title>404 Not found</title></head><body><h1>404 Not found</h1>Essential file is missing</body></html>";
exit;
}
function rangeDownload($file)
{
$fp = @fopen($file, 'rb');
$size = filesize($file); // File size
$length = $size; // Content length
$start = 0; // Start byte
$end = $size - 1; // End byte
// Now that we've gotten so far without errors we send the accept range header
/* At the moment we only support single ranges.
* Multiple ranges requires some more work to ensure it works correctly
* and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
*
* Multirange support annouces itself with:
* header('Accept-Ranges: bytes');
*
* Multirange content must be sent with multipart/byteranges mediatype,
* (mediatype = mimetype)
* as well as a boundry header to indicate the various chunks of data.
*/
header("Accept-Ranges: 0-$length");
// header('Accept-Ranges: bytes');
// multipart/byteranges
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
if (isset($_SERVER['HTTP_RANGE']))
{
$c_start = $start;
$c_end = $end;
// Extract the range string
list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
// Make sure the client hasn't sent us a multibyte range
if (strpos($range, ',') !== false)
{
// (?) Shoud this be issued here, or should the first
// range be used? Or should the header be ignored and
// we output the whole content?
header('HTTP/1.1 416 Requested Range Not Satisfiable');
header("Content-Range: bytes $start-$end/$size");
// (?) Echo some info to the client?
exit;
}
// If the range starts with an '-' we start from the beginning
// If not, we forward the file pointer
// And make sure to get the end byte if spesified
if ($range{0} == '-')
{
// The n-number of the last bytes is requested
$c_start = $size - substr($range, 1);
}
else
{
$range = explode('-', $range);
$c_start = $range[0];
$c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
}
/* Check the range and make sure it's treated according to the specs.
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
*/
// End bytes can not be larger than $end.
$c_end = ($c_end > $end) ? $end : $c_end;
// Validate the requested range and return an error if it's not correct.
if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size)
{
header('HTTP/1.1 416 Requested Range Not Satisfiable');
header("Content-Range: bytes $start-$end/$size");
// (?) Echo some info to the client?
exit;
}
$start = $c_start;
$end = $c_end;
$length = $end - $start + 1; // Calculate new content length
fseek($fp, $start);
header('HTTP/1.1 206 Partial Content');
}
// Notify the client the byte range we'll be outputting
header("Content-Range: bytes $start-$end/$size");
header("Content-Length: $length");
rangeOutput ($fp, $end);
fclose($fp);
}
function rangeOutput (&$fp, $end) {
global $file1patchoffset, $patchsize, $patch;
// Start buffered download
$buffer = 1024 * 8;
while(!feof($fp) && ($p = ftell($fp)) <= $end)
{
if ($p + $buffer > $end)
{
// In case we're only outputting a chunk, make sure we don't
// read past the length
$buffer = $end - $p + 1;
}
set_time_limit(0); // Reset time limit for big files
$chunk = fread($fp, $buffer);
$patch_l_offset = max($p, $file1patchoffset);
$patch_r_offset = min($p + $buffer, $file1patchoffset + strlen($patch));
if ($patch_l_offset < $patch_r_offset)
{
// patched region and current chunk overlap
$chunk = substr_replace($chunk,
substr($patch, $patch_l_offset - $file1patchoffset, $patch_r_offset - $patch_l_offset),
$patch_l_offset - $p, $patch_r_offset - $patch_l_offset);
assert(strlen($chunk) == $buffer);
}
echo $chunk;
flush(); // Free up memory. Otherwise large files will trigger PHP's memory limit.
}
}
?>