2020-01-26 13:19:35 +00:00
|
|
|
<?php declare(strict_types=1);
|
2020-01-26 13:25:02 +00:00
|
|
|
use function MicroHTML\emptyHTML;
|
2020-01-26 19:44:36 +00:00
|
|
|
use function MicroHTML\rawHTML;
|
2019-12-15 15:31:44 +00:00
|
|
|
use function MicroHTML\FORM;
|
|
|
|
use function MicroHTML\INPUT;
|
2020-01-16 19:13:12 +00:00
|
|
|
use function MicroHTML\DIV;
|
|
|
|
use function MicroHTML\PRE;
|
|
|
|
use function MicroHTML\P;
|
|
|
|
use function MicroHTML\TABLE;
|
|
|
|
use function MicroHTML\THEAD;
|
|
|
|
use function MicroHTML\TFOOT;
|
|
|
|
use function MicroHTML\TR;
|
|
|
|
use function MicroHTML\TH;
|
|
|
|
use function MicroHTML\TD;
|
2019-12-09 14:18:25 +00:00
|
|
|
|
2018-11-05 22:30:18 +00:00
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
|
|
|
|
* Misc *
|
|
|
|
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
|
2019-07-05 03:28:39 +00:00
|
|
|
const DATA_DIR = "data";
|
|
|
|
|
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
function mtimefile(string $file): string
|
|
|
|
{
|
|
|
|
$data_href = get_base_href();
|
|
|
|
$mtime = filemtime($file);
|
|
|
|
return "$data_href/$file?$mtime";
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
function get_theme(): string
|
|
|
|
{
|
|
|
|
global $config;
|
2019-08-02 19:40:03 +00:00
|
|
|
$theme = $config->get_string(SetupConfig::THEME, "default");
|
2019-05-28 16:59:38 +00:00
|
|
|
if (!file_exists("themes/$theme")) {
|
|
|
|
$theme = "default";
|
|
|
|
}
|
|
|
|
return $theme;
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
function contact_link(): ?string
|
|
|
|
{
|
|
|
|
global $config;
|
|
|
|
$text = $config->get_string('contact_link');
|
|
|
|
if (is_null($text)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
startsWith($text, "http:") ||
|
|
|
|
startsWith($text, "https:") ||
|
|
|
|
startsWith($text, "mailto:")
|
|
|
|
) {
|
|
|
|
return $text;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strpos($text, "@")) {
|
|
|
|
return "mailto:$text";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strpos($text, "/")) {
|
|
|
|
return "http://$text";
|
|
|
|
}
|
|
|
|
|
|
|
|
return $text;
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if HTTPS is enabled for the server.
|
|
|
|
*/
|
2019-05-28 16:59:38 +00:00
|
|
|
function is_https_enabled(): bool
|
|
|
|
{
|
|
|
|
return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Compare two Block objects, used to sort them before being displayed
|
|
|
|
*/
|
2019-05-28 16:59:38 +00:00
|
|
|
function blockcmp(Block $a, Block $b): int
|
|
|
|
{
|
|
|
|
if ($a->position == $b->position) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
2020-01-26 13:19:35 +00:00
|
|
|
return ($a->position > $b->position) ? 1 : -1;
|
2019-05-28 16:59:38 +00:00
|
|
|
}
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Figure out PHP's internal memory limit
|
|
|
|
*/
|
2019-05-28 16:59:38 +00:00
|
|
|
function get_memory_limit(): int
|
|
|
|
{
|
|
|
|
global $config;
|
|
|
|
|
|
|
|
// thumbnail generation requires lots of memory
|
|
|
|
$default_limit = 8*1024*1024; // 8 MB of memory is PHP's default.
|
2020-01-26 13:19:35 +00:00
|
|
|
$shimmie_limit = $config->get_int(MediaConfig::MEM_LIMIT);
|
2019-05-28 16:59:38 +00:00
|
|
|
|
|
|
|
if ($shimmie_limit < 3*1024*1024) {
|
|
|
|
// we aren't going to fit, override
|
|
|
|
$shimmie_limit = $default_limit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Get PHP's configured memory limit.
|
|
|
|
Note that this is set to -1 for NO memory limit.
|
|
|
|
|
|
|
|
http://ca2.php.net/manual/en/ini.core.php#ini.memory-limit
|
|
|
|
*/
|
|
|
|
$memory = parse_shorthand_int(ini_get("memory_limit"));
|
|
|
|
|
|
|
|
if ($memory == -1) {
|
|
|
|
// No memory limit.
|
|
|
|
// Return the larger of the set limits.
|
|
|
|
return max($shimmie_limit, $default_limit);
|
|
|
|
} else {
|
|
|
|
// PHP has a memory limit set.
|
|
|
|
if ($shimmie_limit > $memory) {
|
|
|
|
// Shimmie wants more memory than what PHP is currently set for.
|
|
|
|
|
|
|
|
// Attempt to set PHP's memory limit.
|
2020-01-26 13:19:35 +00:00
|
|
|
if (ini_set("memory_limit", "$shimmie_limit") === false) {
|
2019-05-28 16:59:38 +00:00
|
|
|
/* We can't change PHP's limit, oh well, return whatever its currently set to */
|
|
|
|
return $memory;
|
|
|
|
}
|
|
|
|
$memory = parse_shorthand_int(ini_get("memory_limit"));
|
|
|
|
}
|
|
|
|
|
|
|
|
// PHP's memory limit is more than Shimmie needs.
|
|
|
|
return $memory; // return the current setting
|
|
|
|
}
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the currently active IP, masked to make it not change when the last
|
|
|
|
* octet or two change, for use in session cookies and such
|
|
|
|
*/
|
2019-05-28 16:59:38 +00:00
|
|
|
function get_session_ip(Config $config): string
|
|
|
|
{
|
|
|
|
$mask = $config->get_string("session_hash_mask", "255.255.0.0");
|
|
|
|
$addr = $_SERVER['REMOTE_ADDR'];
|
|
|
|
$addr = inet_ntop(inet_pton($addr) & inet_pton($mask));
|
|
|
|
return $addr;
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A shorthand way to send a TextFormattingEvent and get the results.
|
|
|
|
*/
|
2019-05-28 16:59:38 +00:00
|
|
|
function format_text(string $string): string
|
|
|
|
{
|
|
|
|
$tfe = new TextFormattingEvent($string);
|
|
|
|
send_event($tfe);
|
|
|
|
return $tfe->formatted;
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
2019-07-05 03:28:39 +00:00
|
|
|
/**
|
|
|
|
* Generates the path to a file under the data folder based on the file's hash.
|
|
|
|
* This process creates subfolders based on octet pairs from the file's hash.
|
|
|
|
* The calculated folder follows this pattern data/$base/octet_pairs/$hash
|
|
|
|
* @param string $base
|
|
|
|
* @param string $hash
|
|
|
|
* @param bool $create
|
|
|
|
* @param int $splits The number of octet pairs to split the hash into. Caps out at strlen($hash)/2.
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
function warehouse_path(string $base, string $hash, bool $create=true, int $splits = WH_SPLITS): string
|
2019-05-28 16:59:38 +00:00
|
|
|
{
|
2019-07-05 03:28:39 +00:00
|
|
|
$dirs =[DATA_DIR, $base];
|
|
|
|
$splits = min($splits, strlen($hash) / 2);
|
2019-09-29 13:30:55 +00:00
|
|
|
for ($i = 0; $i < $splits; $i++) {
|
2019-07-05 03:28:39 +00:00
|
|
|
$dirs[] = substr($hash, $i * 2, 2);
|
|
|
|
}
|
|
|
|
$dirs[] = $hash;
|
2019-06-15 16:18:52 +00:00
|
|
|
|
2019-07-05 03:28:39 +00:00
|
|
|
$pa = join_path(...$dirs);
|
2019-06-15 16:18:52 +00:00
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
if ($create && !file_exists(dirname($pa))) {
|
|
|
|
mkdir(dirname($pa), 0755, true);
|
|
|
|
}
|
|
|
|
return $pa;
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
2019-07-05 03:28:39 +00:00
|
|
|
/**
|
|
|
|
* Determines the path to the specified file in the data folder.
|
|
|
|
*/
|
|
|
|
function data_path(string $filename, bool $create = true): string
|
2019-05-28 16:59:38 +00:00
|
|
|
{
|
2019-07-05 03:28:39 +00:00
|
|
|
$filename = join_path("data", $filename);
|
|
|
|
if ($create&&!file_exists(dirname($filename))) {
|
2019-05-28 16:59:38 +00:00
|
|
|
mkdir(dirname($filename), 0755, true);
|
|
|
|
}
|
|
|
|
return $filename;
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
function transload(string $url, string $mfile): ?array
|
|
|
|
{
|
|
|
|
global $config;
|
|
|
|
|
|
|
|
if ($config->get_string("transload_engine") === "curl" && function_exists("curl_init")) {
|
|
|
|
$ch = curl_init($url);
|
|
|
|
$fp = fopen($mfile, "w");
|
|
|
|
|
|
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
|
|
curl_setopt($ch, CURLOPT_VERBOSE, 1);
|
|
|
|
curl_setopt($ch, CURLOPT_HEADER, 1);
|
|
|
|
curl_setopt($ch, CURLOPT_REFERER, $url);
|
|
|
|
curl_setopt($ch, CURLOPT_USERAGENT, "Shimmie-".VERSION);
|
|
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
|
|
|
|
|
|
|
|
$response = curl_exec($ch);
|
2020-01-26 23:23:01 +00:00
|
|
|
if ($response === false) {
|
|
|
|
log_warning("core-util", "Failed to transload $url");
|
|
|
|
throw new SCoreException("Failed to fetch $url");
|
|
|
|
}
|
2019-05-28 16:59:38 +00:00
|
|
|
|
|
|
|
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
|
|
|
$headers = http_parse_headers(implode("\n", preg_split('/\R/', rtrim(substr($response, 0, $header_size)))));
|
|
|
|
$body = substr($response, $header_size);
|
|
|
|
|
|
|
|
curl_close($ch);
|
|
|
|
fwrite($fp, $body);
|
|
|
|
fclose($fp);
|
|
|
|
|
|
|
|
return $headers;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($config->get_string("transload_engine") === "wget") {
|
|
|
|
$s_url = escapeshellarg($url);
|
|
|
|
$s_mfile = escapeshellarg($mfile);
|
|
|
|
system("wget --no-check-certificate $s_url --output-document=$s_mfile");
|
|
|
|
|
2019-05-28 19:27:23 +00:00
|
|
|
return file_exists($mfile) ? ["ok"=>"true"] : null;
|
2019-05-28 16:59:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($config->get_string("transload_engine") === "fopen") {
|
|
|
|
$fp_in = @fopen($url, "r");
|
|
|
|
$fp_out = fopen($mfile, "w");
|
|
|
|
if (!$fp_in || !$fp_out) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
$length = 0;
|
|
|
|
while (!feof($fp_in) && $length <= $config->get_int('upload_size')) {
|
|
|
|
$data = fread($fp_in, 8192);
|
|
|
|
$length += strlen($data);
|
|
|
|
fwrite($fp_out, $data);
|
|
|
|
}
|
|
|
|
fclose($fp_in);
|
|
|
|
fclose($fp_out);
|
|
|
|
|
|
|
|
$headers = http_parse_headers(implode("\n", $http_response_header));
|
|
|
|
|
|
|
|
return $headers;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the active contents of a .php file
|
|
|
|
*/
|
2019-05-28 16:59:38 +00:00
|
|
|
function manual_include(string $fname): ?string
|
|
|
|
{
|
|
|
|
static $included = [];
|
2018-11-05 22:30:18 +00:00
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
if (!file_exists($fname)) {
|
|
|
|
return null;
|
|
|
|
}
|
2018-11-05 22:30:18 +00:00
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
if (in_array($fname, $included)) {
|
|
|
|
return null;
|
|
|
|
}
|
2018-11-05 22:30:18 +00:00
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
$included[] = $fname;
|
2018-11-05 22:30:18 +00:00
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
print "$fname\n";
|
2018-11-05 22:30:18 +00:00
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
$text = file_get_contents($fname);
|
2018-11-05 22:30:18 +00:00
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
// we want one continuous file
|
|
|
|
$text = str_replace('<'.'?php', '', $text);
|
|
|
|
$text = str_replace('?'.'>', '', $text);
|
2018-11-05 22:30:18 +00:00
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
// most requires are built-in, but we want /lib separately
|
|
|
|
$text = str_replace('require_', '// require_', $text);
|
|
|
|
$text = str_replace('// require_once "lib', 'require_once "lib', $text);
|
2018-11-05 22:30:18 +00:00
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
// @include_once is used for user-creatable config files
|
|
|
|
$text = preg_replace('/@include_once "(.*)";/e', "manual_include('$1')", $text);
|
2018-11-05 22:30:18 +00:00
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
return $text;
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
function path_to_tags(string $path): string
|
|
|
|
{
|
|
|
|
$matches = [];
|
2019-06-21 08:12:44 +00:00
|
|
|
$tags = [];
|
|
|
|
if (preg_match("/\d+ - (.+)\.([a-zA-Z0-9]+)/", basename($path), $matches)) {
|
|
|
|
$tags = explode(" ", $matches[1]);
|
|
|
|
}
|
2019-06-20 04:26:30 +00:00
|
|
|
|
2019-06-21 08:12:44 +00:00
|
|
|
$path = dirname($path);
|
2019-06-14 14:45:40 +00:00
|
|
|
$path = str_replace(";", ":", $path);
|
2019-06-21 08:12:44 +00:00
|
|
|
$path = str_replace("__", " ", $path);
|
2019-10-02 09:49:32 +00:00
|
|
|
|
2019-06-14 14:45:40 +00:00
|
|
|
|
|
|
|
$category = "";
|
2019-06-21 08:12:44 +00:00
|
|
|
foreach (explode("/", $path) as $dir) {
|
2019-06-14 14:45:40 +00:00
|
|
|
$category_to_inherit = "";
|
2019-06-21 08:12:44 +00:00
|
|
|
foreach (explode(" ", $dir) as $tag) {
|
2019-06-14 14:45:40 +00:00
|
|
|
$tag = trim($tag);
|
2019-06-21 08:12:44 +00:00
|
|
|
if ($tag=="") {
|
2019-06-14 14:45:40 +00:00
|
|
|
continue;
|
|
|
|
}
|
2019-06-21 08:12:44 +00:00
|
|
|
if (substr_compare($tag, ":", -1) === 0) {
|
2019-06-14 14:45:40 +00:00
|
|
|
// This indicates a tag that ends in a colon,
|
|
|
|
// which is for inheriting to tags on the subfolder
|
|
|
|
$category_to_inherit = $tag;
|
|
|
|
} else {
|
2019-06-21 08:12:44 +00:00
|
|
|
if ($category!=""&&strpos($tag, ":") === false) {
|
2019-06-14 14:45:40 +00:00
|
|
|
// This indicates that category inheritance is active,
|
|
|
|
// and we've encountered a tag that does not specify a category.
|
|
|
|
// So we attach the inherited category to the tag.
|
|
|
|
$tag = $category.$tag;
|
|
|
|
}
|
2019-06-20 04:26:30 +00:00
|
|
|
$tags[] = $tag;
|
2019-06-14 14:45:40 +00:00
|
|
|
}
|
|
|
|
}
|
2019-06-21 08:12:44 +00:00
|
|
|
// Category inheritance only works on the immediate subfolder,
|
2019-06-14 14:45:40 +00:00
|
|
|
// so we hold a category until the next iteration, and then set
|
|
|
|
// it back to an empty string after that iteration
|
|
|
|
$category = $category_to_inherit;
|
2019-06-14 12:16:58 +00:00
|
|
|
}
|
2019-06-20 04:26:30 +00:00
|
|
|
|
2019-06-21 08:12:44 +00:00
|
|
|
return implode(" ", $tags);
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
2019-07-05 03:28:39 +00:00
|
|
|
|
|
|
|
function join_url(string $base, string ...$paths)
|
|
|
|
{
|
|
|
|
$output = $base;
|
|
|
|
foreach ($paths as $path) {
|
2019-09-29 13:30:55 +00:00
|
|
|
$output = rtrim($output, "/");
|
2019-07-05 03:28:39 +00:00
|
|
|
$path = ltrim($path, "/");
|
|
|
|
$output .= "/".$path;
|
|
|
|
}
|
|
|
|
return $output;
|
|
|
|
}
|
|
|
|
|
2019-10-10 15:25:37 +00:00
|
|
|
function get_dir_contents(string $dir): array
|
|
|
|
{
|
2020-01-26 13:19:35 +00:00
|
|
|
assert(!empty($dir));
|
|
|
|
|
2019-11-02 19:57:34 +00:00
|
|
|
if (!is_dir($dir)) {
|
2019-10-10 15:25:37 +00:00
|
|
|
return [];
|
|
|
|
}
|
2020-01-26 13:19:35 +00:00
|
|
|
return array_diff(
|
2019-10-10 15:25:37 +00:00
|
|
|
scandir(
|
2019-11-02 19:57:34 +00:00
|
|
|
$dir
|
|
|
|
),
|
|
|
|
['..', '.']
|
|
|
|
);
|
2019-10-10 15:25:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns amount of files & total size of dir.
|
|
|
|
*/
|
|
|
|
function scan_dir(string $path): array
|
|
|
|
{
|
|
|
|
$bytestotal = 0;
|
|
|
|
$nbfiles = 0;
|
|
|
|
|
|
|
|
$ite = new RecursiveDirectoryIterator(
|
|
|
|
$path,
|
|
|
|
FilesystemIterator::KEY_AS_PATHNAME |
|
|
|
|
FilesystemIterator::CURRENT_AS_FILEINFO |
|
2019-11-02 19:57:34 +00:00
|
|
|
FilesystemIterator::SKIP_DOTS
|
|
|
|
);
|
2019-10-10 15:25:37 +00:00
|
|
|
foreach (new RecursiveIteratorIterator($ite) as $filename => $cur) {
|
|
|
|
try {
|
|
|
|
$filesize = $cur->getSize();
|
|
|
|
$bytestotal += $filesize;
|
|
|
|
$nbfiles++;
|
|
|
|
} catch (RuntimeException $e) {
|
|
|
|
// This usually just means that the file got eaten by the import
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$size_mb = $bytestotal / 1048576; // to mb
|
|
|
|
$size_mb = number_format($size_mb, 2, '.', '');
|
|
|
|
return ['path' => $path, 'total_files' => $nbfiles, 'total_mb' => $size_mb];
|
|
|
|
}
|
|
|
|
|
2018-11-05 22:30:18 +00:00
|
|
|
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
|
|
|
|
* Debugging functions *
|
|
|
|
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
|
|
|
|
// SHIT by default this returns the time as a string. And it's not even a
|
|
|
|
// string representation of a number, it's two numbers separated by a space.
|
|
|
|
// What the fuck were the PHP developers smoking.
|
|
|
|
$_shm_load_start = microtime(true);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Collects some debug information (execution time, memory usage, queries, etc)
|
|
|
|
* and formats it to stick in the footer of the page.
|
|
|
|
*/
|
2019-05-28 16:59:38 +00:00
|
|
|
function get_debug_info(): string
|
|
|
|
{
|
2019-10-02 09:49:32 +00:00
|
|
|
global $cache, $config, $_shm_event_count, $database, $_shm_load_start;
|
2019-05-28 16:59:38 +00:00
|
|
|
|
|
|
|
$i_mem = sprintf("%5.2f", ((memory_get_peak_usage(true)+512)/1024)/1024);
|
|
|
|
|
|
|
|
if ($config->get_string("commit_hash", "unknown") == "unknown") {
|
|
|
|
$commit = "";
|
|
|
|
} else {
|
|
|
|
$commit = " (".$config->get_string("commit_hash").")";
|
|
|
|
}
|
|
|
|
$time = sprintf("%.2f", microtime(true) - $_shm_load_start);
|
|
|
|
$dbtime = sprintf("%.2f", $database->dbtime);
|
|
|
|
$i_files = count(get_included_files());
|
2019-10-02 09:49:32 +00:00
|
|
|
$hits = $cache->get_hits();
|
|
|
|
$miss = $cache->get_misses();
|
2019-05-28 16:59:38 +00:00
|
|
|
|
|
|
|
$debug = "<br>Took $time seconds (db:$dbtime) and {$i_mem}MB of RAM";
|
|
|
|
$debug .= "; Used $i_files files and {$database->query_count} queries";
|
|
|
|
$debug .= "; Sent $_shm_event_count events";
|
|
|
|
$debug .= "; $hits cache hits and $miss misses";
|
|
|
|
$debug .= "; Shimmie version ". VERSION . $commit; // .", SCore Version ". SCORE_VERSION;
|
|
|
|
|
|
|
|
return $debug;
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
|
|
|
|
* Request initialisation stuff *
|
|
|
|
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
|
|
|
|
/** @privatesection */
|
|
|
|
|
2019-05-29 17:23:29 +00:00
|
|
|
function _version_check(): void
|
2019-05-28 16:59:38 +00:00
|
|
|
{
|
|
|
|
if (MIN_PHP_VERSION) {
|
|
|
|
if (version_compare(phpversion(), MIN_PHP_VERSION, ">=") === false) {
|
|
|
|
print "
|
2018-11-05 22:30:18 +00:00
|
|
|
Shimmie (SCore Engine) does not support versions of PHP lower than ".MIN_PHP_VERSION."
|
|
|
|
(PHP reports that it is version ".phpversion().")
|
|
|
|
If your web host is running an older version, they are dangerously out of
|
|
|
|
date and you should plan on moving elsewhere.
|
|
|
|
";
|
2019-05-28 16:59:38 +00:00
|
|
|
exit;
|
|
|
|
}
|
|
|
|
}
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
2019-05-29 17:23:29 +00:00
|
|
|
function _sanitise_environment(): void
|
2019-05-28 16:59:38 +00:00
|
|
|
{
|
2019-07-05 19:49:47 +00:00
|
|
|
global $_tracer;
|
2019-05-28 16:59:38 +00:00
|
|
|
|
|
|
|
if (TIMEZONE) {
|
|
|
|
date_default_timezone_set(TIMEZONE);
|
|
|
|
}
|
|
|
|
|
2020-01-26 13:19:35 +00:00
|
|
|
# ini_set('zend.assertions', '1'); // generate assertions
|
|
|
|
ini_set('assert.exception', '1'); // throw exceptions when failed
|
2019-05-28 16:59:38 +00:00
|
|
|
if (DEBUG) {
|
|
|
|
error_reporting(E_ALL);
|
|
|
|
}
|
|
|
|
|
2019-07-05 19:49:47 +00:00
|
|
|
$_tracer = new EventTracer();
|
2019-05-28 16:59:38 +00:00
|
|
|
|
|
|
|
if (COVERAGE) {
|
|
|
|
_start_coverage();
|
|
|
|
register_shutdown_function("_end_coverage");
|
|
|
|
}
|
|
|
|
|
|
|
|
ob_start();
|
|
|
|
|
|
|
|
if (PHP_SAPI === 'cli' || PHP_SAPI == 'phpdbg') {
|
|
|
|
if (isset($_SERVER['REMOTE_ADDR'])) {
|
|
|
|
die("CLI with remote addr? Confused, not taking the risk.");
|
|
|
|
}
|
|
|
|
$_SERVER['REMOTE_ADDR'] = "0.0.0.0";
|
|
|
|
$_SERVER['HTTP_HOST'] = "<cli command>";
|
|
|
|
}
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
function _get_themelet_files(string $_theme): array
|
|
|
|
{
|
|
|
|
$base_themelets = [];
|
|
|
|
if (file_exists('themes/'.$_theme.'/custompage.class.php')) {
|
|
|
|
$base_themelets[] = 'themes/'.$_theme.'/custompage.class.php';
|
|
|
|
}
|
|
|
|
$base_themelets[] = 'themes/'.$_theme.'/layout.class.php';
|
|
|
|
$base_themelets[] = 'themes/'.$_theme.'/themelet.class.php';
|
2018-11-05 22:30:18 +00:00
|
|
|
|
2019-08-07 20:57:14 +00:00
|
|
|
$ext_themelets = zglob("ext/{".Extension::get_enabled_extensions_as_string()."}/theme.php");
|
|
|
|
$custom_themelets = zglob('themes/'.$_theme.'/{'.Extension::get_enabled_extensions_as_string().'}.theme.php');
|
2018-11-05 22:30:18 +00:00
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
return array_merge($base_themelets, $ext_themelets, $custom_themelets);
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Used to display fatal errors to the web user.
|
|
|
|
*/
|
2019-05-29 17:23:29 +00:00
|
|
|
function _fatal_error(Exception $e): void
|
2019-05-28 16:59:38 +00:00
|
|
|
{
|
|
|
|
$version = VERSION;
|
|
|
|
$message = $e->getMessage();
|
2019-12-09 14:18:25 +00:00
|
|
|
$phpver = phpversion();
|
2018-11-05 22:30:18 +00:00
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
//$hash = exec("git rev-parse HEAD");
|
|
|
|
//$h_hash = $hash ? "<p><b>Hash:</b> $hash" : "";
|
|
|
|
//'.$h_hash.'
|
2018-11-05 22:30:18 +00:00
|
|
|
|
2019-12-07 22:53:59 +00:00
|
|
|
if (PHP_SAPI === 'cli' || PHP_SAPI == 'phpdbg') {
|
2019-12-15 15:31:44 +00:00
|
|
|
print("Trace: ");
|
|
|
|
$t = array_reverse($e->getTrace());
|
|
|
|
foreach ($t as $n => $f) {
|
|
|
|
$c = $f['class'] ?? '';
|
|
|
|
$t = $f['type'] ?? '';
|
|
|
|
$a = implode(", ", array_map("stringer", $f['args']));
|
|
|
|
print("$n: {$f['file']}({$f['line']}): {$c}{$t}{$f['function']}({$a})\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
print("Message: $message\n");
|
|
|
|
|
|
|
|
if (isset($e->query)) {
|
|
|
|
print("Query: {$e->query}\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
print("Version: $version (on $phpver)\n");
|
|
|
|
} else {
|
|
|
|
$q = (!isset($e->query) || is_null($e->query)) ? "" : "<p><b>Query:</b> " . html_escape($e->query);
|
|
|
|
header("HTTP/1.0 500 Internal Error");
|
|
|
|
echo '
|
2018-11-05 22:30:18 +00:00
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<title>Internal error - SCore-'.$version.'</title>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<h1>Internal Error</h1>
|
2019-11-11 16:43:04 +00:00
|
|
|
<p><b>Message:</b> '.html_escape($message).'
|
|
|
|
'.$q.'
|
2019-12-09 14:18:25 +00:00
|
|
|
<p><b>Version:</b> '.$version.' (on '.$phpver.')
|
2018-11-05 22:30:18 +00:00
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
';
|
2019-12-15 15:31:44 +00:00
|
|
|
}
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Turn ^^ into ^ and ^s into /
|
|
|
|
*
|
|
|
|
* Necessary because various servers and various clients
|
|
|
|
* think that / is special...
|
|
|
|
*/
|
2019-05-28 16:59:38 +00:00
|
|
|
function _decaret(string $str): string
|
|
|
|
{
|
|
|
|
$out = "";
|
|
|
|
$length = strlen($str);
|
|
|
|
for ($i=0; $i<$length; $i++) {
|
|
|
|
if ($str[$i] == "^") {
|
|
|
|
$i++;
|
|
|
|
if ($str[$i] == "^") {
|
|
|
|
$out .= "^";
|
|
|
|
}
|
|
|
|
if ($str[$i] == "s") {
|
|
|
|
$out .= "/";
|
|
|
|
}
|
|
|
|
if ($str[$i] == "b") {
|
|
|
|
$out .= "\\";
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$out .= $str[$i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $out;
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
function _get_user(): User
|
|
|
|
{
|
|
|
|
global $config, $page;
|
2019-11-11 16:24:13 +00:00
|
|
|
$my_user = null;
|
2019-05-28 16:59:38 +00:00
|
|
|
if ($page->get_cookie("user") && $page->get_cookie("session")) {
|
|
|
|
$tmp_user = User::by_session($page->get_cookie("user"), $page->get_cookie("session"));
|
|
|
|
if (!is_null($tmp_user)) {
|
2019-11-11 16:24:13 +00:00
|
|
|
$my_user = $tmp_user;
|
2019-05-28 16:59:38 +00:00
|
|
|
}
|
|
|
|
}
|
2019-11-11 16:24:13 +00:00
|
|
|
if (is_null($my_user)) {
|
|
|
|
$my_user = User::by_id($config->get_int("anon_id", 0));
|
2019-05-28 16:59:38 +00:00
|
|
|
}
|
2019-11-11 16:24:13 +00:00
|
|
|
assert(!is_null($my_user));
|
2019-05-28 16:59:38 +00:00
|
|
|
|
2019-11-11 16:24:13 +00:00
|
|
|
return $my_user;
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
function _get_query(): string
|
|
|
|
{
|
|
|
|
return (@$_POST["q"]?:@$_GET["q"])?:"/";
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
|
|
|
|
* Code coverage *
|
|
|
|
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
function _start_coverage(): void
|
|
|
|
{
|
|
|
|
if (function_exists("xdebug_start_code_coverage")) {
|
|
|
|
#xdebug_start_code_coverage(XDEBUG_CC_UNUSED|XDEBUG_CC_DEAD_CODE);
|
|
|
|
xdebug_start_code_coverage(XDEBUG_CC_UNUSED);
|
|
|
|
}
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
2019-05-28 16:59:38 +00:00
|
|
|
function _end_coverage(): void
|
|
|
|
{
|
|
|
|
if (function_exists("xdebug_get_code_coverage")) {
|
|
|
|
// Absolute path is necessary because working directory
|
|
|
|
// inside register_shutdown_function is unpredictable.
|
|
|
|
$absolute_path = dirname(dirname(__FILE__)) . "/data/coverage";
|
|
|
|
if (!file_exists($absolute_path)) {
|
|
|
|
mkdir($absolute_path);
|
|
|
|
}
|
|
|
|
$n = 0;
|
|
|
|
$t = time();
|
|
|
|
while (file_exists("$absolute_path/$t.$n.log")) {
|
|
|
|
$n++;
|
|
|
|
}
|
|
|
|
file_put_contents("$absolute_path/$t.$n.log", gzdeflate(serialize(xdebug_get_code_coverage())));
|
|
|
|
}
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
|
|
|
|
* HTML Generation *
|
|
|
|
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Give a HTML string which shows an IP (if the user is allowed to see IPs),
|
|
|
|
* and a link to ban that IP (if the user is allowed to ban IPs)
|
|
|
|
*
|
|
|
|
* FIXME: also check that IP ban ext is installed
|
|
|
|
*/
|
2019-05-28 16:59:38 +00:00
|
|
|
function show_ip(string $ip, string $ban_reason): string
|
|
|
|
{
|
|
|
|
global $user;
|
|
|
|
$u_reason = url_escape($ban_reason);
|
|
|
|
$u_end = url_escape("+1 week");
|
2019-11-27 21:06:14 +00:00
|
|
|
$ban = $user->can(Permissions::BAN_IP) ? ", <a href='".make_link("ip_ban/list", "c_ip=$ip&c_reason=$u_reason&c_expires=$u_end#create")."'>Ban</a>" : "";
|
2019-07-09 14:10:21 +00:00
|
|
|
$ip = $user->can(Permissions::VIEW_IP) ? $ip.$ban : "";
|
2019-05-28 16:59:38 +00:00
|
|
|
return $ip;
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Make a form tag with relevant auth token and stuff
|
|
|
|
*/
|
2019-05-28 16:59:38 +00:00
|
|
|
function make_form(string $target, string $method="POST", bool $multipart=false, string $form_id="", string $onsubmit=""): string
|
|
|
|
{
|
|
|
|
global $user;
|
|
|
|
if ($method == "GET") {
|
|
|
|
$link = html_escape($target);
|
|
|
|
$target = make_link($target);
|
|
|
|
$extra_inputs = "<input type='hidden' name='q' value='$link'>";
|
|
|
|
} else {
|
|
|
|
$extra_inputs = $user->get_auth_html();
|
|
|
|
}
|
|
|
|
|
|
|
|
$extra = empty($form_id) ? '' : 'id="'. $form_id .'"';
|
|
|
|
if ($multipart) {
|
|
|
|
$extra .= " enctype='multipart/form-data'";
|
|
|
|
}
|
|
|
|
if ($onsubmit) {
|
|
|
|
$extra .= ' onsubmit="'.$onsubmit.'"';
|
|
|
|
}
|
|
|
|
return '<form action="'.$target.'" method="'.$method.'" '.$extra.'>'.$extra_inputs;
|
2018-11-05 22:30:18 +00:00
|
|
|
}
|
2019-12-09 14:18:25 +00:00
|
|
|
|
2019-12-15 15:31:44 +00:00
|
|
|
function SHM_FORM(string $target, string $method="POST", bool $multipart=false, string $form_id="", string $onsubmit="")
|
|
|
|
{
|
2019-12-09 14:18:25 +00:00
|
|
|
global $user;
|
|
|
|
|
|
|
|
$attrs = [
|
|
|
|
"action"=>$target,
|
|
|
|
"method"=>$method
|
|
|
|
];
|
|
|
|
|
2019-12-15 15:31:44 +00:00
|
|
|
if ($form_id) {
|
2019-12-09 14:18:25 +00:00
|
|
|
$attrs["id"] = $form_id;
|
|
|
|
}
|
|
|
|
if ($multipart) {
|
|
|
|
$attrs["enctype"] = 'multipart/form-data';
|
|
|
|
}
|
|
|
|
if ($onsubmit) {
|
|
|
|
$attrs["onsubmit"] = $onsubmit;
|
|
|
|
}
|
2020-01-26 13:19:35 +00:00
|
|
|
return FORM(
|
2019-12-09 14:18:25 +00:00
|
|
|
$attrs,
|
|
|
|
INPUT(["type"=>"hidden", "name"=>"q", "value"=>$target]),
|
2020-01-26 19:44:36 +00:00
|
|
|
$method == "GET" ? "" : rawHTML($user->get_auth_html())
|
2019-12-09 14:18:25 +00:00
|
|
|
);
|
|
|
|
}
|
2020-01-16 19:13:12 +00:00
|
|
|
|
2020-01-26 16:38:26 +00:00
|
|
|
function SHM_SIMPLE_FORM($target, ...$children)
|
|
|
|
{
|
2020-01-26 13:25:02 +00:00
|
|
|
$form = SHM_FORM($target);
|
|
|
|
$form->appendChild(emptyHTML(...$children));
|
|
|
|
return $form;
|
|
|
|
}
|
|
|
|
|
2020-01-26 23:12:48 +00:00
|
|
|
function SHM_SUBMIT(string $text)
|
|
|
|
{
|
|
|
|
return INPUT(["type"=>"submit", "value"=>$text]);
|
|
|
|
}
|
|
|
|
|
2020-01-16 19:13:12 +00:00
|
|
|
function SHM_COMMAND_EXAMPLE(string $ex, string $desc)
|
|
|
|
{
|
|
|
|
return DIV(
|
|
|
|
["class"=>"command_example"],
|
|
|
|
PRE($ex),
|
|
|
|
P($desc)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function SHM_USER_FORM(User $duser, string $target, string $title, $body, $foot)
|
|
|
|
{
|
|
|
|
if (is_string($foot)) {
|
|
|
|
$foot = TFOOT(TR(TD(["colspan"=>"2"], INPUT(["type"=>"submit", "value"=>$foot]))));
|
|
|
|
}
|
2020-01-26 13:25:02 +00:00
|
|
|
return SHM_SIMPLE_FORM(
|
|
|
|
make_link($target),
|
|
|
|
P(
|
|
|
|
INPUT(["type"=>'hidden', "name"=>'id', "value"=>$duser->id]),
|
|
|
|
TABLE(
|
|
|
|
["class"=>"form"],
|
|
|
|
THEAD(TR(TH(["colspan"=>"2"], $title))),
|
|
|
|
$body,
|
|
|
|
$foot
|
|
|
|
)
|
2020-01-16 19:13:12 +00:00
|
|
|
)
|
2020-01-26 13:25:02 +00:00
|
|
|
);
|
2020-01-16 19:13:12 +00:00
|
|
|
}
|