.
*/
require 'lib/validhostname.php';
require 'lib/ckratelimit.php';
require 'lib/ght.php';
const SNAME='acck';
const SVERS='0.1.6';
const SREPO='https://git.lattuga.net/jones/acck';
const MAXACCLEN=30+253+2;
const MAXHOSTLEN=253;
const INIFP='sec/conf.ini';
$usname=ucfirst(SNAME);
$timeout=5;
$cjr=rand(0,999999);
header('Content-Language: en');
if (file_exists(INIFP)) {
$conf=@parse_ini_file(INIFP,false,INI_SCANNER_RAW);
if ($conf===false)
die('Configuration file «'.INIFP."» exists but {$usname} could not open it.");
}
$errors=[];
if (isset($_GET['acctock'])) {
if (strlen($_GET['acctock'])>MAXACCLEN) {
$_GET['acctock']='';
$errors[]='Value for «Fediverse account address» is too long';
}
$_GET['acctock']=trim($_GET['acctock']);
if ($_GET['acctock']!='' && preg_match('#^@?[0-9a-zA-Z_]+(@[a-z0-9.-]{4,253})?$#',$_GET['acctock'])!==1)// todo: make it better, like split ecc.
$errors[]='Value for «Fediverse account address» is not valid';
} else {
$_GET['acctock']='';
}
$hostok=false;
if (isset($_GET['host'])) {
if (strlen($_GET['host'])>MAXHOSTLEN) {
$_GET['host']='';
$errors[]='Value for «Mastodon instance domain» is too long';
}
$_GET['host']=trim($_GET['host']);
if ($_GET['host']!='' && !validhostname($_GET['host']))
$errors[]='Value for «Mastodon instance domain» is not valid';
else
$hostok=true;
} else {
$_GET['host']='';
}
$rl=['remaining'=>400,'restime'=>0];
if ($hostok) {
$rlfp='sec/'.$_GET['host'].'.rl.state';
if (file_exists($rlfp)) {
$buf=@file($rlfp,FILE_IGNORE_NEW_LINES);
if ($buf===false)
die('Could not open rate limiting state file.');
if (count($buf)!=2 || preg_match('#^\d+$#',$buf[0])!==1 || preg_match('#^\d+$#',$buf[1])!==1)
die('Malformed rate limiting state file.');
$rl=['remaining'=>$buf[0]+0,'restime'=>$buf[1]+0];
}
}
$now=time();
if ($rl['remaining']<=10 && $now<=$rl['restime'])// ten to leave a margin for "many people using it" and to account for 3 calls to host's endpoints
$errors[]="This {$usname} instance has reached rate limit on «{$_GET['host']}», please wait at least ".ght($rl['restime']-$now,null,0).'.';
if (count($errors)>0)
$errors='
{$usname}
Hello, this is {$usname}, a tool to easily check if a fediverse account is limited (AKA «silenced») and-or suspended (AKA «blocked») by a Mastodon instance.
Since an account is reported as limited and-or suspended by a Mastodon instance even when that instance has limited and-or suspended the whole account’s instance, {$usname} also tries to detect if this is the case, and tells about it.
{$usname} does not use cookies or Javascript and does not store any data about you anywhere.
You can find {$usname}’s code here, and you can contact its developer on Mastodon here.
".$errors."
\n";
if ($errors=='' && $_GET['acctock']!='' && $_GET['host']!='') {
echo "
\n";
$context=[
'http'=>[
'method'=>'GET',
'ignore_errors'=>true,
'protocol_version'=>1.1,
'user_agent'=>'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0',
'timeout'=>5
]
];
if (isset($conf['proxy']))
$context['http']['proxy']=$conf['proxy'];
$http_response_header=null;
$context=stream_context_create($context);
$hhost=htmlentities($_GET['host']);
$url=$_GET['host'].'/api/v1/instance';
$hurl=htmlentities($url);
$res=@file_get_contents('https://'.$url,false,$context);
//echo preprint($http_response_header);
if (isset($http_response_header))
$rl=ckratelimit($http_response_header,'echofun',true,false);
if ($res===false) {
echo "
Error: could not connect to «{$hhost}».
\n";
} elseif (is_array($http_response_header) && gethttpcode($http_response_header)!='200') {
echo "
Error: «{$hhost}» seems not to be running Mastodon.
\n";
} elseif (null===$res=@json_decode($res,true)) {
echo "
Error: «{$hurl}» returned data which could not be parsed as JSON (".json_last_error().': '.json_last_error_msg().").
\n";
} elseif (isset($res['error'])) {
echo "
Error: «{$hurl}» replied with this error message: «".htmlentities($res['error'])."».
\n";
} elseif (!isset($res['version'])) {
echo "
Error: «{$hurl}» returned data in an unexpected format.
\n";
} elseif (preg_replace('#[^\d\.].*#','',$res['version'])<'3.4.0') {
echo "
Error: «{$hhost}» is running a version of Mastodon that is earlier than 3.4.0 («{$res['version']}»).
\n";
} else {
$acchost=preg_replace('#^@?[^@]+@(.*)$#','$1',$_GET['acctock']);
if ($acchost==$_GET['acctock'] || $acchost=='')
$acchost=$_GET['host'];
$acchostck=false;
$http_response_header=null;
$url=$_GET['host'].'/api/v1/instance/domain_blocks';
$hurl=htmlentities($url);
$res=@file_get_contents('https://'.$url,false,$context);
//echo preprint($http_response_header);
if (isset($http_response_header))
$rl=ckratelimit($http_response_header,'echofun',true,false);
if ($res!==false && is_array($http_response_header) && gethttpcode($http_response_header)=='200' && null!==$res=@json_decode($res,true)) {
foreach ($res as $val) {
if (isset($val['domain'],$val['severity']) && $val['domain']==$acchost) {
if ($val['severity']=='silence')
$acchostck="The whole domain of the account to check, «{$acchost}», is limited by «{$_GET['host']}».";
elseif ($val['severity']=='suspend')
$acchostck="The whole domain of the account to check, «{$acchost}», is suspended by «{$_GET['host']}».";
else
$acchostck="The whole domain of the account to check, «{$acchost}», is moderated by «{$_GET['host']}», but {$usname} could not detect the moderation severity.";
break;
}
}
if ($acchostck===false)
$acchostck="The domain of the account to check, «{$acchost}», is neither limited nor suspended by «{$_GET['host']}».";
} else {
$acchostck="{$usname} could not detect if the domain of the account to check, «{$acchost}», is limited or suspended by «{$_GET['host']}».";
}
$acchostck=htmlentities($acchostck);
$http_response_header=null;
$url=$_GET['host'].'/api/v1/accounts/lookup?acct='.urlencode($_GET['acctock']);
$hacctock=htmlentities($_GET['acctock']);
$hurl=htmlentities($url);
$res=@file_get_contents('https://'.$url,false,$context);
//echo preprint($http_response_header);
if (isset($http_response_header))
$rl=ckratelimit($http_response_header,'echofun',true,false);
if ($res===false) {
echo "
Error: could not connect to «{$hhost}».
\n";
} elseif (null===$res=@json_decode($res,true)) {
echo "
Error: «{$hurl}» returned data which could not be parsed as JSON (".json_last_error().': '.json_last_error_msg().").
\n";
} elseif (isset($res['error'])) {
if ($res['error']=='Record not found')
echo "
«{$hhost}» does not know the «{$hacctock}» account.
{$acchostck}
\n";
else
echo "
Error: «{$hurl}» replied with this error message: «".htmlentities($res['error'])."».
\n";
} elseif (!isset($res['id'])) {
echo "
Error: «{$hurl}» returned data in an unexpected format.
\n";
} else {
$out="
\n«{$hacctock}» ";
(isset($res['limited']) && $res['limited']) ? $out.='is' : $out.='is not';
$out.=" limited by «{$hhost}».
\n«{$hacctock}» ";
(isset($res['suspended']) && $res['suspended']) ? $out.='is' : $out.='is not';
$out.=" suspended by «{$hhost}».
\n
\n{$acchostck}
\n";
echo $out;
}
}
if (is_array($rl) && @file_put_contents($rlfp,$rl['remaining']."\n".($rl['secstoreset']+time())."\n")===false)
echo "
Warning: could not write to rate limit state file.
\n";
}
if (isset($conf['footer']))
echo "\n";
echo "
\n";
function preprint($var) {
return '