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

{$usname}

Hello, this is a {$usname} instance. {$usname} is a tool to easily check if a Mastodon instance is censoring a given fediverse post. 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
\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); //echo preprint($rl); if (isset($rl['ok']) && $rl['ok'] && @file_put_contents(RLFP,$rl['remaining']."\n".($rl['sleep']+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 "
{$conf['footer']}
\n"; echo "
".SNAME." ".SVERS."
\n"; function preprint($var) { return '
'.print_r($var,true)."
\n"; } function gethttpcode($headers) { return preg_replace('#^[^ ]+ (\d+).*$#','$1',$headers[0]); } ?>