. */ 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='
There are some errors
'; else $errors=''; echo " {$usname}

{$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
\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 "
{$conf['footer']}
\n"; echo "
{$usname} ".SVERS."
\n"; function preprint($var) { return '
'.print_r($var,true)."
\n"; } function gethttpcode($headers) { return preg_replace('#^[^ ]+ (\d+).*$#','$1',$headers[0]); } function echofun($msg) { echo $msg; } ?>