.
*/
// example of a post censored by mastodon.uno: https://livellosegreto.it/@xabacadabra/110662830871887169
require 'lib/ght.php';
require 'lib/ckratelimit.php';
const SNAME='unocck';
const SVERS='0.1.1';
const SREPO='https://git.lattuga.net/jones/unocck';
const MULEN=1024;
const INIFP='sec/conf.ini';
const RLFP='sec/rl.state';
header('Content-Language: en');
$usname=ucfirst(SNAME);
$timeout=5;
$cjr=rand(0,999999);
$conf=@parse_ini_file(INIFP,false,INI_SCANNER_RAW);
if ($conf===false)
die('Could not open configuration file.');
elseif (!isset($conf['host']) || !isset($conf['hostdesc']) || !isset($conf['token']) || !isset($conf['maintref']))
die('Configuration file is malformed.');
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];
} else {
$rl=['remaining'=>400,'restime'=>0];
}
$now=time();
if ($rl['remaining']==10 && $now<=$rl['restime'])// ten to leave a margin for "many people using it"
die("Sorry, this {$usname} instance has reached rate limit on «{$conf['host']}», please wait at least ".ght($rl['restime']-$now,null,0).'.');
$errors=[];
if (isset($argv[1]))
$_GET=['purl'=>$argv[1]];
if (isset($_GET['purl'])) {
if (strlen($_GET['purl'])>MULEN) {
$_GET['purl']='';
$errors[]='“Post URL” is too long';
}
$_GET['purl']=trim($_GET['purl']);
if ($_GET['purl']!='' && preg_match('#^https?://#',$_GET['purl'])!==1)// todo: make it better
$errors[]='“Post URL” is not a valid http(s) address';
} else {
$_GET['purl']='';
}
if ($_GET['purl']!=='')
$purlhn=preg_replace('#^https?://([^/]+).*$#','$1',$_GET['purl']);
if (count($errors)>0)
$errors='
{$usname}
Hello, this is a {$usname} instance. {$usname} is a tool to easily check if a Mastodon instance is censoring a given Mastodon post (currently, it works only with Mastodon posts, but i’ll try to make it work with other platforms’ posts too). This {$usname} instance is set to check «{$conf['host']}», {$conf['hostdesc']}. It works by querying {$conf['host']}’s /api/v2/search API endpoint with the credentials of an application defined inside a {$conf['host']} account, but to avoid false positives it does so only after it has verified that the URL you passed to it actually points to a publicly accessible ActivityPub object.
{$usname} does not use cookies or Javascript and does not store any data about you anywhere.
You can find {$usname}’s code here.
This {$usname} instance is maintained by {$conf['maintref']}.
".$errors."
Check
\n";
if ($errors=='' && $_GET['purl']!='') {
echo "
\n";
$context=[
'http'=>[
'header'=>"Content-type: application/x-www-form-urlencoded\r\nAccept: application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"\r\n",
'method'=>'GET',
'ignore_errors'=>true,
'user_agent'=>'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0',
'timeout'=>5
]
];
if (isset($conf['proxy']))
$context['http']['proxy']=$conf['proxy'];
$http_response_header=null;
$res=@file_get_contents($_GET['purl'],false,stream_context_create($context));
/*$xres=json_decode($res,true);
echo preprint($xres);*/
if (isset($http_response_header))
$hcode=gethttpcode($http_response_header);
if ($res===false || !isset($http_response_header)) {
echo "
Error: {$usname} could not connect to «{$purlhn}» and won’t proceed with the check.
\n";
} elseif ($hcode[0]!='2') {
if ($hcode[0]=='5')
echo "
«{$purlhn}» returned a server error. {$usname} won’t proceed with the check.
\n";
elseif ($hcode[0]=='4')
echo "
Error: {$usname} could not access the “Post URL” you passed to it: probably the post visibility is not public/unlisted, or you passed a wrong URL. {$usname} won’t proceed with the check.
\n";
elseif ($hcode[0]=='3')
echo "
Error: the “Post URL” you passed to {$usname} redirects, and since its programmer is lazy, {$usname} currently only accepts URLs which point to original posts (on Mastodon web you can copy a post’s original URL by opening the “three vertical dots” menu you find on every post and selecting “Copy link to post”). {$usname} won’t proceed with the check.
\n";
elseif ($hcode[0]=='1')
echo "
«{$purlhn}» returned an unexpected and useless informational message. {$usname} won’t proceed with the check.
\n";
else
echo "
«{$purlhn}» returned an unexpected HTTP code. {$usname} won’t proceed with the check.
\n";
} elseif (null===$res=@json_decode($res,true)) {
echo "
Error: «{$purlhn}» returned data which could not be parsed as JSON (".json_last_error().': '.json_last_error_msg().").
\n";
} elseif (isset($res['error'])) {
echo "
Error: «{$purlhn}» returned «".htmlentities($res['error'])."». {$usname} won’t proceed with the check.
\n";
} elseif (!isset($res['@context'][0]) || $res['@context'][0]!='https://www.w3.org/ns/activitystreams') {
echo "
Error: the “Post URL” you passed to {$usname} doesn’t point to an ActivityPub post. {$usname} won’t proceed with the check.
\n";
} else {
$context['http']['header']="Content-type: application/x-www-form-urlencoded\r\nAccept: application/json\r\nAuthorization: Bearer {$conf['token']}\r\n";
$http_response_header=null;
$hhost=htmlentities($conf['host']);
$url=$conf['host'].'/api/v2/search?q='.urlencode($_GET['purl']).'&type=statuses&resolve=1&limit=1';
$hurl=htmlentities($url);
//while (true) {
$res=@file_get_contents('https://'.$url,false,stream_context_create($context));
if (isset($http_response_header))
$rl=ckratelimit($http_response_header,'echofun',true,false);
//echo preprint($rl);
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 ($rl['remaining']==0)
break;
usleep(250000);
}*/
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'])) {
echo "
Error: «{$hurl}» replied with this error message: «".htmlentities($res['error'])."».
\n";
} elseif (!isset($res['statuses'])) {
echo "
Error: «{$hurl}» returned data in an unexpected format.
\n";
} else {
if (isset($res['statuses'][0]['id']))
echo "
\nPost is not censored.\n
\n";
else
echo "
\nPost is censored.\n
\n";
}
}
}
if (isset($conf['footer']))
echo "\n";
echo "
\n";
function preprint($var) {
return '