1
0
mirror of https://github.com/vichan-devel/vichan.git synced 2024-09-24 03:28:22 +02:00

Merge pull request #661 from Zankaria/refactor-functions

Initial break up of functions.php
This commit is contained in:
Lorenzo Yario 2024-02-04 05:08:37 -08:00 committed by GitHub
commit 1b33080214
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 680 additions and 645 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,24 @@
<?php
defined('TINYBOARD') or exit;
function base32_decode($d) {
$charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
$d = str_split($d);
$l = array_pop($d);
$b = '';
foreach ($d as $c) {
$b .= sprintf("%05b", strpos($charset, $c));
}
$padding = 8 - strlen($b) % 8;
$b .= str_pad(decbin(strpos($charset, $l)), $padding, '0', STR_PAD_LEFT);
return implode('', array_map(function($c) { return chr(bindec($c)); }, str_split($b, 8)));
}
function base32_encode($d) {
$charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
$b = implode('', array_map(function($c) { return sprintf("%08b", ord($c)); }, str_split($d)));
return implode('', array_map(function($c) use ($charset) { return $charset[bindec($c)]; }, str_split($b, 5)));
}

60
inc/functions/error.php Normal file
View File

@ -0,0 +1,60 @@
<?php // Error handling
defined('TINYBOARD') or exit;
function _syslog($priority, $message) {
if (isset($_SERVER['REMOTE_ADDR'], $_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'])) {
// CGI
syslog($priority, $message . ' - client: ' . $_SERVER['REMOTE_ADDR'] . ', request: "' . $_SERVER['REQUEST_METHOD'] . ' ' . $_SERVER['REQUEST_URI'] . '"');
} else {
syslog($priority, $message);
}
}
function basic_error_function_because_the_other_isnt_loaded_yet($message, $priority = true) {
global $config;
if ($config['syslog'] && $priority !== false) {
// Use LOG_NOTICE instead of LOG_ERR or LOG_WARNING because most error message are not significant.
_syslog($priority !== true ? $priority : LOG_NOTICE, $message);
}
// Yes, this is horrible.
die('<!DOCTYPE html><html><head><title>Error</title>' .
'<style type="text/css">' .
'body{text-align:center;font-family:arial, helvetica, sans-serif;font-size:10pt;}' .
'p{padding:0;margin:20px 0;}' .
'p.c{font-size:11px;}' .
'</style></head>' .
'<body><h2>Error</h2>' . $message . '<hr/>' .
'<p class="c">This alternative error page is being displayed because the other couldn\'t be found or hasn\'t loaded yet.</p></body></html>');
}
function fatal_error_handler() {
if ($error = error_get_last()) {
if ($error['type'] == E_ERROR) {
if (function_exists('error')) {
error('Caught fatal error: ' . $error['message'] . ' in <strong>' . $error['file'] . '</strong> on line ' . $error['line'], LOG_ERR);
} else {
basic_error_function_because_the_other_isnt_loaded_yet('Caught fatal error: ' . $error['message'] . ' in ' . $error['file'] . ' on line ' . $error['line'], LOG_ERR);
}
}
}
}
function verbose_error_handler($errno, $errstr, $errfile, $errline) {
global $config;
if (error_reporting() == 0)
return false; // Looks like this warning was suppressed by the @ operator.
if ($errno == E_DEPRECATED && !$config['deprecation_errors'])
return false;
error(utf8tohtml($errstr), true, array(
'file' => $errfile . ':' . $errline,
'errno' => $errno,
'error' => $errstr,
'backtrace' => array_slice(debug_backtrace(), 1)
));
}

188
inc/functions/fs.php Normal file
View File

@ -0,0 +1,188 @@
<?php
defined('TINYBOARD') or exit;
function file_write($path, $data, $simple = false, $skip_purge = false) {
global $config, $debug;
if (preg_match('/^remote:\/\/(.+)\:(.+)$/', $path, $m)) {
if (isset($config['remote'][$m[1]])) {
require_once 'inc/remote.php';
$remote = new Remote($config['remote'][$m[1]]);
$remote->write($data, $m[2]);
return;
} else {
error('Invalid remote server: ' . $m[1]);
}
}
if (!$fp = fopen($path, $simple ? 'w' : 'c')) {
error('Unable to open file for writing: ' . $path);
}
// File locking
if (!$simple && !flock($fp, LOCK_EX)) {
error('Unable to lock file: ' . $path);
}
// Truncate file
if (!$simple && !ftruncate($fp, 0)) {
error('Unable to truncate file: ' . $path);
}
// Write data
if (($bytes = fwrite($fp, $data)) === false) {
error('Unable to write to file: ' . $path);
}
// Unlock
if (!$simple) {
flock($fp, LOCK_UN);
}
// Close
if (!fclose($fp)) {
error('Unable to close file: ' . $path);
}
/**
* Create gzipped file.
*
* When writing into a file foo.bar and the size is larger or equal to 1
* KiB, this also produces the gzipped version foo.bar.gz
*
* This is useful with nginx with gzip_static on.
*/
if ($config['gzip_static']) {
$gzpath = "$path.gz";
// if ($bytes >= 1024)
if ($bytes & ~0x3ff) {
if (file_put_contents($gzpath, gzencode($data), $simple ? 0 : LOCK_EX) === false) {
error("Unable to write to file: $gzpath");
}
}
else {
@unlink($gzpath);
}
}
if (!$skip_purge && isset($config['purge'])) {
// Purge cache
if (basename($path) == $config['file_index']) {
// Index file (/index.html); purge "/" as well
$uri = dirname($path);
// root
if ($uri == '.') {
$uri = '';
} else {
$uri .= '/';
}
purge($uri);
}
purge($path);
}
if ($config['debug']) {
$debug['write'][] = $path . ': ' . $bytes . ' bytes';
}
event('write', $path);
}
function file_unlink($path) {
global $config, $debug;
if ($config['debug']) {
if (!isset($debug['unlink'])) {
$debug['unlink'] = array();
}
$debug['unlink'][] = $path;
}
$ret = @unlink($path);
if ($config['gzip_static']) {
$gzpath = "$path.gz";
@unlink($gzpath);
}
if (isset($config['purge']) && $path[0] != '/' && isset($_SERVER['HTTP_HOST'])) {
// Purge cache
if (basename($path) == $config['file_index']) {
// Index file (/index.html); purge "/" as well
$uri = dirname($path);
// root
if ($uri == '.') {
$uri = '';
} else {
$uri .= '/';
}
purge($uri);
}
purge($path);
}
event('unlink', $path);
return $ret;
}
function purge($uri) {
global $config, $debug;
// Fix for Unicode
$uri = rawurlencode($uri);
$noescape = "/!~*()+:";
$noescape = preg_split('//', $noescape);
$noescape_url = array_map("rawurlencode", $noescape);
$uri = str_replace($noescape_url, $noescape, $uri);
if (preg_match($config['referer_match'], $config['root']) && isset($_SERVER['REQUEST_URI'])) {
$uri = (str_replace('\\', '/', dirname($_SERVER['REQUEST_URI'])) == '/' ? '/' : str_replace('\\', '/', dirname($_SERVER['REQUEST_URI'])) . '/') . $uri;
} else {
$uri = $config['root'] . $uri;
}
if ($config['debug']) {
$debug['purge'][] = $uri;
}
foreach ($config['purge'] as &$purge) {
$host = &$purge[0];
$port = &$purge[1];
$http_host = isset($purge[2]) ? $purge[2] : (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost');
$request = "PURGE {$uri} HTTP/1.1\r\nHost: {$http_host}\r\nUser-Agent: Tinyboard\r\nConnection: Close\r\n\r\n";
if ($fp = fsockopen($host, $port, $errno, $errstr, $config['purge_timeout'])) {
fwrite($fp, $request);
fclose($fp);
} else {
// Cannot connect?
error('Could not PURGE for ' . $host);
}
}
}
/**
* Recursively delete a directory with it's content.
*/
function rrmdir($dir) {
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != "." && $object != "..") {
if (filetype($dir."/".$object) == "dir") {
rrmdir($dir."/".$object);
} else {
file_unlink($dir."/".$object);
}
}
}
reset($objects);
rmdir($dir);
}
}

