916 lines
26 KiB
PHP
916 lines
26 KiB
PHP
<?php
|
|
require 'config/application.php';
|
|
|
|
$session = null;
|
|
if (isset($_SERVER['REQUEST_URI'])) {
|
|
if (!isset($_COOKIE['_session']) || !file_exists('/tmp/apcontrol-sessions')) {
|
|
if (substr($_SERVER['REQUEST_URI'],0,5) === '/api/') {
|
|
http_response_code(403); die('<h3>403, API Forbidden</h3>');
|
|
}
|
|
header('Location: login.php'); die;
|
|
}
|
|
$session = trim($_COOKIE['_session']);
|
|
$ps_sess = explode('.', $session);
|
|
$sessions = explode("\n", trim(file_get_contents('/tmp/apcontrol-sessions')));
|
|
if (!in_array($session, $sessions) || !isset($GLOBALS['appconf']['users'][$ps_sess[0]])) {
|
|
if (substr($_SERVER['REQUEST_URI'],0,5) === '/api/') {
|
|
http_response_code(403); die('<h3>403, API Forbidden</h3>');
|
|
}
|
|
header('Location: login.php'); die;
|
|
}
|
|
unset($sessions);
|
|
unset($ps_sess);
|
|
}
|
|
|
|
function __session_delete($user) {
|
|
if (!file_exists('/tmp/apcontrol-sessions'))
|
|
return false;
|
|
$newsessions = [];
|
|
$sessions = explode("\n", trim(file_get_contents('/tmp/apcontrol-sessions')));
|
|
foreach ($sessions as $session) {
|
|
$ps = explode('.', $session);
|
|
if ($ps[0] !== $user)
|
|
$newsessions []= $session;
|
|
}
|
|
file_put_contents('/tmp/apcontrol-sessions', implode("\n", $newsessions));
|
|
return true;
|
|
}
|
|
|
|
if (!file_exists($GLOBALS['appconf']['data_dir']))
|
|
mkdir($GLOBALS['appconf']['data_dir']);
|
|
|
|
|
|
// global variables
|
|
$GLOBALS['_cache'] = [];
|
|
$GLOBALS['supported_ap_software'] = [
|
|
'mastodon',
|
|
];
|
|
|
|
// functions
|
|
function apiresult($data, $code=200) {
|
|
if (isset($GLOBALS['IS_PHP']) && $GLOBALS['IS_PHP']) {
|
|
$GLOBALS['api_data'] = $data;
|
|
return false;
|
|
}
|
|
if (isset($_SERVER['REQUEST_URI'])) {
|
|
if ($code !== 200) http_response_code($code);
|
|
header('Content-Type: application/json');
|
|
}
|
|
|
|
if (isset($GLOBALS['http_cache_config'])
|
|
&& gettype($data) !== 'string'
|
|
&& !isset($data['error'])
|
|
&& !isset($data['ok']))
|
|
content_cache__put($GLOBALS['http_cache_config']['key'],
|
|
'ondemand,'.$GLOBALS['http_cache_config']['seconds'], $data);
|
|
|
|
if (gettype($data) !== 'string')
|
|
echo json_encode($data);
|
|
else echo $data;
|
|
die;
|
|
}
|
|
|
|
function no_http() {
|
|
if (isset($_SERVER['REQUEST_URI']))
|
|
die('<h3>Forbidden, this method can only be called from shell</h3>');
|
|
}
|
|
|
|
function arg($arg, $default=null, $validator=null) {
|
|
$arg = trim($arg);
|
|
$type = null;
|
|
|
|
$require = false;
|
|
if (strpos($arg,'*') === 0) {
|
|
$require = true;
|
|
$arg = substr($arg, 1);
|
|
}
|
|
|
|
if (strpos($arg,'s:') === 0) {
|
|
$type = 'string';
|
|
$arg = substr($arg, 2);
|
|
} else if (strpos($arg,'i:') === 0) {
|
|
$type = 'integer';
|
|
$arg = substr($arg, 2);
|
|
} else if (strpos($arg,'d:') === 0) {
|
|
$type = 'double';
|
|
$arg = substr($arg, 2);
|
|
} else if (strpos($arg, 'b:') === 0) {
|
|
$type = 'boolean';
|
|
$arg = substr($arg, 2);
|
|
} else if (strpos($arg,'b64:') === 0) {
|
|
$type = 'base64';
|
|
$arg = substr($arg, 4);
|
|
} else if (strpos($arg,'set:') === 0) {
|
|
$type = 'exists';
|
|
$arg = substr($arg, 4);
|
|
}
|
|
|
|
$ps = explode('.', $arg);
|
|
$value = null;
|
|
$arr = null;
|
|
switch (strtolower($ps[0])) {
|
|
case 'get':
|
|
$arr = '_GET';
|
|
break;
|
|
case 'post':
|
|
$arr = '_POST';
|
|
break;
|
|
}
|
|
if ($arr === null)
|
|
$arr = $ps[0];
|
|
eval('$value = isset($'.$arr.'["'.$ps[1].'"]) ? $'.$arr.'["'.$ps[1].'"] : null;');
|
|
|
|
if ($type === 'exists')
|
|
return $value !== null;
|
|
if ($value === null)
|
|
return !$require ? $default : apiresult(['error' => '<'.$arg.'> parameter must be specified'], 400);
|
|
|
|
if (gettype($value) === 'string')
|
|
$value = trim($value);
|
|
if ($type === 'string')
|
|
$value = strval($value);
|
|
else if ($type === 'integer')
|
|
$value = intval($value);
|
|
else if ($type === 'double')
|
|
$value = doubleval($value);
|
|
else if ($type === 'boolean')
|
|
$value = in_array($value,[1,"1","yes","true"]) ? true : false;
|
|
else if ($type === 'base64')
|
|
$value = base64_decode($value);
|
|
|
|
if ($validator !== null) {
|
|
$bool = false;
|
|
eval('$bool = '.$validator.'($value);');
|
|
if (!$bool)
|
|
return apiresult(['error' => '<'.$arg.'> parameter validation has failed'], 400);
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
function mod_php($module_name=null) {
|
|
$null_mod = false;
|
|
if ($module_name === null) {
|
|
$null_mod = true;
|
|
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
|
|
$fl = preg_replace('#^'.__DIR__.'#', '', $trace[0]['file']);
|
|
$fl = trim($fl, '/');
|
|
$module_name = substr($fl, 0, strrpos($fl,'/'));
|
|
}
|
|
|
|
$is_php_set = isset($GLOBALS['IS_PHP']) && $GLOBALS['IS_PHP'];
|
|
if (!$null_mod) {
|
|
$GLOBALS['IS_PHP'] = true;
|
|
unset($GLOBALS['api_data']);
|
|
}
|
|
if (!file_exists($module_name.'/mod.php'))
|
|
return 'does_not_exist';
|
|
require $module_name.'/mod.php';
|
|
if (!$null_mod) {
|
|
if (!$is_php_set)
|
|
unset($GLOBALS['IS_PHP']);
|
|
return $GLOBALS['api_data'];
|
|
}
|
|
}
|
|
|
|
function filedb_exists($ns, $file=''){ return file_exists(filedb_getdir($ns,$file)); }
|
|
function filedb_getdir($ns, $file='') {
|
|
$dir = $GLOBALS['appconf']['data_dir'];
|
|
$ns = trim($ns, '/');
|
|
$_ = realpath($dir.'/'.$ns);
|
|
if ($_ === false) {
|
|
$_ = filedb_put($ns);
|
|
if ($_ === false)
|
|
return false;
|
|
$_ = realpath($dir.'/'.$ns);
|
|
}
|
|
$dir = $_;
|
|
if (strpos($dir, $GLOBALS['appconf']['data_dir']) === false)
|
|
return false;
|
|
return $dir.'/'.$file;
|
|
}
|
|
|
|
function filedb_put($ns, $file='', $content=null) {
|
|
$dir = $GLOBALS['appconf']['data_dir'];
|
|
$ns = trim($ns, '/');
|
|
foreach (explode('/', $ns) as $d) {
|
|
$dir .= '/'.$d;
|
|
if (!file_exists($dir))
|
|
mkdir($dir);
|
|
}
|
|
$dir = realpath($dir);
|
|
if (strpos($dir, $GLOBALS['appconf']['data_dir']) === false)
|
|
return false;
|
|
if ($file === '')
|
|
return true;
|
|
$content_file = $dir.'/'.$file;
|
|
if ($content === null)
|
|
return touch($content_file);
|
|
return file_put_contents($content_file, $content);
|
|
}
|
|
|
|
function filedb_del($ns, $file='') {
|
|
$file = filedb_getdir($ns, $file);
|
|
if ($file === false || !file_exists($file))
|
|
return false;
|
|
if (is_dir($file))
|
|
return rmdir($file);
|
|
return unlink($file);
|
|
}
|
|
|
|
function filedb_delall($ns) {
|
|
if (!filedb_exists($ns))
|
|
return false;
|
|
foreach (scandir(filedb_getdir($ns)) as $fil) {
|
|
if (in_array($fil, ['.','..']))
|
|
continue;
|
|
filedb_del($ns, $fil);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function filedb_get($ns, $file) {
|
|
$file = filedb_getdir($ns, $file);
|
|
if ($file === false)
|
|
return false;
|
|
if (!file_exists($file))
|
|
return null;
|
|
return file_get_contents($file);
|
|
}
|
|
|
|
function instance_config($software=null, $instance=null) {
|
|
if ($software === null) {
|
|
foreach ($GLOBALS['supported_ap_software'] as $sf) {
|
|
$result = instance_config($sf);
|
|
if ($result !== null)
|
|
return $result;
|
|
}
|
|
return null;
|
|
}
|
|
if (!in_array($software, $GLOBALS['supported_ap_software']))
|
|
return null;
|
|
|
|
if ($instance === null && isset($_GET['instance']))
|
|
$instance = trim($_GET['instance']);
|
|
|
|
if ($instance === null)
|
|
return apiresult(['error' => '<instance> GET argument must be provided'], 400);
|
|
|
|
$GLOBALS['ap_instance'] = $instance;
|
|
$GLOBALS['ap_software'] = $software;
|
|
if (isset($GLOBALS['_cache'][$software.$instance]))
|
|
return $GLOBALS['_cache'][$software.$instance];
|
|
|
|
mod_php('api/v1/config/get');
|
|
if (!isset($GLOBALS['api_data']))
|
|
return null;
|
|
|
|
|
|
$found = false;
|
|
foreach ($GLOBALS['api_data']['hosts'][$software] as $ins_cfg) {
|
|
if ($ins_cfg['instance'] === $instance) {
|
|
$found = $ins_cfg;
|
|
}
|
|
}
|
|
if ($found === false)
|
|
return null;
|
|
|
|
$config = [];
|
|
$config_raw = explode("\n", trim($found['config']));
|
|
foreach ($config_raw as $ln) {
|
|
$k = substr($ln, 0, strpos($ln,'='));
|
|
$v = substr($ln, strpos($ln,'=')+1);
|
|
$config[$k] = $v;
|
|
}
|
|
|
|
$GLOBALS['_cache'][$software.$instance] = $config;
|
|
return $config;
|
|
}
|
|
|
|
function instance_config_require($software, $instance=null) {
|
|
$config = instance_config($software, $instance);
|
|
if ($config === null)
|
|
apiresult(['error' => 'instance parameter is incorrect. Does not exist'], 400);
|
|
return $config;
|
|
}
|
|
|
|
function instance_http_get($url) { return instance_http_request($url, 'GET'); }
|
|
function instance_http_post($url, $data=null) { return instance_http_request($url, 'POST', $data); }
|
|
function instance_http_delete($url) { return instance_http_request($url, 'DELETE'); }
|
|
function instance_http_request($url, $method, $data=null) {
|
|
$software = null;
|
|
if (isset($_SERVER['REQUEST_URI'])) {
|
|
$huri = substr(trim($_SERVER['REQUEST_URI']), 7);
|
|
$ps = explode('/', trim($huri, '/'));
|
|
if ($ps[0] !== 'http' || !in_array($ps[1], $GLOBALS['supported_ap_software']))
|
|
return apiresult(['error' => 'this method can only be called from api/v1/http/<software> URIs'], 500);
|
|
$software = $ps[1];
|
|
}
|
|
|
|
if ($software === null && isset($GLOBALS['ap_software']))
|
|
$software = $GLOBALS['ap_software'];
|
|
|
|
$config = instance_config($software);
|
|
$url = $config['instance_url'].$url;
|
|
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $url);
|
|
if ($method === 'POST' || $method === 'DELETE') {
|
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
|
if ($data !== null)
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
|
}
|
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
|
|
if (isset($config['api_authkey'])) {
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'Authorization: Bearer '.$config['api_authkey'],
|
|
]);
|
|
}
|
|
|
|
$output = curl_exec($ch);
|
|
curl_close($ch);
|
|
return $output;
|
|
}
|
|
|
|
function valid_mastodon_account_id($id) {
|
|
return preg_match('/^\d+$/', strval($id));
|
|
}
|
|
|
|
function valid_for_path($str) {
|
|
return strpos($str, '/') === false;
|
|
}
|
|
|
|
function valid_uuid($str) {
|
|
return preg_match('/^[a-z0-9\-]+$/', trim($str));
|
|
}
|
|
|
|
// source: https://stackoverflow.com/questions/1755144/how-to-validate-domain-name-in-php
|
|
function valid_domain_name($domain_name)
|
|
{
|
|
return (preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $domain_name)
|
|
&& preg_match("/^.{1,253}$/", $domain_name)
|
|
&& preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name));
|
|
}
|
|
|
|
function remove_accents($string) {
|
|
if ( !preg_match('/[\x80-\xff]/', $string) )
|
|
return $string;
|
|
|
|
$chars = array(
|
|
// Decompositions for Latin-1 Supplement
|
|
chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
|
|
chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
|
|
chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
|
|
chr(195).chr(135) => 'C', chr(195).chr(136) => 'E',
|
|
chr(195).chr(137) => 'E', chr(195).chr(138) => 'E',
|
|
chr(195).chr(139) => 'E', chr(195).chr(140) => 'I',
|
|
chr(195).chr(141) => 'I', chr(195).chr(142) => 'I',
|
|
chr(195).chr(143) => 'I', chr(195).chr(145) => 'N',
|
|
chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
|
|
chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
|
|
chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
|
|
chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
|
|
chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
|
|
chr(195).chr(159) => 's', chr(195).chr(160) => 'a',
|
|
chr(195).chr(161) => 'a', chr(195).chr(162) => 'a',
|
|
chr(195).chr(163) => 'a', chr(195).chr(164) => 'a',
|
|
chr(195).chr(165) => 'a', chr(195).chr(167) => 'c',
|
|
chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
|
|
chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
|
|
chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
|
|
chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
|
|
chr(195).chr(177) => 'n', chr(195).chr(178) => 'o',
|
|
chr(195).chr(179) => 'o', chr(195).chr(180) => 'o',
|
|
chr(195).chr(181) => 'o', chr(195).chr(182) => 'o',
|
|
chr(195).chr(182) => 'o', chr(195).chr(185) => 'u',
|
|
chr(195).chr(186) => 'u', chr(195).chr(187) => 'u',
|
|
chr(195).chr(188) => 'u', chr(195).chr(189) => 'y',
|
|
chr(195).chr(191) => 'y',
|
|
// Decompositions for Latin Extended-A
|
|
chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
|
|
chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
|
|
chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
|
|
chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
|
|
chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
|
|
chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
|
|
chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
|
|
chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
|
|
chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
|
|
chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
|
|
chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
|
|
chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
|
|
chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
|
|
chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
|
|
chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
|
|
chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
|
|
chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
|
|
chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
|
|
chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
|
|
chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
|
|
chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
|
|
chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
|
|
chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
|
|
chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
|
|
chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
|
|
chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
|
|
chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
|
|
chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
|
|
chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
|
|
chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
|
|
chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
|
|
chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
|
|
chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
|
|
chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
|
|
chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
|
|
chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
|
|
chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
|
|
chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
|
|
chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
|
|
chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
|
|
chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
|
|
chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
|
|
chr(197).chr(148) => 'R',chr(197).chr(149) => 'r',
|
|
chr(197).chr(150) => 'R',chr(197).chr(151) => 'r',
|
|
chr(197).chr(152) => 'R',chr(197).chr(153) => 'r',
|
|
chr(197).chr(154) => 'S',chr(197).chr(155) => 's',
|
|
chr(197).chr(156) => 'S',chr(197).chr(157) => 's',
|
|
chr(197).chr(158) => 'S',chr(197).chr(159) => 's',
|
|
chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
|
|
chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
|
|
chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
|
|
chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
|
|
chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
|
|
chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
|
|
chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
|
|
chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
|
|
chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
|
|
chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
|
|
chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
|
|
chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
|
|
chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
|
|
chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
|
|
chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
|
|
chr(197).chr(190) => 'z', chr(197).chr(191) => 's'
|
|
);
|
|
|
|
$string = strtr($string, $chars);
|
|
|
|
return $string;
|
|
}
|
|
|
|
if (!function_exists('str_starts_with')) {
|
|
function str_starts_with($haystack, $needle) {
|
|
return (string)$needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0;
|
|
}
|
|
}
|
|
|
|
function set_chat_at($str, $i, $c) {
|
|
return substr($str, 0, $i).$c.substr($str, $i+1);
|
|
}
|
|
|
|
function normalize_word_sound($word, $cback=null) {
|
|
$hashes = [
|
|
'4:a', '3:e', '1:i', '0:o',
|
|
'5:s', 'b:v', '8:b', 'k:c',
|
|
'y:i', 'que:kee', 'q:k',
|
|
];
|
|
foreach ($hashes as $hash) {
|
|
$hash = explode(':', $hash);
|
|
if ($cback !== null)
|
|
$word = $cback($word, $hash);
|
|
else $word = str_replace($hash[0], $hash[1], $word);
|
|
}
|
|
return $word;
|
|
}
|
|
|
|
function normalize_for_search($str, $wsound=true) {
|
|
if (trim($str) === '') return '';
|
|
$str = trim(strip_tags(str_replace('>', '> ', $str)));
|
|
$str = strtolower(remove_accents($str));
|
|
$str = preg_replace('/[^a-z0-9]+/', ' ', $str);
|
|
$str = preg_replace('/\s+/', ' ', $str);
|
|
$words = explode(' ', trim($str));
|
|
$newwords = [];
|
|
foreach ($words as $word) {
|
|
if (trim($word) === '')
|
|
continue;
|
|
if ($wsound)
|
|
$word = normalize_word_sound($word);
|
|
$nword = '';
|
|
if (strlen($word) <= 3) {
|
|
$newwords [] = $word;
|
|
continue;
|
|
}
|
|
for ($i = 0; $i < strlen($word); $i++) {
|
|
if ($i === 0) {
|
|
$nword .= $word[0];
|
|
continue;
|
|
}
|
|
if ($word[$i] !== $word[$i-1]) {
|
|
$nword .= $word[$i];
|
|
}
|
|
}
|
|
$newwords [] = $nword;
|
|
}
|
|
return trim(implode(' ', $newwords));
|
|
}
|
|
|
|
function parse_comparing_expression($expr) {
|
|
$expr = preg_replace('/\n/', ' ', $expr);
|
|
$expr = preg_replace('/\s+/', ' ', $expr);
|
|
$p_expr = $expr;
|
|
$quot = false;
|
|
for ($i = 0; $i < strlen($p_expr); $i++) {
|
|
if ($p_expr[$i] === '"') {
|
|
$quot = !$quot;
|
|
continue;
|
|
}
|
|
if ($quot) {
|
|
if ($p_expr[$i] === '(')
|
|
$p_expr = set_chat_at($p_expr, $i, 'º');
|
|
else if ($p_expr[$i] === ')')
|
|
$p_expr = set_chat_at($p_expr, $i, 'ª');
|
|
}
|
|
}
|
|
$p_expr = preg_replace('/\(|\)/', ' ', $p_expr);
|
|
$p_expr = preg_replace('/\s+/', ' ', $p_expr);
|
|
$p_expr = preg_split('/OR|AND(\sNOT)?/', $p_expr);
|
|
$p_expr = str_replace('º', '(', $p_expr);
|
|
$p_expr = str_replace('ª', ')', $p_expr);
|
|
foreach ($p_expr as &$e) $e = trim($e);
|
|
return [
|
|
'original' => $expr,
|
|
'parsed' => $p_expr,
|
|
];
|
|
}
|
|
|
|
function matches_comparing_expression($expr, $o_text, $debug=false) {
|
|
if (gettype($expr) === 'string')
|
|
$expr = parse_comparing_expression($expr);
|
|
$result = $expr['original'];
|
|
if ($debug) {
|
|
echo '<h2>Original</h2>';
|
|
echo '<pre>'.$o_text.'</pre>';
|
|
}
|
|
$text = normalize_for_search($o_text);
|
|
$i_text = null;
|
|
if (strpos($expr['original'], '=') !== false)
|
|
$i_text = normalize_for_search($o_text, false);
|
|
if ($debug) {
|
|
echo '<h2>Normalized</h2>';
|
|
echo '<pre>'.$text.'</pre>';
|
|
echo '<br><br>';
|
|
}
|
|
|
|
$m_text_words = explode(' ', $text);
|
|
$i_text_words = null;
|
|
if ($i_text !== null)
|
|
$i_text_words = explode(' ', $i_text);
|
|
|
|
$o_text_words = null;
|
|
if (preg_match('/(^|\s)\*\w+/', $expr['original']))
|
|
{
|
|
$nw_text_words = [];
|
|
$o_text_words = explode(' ', $o_text);
|
|
foreach ($o_text_words as $o)
|
|
if ($o !== '')
|
|
$nw_text_words []= $o;
|
|
$o_text_words = $nw_text_words;
|
|
}
|
|
|
|
foreach ($expr['parsed'] as $t)
|
|
{
|
|
$bool = false;
|
|
$t = trim($t);
|
|
$selector = substr($t, 0, strpos($t, ' '));
|
|
$content = substr($t, strpos($t, ' ')+1);
|
|
$content = trim(trim(trim($content), '"'));
|
|
$o_bool = ($selector[0] === '*' && $o_text_words !== null);
|
|
$i_bool = ($selector[0] === '=' && $i_text_words !== null);
|
|
if ($o_bool || $i_bool)
|
|
$selector = substr($selector, 1);
|
|
|
|
switch ($selector)
|
|
{
|
|
case 'words':
|
|
case 'hasall':
|
|
$ws = explode(' ', $content);
|
|
$hasall = true;
|
|
foreach ($ws as $w)
|
|
{
|
|
if ($o_bool) {
|
|
if (!in_array($w, $o_text_words)) {
|
|
$hasall = false;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
if ($i_bool) {
|
|
if (!in_array(normalize_for_search($w, false), $i_text_words)) {
|
|
$hasall = false;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
if (!in_array(normalize_for_search($w), $m_text_words)) {
|
|
$hasall = false;
|
|
break;
|
|
}
|
|
}
|
|
$bool = $hasall;
|
|
if ($debug) {
|
|
echo '<br><br>';
|
|
echo '<h3>words</h3>';
|
|
echo '<pre>';
|
|
echo $content;
|
|
echo '</pre>';
|
|
echo '<br>Result: ';
|
|
var_dump($bool);
|
|
echo '<br>';
|
|
}
|
|
break;
|
|
|
|
case 'anyword':
|
|
case 'hasany':
|
|
$ws = explode(' ', $content);
|
|
$hasany = false;
|
|
foreach ($ws as $w)
|
|
{
|
|
if ($o_bool) {
|
|
if (in_array($w, $o_text_words)) {
|
|
$hasany = true;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
if ($i_bool) {
|
|
if (in_array(normalize_for_search($w, false), $i_text_words)) {
|
|
$hasany = true;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
if (in_array(normalize_for_search($w), $m_text_words)) {
|
|
$hasany = true;
|
|
break;
|
|
}
|
|
}
|
|
$bool = $hasany;
|
|
if ($debug) {
|
|
echo '<br><br>';
|
|
echo '<h3>hasany</h3>';
|
|
echo '<pre>';
|
|
echo $content;
|
|
echo '</pre>';
|
|
echo '<br>Result: ';
|
|
var_dump($bool);
|
|
echo '<br>';
|
|
}
|
|
break;
|
|
|
|
case 'has':
|
|
case 'includes':
|
|
case 'contains':
|
|
$a = null; $b = null;
|
|
if ($o_bool) {
|
|
$a = str_replace(' ', '', $o_text);
|
|
$b = str_replace(' ', '', $content);
|
|
} else if ($i_bool) {
|
|
$a = str_replace(' ', '', $i_text);
|
|
$b = str_replace(' ', '', normalize_for_search($content, false));
|
|
} else {
|
|
$a = str_replace(' ', '', $text);
|
|
$b = str_replace(' ', '', normalize_for_search($content));
|
|
}
|
|
if ($debug) {
|
|
echo '<br><br>';
|
|
echo '<h3>contains</h3>';
|
|
echo '<pre>';
|
|
echo $content;
|
|
echo '</pre>';
|
|
}
|
|
$bool = strpos($a, $b) !== false;
|
|
if ($debug) {
|
|
echo '<br>Result: ';
|
|
var_dump($bool);
|
|
echo '<br>';
|
|
}
|
|
unset($a); unset($b);
|
|
break;
|
|
|
|
case 'regex':
|
|
case 'matches':
|
|
if (strlen($content) === 0)
|
|
continue 2;
|
|
if (!$o_bool)
|
|
{
|
|
$content = normalize_word_sound($content, function($w, $h) {
|
|
if ($h[0] === 'b')
|
|
return preg_replace('/([^\\\])b/', '\1'.$h[1], $w);
|
|
if (preg_match('/^\d$/', $h[0])) {
|
|
$b = false;
|
|
for ($i = 0; $i < strlen($w); $i++) {
|
|
if (in_array($w[$i], ['{','}','[',']'])) {
|
|
$b = !$b;
|
|
continue;
|
|
}
|
|
if (!$b && $w[$i] === $h[0])
|
|
$w = set_chat_at($w, $i, $h[1]);
|
|
}
|
|
return $w;
|
|
}
|
|
return str_replace($h[0], $h[1], $w);
|
|
});
|
|
preg_match_all('/\w{4,}/', $content, $mts);
|
|
foreach ((isset($mts[0]) ? $mts[0] : []) as $m)
|
|
$content = preg_replace('/\b'.$m.'\b/', normalize_for_search($m, !$i_bool), $content);
|
|
}
|
|
if ($content[0] != '^')
|
|
$content = '^.*'.$content;
|
|
if ($content[strlen($content)-1] != '$')
|
|
$content .= '.*$';
|
|
$content = str_replace('#', '\#', $content);
|
|
if ($debug) {
|
|
echo '<br><br>';
|
|
echo '<h3>regex</h3>';
|
|
echo '<pre>';
|
|
echo $content;
|
|
echo '</pre>';
|
|
}
|
|
$bool = false;
|
|
if ($o_bool)
|
|
$bool = preg_match('#'.$content.'#i', $o_text) === 1;
|
|
else $bool = preg_match('#'.$content.'#', $text) === 1;
|
|
if ($debug) {
|
|
echo '<br>Result: ';
|
|
var_dump($bool);
|
|
echo '<br>';
|
|
}
|
|
break;
|
|
case 'len':
|
|
case 'length':
|
|
case 'size':
|
|
$bool = false;
|
|
if (preg_match('/^(\<|\>)\=?\s*\d+$/', $content))
|
|
eval('$bool = strlen($o_text) '.$content.';');
|
|
break;
|
|
|
|
}
|
|
$result = str_replace($t, $bool ? 'true' : 'false', $result);
|
|
}
|
|
$result = str_replace('OR', '||', $result);
|
|
$result = str_replace('AND', '&&', $result);
|
|
$result = str_replace('NOT ', '!', $result);
|
|
$result = str_replace('NOT', '!', $result);
|
|
try {
|
|
if ($debug) {
|
|
echo '<br><hr>';
|
|
echo '<h3>Final result</h3>';
|
|
echo '<br>eval: ';
|
|
var_dump($result);
|
|
}
|
|
eval('$result = '.$result.';');
|
|
if ($debug) {
|
|
echo '<br>';
|
|
echo '<br>result: ';
|
|
var_dump($result);
|
|
die;
|
|
}
|
|
} catch (\Throwable $e) {
|
|
return apiresult(['error' => 'Query contains errors, please correct it and try again.']);
|
|
}
|
|
return in_array($result, [0,false]) ? false : true;
|
|
}
|
|
|
|
function http_cache($seconds, $data=null) {
|
|
if ($data === null)
|
|
$data = $_GET;
|
|
$cache_key = sha1('http_cache'.json_encode($data));
|
|
$cache = content_cache__get($cache_key);
|
|
if ($cache !== null)
|
|
return apiresult($cache);
|
|
$GLOBALS['http_cache_config'] = [
|
|
'seconds' => $seconds,
|
|
'key' => $cache_key
|
|
];
|
|
}
|
|
|
|
function _cachekey_construct(...$keys) {
|
|
$nkeys = [];
|
|
foreach ($keys as $key)
|
|
$nkeys []= _cachekey($key);
|
|
return implode(',', $nkeys);
|
|
}
|
|
|
|
function _cachekey($str) {
|
|
return preg_replace('/[^a-z0-9\_]+/', '', strtolower($str));
|
|
}
|
|
|
|
function content_cache__exists($key) {
|
|
$cache_dir = $GLOBALS['appconf']['data_dir'].'/cache';
|
|
if (!file_exists($cache_dir)) {
|
|
mkdir($cache_dir);
|
|
return false;
|
|
}
|
|
$key = md5($key);
|
|
$cache_file = null;
|
|
foreach (scandir($cache_dir) as $fil) {
|
|
if (strpos($fil, $key) !== false) {
|
|
$cache_file = $cache_dir.'/'.$fil;
|
|
$ps = explode(',',$fil);
|
|
array_splice($ps, 0, 1);
|
|
if ($ps[0] !== 'always' && count($ps) > 1)
|
|
$ps[1] = intval($ps[1]);
|
|
return (object)[
|
|
'file' => $cache_file,
|
|
'config' => $ps
|
|
];
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function content_cache__get($key) {
|
|
$cfg = content_cache__exists($key);
|
|
if ($cfg === false)
|
|
return null;
|
|
$ms = time() - filemtime($cfg->file);
|
|
if ($cfg->config[0] === 'ondemand' &&
|
|
$ms > $cfg->config[1]) {
|
|
unlink($cfg->file);
|
|
return null;
|
|
}
|
|
$fname = basename($cfg->file);
|
|
$ps = explode(',', $fname);
|
|
if (count($ps) === 2 && $ps[1] === 'always') {
|
|
$dir = filedb_getdir('cron/db');
|
|
foreach (scandir($dir) as $fl) {
|
|
if (in_array($fl,['.','..']))
|
|
continue;
|
|
if (strpos($fl, ','.$ps[0].',') !== false) {
|
|
$data = @unserialize(filedb_get('cron/db',$fl));
|
|
if ($data === false) break;
|
|
$data['last_accessed'] = time();
|
|
file_put_contents($dir.'/'.$fl, serialize($data));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return unserialize(file_get_contents($cfg->file));
|
|
}
|
|
|
|
function content_cache__put($key, $config, $data) {
|
|
$cache_dir = $GLOBALS['appconf']['data_dir'].'/cache';
|
|
if (!file_exists($cache_dir))
|
|
mkdir($cache_dir);
|
|
$ps = explode(',', trim($config));
|
|
$cache_file = $cache_dir.'/'.md5($key);
|
|
if ($ps[0] === 'always')
|
|
$cache_file .= ','.$ps[0];
|
|
else if ($ps[0] === 'ondemand')
|
|
$cache_file .= ','.$config;
|
|
return file_put_contents($cache_file, serialize($data));
|
|
}
|
|
|
|
function cronjob_db_create__check_cachecfg($software, $instance, $sql, $cache=null) {
|
|
if ($cache === null)
|
|
return false;
|
|
$ps = explode(',',trim($cache));
|
|
if ($ps[0] !== 'always')
|
|
return false;
|
|
return cronjob_db_create($software, $instance, $sql, intval($ps[1]));
|
|
}
|
|
|
|
function cronjob_db_create($software, $instance, $sql, $time=3600) {
|
|
$job_key = md5($software.$instance.$sql);
|
|
$dir_crons = $GLOBALS['appconf']['data_dir'].'/cron';
|
|
if (!file_exists($dir_crons))
|
|
mkdir($dir_crons);
|
|
$dir_crons_db = $dir_crons.'/db';
|
|
if (!file_exists($dir_crons_db))
|
|
mkdir($dir_crons_db);
|
|
|
|
$cron_file = $software.','.$instance.','.$job_key;
|
|
$result_file = $dir_crons_db.'/'.$cron_file.','.$time;
|
|
if (!file_exists($result_file)) {
|
|
file_put_contents($result_file, serialize(
|
|
['last_accessed' => time(), 'sql' => $sql]));
|
|
touch($result_file, 1000);
|
|
}
|
|
|
|
$result_fname = basename($result_file);
|
|
foreach (scandir($dir_crons_db) as $fl) {
|
|
if (in_array($fl,['.','..']))
|
|
continue;
|
|
if (strpos($fl, $cron_file) !== false && $fl !== $result_fname)
|
|
unlink($dir_crons_db.'/'.$fl);
|
|
}
|
|
return $cron_file;
|
|
}
|
|
|
|
// classes
|
|
require 'classes/PgDatabase.php';
|
|
require 'classes/RedisDatabase.php';
|