#!/bin/php
<?php

/*
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

define('N',"\n");

$inifp=null;

$opts=array(
	'excludeafter'=>60*60*24*30,
	'startinstancesfp'=>null,
	'loadbiglist'=>true,
	'onlinecheck'=>true,
	'timeout'=>5,
	'biglistfp'=>null,
	'prodlistfp'=>null,
	'blacklists'=>array(),
	'whitelists'=>array()
);

$help='DESCRIZIONE
 Questo script parte da una selezione di istanze Mastodon («istanze
 di partenza»), ciascuna con una relativa lista di istanze da essa
 bloccate (che può anche essere omessa), e genera/aggiorna due liste:
 una che conterrà i dati di tutte le istanze di partenza e delle istanze
 ad esse note (comprese quelle bloccate, escluse soltanto quelle che non
 rispondono da un lasso di tempo impostabile), e una che conterrà solo
 le istanze non bloccate, la cui piattaforma è mastodon, che danno
 possibilità di iscrizione di nuovi utenti, il cui numero di utenti
 è compreso tra 11 e 30000, che conoscono almeno altre 500 istanze,
 che hanno avuto almeno 10 utenti attivi nell\'ultimo mese o, se questo
 dato non è disponibile, la cui media di toot per utente è maggiore
 o uguale a 10.
SINTASSI
 crawler.php -i <file> | -s <file> -b <file> -p <file> [altre opzioni]
OPZIONI
 -i, --inifp <file>
  Imposta un file di configurazione da cui leggere le opzioni.
  Il formato di questo file è semplice: una opzione per riga in formato
  <opzione>=<valore>, dove «opzione» è una qualsiasi tra le opzioni
  descritte qui nel suo formato lungo, tranne «inifp» e «help».
  Esempio: «startinstancesfp=startinstances.txt».
  Il file di configurazione può non contenere tutte le opzioni
  disponibili.
  Nota bene: tutte le opzioni impostate da riga di comando, che siano
  specificate prima o dopo questa, hanno la precedenza su quelle
  definite nel file di configurazione.
 -s, --startinstancesfp <file>
  DEVE essere specificata.
  Imposta il file da cui leggere le istanze di partenza e le relative
  liste di istanze sospese-silenziate.
  Il formato del file è questo: per ogni riga:
  <uri della istanza di partenza>|[uri della relativa lista di istanze
  bloccate]
  Ogni riga vuota o che cominci con il carattere «#» sarà ignorata.
  Il formato del file delle istanze bloccate è questo: per ogni riga:
  |<uri della istanza bloccata>|<data del blocco>|<tipo di blocco>|
  <motivazione pubblica del blocco>
  Esempio di <data del blocco>: «2019-12-11 18:44:06.710862»
  <tipo di blocco>: «0» per silenziata, «1» per sospesa, «2» per "solo
  file media e rapporti".
 -b, --biglistfp <file>
  DEVE essere specificata.
  Imposta il file da cui leggere le istanze già testate in passato
  (se il file esiste e non è specificata l’opzione «-d», vedi sotto)
  e in cui scrivere tutti i dati recuperabili delle istanze testate.
 -p, --prodlistfp <file>
  DEVE essere specificata.
  Imposta il file da cui leggere (se esiste) e in cui scrivere i dati
  relativi alle istanze corrispondenti ai criteri di selezione descritti
  nel paragrafo «DESCRIZIONE».
 -B, --blacklistfp
  Imposta un eventuale file di istanze bloccate aggiuntivo. Per il
  formato di questi file vedi sopra il paragrafo relativo nella
  descrizione dell\'opzione «-s, --startinstancesfp». Questa opzione può
  essere utilizzata più volte per specificare più file di istanze
  bloccate. Nel file di configurazione ha un formato particolare:
  «blacklistfp=file1[,file2[,file3[...]]]».
 -w, --whitelistfp
  Imposta un eventuale file di istanze da non scartare mai, nemmeno
  se fanno parte di una delle blacklist utilizzato o non corrispondono
  ai criteri di filtraggio. Il formato di questi file è semplice:
  un dominio per riga (le righe vuote o che cominciano con il carattere
  «#» vengono ignorate. Questa opzione può essere utilizzata più volte
  per specificare più whitelist. Nel file di configurazione
  ha un formato particolare: «whitelistfp=file1[,file2[,file3[...]]]».
 -t, --timeout <secondi>
  Imposta il timeout delle richieste http(s) in secondi.
  DEFAULT: '.$opts['timeout'].' secondi.
 -e, --excludeafter <tempo>
  Imposta il lasso di tempo dopo il quale un’istanza che non risponde
  viene eliminata dal listone di tutte le istanze testate.
  «tempo» deve essere specificato come un numero, seguito eventualmente
  da un carattere che ne indica l’unità di misura: «s» o nessun
  carattere per secondi, «m» per minuti, «o» per ore, «g» per giorni,
  «S» per settimane, «M» per mesi (30 giorni), «A» per anni.
  DEFAULT: 1 mese.
 -l, --loadbiglist <si|no>
  Dice al programma se caricare o meno il listone delle istanze già
  testate in passato.
  DEFAULT: «si».
 -c, --onlinecheck <si|no>
  Dice al programma se interrogare o meno le istanze note.
  Se impostato a «no» forza a «si» «loadbiglist» (vedi opzione
  precedente).
  DEFAULT: «si».
 -h, --help
  Mostra questo aiuto ed esce.

 This program comes with ABSOLUTELY NO WARRANTY; for details see
 the source.
 This is free software, and you are welcome to redistribute it under
 certain conditions; see <http://www.gnu.org/licenses/> for details.'.N;

function mexit($msg,$code) {
	echo($msg);
	exit($code);
}

function tosec($str) {
	if (preg_match('/^([0-9]+)([smogSMA]?)/',$str,$buf)===1) {
		switch ($buf[2]) {
			case '':
			case 's':
			return($buf[1]);
			break;
			case 'm':
			return($buf[1]*60);
			break;
			case 'o':
			return($buf[1]*60*60);
			break;
			case 'g':
			return($buf[1]*60*60*24);
			break;
			case 'S':
			return($buf[1]*60*60*24*7);
			break;
			case 'M':
			return($buf[1]*60*60*24*30);
			break;
			case 'A':
			return($buf[1]*60*60*24*365);
			break;
		}
	} else {
		return(false);
	}
}

for ($i=1; $i<$argc; $i++) {
	if ($argv[$i]=='-i' || $argv[$i]=='--inifp') {
		if ($i+1>=$argc || $argv[$i+1]=='')
			mexit('L’opzione «'.$argv[$i].'» richiede di specificare un file di configurazione (usa «-h» per vedere la guida).'.N,1);
		$i++;
		$inifp=$argv[$i];
	}
}

if (!is_null($inifp)) {
	$buf=@parse_ini_file($inifp);
	if ($buf!==false) {
		foreach ($buf as $key=>$val) {
			if (array_key_exists($key,$opts)) {
				if ($key=='excludeafter') {
					$opts['excludeafter']=tosec($val);
					if ($opts['excludeafter']===false)
						mexit('L’opzione «excludeafter» specificata in «'.$inifp.'» non è in un formato corretto (usa «-h» per vedere la guida).'.N,1);
				} elseif ($key=='blacklists') {
					$opts['blacklists']=explode(',',$val);
				} elseif ($key=='whitelists') {
					$opts['whitelists']=explode(',',$val);
				} else {
					$opts[$key]=$val;
				}
			} else {
				echo('Attenzione: l’opzione «'.$key.'» in «'.$inifp.'» è sconosciuta e sarà ignorata.'.N);
			}
		}
	} else {
		mexit('Attenzione: non ho potuto leggere la configurazione dal file «'.$inifp.'».'.N,1);
	}
}

for ($i=1; $i<$argc; $i++) {
	if (substr($argv[$i],0,1)=='-') {
		switch($argv[$i]) {
			case '-i':
			case '--inifp':
			$i++;
			break;
			case '-e':
			case '--excludeafter':
			if ($i+1>=$argc)
			$i++;
			$opts['excludeafter']=tosec($argv[$i]);
			if ($opts['excludeafter']===false)
				mexit('Opzione «'.$argv[$i].'»: formato non corretto (usa «-h» per vedere la guida).'.N,1);
			break;
			case '-t':
			case '--timeout':
			if ($i+1>=$argc || preg_match('/^[0-9]+$/',$argv[$i+1])!==1)
				mexit('L’opzione «'.$argv[$i].'» richiede un parametro numerico intero (usa «-h» per vedere la guida).'.N,1);
			$i++;
			$opts['timeout']=$argv[$i];
			break;
			case '-b':
			case '--biglistfp':
			if ($i+1>=$argc || $argv[$i+1]=='')
				mexit('L’opzione «'.$argv[$i].'» richiede un parametro di tipo file (usa «-h» per vedere la guida).'.N,1);
			$i++;
			$opts['biglistfp']=$argv[$i];
			break;
			case '-p':
			case '--prodlistfp':
			if ($i+1>=$argc || $argv[$i+1]=='')
				mexit('L’opzione «'.$argv[$i].'» richiede un parametro di tipo file (usa «-h» per vedere la guida).'.N,1);
			$i++;
			$opts['prodlistfp']=$argv[$i];
			break;
			case '-s':
			case '--startinstancesfp':
			if ($i+1>=$argc || $argv[$i+1]=='')
				mexit('L’opzione «'.$argv[$i].'» richiede un parametro di tipo file (usa «-h» per vedere la guida).'.N,1);
			$i++;
			$opts['startinstancesfp']=$argv[$i];
			break;
			case '-B':
			case '--blacklistfp':
			if ($i+1>=$argc || $argv[$i+1]=='')
				mexit('L’opzione «'.$argv[$i].'» richiede un parametro di tipo file (usa «-h» per vedere la guida).'.N,1);
			$i++;
			$opts['blacklists'][]=$argv[$i];
			break;
			case '-w':
			case '--whitelistfp':
			if ($i+1>=$argc || $argv[$i+1]=='')
				mexit('L’opzione «'.$argv[$i].'» richiede un parametro di tipo file (usa «-h» per vedere la guida).'.N,1);
			$i++;
			$opts['whitelists'][]=$argv[$i];
			break;
			case '-l':
			case '--loadbiglist':
			if ($i+1>=$argc || ($argv[$i+1]!='si' && $argv[$i+1]!='no'))
				mexit('L’opzione «'.$argv[$i].'» richiede un parametro («si/no») (usa «-h» per vedere la guida).'.N,1);
			$i++;
			$opts['loadbiglist']=true;
			if ($argv[$i]=='no') $opts['loadbiglist']=false;
			break;
			case '-c':
			case '--onlinecheck':
			if ($i+1>=$argc || ($argv[$i+1]!='si' && $argv[$i+1]!='no'))
				mexit('L’opzione «'.$argv[$i].'» richiede un parametro («si/no») (usa «-h» per vedere la guida).'.N,1);
			$i++;
			$opts['onlinecheck']=true;
			if ($argv[$i]=='no') $opts['onlinecheck']=false;
			break;
			case '-h':
			case '--help':
			mexit($help,1);
			break;
			default:
			mexit('Opzione «'.$argv[$i].'» sconosciuta (usa «-h» per vedere la guida).'.N,1);
			break;
		}
	} else {
		mexit('Opzione «'.$argv[$i].'» sconosciuta (usa «-h» per vedere la guida).'.N,1);
	}
}

$buf=null;
if (is_null($opts['startinstancesfp']))
	$buf.='- Non hai specificato il file delle istanze di partenza («-s/--startinstancesfp»)'.N;
if (is_null($opts['biglistfp']))
	$buf.='- Non hai specificato il file da cui leggere e in cui salvare i dati di tutte le istanze testate («-b/--biglistfp»)'.N;
if (is_null($opts['prodlistfp']))
	$buf.='- Non hai specificato il file da cui leggere e in cui salvare i dati di tutte le istanze testate che corrispondono ai criteri di selezione («-p/--prodlistfp»)'.N;
if (!is_null($buf))
	mexit('ERRORI'.N.$buf.'Usa «-h/--help» per leggere la guida.'.N,1);

if (!$opts['onlinecheck'])
	$opts['loadbiglist']=true;

$biglist=array();
$ibiglistc=0;
if ($opts['loadbiglist']) {
	if (file_exists($opts['biglistfp']) && is_file($opts['biglistfp']) && is_readable($opts['biglistfp'])) {
		echo('Carico la listona pre-esistente («'.$opts['biglistfp'].'») ... ');
		$buf=@file_get_contents($opts['biglistfp']);
		if ($buf!==false) {
			echo('OK :-)'.N);
			$biglist=json_decode($buf,true);
			$ibiglistc=count($biglist);
		} else {
			echo('ERRORE :-('.N);
		}
	}
}

$blinstances=array();
$wlinstances=array();

function loadblacklist($bluri) {
	global $blinstances, $opts;
	$context=stream_context_create(array('http'=>array('timeout'=>$opts['timeout'])));
	$f=@fopen($bluri,'r',false,$context);
	if ($f!==false) {
		$i=0;
		while (!feof($f)) {
			$lin=fgets($f);
//bsd.moe|2019-12-11 18:44:06.710862|1|https://mastodon.bida.im/@Ca_Gi/101270762003908554
			if (preg_match('/^([^#\|]{1}[^\|]+)\|{1}([^\|]+)\|{1}([012]{1})\|{1}(.*)$/',$lin,$buf)===1 && !in_array($buf[1],$blinstances) ) {
				$i++;
				$blinstances[]=$buf[1];
			}
		}
		fclose($f);
		echo('OK :-) (+'.$i.' istanze bloccate caricate; totale: '.count($blinstances).')'.N);
	} else {
		mexit('ERRORE :-('.N,1);
	}
}

$startinstances=array();
echo('Carico il file delle istanze di partenza («'.$opts['startinstancesfp'].'») ... ');
$buf=@file_get_contents($opts['startinstancesfp']);
if ($buf!==false) {
	echo('OK :-)'.N);
	$buf=explode(N,$buf);
	foreach ($buf as $val) {
		if ($val!='' && $val[0]!='#') {
			$kv=explode('|',$val);
			if ($kv[1]=='') $kv[1]=null;
			$startinstances[$kv[0]]=$kv[1];
		}
	}
} else {
	mexit(N.'Non ho potuto caricare il file delle istanze di partenza «'.$opts['startinstancesfp'].'», muoio.'.N,1);
}
if (count($startinstances)<1)
	mexit('Il file delle istanze di partenza «'.$opts['startinstancesfp'].'» non contiene alcuna voce, muoio.'.N,1);

foreach ($startinstances as $dom=>$bluri) {
	if (!is_null($bluri)) {
		echo('Recupero la lista delle istanze bloccate da «'.$dom.'» («'.$bluri.'») ... ');
		loadblacklist($bluri);
	} else {
		echo('NON recupero la lista delle istanze bloccate da «'.$dom.'»: la uri della stessa non è definita.'.N);
	}
}
foreach ($opts['blacklists'] as $bluri) {
	echo('Carico la lista delle istanze bloccate dall\'URI «'.$bluri.'» ... ');
	loadblacklist($bluri);
}
sort($blinstances);
echo(count($blinstances).' istanze bloccate.'.N);

foreach ($opts['whitelists'] as $wluri) {
	echo('Carico la whitelist delle istanze dall\'URI «'.$wluri.'» ... ');
	$buf=@file_get_contents($wluri);
	if ($buf!==false) {
		echo('OK :-)'.N);
		$buf=explode(N,$buf);
		foreach ($buf as $val) {
			if ($val!='' && $val[0]!='#' && !in_array($val,$wlinstances))
				$wlinstances[]=$val;
		}
	} else {
		mexit(N.'Non ho potuto caricare la whitelist delle istanze «'.$wluri.'», muoio.'.N,1);
	}
}
sort($wlinstances);
echo(count($wlinstances).' istanze whitelistate.'.N);

if ($opts['onlinecheck']) {

	$context=stream_context_create(array('http'=>array('timeout'=>$opts['timeout'])));

	foreach ($startinstances as $dom=>$bluri) {
		if (!array_key_exists($dom,$biglist))
			$biglist[$dom]=null;
		echo('Recupero la lista delle istanze note a «'.$dom.'» ... ');
		$buf=@file_get_contents('https://'.$dom.'/api/v1/instance/peers',false,$context);
		if ($buf!==false) {
			echo('OK :-)'.N);
			$peers=json_decode($buf,true);
			foreach ($peers as $pdom) {
				if (!array_key_exists($pdom,$biglist)) {
					$biglist[$pdom]=null;
				}
			}
		} else {
			echo('ERRORE :-('.N);
		}
	}
	ksort($biglist);
	$diff=count($biglist)-$ibiglistc;
	if ($diff>=0) $diff='+'.$diff;
	echo('Totale istanze note: '.count($biglist).' ('.$diff.' rispetto all\'ultima volta).'.N);
}

$prodlist=array();
$iprodlistc=0;
$buf=@file_get_contents($opts['prodlistfp']);
if ($buf!==false) {
	$prodlist=json_decode($buf,true);
	$iprodlistc=count($prodlist);
}

$newbiglist=array();
$i=0;
$biglistc=count($biglist);
foreach ($biglist as $dom=>$oinfo) {
	$i++;
	echo('~~~~~~'.N);
	if ($opts['onlinecheck']) {

		echo('Recupero le informazioni su «'.$dom.'» ('.$i.'/'.$biglistc.' - '.round(100/$biglistc*$i).'%)'.N);

		echo('Recupero le informazioni Nodeinfo ... ');
		$ninfo=null;
		$buf=@file_get_contents('https://'.$dom.'/nodeinfo/2.0',false,$context);
		if ($buf!==false) {
			echo('OK :-)'.N);
			$ninfo=json_decode($buf,true);
		} else {
			echo('ERRORE :-('.N);
		}

		echo('Recupero le informazioni API sull\'attività dell\'istanza ... ');
		$activity=null;
		$buf=@file_get_contents('https://'.$dom.'/api/v1/instance/activity',false,$context);
		if ($buf!==false) {
			echo('OK :-)'.N);
			$activity=json_decode($buf,true);
		} else {
			echo('ERRORE :-('.N);
		}

		echo('Recupero le informazioni API sull\'istanza ... ');
		$info=null;
		$buf=@file_get_contents('https://'.$dom.'/api/v1/instance',false,$context);
		if ($buf!==false) {
			echo('OK :-)'.N);
			$info=json_decode($buf,true);
			if (!is_null($oinfo) && array_key_exists('X-Checks',$oinfo))
				$info['X-Checks']=$oinfo['X-Checks'];
			$info['X-Checks'][]=array('time'=>time(),'ok'=>true);
			if (!is_null($ninfo)) {
				if (array_key_exists('usage',$ninfo) && array_key_exists('users',$ninfo['usage'])) {
					if (array_key_exists('activeMonth',$ninfo['usage']['users']))
						$info['X-ActiveUsersPerMonth']=$ninfo['usage']['users']['activeMonth'];
					if (array_key_exists('activeHalfyear',$ninfo['usage']['users']))
						$info['X-ActiveUsersPerHalfYear']=$ninfo['usage']['users']['activeHalfyear'];
				}
				if (array_key_exists('software',$ninfo)) {
					if (array_key_exists('name',$ninfo['software']))
						$info['X-Software']=$ninfo['software']['name'];
					if (array_key_exists('version',$ninfo['software']))
						$info['X-Version']=$ninfo['software']['version'];
				}
			}
			if (!is_null($activity))
				$info['X-Activity']=$activity;
			$newbiglist[$dom]=$info;
		} else {
			echo('ERRORE :-( ... ');
			$lastokk=null;
			if (!is_null($oinfo) && array_key_exists('X-Checks',$oinfo)) {
				foreach ($oinfo['X-Checks'] as $key=>$val)
					if ($val['ok']) $lastokk=$key;
			}
			if (is_null($oinfo) || is_null($lastokk) || time()-$oinfo['X-Checks'][$lastokk]['time']<=$opts['excludeafter']) {
				echo('ma riproveremo...'.N);
				$oinfo['X-Checks'][]=array('time'=>time(),'ok'=>false);
				$newbiglist[$dom]=$oinfo;
			} else {
				echo('e non riproveremo...'.N);
				$oinfo=null;
			}
			$info=$oinfo;
		}
	} else {
		$info=$oinfo;
	}
	$whynot=array();
	if (array_key_exists('uri',$info)) {
		if (!in_array($dom,$wlinstances)) {
			if (in_array($dom,$blinstances))
				$whynot[]='Istanza blacklistata';
			if (array_key_exists('X-Software',$info) && !in_array($info['X-Software'],array('mastodon','corgidon')))
				$whynot[]='Il software non è Mastodon (ma '.$info['X-Software'].')';
			if (!array_key_exists('registrations',$info))
				$whynot[]='Stato delle registrazioni non disponibile';
			elseif ($info['registrations']==false)
				$whynot[]='Registrazioni chiuse';
			if (!array_key_exists('stats',$info)) {
				$whynot[]='Stats non disponibili';
			} else {
				if (!array_key_exists('user_count',$info['stats']))
					$whynot[]='Numero utenti non disponibile';
				elseif ($info['stats']['user_count']<10 || $info['stats']['user_count']>30000)
					$whynot[]='Numero utenti ('.$info['stats']['user_count'].') non compreso tra 10 e 30000';
				if (!array_key_exists('domain_count',$info['stats']))
					$whynot[]='Numero istanze conosciute non disponibile';
				elseif ($info['stats']['domain_count']<500)
					$whynot[]='Numero istanze conosciute minore di 500';
	/*			if (!array_key_exists('status_count',$info['stats']))
					$whynot[]='Numero di toots non disponibile';
				elseif ($info['stats']['status_count']/$info['stats']['user_count']<10)
					$whynot[]='Media dei toots per utente minore di 10';*/
			}
			if (array_key_exists('X-ActiveUsersPerMonth',$info)) {
				if ($info['X-ActiveUsersPerMonth']<10)
					$whynot[]='Numero utenti attivi nell\'ultimo mese minore di 10';
			} elseif (array_key_exists('stats',$info) && array_key_exists('status_count',$info['stats']) && array_key_exists('user_count',$info['stats']) && $info['stats']['user_count']>0 && $info['stats']['status_count']/$info['stats']['user_count']<10) {
					$whynot[]='Media dei toots per utente minore di 10';
			}
			if (!array_key_exists('contact_account',$info) || is_null($info['contact_account'])) {
				$whynot[]='Informazioni sull\'account admin principale non disponibili';
			}/* else {
				if (!array_key_exists('created_at',$info['contact_account']))
					$whynot[]='Data di creazione dell\'account admin principale non disponibile';
				elseif (time()-strtotime($info['contact_account']['created_at'])<6*31*24*60*60)
					$whynot[]='L\'account admin principale risulta esser stato creato meno di 6 mesi fa';
			}*/
		} else {
			echo('«'.$dom.'» è whitelistata, la teniamo a prescindere.'.N);
		}
	} elseif (!array_key_exists($dom,$prodlist)) {
		$whynot[]='Info non disponibili, e l\'istanza non era già presente nella lista delle istanze occhei';
	}
	if (count($whynot)==0) {
		if (array_key_exists($dom,$prodlist)) {
			if (array_key_exists('short_description',$info) && (!array_key_exists('short_description',$prodlist[$dom]) || $prodlist[$dom]['short_description']!=$info['short_description'])) {
				$info['X-ShortDescriptionChanged']=true;
				$info['X-PrevShortDescription']=$prodlist[$dom]['short_description'];
			} else {
				$info['X-ShortDescriptionChanged']=false;
			}
			if (array_key_exists('description',$info) && (!array_key_exists('description',$prodlist[$dom]) || $prodlist[$dom]['description']!=$info['description'])) {
				$info['X-DescriptionChanged']=true;
				$info['X-PrevDescription']=$prodlist[$dom]['description'];
			} else {
				$info['X-DescriptionChanged']=false;
			}
			echo('«'.$dom.'» era nella lista delle istanze occhei ed è stata AGGIORNATA! :-)'.N);
			if (array_key_exists('X-Show',$prodlist[$dom]))
				$info['X-Show']=$prodlist[$dom]['X-Show'];
			else
				$info['X-Show']=-1;
		} else {
			$info['X-ShortDescriptionChanged']=false;
			$info['X-DescriptionChanged']=false;
			$info['X-Show']=-1;
			echo('«'.$dom.'» non era nella lista delle istanze occhei ed è stata AGGIUNTA! :-)'.N);
		}
		$prodlist[$dom]=$info;
	} else {
		if (array_key_exists($dom,$prodlist)) {
			unset($prodlist[$dom]);
			echo('«'.$dom.'» era nella lista delle istanze occhei ma è stata SCARTATA! :-('.N);
		} else {
			echo('«'.$dom.'» non era nella lista delle istanze occhei e NON CI È ENTRATA! :-('.N);
		}
		echo('Motivazioni: '.implode('; ',$whynot).'.'.N);
	}
}

echo('~~~~~~'.N);

if ($opts['onlinecheck']) {
	$json=json_encode($newbiglist,JSON_PRETTY_PRINT);
	file_put_contents($opts['biglistfp'],$json);
	$newbiglistc=count($newbiglist);
	$diff=$newbiglistc-$ibiglistc;
	if ($diff>=0) $diff='+'.$diff;
	echo('Totale istanze nella listona: '.$newbiglistc.' ('.$diff.' rispetto all\'ultima volta)'.N);
} else {
	echo('Totale istanze nella listona: '.count($biglist).N);
}
$json=json_encode($prodlist,JSON_PRETTY_PRINT);
file_put_contents($opts['prodlistfp'],$json);
$diff=count($prodlist)-$iprodlistc;
if ($diff>=0) $diff='+'.$diff;
echo('Totale istanze nella listina di quelle occhei: '.count($prodlist).' ('.$diff.' rispetto all\'ultima volta)'.N);

?>
