soselo/base.php

497 lines
16 KiB
PHP
Raw Normal View History

2022-11-21 22:56:44 +00:00
<?php
require 'config/application.php';
2022-11-21 23:57:29 +00:00
$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);
2022-11-21 23:57:29 +00:00
}
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;
}
2022-12-03 16:03:23 +00:00
if (isset($_SERVER['REQUEST_URI'])) {
if ($code !== 200) http_response_code($code);
header('Content-Type: application/json');
}
if (gettype($data) !== 'string')
echo json_encode($data);
else echo $data;
die;
}
2022-11-24 22:41:05 +00:00
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 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 normalize_for_search($str) {
if (trim($str) === '') return '';
$str = preg_replace('/\s\s+/', ' ', trim($str));
$words = explode(' ', $str);
$newwords = [];
foreach ($words as $word) {
$word = remove_accents($word);
$word = strtolower($word);
$word = preg_replace('/[^a-z0-9]+/', '', $word);
if (trim($word) === '')
continue;
$word = str_replace('4', 'a', $word);
$word = str_replace('3', 'e', $word);
$word = str_replace('1', 'i', $word);
$word = str_replace('0', 'o', $word);
$word = str_replace('5', 's', $word);
$word = str_replace('8', 'b', $word);
$nword = '';
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) {
$lvl = 0;
$str_levels = [];
for ($i = 0; $i < strlen($expr); $i++) {
$s = $expr[$i];
if ($s === '(') {
$lvl++; continue;
} else if ($s === ')') {
$lvl--; continue;
}
if (!isset($str_levels[$lvl]))
$str_levels[$lvl] = '';
$str_levels[$lvl] .= $s;
}
foreach ($str_levels as $key => $val) {
$val = trim($val);
if (strpos($val, 'OR') === 0)
$val = trim(substr($val, 2));
else if (strpos($val, 'AND') === 0)
$val = trim(substr($val, 3));
$val = str_replace('OR', '[[<SEP>]]', $val);
$val = str_replace('AND', '[[<SEP>]]', $val);
$val = explode('[[<SEP>]]', $val);
foreach ($val as &$v)
$v = trim($v);
$str_levels[$key] = $val;
}
return [
'original' => $expr,
'parsed' => $str_levels
];
}
function matches_comparing_expression($expr, $text) {
if (gettype($expr) === 'string')
$expr = parse_comparing_expression($expr);
$result = $expr['original'];
foreach ($expr['parsed'] as $lvl => $exl) {
foreach ($exl as &$t) {
if (strpos($t, 'words ') === 0) {
$w = substr($t, strlen('words')+1);
$ws = explode(' ', trim(trim($w, '"')));
$haveall = true;
foreach ($ws as $w) {
if (strpos($text, $w) === false) {
$haveall = false;
break;
}
}
$result = str_replace($t, $haveall ? 'true' : 'false', $result);
} else if (strpos($t, 'contains ') === 0) {
$w = substr($t, strlen('contains')+1);
$w = trim(trim($w, '"'));
$contains = strpos($text, $w) !== false;
$result = str_replace($t, $contains ? 'true' : 'false', $result);
}
}
}
$result = str_replace('OR', '|', $result);
$result = str_replace('AND', '&', $result);
eval('$result = '.$result.';');
return in_array($result, [0,false]) ? false : true;
}
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);
2022-12-03 16:05:38 +00:00
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;
}
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;
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;
foreach (scandir($dir_crons_db) as $fl) {
if (strpos($fl, $cron_file) !== false)
unlink($dir_crons_db.'/'.$fl);
}
$touch_1970 = false;
$result_file = $dir_crons_db.'/'.$cron_file.','.$time;
if (!file_exists($result_file))
$touch_1970 = true;
file_put_contents($result_file, $sql);
if ($touch_1970) touch($result_file, 1000);
return $cron_file;
}
2022-11-24 22:41:05 +00:00
// classes
require 'classes/PgDatabase.php';