View File

@ -0,0 +1,63 @@
<?php // Vichan specific generation strategy
defined('TINYBOARD') or exit;
function generation_strategy($fun, $array=array()) { global $config;
$action = false;
foreach ($config['generation_strategies'] as $s) {
if ($action = $s($fun, $array)) {
break;
}
}
switch ($action[0]) {
case 'immediate':
return 'rebuild';
case 'defer':
// Ok, it gets interesting here :)
get_queue('generate')->push(serialize(array('build', $fun, $array, $action)));
return 'ignore';
case 'build_on_load':
return 'delete';
}
}
function strategy_immediate($fun, $array) {
return array('immediate');
}
function strategy_smart_build($fun, $array) {
return array('build_on_load');
}
function strategy_sane($fun, $array) { global $config;
if (php_sapi_name() == 'cli') {
return false;
}
elseif (isset($_POST['mod'])) {
return false;
}
// Thread needs to be done instantly. Same with a board page, but only if posting a new thread.
elseif ($fun == 'sb_thread' || ($fun == 'sb_board' && $array[1] == 1 && isset ($_POST['page']))) {
return array('immediate');
}
return false;
}
// My first, test strategy.
function strategy_first($fun, $array) {
switch ($fun) {
case 'sb_thread':
case 'sb_api':
case 'sb_catalog':
case 'sb_ukko':
return array('defer');
case 'sb_board':
return $array[1] > 8 ? array('build_on_load') : array('defer');
case 'sb_recent':
case 'sb_sitemap':
return array('build_on_load');
}
}

28
inc/functions/interop.php Normal file
View File

