initial import
This commit is contained in:
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
function catalog_http_normalize_header_name($name)
|
||||
{
|
||||
return strtolower(trim($name));
|
||||
}
|
||||
|
||||
function catalog_http_ensure_dir($dir)
|
||||
{
|
||||
if (!is_dir($dir)) {
|
||||
if (!mkdir($dir, 0755, true)) {
|
||||
throw new RuntimeException('Kan cache directory niet maken: ' . $dir);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_writable($dir)) {
|
||||
throw new RuntimeException('Cache directory is niet schrijfbaar: ' . $dir);
|
||||
}
|
||||
}
|
||||
|
||||
function catalog_http_read_meta($metaFile)
|
||||
{
|
||||
if ($metaFile === null || !is_file($metaFile)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$json = file_get_contents($metaFile);
|
||||
|
||||
if ($json === false || $json === '') {
|
||||
return array();
|
||||
}
|
||||
|
||||
$meta = json_decode($json, true);
|
||||
|
||||
return is_array($meta) ? $meta : array();
|
||||
}
|
||||
|
||||
function catalog_http_write_meta($metaFile, $meta)
|
||||
{
|
||||
if ($metaFile === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$json = json_encode($meta, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
if ($json === false) {
|
||||
throw new RuntimeException('Kan catalogus-cache metadata niet coderen.');
|
||||
}
|
||||
|
||||
if (file_put_contents($metaFile, $json . "\n", LOCK_EX) === false) {
|
||||
throw new RuntimeException('Kan catalogus-cache metadata niet schrijven: ' . $metaFile);
|
||||
}
|
||||
}
|
||||
|
||||
function catalog_http_validator_headers($meta)
|
||||
{
|
||||
$headers = array();
|
||||
|
||||
if (!empty($meta['etag'])) {
|
||||
$headers[] = 'If-None-Match: ' . $meta['etag'];
|
||||
}
|
||||
|
||||
if (!empty($meta['last_modified'])) {
|
||||
$headers[] = 'If-Modified-Since: ' . $meta['last_modified'];
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
function catalog_http_fetch($url, $requestHeaders = array(), $userAgent = 'rktsndbx-catalog/1.0', $timeout = 180)
|
||||
{
|
||||
if (function_exists('curl_init')) {
|
||||
$ch = curl_init($url);
|
||||
$responseHeaders = array();
|
||||
|
||||
curl_setopt_array($ch, array(
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_CONNECTTIMEOUT => 20,
|
||||
CURLOPT_TIMEOUT => $timeout,
|
||||
CURLOPT_USERAGENT => $userAgent,
|
||||
CURLOPT_FAILONERROR => false,
|
||||
CURLOPT_HTTPHEADER => $requestHeaders,
|
||||
CURLOPT_HEADERFUNCTION => function ($ch, $line) use (&$responseHeaders) {
|
||||
$pos = strpos($line, ':');
|
||||
|
||||
if ($pos !== false) {
|
||||
$name = catalog_http_normalize_header_name(substr($line, 0, $pos));
|
||||
$value = trim(substr($line, $pos + 1));
|
||||
$responseHeaders[$name] = $value;
|
||||
}
|
||||
|
||||
return strlen($line);
|
||||
},
|
||||
));
|
||||
|
||||
$body = curl_exec($ch);
|
||||
|
||||
if ($body === false) {
|
||||
$err = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new RuntimeException('Catalogus ophalen mislukt: ' . $err);
|
||||
}
|
||||
|
||||
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($status !== 304 && ($status < 200 || $status >= 300)) {
|
||||
throw new RuntimeException('Catalogus gaf HTTP status ' . $status . ' voor ' . $url);
|
||||
}
|
||||
|
||||
return array(
|
||||
'status' => $status,
|
||||
'headers' => $responseHeaders,
|
||||
'body' => $body,
|
||||
);
|
||||
}
|
||||
|
||||
$headerLines = array('User-Agent: ' . $userAgent);
|
||||
|
||||
foreach ($requestHeaders as $header) {
|
||||
$headerLines[] = $header;
|
||||
}
|
||||
|
||||
$ctx = stream_context_create(array(
|
||||
'http' => array(
|
||||
'method' => 'GET',
|
||||
'timeout' => $timeout,
|
||||
'header' => implode("\r\n", $headerLines) . "\r\n",
|
||||
'ignore_errors' => true,
|
||||
),
|
||||
));
|
||||
|
||||
$body = @file_get_contents($url, false, $ctx);
|
||||
|
||||
if ($body === false) {
|
||||
throw new RuntimeException('Catalogus ophalen mislukt: ' . $url);
|
||||
}
|
||||
|
||||
$status = 0;
|
||||
$responseHeaders = array();
|
||||
$rawHeaders = isset($http_response_header) && is_array($http_response_header)
|
||||
? $http_response_header
|
||||
: array();
|
||||
|
||||
foreach ($rawHeaders as $line) {
|
||||
if (preg_match('/^HTTP\/\S+\s+(\d+)/', $line, $m)) {
|
||||
$status = (int)$m[1];
|
||||
continue;
|
||||
}
|
||||
|
||||
$pos = strpos($line, ':');
|
||||
|
||||
if ($pos !== false) {
|
||||
$name = catalog_http_normalize_header_name(substr($line, 0, $pos));
|
||||
$value = trim(substr($line, $pos + 1));
|
||||
$responseHeaders[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if ($status !== 304 && ($status < 200 || $status >= 300)) {
|
||||
throw new RuntimeException('Catalogus gaf HTTP status ' . $status . ' voor ' . $url);
|
||||
}
|
||||
|
||||
return array(
|
||||
'status' => $status,
|
||||
'headers' => $responseHeaders,
|
||||
'body' => $body,
|
||||
);
|
||||
}
|
||||
|
||||
function catalog_http_fetch_cached($url, $cacheFile, $metaFile, $ttl, $userAgent, $timeout = 180)
|
||||
{
|
||||
catalog_http_ensure_dir(dirname($cacheFile));
|
||||
|
||||
if (is_file($cacheFile)) {
|
||||
$mtime = filemtime($cacheFile);
|
||||
|
||||
if ($mtime !== false && $mtime >= time() - $ttl) {
|
||||
$data = file_get_contents($cacheFile);
|
||||
|
||||
if ($data !== false && $data !== '') {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$meta = catalog_http_read_meta($metaFile);
|
||||
$requestHeaders = is_file($cacheFile) ? catalog_http_validator_headers($meta) : array();
|
||||
$response = catalog_http_fetch($url, $requestHeaders, $userAgent, $timeout);
|
||||
|
||||
if ($response['status'] === 304) {
|
||||
$data = file_get_contents($cacheFile);
|
||||
|
||||
if ($data !== false && $data !== '') {
|
||||
touch($cacheFile);
|
||||
$meta['checked_at'] = time();
|
||||
catalog_http_write_meta($metaFile, $meta);
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
$data = $response['body'];
|
||||
|
||||
if (file_put_contents($cacheFile, $data, LOCK_EX) === false) {
|
||||
throw new RuntimeException('Kan catalogus-cache niet schrijven: ' . $cacheFile);
|
||||
}
|
||||
|
||||
$headers = $response['headers'];
|
||||
$now = time();
|
||||
$meta = array(
|
||||
'url' => $url,
|
||||
'fetched_at' => $now,
|
||||
'checked_at' => $now,
|
||||
);
|
||||
|
||||
if (!empty($headers['etag'])) {
|
||||
$meta['etag'] = $headers['etag'];
|
||||
}
|
||||
|
||||
if (!empty($headers['last-modified'])) {
|
||||
$meta['last_modified'] = $headers['last-modified'];
|
||||
}
|
||||
|
||||
catalog_http_write_meta($metaFile, $meta);
|
||||
|
||||
return $data;
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
<?php
|
||||
|
||||
function rktd_package_name_ok($name)
|
||||
{
|
||||
return is_string($name) && preg_match('/^[A-Za-z0-9_.+-]+$/', $name);
|
||||
}
|
||||
|
||||
function rktd_unescape_string($s)
|
||||
{
|
||||
$out = '';
|
||||
$n = strlen($s);
|
||||
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
$c = $s[$i];
|
||||
|
||||
if ($c !== '\\') {
|
||||
$out .= $c;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($i + 1 >= $n) {
|
||||
break;
|
||||
}
|
||||
|
||||
$i++;
|
||||
$e = $s[$i];
|
||||
|
||||
if ($e === 'n') {
|
||||
$out .= "\n";
|
||||
} elseif ($e === 'r') {
|
||||
$out .= "\r";
|
||||
} elseif ($e === 't') {
|
||||
$out .= "\t";
|
||||
} else {
|
||||
$out .= $e;
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
function rktd_read_string($s, &$i)
|
||||
{
|
||||
$n = strlen($s);
|
||||
|
||||
if ($i >= $n || $s[$i] !== '"') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$i++;
|
||||
$out = '';
|
||||
|
||||
while ($i < $n) {
|
||||
$c = $s[$i];
|
||||
$i++;
|
||||
|
||||
if ($c === '"') {
|
||||
return $out;
|
||||
}
|
||||
|
||||
if ($c === '\\') {
|
||||
if ($i >= $n) {
|
||||
break;
|
||||
}
|
||||
|
||||
$e = $s[$i];
|
||||
$i++;
|
||||
|
||||
if ($e === 'n') {
|
||||
$out .= "\n";
|
||||
} elseif ($e === 'r') {
|
||||
$out .= "\r";
|
||||
} elseif ($e === 't') {
|
||||
$out .= "\t";
|
||||
} else {
|
||||
$out .= $e;
|
||||
}
|
||||
} else {
|
||||
$out .= $c;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function rktd_skip_space($s, &$i)
|
||||
{
|
||||
$n = strlen($s);
|
||||
|
||||
while ($i < $n) {
|
||||
$c = $s[$i];
|
||||
|
||||
if ($c === ';') {
|
||||
while ($i < $n && $s[$i] !== "\n") {
|
||||
$i++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($c === ' ' || $c === "\t" || $c === "\r" || $c === "\n") {
|
||||
$i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function rktd_skip_string($s, &$i)
|
||||
{
|
||||
$dummy = rktd_read_string($s, $i);
|
||||
return $dummy !== null;
|
||||
}
|
||||
|
||||
function rktd_skip_atom($s, &$i)
|
||||
{
|
||||
$n = strlen($s);
|
||||
|
||||
while ($i < $n) {
|
||||
$c = $s[$i];
|
||||
|
||||
if ($c === ' ' || $c === "\t" || $c === "\r" || $c === "\n" ||
|
||||
$c === '(' || $c === ')' || $c === '"' || $c === ';') {
|
||||
break;
|
||||
}
|
||||
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
function rktd_skip_expr($s, &$i)
|
||||
{
|
||||
$n = strlen($s);
|
||||
|
||||
rktd_skip_space($s, $i);
|
||||
|
||||
if ($i >= $n) {
|
||||
return;
|
||||
}
|
||||
|
||||
$c = $s[$i];
|
||||
|
||||
if ($c === '"') {
|
||||
rktd_skip_string($s, $i);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($c === "'") {
|
||||
$i++;
|
||||
rktd_skip_expr($s, $i);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($c === '#') {
|
||||
if (substr($s, $i, 2) === '#(') {
|
||||
$i++;
|
||||
rktd_skip_expr($s, $i);
|
||||
return;
|
||||
}
|
||||
|
||||
if (substr($s, $i, 5) === '#hash' ||
|
||||
substr($s, $i, 7) === '#hasheq' ||
|
||||
substr($s, $i, 8) === '#hasheqv') {
|
||||
while ($i < $n && $s[$i] !== '(') {
|
||||
$i++;
|
||||
}
|
||||
rktd_skip_expr($s, $i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($c === '(') {
|
||||
$depth = 0;
|
||||
|
||||
while ($i < $n) {
|
||||
$c = $s[$i];
|
||||
|
||||
if ($c === '"') {
|
||||
rktd_skip_string($s, $i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($c === ';') {
|
||||
while ($i < $n && $s[$i] !== "\n") {
|
||||
$i++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($c === '(') {
|
||||
$depth++;
|
||||
$i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($c === ')') {
|
||||
$depth--;
|
||||
$i++;
|
||||
|
||||
if ($depth <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$i++;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
rktd_skip_atom($s, $i);
|
||||
}
|
||||
|
||||
function rktd_extract_top_level_package_names($text)
|
||||
{
|
||||
$names = array();
|
||||
|
||||
$i = strpos($text, '#hash');
|
||||
|
||||
if ($i === false) {
|
||||
$i = strpos($text, '#hasheq');
|
||||
}
|
||||
|
||||
if ($i === false) {
|
||||
$i = strpos($text, '#hasheqv');
|
||||
}
|
||||
|
||||
if ($i === false) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$n = strlen($text);
|
||||
|
||||
while ($i < $n && $text[$i] !== '(') {
|
||||
$i++;
|
||||
}
|
||||
|
||||
if ($i >= $n || $text[$i] !== '(') {
|
||||
return array();
|
||||
}
|
||||
|
||||
$i++;
|
||||
|
||||
while ($i < $n) {
|
||||
rktd_skip_space($text, $i);
|
||||
|
||||
if ($i >= $n) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($text[$i] === ')') {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($text[$i] !== '(') {
|
||||
$i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$i++;
|
||||
rktd_skip_space($text, $i);
|
||||
$name = rktd_read_string($text, $i);
|
||||
|
||||
if ($name === null) {
|
||||
while ($i < $n && $text[$i] !== ')') {
|
||||
rktd_skip_expr($text, $i);
|
||||
rktd_skip_space($text, $i);
|
||||
}
|
||||
|
||||
if ($i < $n && $text[$i] === ')') {
|
||||
$i++;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rktd_package_name_ok($name)) {
|
||||
$names[$name] = true;
|
||||
}
|
||||
|
||||
rktd_skip_space($text, $i);
|
||||
|
||||
if ($i < $n && $text[$i] === '.') {
|
||||
$i++;
|
||||
}
|
||||
|
||||
rktd_skip_expr($text, $i);
|
||||
rktd_skip_space($text, $i);
|
||||
|
||||
if ($i < $n && $text[$i] === ')') {
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
$out = array_keys($names);
|
||||
sort($out, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
function rktd_extract_catalog_source($catalogText)
|
||||
{
|
||||
$patterns = array(
|
||||
'/\\(\\s*source\\s*\\.\\s*"((?:[^"\\\\]|\\\\.)*)"\\s*\\)/s',
|
||||
'/\\(\\s*"source"\\s*\\.\\s*"((?:[^"\\\\]|\\\\.)*)"\\s*\\)/s',
|
||||
);
|
||||
|
||||
foreach ($patterns as $pat) {
|
||||
if (preg_match($pat, $catalogText, $m)) {
|
||||
return rktd_unescape_string($m[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user