403, API Forbidden'); } header('Location: login.php'); die; } $session = trim($_COOKIE['_session']); $sessions = explode("\n", trim(file_get_contents('/tmp/apcontrol-sessions'))); if (!in_array($session, $sessions)) { if (substr($_SERVER['REQUEST_URI'],0,5) === '/api/') { http_response_code(403); die('

403, API Forbidden

'); } header('Location: login.php'); die; } unset($sessions); } 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 (gettype($data) !== 'string') echo json_encode($data); else echo $data; die; } 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' => ' 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/ 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_search_expression($q) { var_dump($q); die; } 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; } 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); } file_put_contents($dir_crons_db.'/'.$cron_file.','.$time, $sql); return $cron_file; } // classes require 'classes/PgDatabase.php';