@ -0,0 +1,28 @@
<?php
defined('TINYBOARD') or exit;
function shell_exec_error($command, $suppress_stdout = false) {
global $config, $debug;
if ($config['debug']) {
$start = microtime(true);
}
$return = trim(shell_exec('PATH="' . escapeshellcmd($config['shell_path']) . ':$PATH";' .
$command . ' 2>&1 ' . ($suppress_stdout ? '> /dev/null ' : '') . '&& echo "TB_SUCCESS"'));
$return = preg_replace('/TB_SUCCESS$/', '', $return);
if ($config['debug']) {
$time = microtime(true) - $start;
$debug['exec'][] = array(
'command' => $command,
'time' => '~' . round($time * 1000, 2) . 'ms',
'response' => $return ? $return : null
);
$debug['time']['exec'] += $time;
}
return $return === 'TB_SUCCESS' ? false : $return;
}

33
inc/functions/math.php Normal file
View File

@ -0,0 +1,33 @@
<?php // Math related functions
defined('TINYBOARD') or exit;
// Highest common factor
function hcf($a, $b){
$gcd = 1;
if ($a>$b) {
$a = $a+$b;
$b = $a-$b;
$a = $a-$b;
}
if ($b == round($b / $a) * $a) {
$gcd=$a;
} else {
for ($i = round($a / 2); $i; $i--) {
if ($a == round($a / $i) * $i && $b == round($b / $i) * $i) {
$gcd = $i;
$i = false;
}
}
}
return $gcd;
}
function fraction($numerator, $denominator, $sep) {
$gcf = hcf($numerator, $denominator);
$numerator = $numerator / $gcf;
$denominator = $denominator / $gcf;
return "{$numerator}{$sep}{$denominator}";
}

71
inc/functions/net.php Normal file
View File

@ -0,0 +1,71 @@
<?php // Networking related functions
defined('TINYBOARD') or exit;
function isIPv6() {
return strstr($_SERVER['REMOTE_ADDR'], ':') !== false;
}
function ReverseIPOctets($ip) {
return implode('.', array_reverse(explode('.', $ip)));
}
function rDNS($ip_addr) {
global $config;
if ($config['cache']['enabled'] && ($host = cache::get('rdns_' . $ip_addr))) {
return $host;
}
if (!$config['dns_system']) {
$host = gethostbyaddr($ip_addr);
} else {
$resp = shell_exec_error('host -W 3 ' . $ip_addr);
if (preg_match('/domain name pointer ([^\s]+)$/', $resp, $m)) {
$host = $m[1];
} else {
$host = $ip_addr;
}
}
$isip = filter_var($host, FILTER_VALIDATE_IP);
if ($config['fcrdns'] && !$isip && DNS($host) != $ip_addr) {
$host = $ip_addr;
}
if ($config['cache']['enabled']) {
cache::set('rdns_' . $ip_addr, $host);
}
return $host;
}
function DNS($host) {
global $config;
if ($config['cache']['enabled'] && ($ip_addr = cache::get('dns_' . $host))) {
return $ip_addr != '?' ? $ip_addr : false;
}
if (!$config['dns_system']) {
$ip_addr = gethostbyname($host);
if ($ip_addr == $host) {
$ip_addr = false;
}
} else {
$resp = shell_exec_error('host -W 1 ' . $host);
if (preg_match('/has address ([^\s]+)$/', $resp, $m)) {
$ip_addr = $m[1];
} else {
$ip_addr = false;
}
}
if ($config['cache']['enabled']) {
cache::set('dns_' . $host, $ip_addr !== false ? $ip_addr : '?');
}
return $ip_addr;
}

43
inc/functions/text.php Normal file
View File

@ -0,0 +1,43 @@
<?php // Text handling functions.
defined('TINYBOARD') or exit;
function sprintf3($str, $vars, $delim = '%') {
$replaces = array();
foreach ($vars as $k => $v) {
$replaces[$delim . $k . $delim] = $v;
}
return str_replace(array_keys($replaces), array_values($replaces), $str);
}
function mb_substr_replace($string, $replacement, $start, $length) {
return mb_substr($string, 0, $start) . $replacement . mb_substr($string, $start + $length);
}
function format_timestamp($timestamp) {
switch(TRUE) {
case ($timestamp < 60):
return $timestamp . ' ' . ngettext('second', 'seconds', $timestamp);
case ($timestamp < 3600): //60*60 = 3600
return ($num = round($timestamp / 60)) . ' ' . ngettext('minute', 'minutes', $num);
case ($timestamp < 86400): //60*60*24 = 86400
return ($num = round($timestamp / 3600)) . ' ' . ngettext('hour', 'hours', $num);
case ($timestamp < 604800): //60*60*24*7 = 604800
return ($num = round($timestamp / 86400)) . ' ' . ngettext('day', 'days', $num);
case ($timestamp < 31536000): //60*60*24*365 = 31536000
return ($num = round($timestamp / 604800)) . ' ' . ngettext('week', 'weeks', $num);
default:
return ($num = round($timestamp / 31536000)) . ' ' . ngettext('year', 'years', $num);
}
}
function until($timestamp) {
$difference = $timestamp - time();
return format_timestamp($difference);
}
function ago($timestamp) {
$difference = time() - $timestamp;
return format_timestamp($difference);
}