Browse Source

26 dicembre 2019

pezcurrel 4 years ago
parent
commit
113d3090e7

+ 237 - 0
mastblocksdump/mastblocksmerge.wip.sh

@@ -0,0 +1,237 @@
+#!/bin/bash
+
+# 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/>.
+
+MASTHOME='/var/lib/mastodon'
+MASTENVFP="$MASTHOME/live/.env.production"
+SISTERSFP="istanzesorelle"
+LOCAL=0
+INTERACTIVE=0
+
+HELP="SINTASSI
+ mastblocksmerge.sh [opzioni]
+DESCRIZIONE
+ Questo script integra i dati di una o più blocklist esterne nella
+ tabella domain_blocks di mastodon.
+ Legge i domini delle istanze di cui recuperare la blocklist da un file
+ di inizializzazione, per default \"$SISTERSP\" (formato del file:
+ un dominio per riga), prova a recuperare ciascuna blocklist da
+ https://[dominio]/domain_blocks.txt, costruisce dalle liste recuperate
+ un\'unica lista senza duplicati, si collega al db di mastodon e scrive
+ nella tabella domain_blocks i dati relativi alle istanze bloccate che
+ non sono già presenti nella tabella stessa.
+ Per la connessione al db di mastodon legge i dati necessari dal file
+ di configurazione di mastodon, per default
+ \"$MASTENVFP\"
+ È pensato per essere eseguito periodicamente da un cron job, come
+ utente mastodon oppure root oppure altro utente che abbia accesso in
+ lettura al file di configurazione di mastodon.
+OPZIONI
+ -H, --home
+  Definisce la home di mastodon (per default \"$MASTHOME\")
+  e di conseguenza il percorso del suo file di configurazione
+  (per default \"$MASTENVFP\").
+  È comunque possibile specificare individualmente il percorso
+  del file di configurazione di mastodon con l\'opzione che segue.
+ -e, --envfp
+  Definisce il percorso del file di configurazione di mastodon in uso.
+ -s, --sistersfp
+  Definisce il percorso del file di inizializzazione da cui leggere
+  la lista dei domini delle istanze sorelle.
+ -l, --local
+  Interpreta il file di inizializzazione come lista di file locali
+  contenenti blacklist (formato: un file per riga).
+ -i, --interactive
+  Modalità interattiva: se vengono nelle blocklist vengono trovate
+  istanze ancora non presenti nel database di mastodon, viene chiesto
+  per ciascuna se aggiungerla o meno.
+ -h, --help
+  Mostra questo aiuto ed esce."
+
+args=("$@")
+i=0
+while [ $i -lt ${#args[@]} ]; do
+	if [ "${args[$i]:0:1}" == "-" ]; then
+		case "${args[$i]}" in
+			"-H" | "--home" )
+				if [ -z "${args[$i+1]}" ]; then
+					echo "L'opzione \"${args[$i]}\" richiede un parametro (usa \"-h\" per l'aiuto)."
+					exit 1
+				else
+					((i++))
+					MASTHOME=$(echo "${args[$i]}" | sed -e 's/\/$//')
+					MASTENVFP="$MASTHOME/live/.env.production"
+				fi
+			;;
+			"-e" | "--envfp" )
+				if [ -z "${args[$i+1]}" ]; then
+					echo "L'opzione \"${args[$i]}\" richiede un parametro (usa \"-h\" per l'aiuto)."
+					exit 1
+				else
+					((i++))
+					MASTENVFP="${args[$i]}"
+				fi
+			;;
+			"-s" | "--sistersfp" )
+				if [ -z "${args[$i+1]}" ]; then
+					echo "L'opzione \"${args[$i]}\" richiede un parametro (usa \"-h\" per l'aiuto)."
+					exit 1
+				else
+					((i++))
+					SISTERSFP="${args[$i]}"
+				fi
+			;;
+			"-l" | "--local" )
+				LOCAL=1
+			;;
+			"-i" | "--interactive" )
+				INTERACTIVE=1
+			;;
+			"-h" | "--help" )
+				echo "$HELP"
+				exit 0
+			;;
+			*)
+				echo "\"${args[$i]}\": opzione sconosciuta (usa \"-h\" per l'aiuto)."
+				exit 1
+			;;
+		esac
+	else
+		echo "\"${args[$i]}\": opzione sconosciuta (usa \"-h\" per l'aiuto)."
+		exit 1
+	fi
+	((i++))
+done
+
+[ ! -e "$MASTENVFP" ] && echo "\"$MASTENVFP\" non esiste, muoio (usa \"-h\" per l'aiuto)." && exit 1
+[ ! -f "$MASTENVFP" ] && echo "\"$MASTENVFP\" non è un file, muoio (usa \"-h\" per l'aiuto)." && exit 1
+[ ! -r "$MASTENVFP" ] && echo "\"$MASTENVFP\" non è leggibile, muoio (usa \"-h\" per l'aiuto)." && exit 1
+
+[ ! -e "$SISTERSFP" ] && echo "\"$SISTERSFP\" non esiste, muoio (usa \"-h\" per l'aiuto)." && exit 1
+[ ! -f "$SISTERSFP" ] && echo "\"$SISTERSFP\" non è un file, muoio (usa \"-h\" per l'aiuto)." && exit 1
+[ ! -r "$SISTERSFP" ] && echo "\"$SISTERSFP\" non è leggibile, muoio (usa \"-h\" per l'aiuto)." && exit 1
+
+DB_HOST=`grep 'DB_HOST' "$MASTENVFP"|sed -e 's/[^=]*=//'`
+DB_PORT=`grep 'DB_PORT' "$MASTENVFP"|sed -e 's/[^=]*=//'`
+DB_NAME=`grep 'DB_NAME' "$MASTENVFP"|sed -e 's/[^=]*=//'`
+DB_USER=`grep 'DB_USER' "$MASTENVFP"|sed -e 's/[^=]*=//'`
+DB_PASS=`grep 'DB_PASS' "$MASTENVFP"|sed -e 's/[^=]*=//'`
+
+IFS=$'\n'
+
+function decode {
+	#dominio
+	record[0]=$(echo "$1" | sed -e 's/\t.*//')
+	#data creazione
+	record[1]=$(echo "$1" | sed -e 's/^[^\t]*\t\([^\t]*\).*/\1/')
+	#data ultima modifica
+	record[2]=$(echo "$1" | sed -e 's/^[^\t]*\t[^\t]*\t\([^\t]*\).*/\1/')
+	#tipo di blocco
+	record[3]=$(echo "$1" | sed -e 's/^[^\t]*\t[^\t]*\t[^\t]*\t\([^\t]*\).*/\1/')
+	#rifiuto media
+	record[4]=$(echo "$1" | sed -e 's/^[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t\([^\t]*\).*/\1/')
+	#rifiuto reports
+	record[5]=$(echo "$1" | sed -e 's/^[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t\([^\t]*\).*/\1/')
+	#commento pubblico
+	record[6]=$(echo "$1" | sed -e 's/^[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t\([^\t]*\).*/\1/')
+	if [ "${record[4]}" == "f" ]; then
+		record[4]="No"
+	elif [ "${record[4]}" == "t" ]; then
+		record[4]="Si"
+	fi
+	if [ "${record[5]}" == "f" ]; then
+		record[5]="No"
+	elif [ "${record[5]}" == "t" ]; then
+		record[5]="Si"
+	fi
+	if [ "${record[3]}" == "0" ]; then
+		record[3]="Silenziata"
+	elif [ "${record[3]}" == "1" ]; then
+		record[3]="Sospesa"
+	elif [ "${record[3]}" == "2" ]; then
+		record[3]="Nessuno"
+	fi
+}
+
+for sisdom in $(grep -P '^[^#]+[^\s]+' "$SISTERSFP"); do
+	if [ $LOCAL -eq 0 ]; then
+		blocks+="$(curl --connect-timeout 3 -s "https://$sisdom/domain_blocks.txt" | grep -P '^[^#]+[^\s]+')$IFS"
+	else
+		blocks+="$(cat "$sisdom" | grep -P '^[^#]+[^\s]+')$IFS"
+	fi
+done
+blocks=$(echo "$blocks" | head -n -1 | sort -u)
+echo "$blocks"
+echo "~~~~~~~~~~~~~~~~~~~~"
+okblocks=''
+for line in $blocks; do
+	dom=$(echo "$line" | sed -e 's/\t.*//' -e 's/\./\\./g')
+	echo "$okblocks" | grep -P "^$dom\t"
+	if [ $? -ne 0 ]; then
+		entries=$(echo "$blocks" | grep -P "^$dom\t")
+		howmany=$(echo "$entries" | wc -l)
+		if [ $howmany -gt 1 ]; then
+			if [ $INTERACTIVE -eq 1 ]; then
+				i=0
+				for entry in $entries; do
+					((i++))
+					echo "$i: $entry"
+				done
+				read
+			else
+				# qui ci vorrebbe codice "intelligente" che sceglie la entry più recente, ma per ora così
+				okblocks+="$line$IFS"
+			fi
+		fi
+	fi
+done
+okblocks=$(echo "$okblocks" | head -n -1 | sort)
+echo "$okblocks"
+echo "~~~~~~~~~~~~~~~~~~~~"
+
+exit
+
+
+
+
+blocks=$(PGPASSWORD="$DB_PASS" psql -h "$DB_HOST" -p "$DB_PORT" -d "$DB_NAME" -U "$DB_USER" -c 'SELECT domain, updated_at, severity, public_comment FROM domain_blocks' -A -t -F $'\t')
+
+i=0
+for line in $allblocks; do
+	dom=$(echo "$line" | sed -e 's/\t.*//' -e 's/\./\\./g')
+	echo "$blocks" | grep -P "^$dom\t" &>/dev/null
+	[ $? -ne 0 ] && newblocks+="$line$IFS" && ((i++))
+done
+newblocks=$(echo "$newblocks" | head -n -1 | sort)
+
+[ $i -eq 0 ] && echo "Non ho trovato nessuna nuova istanza bloccata." && exit 0
+
+echo "Ho trovato $i istanza/e bloccata da aggiungere."
+
+if [ $INTERACTIVE -eq 1 ]; then
+	for line in $newblocks; do
+		decode "$line"
+		echo "Dominio: ${record[0]}; Data creaz.: ${record[1]}; Data ult. mod.: ${record[2]}; Tipo blocco: ${record[3]}; Rifiuto media: ${record[4]}; Rifiuto reports: ${record[5]}; Commento pub.: ${record[6]}"
+		ask=1
+		while [ $ask -eq 1 ]; do
+			read -p "Vuoi aggiungere questa istanza alla tabella delle istanze bloccate? [S/n] " inp
+			echo "$inp" | grep -P '^[sSnN]{0,1}$' &>/dev/null
+			[ $? -eq 0 ] && ask=0
+		done
+		if [ "$inp" == "" ] || [ "$inp" == "s" ] || [ "$inp" == "S" ]; then
+			buf+="$line$IFS"
+		fi
+	done
+	newblocks=$(echo "$buf" | head -n -1)
+fi
+
+echo "$newblocks" | PGPASSWORD="$DB_PASS" psql -h "$DB_HOST" -p "$DB_PORT" -d "$DB_NAME" -U "$DB_USER" -A -t -c "COPY domain_blocks ( domain, created_at, updated_at, severity, reject_media, reject_reports, public_comment ) FROM STDIN WITH ( FORMAT text, DELIMITER '	' )"

+ 38 - 0
mastblocksdump/php/domblocks.php

@@ -0,0 +1,38 @@
+<?php
+
+define('N',"\n");
+
+$conffp='../.env.production';
+
+$conf=array(
+	'DB_HOST'=>null,
+	'DB_PORT'=>null,
+	'DB_NAME'=>null,
+	'DB_USER'=>null,
+	'DB_PASS'=>null
+);
+
+$confa=file($conffp,FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES);
+
+foreach ($confa as $line) {
+	if (preg_match('/^([A-Z_]+)=(.*)$/',$line,$buf)===1 && array_key_exists($buf[1],$conf))
+		$conf[$buf[1]]=$buf[2];
+}
+
+$dbconn=pg_connect('host='.$conf['DB_HOST'].' port='.$conf['DB_PORT'].' dbname='.$conf['DB_NAME'].' user='.$conf['DB_USER'].' password='.$conf['DB_PASS'])
+	or die('Connessione fallita: '.pg_last_error());
+
+$res=pg_query('SELECT domain, created_at, updated_at, severity, reject_media, reject_reports, public_comment FROM domain_blocks')
+	or die('Query fallita: '.pg_last_error());
+
+while ($row=pg_fetch_assoc($res))
+	$domblocks[]=$row;
+
+pg_free_result($res);
+pg_close($dbconn);
+
+echo(json_encode($domblocks));
+
+exit(0);
+
+?>

+ 2 - 0
web/admin/crawler/.htaccess

@@ -0,0 +1,2 @@
+Order deny,allow
+Deny from all

+ 0 - 36
web/admin/crawler/blacklist_bida.txt

@@ -1,36 +0,0 @@
-anitwitter.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-anitwitter.moe	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-babymetal.party	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-baraag.net	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-bsd.moe	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	https://mastodon.bida.im/@Ca_Gi/101270762003908554
-ediot.social	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-freespeechextremist.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-freespeech.firedragonstudios.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	https://mastodon.bida.im/@Ca_Gi/101344114624456297
-freezepeach.xyz	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-gorf.club	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-gs.smuglo.li	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-humblr.social	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	0	f	f	https://mastodon.bida.im/@cirku17/101399587014096355
-ika.moe	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-mastodon.starrevolution.org	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	https://a.nom.pl/notice/450131
-mobile.co	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	https://mastodon.bida.im/@Ca_Gi/101355947506820592
-neckbeard.xyz	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	Instance suspended: neckbeard.xyz - anime nazi shit, irony bro admin
-newjack.city	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-noagendasocial.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	0	f	f	
-pawoo.net	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-pl.smuglo.li	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-porntoot.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	0	f	f	
-preteengirls.biz	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-quodverum.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	https://mastodon.bida.im/@Ca_Gi/101514801964087604
-sealion.club	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	0	f	f	
-shitposter.club	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	0	f	f	
-shitposter.club	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	https://mastodon.bida.im/@Ca_Gi/101270762003908554
-social.au2pb.net	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-social.heldscal.la	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-social.imirhil.fr	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-social.quodverum.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	Razzisti
-social.targaryen.house	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	0	f	f	
-switter.at	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	0	f	f	https://mastodon.bida.im/@jops/101404791975700441
-toot.love	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	0	f	f	
-unsafe.space	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-woofer.alfter.us	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	
-wrongthink.net	2019-12-11 18:44:05.111111	2019-12-11 18:44:05.111111	1	f	f	

+ 0 - 157
web/admin/crawler/blacklist_cagi.txt

@@ -1,157 +0,0 @@
-2.distsn.org	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Spam
-2hu.club	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-anitwitter.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-anitwitter.moe	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Bad Moderation
-ap.torlipen.net	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-ap.uwu.st	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Harassment or abuse
-babymetal.party	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-baraag.net	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-beehub.org	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	
-blob.cat	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-bodybuilding.im	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-bofa.lol	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-bsd.moe	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-carnal-gabhub.protohype.net	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-civiq.social	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-cofe.moe	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Bad Moderation
-comm.network	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-community.halle-leaks.de	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-community.highlandarrow.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-counter.social	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Bad Moderation
-cryzed.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	
-cyzed.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-daffodil-11.org	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Malicious site
-dev.civiq.social	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-develop.gab.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-dickshow.social	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Bad Moderation
-djitter.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-ediot.social	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	Fascism – Hatespeech
-ediot.socialsilence	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-ekrem.develop.gab.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-exited.eu	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-explosion.party	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Conspiracy theories
-fedichive.tk	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-feminism.lgbt	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-freefedifollowers.ga	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-freehold.earth	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Bad Moderation
-freespeechextremist.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-freespeech.firedragonstudios.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-freespeech.host	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-freevoice.space	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	Fascism – Hatespeech
-freezepeach.xyz	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-gab.ai	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-gabble.xyz	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-gab.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-gabfed.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-gab.io	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-gab.polaris-1.work	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-gab.protohype.net	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-gab.sleek.eu	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-gameliberty.club	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-gasthe.lgbt	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-gnusocial.no	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-goldandblack.xyz	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-gorf.club	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	Fascism – Hatespeech
-gorf.club	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-gs.archae.me	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-gs.kawa-kun.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-gs.mon5t3r.info	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-gs.smuglo.li	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-hakui.club	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-homura.space	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Harassment or abuse
-ika.moe	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Bad Moderation
-impeccable.social	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-inditoot.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Bad Moderation
-jabb.in	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Conspiracy theories
-juche.town	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-karolat.press	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-kawaiistu.moe	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-kawen.space	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-kazvam.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-kipper.im	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Bad Moderation
-kiwifarms.cc	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	
-kneegrows.top	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-kowai.youkai.town	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-kyot.me	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Bad Moderation
-liberdon.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-libertarianism.club	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-libre.tube	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	
-loli.estate	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-lolis.world	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-manx.social	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Bad Moderation
-mastodon.loliandstuff.moe	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-mastodon.starrevolution.org	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-mast.wholemars.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	
-melalandia.tk	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-me.nooruul.xyz	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-mobile.co	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-neckbeard.xyz	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-neenster.org	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-newjack.city	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Spam
-niu.moe	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Bad Moderation
-noagendasocial.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	Fascism – Hatespeech
-not-develop.gab.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-not.phrack.fyi	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Conspiracy theories
-npf.mlpol.net	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-pawoo.net	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-pleroma.cucked.me	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-pleroma.rareome.ga	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Malicious site
-pleroma.soykaf.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-pleroma.wolfie.pw	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-pleroma.yorha.club	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Harassment or abuse
-pleville.net	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	
-pl.smuglo.li	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-porntoot.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-preteen.biz	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-preteengirls.bi	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-preteengirls.biz	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-pridelands.io	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-qoto.org	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-quey.org	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	
-quitter.pw	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-quodverum.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-rainbowdash.net	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-rapefeminists.network	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-sealion.club	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-shitasstits.life	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-shitposter.club	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-social.allthefallen.ninja	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-social.au2pb.net	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-social.guizzyordi.info	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-social.heldscal.la	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-social.hidamari.blue	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	
-social.homunyan.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	
-social.i2p.rocks	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-social.imirhil.fr	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-social.louisoft01.moe	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Conspiracy theories
-social.lucci.xyz	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Conspiracy theories
-social.quodverum.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-social.raptorengineering.io	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Advertisement
-social.sunshinegardens.org	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Harassment or abuse
-social.super-niche.club	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-social.targaryen.house	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	Fascism – Hatespeech
-social.wiuwiu.de	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Bad Moderation
-socnet.supes.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Bad Moderation
-spinster.dev	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-spinster.xyz	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-sunshinegardens.org	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Harassment or abuse
-thechad.zone	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-the.hedgehoghunter.club	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	
-toot.love	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Bad Moderation
-unsafe.space	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	Fascism – Hatespeech
-vampire.estate	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-video.halle-leaks.de	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	
-vipgirlfriend.xxx	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Unflagged porn
-voluntaryism.club	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-wagesofsinisdeath.com	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-waifu.social	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-warc.space	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	
-weeaboo.space	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-weedis.life	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	
-wogan.im	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-woofer.alfter.us	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech
-wrongthink.net	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	0	f	f	Fascism – Hatespeech
-wxw.moe	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-yiff.rocks	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Harassment or abuse
-youkai.town	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Pedo – Loli
-zerohack.xyz	2019-12-11 18:44:05.111111	2019-12-11 18:44:06.710862	1	f	f	Fascism – Hatespeech

+ 351 - 495
web/admin/crawler/crawler.php

@@ -18,118 +18,38 @@
 
 define('N',"\n");
 
-$inifp=null;
+declare(ticks=1);
+pcntl_signal(SIGTERM,'signalHandler');// Termination ('kill' was called)
+pcntl_signal(SIGHUP,'signalHandler');// Terminal log-out
+pcntl_signal(SIGINT,'signalHandler');// Interrupted (Ctrl-C is pressed)
+function signalHandler($signal) {
+	global $link, $logf, $jsonf;
+	lecho(N.'Sono stato interrotto.'.N);
+	if ($link) {
+		lecho('La connessione MySQL è aperta, la chiudo.'.N);
+		mysqli_close($link);
+	}
+	if ($jsonf) {
+		echo('Il file di dump json è aperto, lo chiudo.'.N);
+		fwrite($jsonf,'"Fine?": true'.N.'}'.N);
+		fclose($jsonf);
+	}
+	if ($logf) {
+		echo('Il file di log è aperto, lo chiudo.'.N);
+		fclose($logf);
+	}
+	exit(2);
+}
 
 $opts=array(
-	'excludeafter'=>60*60*24*30,
-	'startinstancesfp'=>null,
-	'loadbiglist'=>true,
-	'onlinecheck'=>true,
-	'timeout'=>5,
-	'biglistfp'=>null,
-	'prodlistfp'=>null,
-	'blacklists'=>array(),
-	'whitelists'=>array()
+	'timeout'=>3,
+	'log'=>true,
+	'jsonfp'=>'instances.json',
+	'jsonwrite'=>true,
+	'jsonread'=>false
 );
 
-$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);
-}
+use function mysqli_real_escape_string as myesc;
 
 function tosec($str) {
 	if (preg_match('/^([0-9]+)([smogSMA]?)/',$str,$buf)===1) {
@@ -162,441 +82,377 @@ function tosec($str) {
 	}
 }
 
-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];
-	}
+function mexit($msg,$code,$closemy=false) {
+	global $link;
+	lecho($msg);
+	if ($closemy)
+		mysqli_close($link);
+	if ($logf)
+		fclose($logf);
+	exit($code);
 }
 
-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);
-	}
+function lecho($msg,$logonly=false) {
+	global $opts, $logf;
+	if (!$logonly)
+		echo($msg);
+	if ($opts['log'])
+		fwrite($logf,$msg);
 }
 
-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);
-	}
+$logfp='crawler.log';
+if ($opts['log']) {
+	$logf=@fopen(__DIR__.'/'.$logfp,'w')
+		or mexit('Non ho potuto aprire in scrittura il file di log «'.$logfp.'».',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);
-		}
-	}
+$inifp='../sec/mastostartadmin.ini';
+$iniarr=parse_ini_file($inifp)
+	or mexit('Impossibile aprire il file di configurazione «'.$inifp.'»'.N,1);
+$link=mysqli_connect($iniarr['db_host'],$iniarr['db_admin_name'],$iniarr['db_admin_password'],$iniarr['db_name'],$iniarr['db_port'],$iniarr['db_socket'])
+	or mexit(mysqli_error($link).N,1);
+mysqli_set_charset($link,'utf8');
+
+$contextopts=array(
+	'http'=>array(
+		'timeout'=>$opts['timeout']
+	),
+	'socket'=>array(
+		'tcp_nodelay'=>true
+    )
+);
+$context=stream_context_create($contextopts);
+
+$blacklist=array();
+lecho('Carico la blacklist dal database...'.N);
+$res=mysqli_query($link,'SELECT * FROM Blacklist')
+	or mexit(mysqli_error($link).N,3,true);
+lecho(mysqli_num_rows($res).' istanze nella blacklist.'.N);
+while($row=mysqli_fetch_assoc($res)) {
+	$blacklist[$row['Domain']]=$row;
 }
 
-$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);
+function pgdatetomy($pgdate) {
+	if (preg_match('/^(\d+)-(\d+)-(\d+)[ T]{1}(\d+):(\d+):(\d+)\.(\d+)Z?$/',$pgdate,$buf)===1) {
+		return(mktime($buf[4],$buf[5],$buf[6],$buf[2],$buf[3],$buf[1])+floatval('0.'.$buf[7]));
 	} else {
-		mexit('ERRORE :-('.N,1);
+		return(false);
 	}
 }
 
-$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);
+function blpgdumplinetomy($line) {
+	$truefalse=array('f'=>0,'t'=>1);
+	$row=explode("\t",$line);
+	$row=array('Domain'=>$row[0],
+	'CreatedAt'=>pgdatetomy($row[1]),
+	'ModifiedAt'=>pgdatetomy($row[2]),
+	'Severity'=>$row[3],
+	'RejectMedia'=>$truefalse[$row[4]],
+	'RejectReports'=>$truefalse[$row[5]],
+	'PublicComment'=>$row[6]);
+	return($row);
 }
-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);
+$blacklistnew=array();
+$insts=array();
+lecho('Carico le istanze di partenza...'.N);
+$res=mysqli_query($link,'SELECT Domain FROM StartNodes')
+	or mexit(mysqli_error($link).N,3,true);
+lecho(mysqli_num_rows($res).' istanze di partenza.'.N);
+while($row=mysqli_fetch_assoc($res)) {
+	$insts[$row['Domain']]=null;
+	lecho('Recupero la lista delle istanze note a «'.$row['Domain'].'» ... ');
+	$buf=@file_get_contents('https://'.$row['Domain'].'/api/v1/instance/peers',false,$context);
+	if ($buf!==false) {
+		lecho('OK :-)'.N);
+		$peers=json_decode($buf,true);
+		foreach ($peers as $pdom) {
+			if (!array_key_exists($pdom,$insts) && strlen($pdom)<=64) {
+				$insts[$pdom]=null;
+			}
+		}
 	} else {
-		echo('NON recupero la lista delle istanze bloccate da «'.$dom.'»: la uri della stessa non è definita.'.N);
+		lecho('ERRORE :-('.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);
+	lecho('Recupero la blacklist di «'.$row['Domain'].'» ... ');
+	$buf=@file_get_contents('https://'.$row['Domain'].'/domain_blocks.txt',false,$context);
 	if ($buf!==false) {
-		echo('OK :-)'.N);
+		lecho('OK :-)'.N);
 		$buf=explode(N,$buf);
-		foreach ($buf as $val) {
-			if ($val!='' && $val[0]!='#' && !in_array($val,$wlinstances))
-				$wlinstances[]=$val;
+		foreach ($buf as $line) {
+			if (preg_match('/(^#.*$)|(^\s*$)/',$line)===0) {
+				$brow=blpgdumplinetomy($line);
+				if (!array_key_exists($brow['Domain'],$blacklist)) {
+					$blacklistnew[$brow['Domain']]=$brow;
+				}
+				$blacklist[$brow['Domain']]=$brow;
+			}
 		}
 	} else {
-		mexit(N.'Non ho potuto caricare la whitelist delle istanze «'.$wluri.'», muoio.'.N,1);
+		lecho('ERRORE :-('.N);
 	}
 }
-sort($wlinstances);
-echo(count($wlinstances).' istanze whitelistate.'.N);
-
-if ($opts['onlinecheck']) {
+//lecho('Carico le istanze note dal DB e aggiungo alla lista di quelle da controllare quelle che non ci sono già.'.N);
+$res=mysqli_query($link,'SELECT URI FROM Instances')
+	or mexit(mysqli_error($link).N,3,true);
+while($row=mysqli_fetch_assoc($res)) {
+	if (!array_key_exists($row['URI'],$insts))
+		$insts[$row['URI']]=null;
+}
+ksort($insts);
+ksort($blacklist);
+ksort($blacklistnew);
+lecho('Istanze recuperate: '.count($insts).N);
+lecho('Istanze blacklistate: '.count($blacklist).', di cui '.count($blacklistnew).' nuove da aggiungere al DB.'.N);
+
+foreach ($blacklistnew as $row) {
+	foreach($row as $key=>$val)
+		$row[$key]=myesc($link,$val);
+	mysqli_query($link,'INSERT INTO Blacklist (ID, Domain, CreatedAt, ModifiedAt, Severity, RejectMedia, RejectReports, PrivateComment, PublicComment) VALUES (NULL, \''.$row['Domain'].'\', \''.$row['CreatedAt'].'\', \''.$row['ModifiedAt'].'\', \''.$row['Severity'].'\', \''.$row['RejectMedia'].'\', \''.$row['RejectReports'].'\', NULL, \''.$row['PublicComment'].'\')')
+		or mexit(mysqli_error($link).N,3,true);
+}
 
-	$context=stream_context_create(array('http'=>array('timeout'=>$opts['timeout'])));
+//INSERT INTO `Instances` (`ID`, `New`, `Chosen`, `Visible`, `BlackListed`, `URI`, `Title`, `ShortDesc`, `LongDesc`, `OurDesc`, `PlaceID`, `Email`, `Software`, `Version`, `UserCount`, `StatusCount`, `DomainCount`, `ActiveUsersMonth`, `ActiveUsersHalfYear`, `Thumb`, `RegOpen`, `RegReqApproval`, `MaxTootChars`, `AdmAccount`, `AdmDisplayName`, `AdmCreatedAt`, `AdmNote`, `AdmURL`, `AdmAvatar`, `AdmHeader`) VALUES (NULL, '1', '0', '0', '0', 'pantagruel.dnsup.net', 'Pantagruel', 'Descrizione breve', 'Descrizione lunga', 'Istanza molto carina senza soffitto, senza cucina', '1', 'Graume <graume@inventati.org>', 'mastodon', '3.0.1', '2', '12', '345', '5', '10', 'http://www.iedm.it', '1', '0', '540', 'admin', 'Admin', '2019-12-11', 'Note \'admin\'', 'https://rame.altervista.org', 'http://www.iedm.it', 'http://www.iedm.it');
 
-	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);
+function b2i($bool) {
+	if ($bool)
+		return(1);
+	else
+		return(0);
 }
 
-$prodlist=array();
-$iprodlistc=0;
-$buf=@file_get_contents($opts['prodlistfp']);
-if ($buf!==false) {
-	$prodlist=json_decode($buf,true);
-	$iprodlistc=count($prodlist);
+//array key exists and value is not null
+function akeavinn($key,&$arr) {
+	if (array_key_exists($key,$arr) && !is_null($arr[$key]))
+		return(true);
+	else
+		return(false);
 }
 
-$newbiglist=array();
-$i=0;
-$biglistc=count($biglist);
-foreach ($biglist as $dom=>$oinfo) {
-	$i++;
-	echo('~~~~~~'.N);
-	if ($opts['onlinecheck']) {
+function nempty($str) {
+	if (preg_match('/^\s*$/',$str)===1)
+		return(null);
+	else
+		return($str);
+}
 
-		echo('Recupero le informazioni su «'.$dom.'» ('.$i.'/'.$biglistc.' - '.round(100/$biglistc*$i).'%)'.N);
+function subarim($glue,$key,&$arr) {
+	$str='';
+	$i=1;
+	$carr=count($arr);
+	foreach ($arr as $inarr) {
+		$str.=$inarr[$key];
+		if ($i<$carr)
+			$str.=$glue;
+		$i++;
+	}
+	return($str);
+}
 
-		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);
-		}
+function notify($msg,$sev) {
+	global $link;
+	mysqli_query($link,'INSERT INTO Notifications (ID, Notification, Severity, Microtime) VALUES (NULL, \''.myesc($link,$msg).'\', '.$sev.', '.microtime().')')
+		or mexit(mysqli_error($link).N,3,true);
+}
 
-		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);
-		}
+/*
+ * Nodeinfo ('https://'.$dom.'/nodeinfo/2.0') è stato aggiunto nella 3.0.0
+ * Trends ('https://'.$dom.'/api/v1/trends') è stato aggiunto nella 3.0.0
+ * Activity ('https://'.$dom.'/api/v1/instance/activity') è stato aggiunto nella 2.1.2
+*/
 
-		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 ($opts['jsonwrite']) {
+	$jsonf=@fopen(__DIR__.'/'.$opts['jsonfp'],'w')
+		or mexit('Non ho potuto aprire in scrittura il file di dump delle info json «'.$opts['jsonfp'].'».',1);
+	fwrite($jsonf,'{'.N);
+}
+$cinsts=count($insts);
+$i=0;
+$ok=0;
+foreach ($insts as $dom=>$row) {
+	$i++;
+	$info=null;
+	lecho('~~~~~~~~~~~~~~~'.N);
+	lecho('Provo a recuperare info su «'.$dom.'» ['.$i.'/'.$cinsts.' ('.$ok.' OK) - '.round(100/$cinsts*$i).'%]'.N);
+	lecho('Provo a recuperare le informazioni API sull’istanza ... ');
+	$buf=@file_get_contents('https://'.$dom.'/api/v1/instance',false,$context);
+	if ($buf!==false) {
+		$ok++;
+		lecho('OK :-)'.N);
+		$info=json_decode($buf,true);
+		if (array_key_exists('version',$info)) {
+			if ($info['version']>='2.1.2') {
+				lecho('Provo a recuperare le informazioni API sull’attività dell’istanza ... ');
+				$buf=@file_get_contents('https://'.$dom.'/api/v1/instance/activity',false,$context);
+				if ($buf!==false) {
+					lecho('OK :-)'.N);
+					$info['x-activity']=json_decode($buf,true);
+				} else {
+					lecho('ERRORE :-('.N);
 				}
 			}
-			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;
+			if ($info['version']>='3.0.0') {
+				lecho('Provo a recuperare le informazioni Nodeinfo sull’istanza ... ');
+				$buf=@file_get_contents('https://'.$dom.'/nodeinfo/2.0',false,$context);
+				if ($buf!==false) {
+					lecho('OK :-)'.N);
+					$info['x-nodeinfo']=json_decode($buf,true);
+				} else {
+					lecho('ERRORE :-('.N);
+				}
+				lecho('Provo a recuperare le informazioni API sui trends dell’istanza ... ');
+				$buf=@file_get_contents('https://'.$dom.'/api/v1/trends',false,$context);
+				if ($buf!==false) {
+					lecho('OK :-)'.N);
+					$info['x-trends']=json_decode($buf,true);
+				} else {
+					lecho('ERRORE :-('.N);
+				}
 			}
-			$info=$oinfo;
 		}
 	} else {
-		$info=$oinfo;
+		lecho('ERRORE :-('.N);
 	}
-	$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 (!is_null($info) && akeavinn('uri',$info) && !is_null(nempty($info['uri']))) {
+		lecho(json_encode($info,JSON_PRETTY_PRINT).N,true);
+		if ($opts['jsonwrite'])
+			fwrite($jsonf,'"'.$info['uri'].'": '.json_encode($info,JSON_PRETTY_PRINT).','.N);
+//INSERT INTO `Instances` (`ID`, `New`, `Chosen`, `Visible`, `BlackListed`, `URI`, `Title`, `ShortDesc`, `LongDesc`, `OurDesc`, `PlaceID`, `Email`, `Software`, `Version`, `UserCount`, `StatusCount`, `DomainCount`, `ActiveUsersMonth`, `ActiveUsersHalfYear`, `Thumb`, `RegOpen`, `RegReqApproval`, `MaxTootChars`, `AdmAccount`, `AdmDisplayName`, `AdmCreatedAt`, `AdmNote`, `AdmURL`, `AdmAvatar`, `AdmHeader`) VALUES (NULL, '1', '0', '0', '0', 'pantagruel.dnsup.net', 'Pantagruel', 'Descrizione breve', 'Descrizione lunga', 'Istanza molto carina senza soffitto, senza cucina', '1', 'Graume <graume@inventati.org>', 'mastodon', '3.0.1', '2', '12', '345', '5', '10', 'http://www.iedm.it', '1', '0', '540', 'admin', 'Admin', '2019-12-11', 'Note \'admin\'', 'https://rame.altervista.org', 'http://www.iedm.it', 'http://www.iedm.it');
+		$instrow=array('ID'=>null, 'New'=>0, 'Chosen'=>0, 'Visible'=>0, 'BlackListed'=>0, 'URI'=>null, 'Title'=>null, 'ShortDesc'=>null, 'LongDesc'=>null, 'OurDesc'=>null, 'PlaceID'=>null, 'Email'=>null, 'Software'=>null, 'Version'=>null, 'UserCount'=>null, 'StatusCount'=>null, 'DomainCount'=>null, 'ActiveUsersMonth'=>null, 'ActiveUsersHalfYear'=>null, 'Thumb'=>null, 'RegOpen'=>null, 'RegReqApproval'=>null, 'MaxTootChars'=>null, 'AdmAccount'=>null, 'AdmDisplayName'=>null, 'AdmCreatedAt'=>null, 'AdmNote'=>null, 'AdmURL'=>null, 'AdmAvatar'=>null, 'AdmHeader'=>null);
+		if (array_key_exists($info['uri'],$blacklist))
+			$instrow['BlackListed']=1;
+		$instrow['URI']=nempty($info['uri']);
+		if (akeavinn('title',$info))
+			$instrow['Title']=nempty($info['title']);
+		if (akeavinn('short_description',$info))
+			$instrow['ShortDesc']=nempty($info['short_description']);
+		if (akeavinn('description',$info))
+			$instrow['LongDesc']=nempty($info['description']);
+		if (akeavinn('email',$info))
+			$instrow['Email']=nempty($info['email']);
+		if (akeavinn('version',$info))
+			$instrow['Version']=nempty($info['version']);
+		if (akeavinn('stats',$info)) {
+			if (akeavinn('user_count',$info['stats']))
+				 $instrow['UserCount']=$info['stats']['user_count'];
+			if (akeavinn('status_count',$info['stats']))
+				 $instrow['StatusCount']=$info['stats']['status_count'];
+			if (akeavinn('domain_count',$info['stats']))
+				 $instrow['DomainCount']=$info['stats']['domain_count'];
+		}
+		if (akeavinn('thumbnail',$info))
+			$instrow['Thumb']=nempty($info['thumbnail']);
+		if (akeavinn('max_toot_chars',$info))
+			$instrow['MaxTootChars']=$info['max_toot_chars'];
+		if (akeavinn('registrations',$info))
+			$instrow['RegOpen']=b2i($info['registrations']);
+		if (akeavinn('approval_required',$info))
+			$instrow['RegReqApproval']=b2i($info['approval_required']);
+		if (akeavinn('contact_account',$info)) {
+			if (akeavinn('acct',$info['contact_account']))
+				$instrow['AdmAccount']=nempty($info['contact_account']['acct']);
+			if (akeavinn('display_name',$info['contact_account']))
+				$instrow['AdmDisplayName']=nempty($info['contact_account']['display_name']);
+			if (akeavinn('created_at',$info['contact_account']))
+				$instrow['AdmCreatedAt']=pgdatetomy($info['contact_account']['created_at']);
+			if (akeavinn('note',$info['contact_account']))
+				$instrow['AdmNote']=nempty(strip_tags($info['contact_account']['note'],'<a>'));
+			if (akeavinn('url',$info['contact_account']))
+				$instrow['AdmURL']=nempty($info['contact_account']['url']);
+			if (akeavinn('avatar',$info['contact_account']))
+				$instrow['AdmAvatar']=nempty($info['contact_account']['avatar']);
+			if (akeavinn('header',$info['contact_account']))
+				$instrow['AdmHeader']=nempty($info['contact_account']['header']);
+		}
+		if (akeavinn('x-nodeinfo',$info)) {
+			if (akeavinn('software',$info['x-nodeinfo']) && akeavinn('name',$info['x-nodeinfo']['software']))
+				$instrow['Software']=nempty($info['x-nodeinfo']['software']['name']);
+			if (akeavinn('usage',$info['x-nodeinfo']) && akeavinn('users',$info['x-nodeinfo']['usage'])) {
+				if (akeavinn('activeMonth',$info['x-nodeinfo']['usage']['users']))
+					$instrow['ActiveUsersMonth']=$info['x-nodeinfo']['usage']['users']['activeMonth'];
+				if (akeavinn('activeHalfyear',$info['x-nodeinfo']['usage']['users']))
+					$instrow['ActiveUsersHalfYear']=$info['x-nodeinfo']['usage']['users']['activeHalfyear'];
 			}
-			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';
+		}
+		$res=mysqli_query($link,'SELECT * FROM Instances WHERE URI=\''.myesc($link,$instrow['URI']).'\'')
+			or mexit(mysqli_error($link).N,3,true);
+		if (mysqli_num_rows($res)>0) {
+			lecho('«'.$instrow['URI'].'» è già presente nel DB, la aggiorno...'.N);
+			$oldinstrow=mysqli_fetch_assoc($res);
+			$query='UPDATE Instances SET ';
+			foreach ($instrow as $field=>$value) {
+				if (!is_null($value))
+					$query.=$field.'=\''.myesc($link,$value).'\', ';
+				else
+					$query.=$field.'=\'NULL\', ';
 			}
-			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';
+			$query=substr($query,0,-2).' WHERE Instances.ID='.$oldinstrow['ID'];
+			echo('QUERONA DI UPDATE: «'.$query.'».'.N);
+/*			$res=mysql_query($link,'SELECT InstID, LangID, Pos, Code FROM InstLangs LEFT JOIN Languages ON Languages.ID=LangID WHERE InstID='.$oldinstrow['ID'].' ORDER BY Pos ASC')
+				or mexit(mysqli_error($link).N,3,true);
+			$oldinstlangs=array();
+			while ($row=mysql_fetch_assoc($res))
+				$oldinstlangs[]=$row;
+			if (akeavinn('languages',$info)) {
+				$instlangs=array();
+				$pos=0;
+				foreach ($info['languages'] as $lang) {
+					$res=mysqli_query($link,'SELECT * FROM Languages WHERE Code=\''.myesc($link,$lang).'\'')
+						or mexit(mysqli_error($link).N,3,true);
+					if (mysqli_num_rows($res)<1) {
+						mysqli_query($link,'INSERT INTO Languages (ID, Code, Name) VALUES (NULL, \''.myesc($link,$lang).'\', NULL)')
+							or mexit(mysqli_error($link).N,3,true);
+						$langid=mysqli_insert_id($link);
+						notify('L’aggiornamento dei dati relativi all’istanza «<a href="editinst.php?id='.$oldinstrow['ID'].'">'.$info['URI'].'</a>» ha aggiunto un codice lingua non ancora noto, «'.$lang.'», di cui non conosco il nome per esteso. Puoi <a href="editlang.php?id='.$langid.'">editarlo qui</a>.',1);
+					} else {
+						$row=mysqli_fetch_assoc($res);
+						$langid=$row['ID'];
+					}
+					$pos++;
+					$instlangs[]=array('InstID'=>$oldinstrow['ID'],'LangID'=>$langid,'Pos'=>$pos,'Code'=>$lang);
+				}
+				print_r($instlangs);
+				print_r($oldinstlangs);
+				if ($instlangs!=$oldinstlangs) {
+					notify('La lista delle lingue utilizzate dichiarate dall’istanza «<a href="editinst.php?id='.$oldinstrow['ID'].'">'.$info['URI'].'</a>» è cambiata da «'.subarim(', ','Code',$oldinstlangs).'» a «'.subarim(', ','Code',$oldinstlangs).'».',1);
+					mysqli_query($link,'DELETE FROM InstLangs WHERE InstID='.$oldinstrow['ID'])
+						or mexit(mysqli_error($link).N,3,true);
+					foreach ($instlangs as $row) {
+						mysqli_query($link,'INSERT INTO InstLangs (InstID, LangID, Pos) VALUES ('.$row['InstID'].', '.$row['LangID'].', '.$row['Pos'].')')
+							or mexit(mysqli_error($link).N,3,true);
+					}
+				}
 			}*/
 		} 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;
+			lecho('«'.$info['uri'].'» non è già presente nel DB, la aggiungo...'.N);
+			$instrow['New']=1;
+			$fields=array();
+			$values='';
+			foreach ($instrow as $field=>$value) {
+				$fields[]=$field;
+				if (!is_null($value))
+					$values.='\''.myesc($link,$value).'\', ';
+				else
+					$values.='NULL, ';
 			}
-			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);
+			$values=substr($values,0,-2);
+			$query='INSERT INTO Instances ('.implode(', ',$fields).') VALUES ('.$values.')';
+			echo('QUERONA DI INSERT: «'.$query.'»'.N);
 		}
-		echo('Motivazioni: '.implode('; ',$whynot).'.'.N);
+//		var_dump($instrow);
 	}
 }
 
-echo('~~~~~~'.N);
+mysqli_close($link);
 
-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);
+if ($opts['jsonwrite']) {
+	fwrite($jsonf,'"Fine?": true'.N.'}'.N);
+	fclose($jsonf);
 }
-$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);
+
+if ($opts['log'])
+	fclose($logf);
+
+exit(0);
 
 ?>

+ 0 - 9
web/admin/crawler/crawler_pant.ini

@@ -1,9 +0,0 @@
-excludeafter=1M
-startinstancesfp=istanzesorelle_pant
-loadbiglist=true
-onlinecheck=true
-timeout=5
-biglistfp=biglist_pant.json
-prodlistfp=prodlist_pant.json
-blacklists=blacklist_cagi.txt
-whitelists=whitelist_sorellanza.txt

+ 0 - 9
web/admin/crawler/crawler_sorellanza.ini

@@ -1,9 +0,0 @@
-excludeafter=1M
-startinstancesfp=istanzesorelle
-loadbiglist=true
-onlinecheck=true
-timeout=3
-biglistfp=biglist.json
-prodlistfp=prodlist.json
-blacklists=blacklist_cagi.txt
-whitelists=whitelist_sorellanza.txt

+ 0 - 5
web/admin/crawler/istanzesorelle

@@ -1,5 +0,0 @@
-mastodon.bida.im|blacklist_bida.txt
-mastodon.cisti.org|
-nebbia.fail|
-stereodon.social|
-snapj.saja.freemyip.com|

+ 0 - 1
web/admin/crawler/istanzesorelle_pant

@@ -1 +0,0 @@
-pantagruel.dnsup.net|blacklist_bida.txt

BIN
web/admin/favicon.ico


BIN
web/admin/imgs/icona-180.png


BIN
web/admin/imgs/icona-192.png


BIN
web/admin/imgs/icona-32.png


BIN
web/admin/imgs/icona-512.png


+ 10 - 0
web/admin/include/connuser.php

@@ -0,0 +1,10 @@
+<?php
+
+$iniarr=parse_ini_file('sec/marcovaldo.ini') or muoribene(_('Impossibile aprire il file di configurazione.'),false);
+$link=mysqli_connect($iniarr['myhost'],$iniarr['myuser'],$iniarr['mypassword'],$iniarr['mydb']) or muoribene(_('Impossibile connettersi al database').': '.mysqli_connect_error().' ['.mysqli_connect_errno().']',false);
+mysqli_set_charset($link,'utf8');
+$res=mysqli_query($link,'SELECT * FROM Users WHERE ID='.$_SESSION['uid']) or muoribene(mysqli_error($link),true);
+if (mysqli_num_rows($res)!=1) muoribene(_('Utente non esistente.').'<br><a href="index.php">Login</a>.',true);
+$user=mysqli_fetch_assoc($res);
+
+?>

+ 10 - 0
web/admin/include/glob.php

@@ -0,0 +1,10 @@
+<?php
+
+header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP 1.1.
+header("Pragma: no-cache"); // HTTP 1.0.
+header("Expires: 0"); // Proxies.
+
+define('N',"\n");
+$cjrand=rand(0,999999);
+
+?>

+ 52 - 0
web/admin/include/menu.php

@@ -0,0 +1,52 @@
+<?php
+
+$menu=array(
+	'istanze'=>array('liadd'=>null, 'href'=>'istanze.php', 'title'=>'Istanze', 'selected'=>false, 'submenu'=>
+			array(
+			'aggiungi'=>array('liadd'=>'onclick="golang(\'en\')" onmouseover="this.style.cursor=\'pointer\'"', 'href'=>null, 'title'=>'English', 'selected'=>false, 'submenu'=>null),
+			'italiano'=>array('liadd'=>'onclick="golang(\'it\')" onmouseover="this.style.cursor=\'pointer\'"', 'href'=>null, 'title'=>'Italiano', 'selected'=>false, 'submenu'=>null)
+			)
+		),
+	'boh'=>array('liadd'=>null, 'href'=>'#', 'title'=>'Non saprei', 'selected'=>false, 'submenu'=>null),
+	'vedremo'=>array('liadd'=>null, 'href'=>'#', 'title'=>'Vedremo', 'selected'=>false, 'submenu'=>null),
+	'forse'=>array('liadd'=>null, 'href'=>'#', 'title'=>'Forse', 'selected'=>false, 'submenu'=>null)
+);
+
+
+/*
+ <li><a href=".">Guide</a></li>
+ <li><a href="instances">Instances</a></li>
+ <li><a href="about">About us</a></li>
+ <li onmouseover="chulsh(this,true)" onmouseout="chulsh(this,false)">Language
+  <ul class="ula" onmouseover="ulsh(this,true)" onmouseout="ulsh(this,false)">
+  <li><a href="#">English</a></li>
+  <li><a href="#">Italiano</a></li>
+  </ul>
+ </li>
+*/
+
+$menuout='';
+
+function buildmenu($menu) {
+	global $menuout;
+	foreach ($menu as $key=>$arr) {
+		$menuout.='<li';
+		if (!is_null($arr['liadd'])) $menuout.=' '.$arr['liadd'];
+		if (!is_null($arr['submenu'])) $menuout.=' onmouseover="chulsh(this,true)" onmouseout="chulsh(this,false)"';
+		if ($arr['selected']) $menuout.=' class="hil"';
+		$menuout.='>';
+		if (!is_null($arr['href']))
+			$menuout.='<a href="'.$arr['href'].'">'.$arr['title'].'</a>';
+		else
+			$menuout.=$arr['title'];
+		if (!is_null($arr['submenu'])) {
+// qui bisognerebbe aggiungere che a seconda del "livello" imposta class giusta: ula, oppure ulb per livello > 1
+			$menuout.=N.'<ul class="ula" onmouseover="ulsh(this,true)" onmouseout="ulsh(this,false)">'.N;
+			buildmenu($arr['submenu']);
+			$menuout.='</ul>'.N;
+		}
+		$menuout.='</li>'.N;
+	}
+}
+
+?>

+ 31 - 0
web/admin/include/muoribene.php

@@ -0,0 +1,31 @@
+<?php
+
+function muoribene($msg,$close) {
+	global $mylink;
+	if ($close) mysqli_close($mylink);
+	echo('<!DOCTYPE HTML>
+<html lang="it">
+<head>
+<title>Mastodon Startpage Admin - Errori</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<meta name="description" content="Mastodon Startpage Admin - Errori">
+<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
+<link rel="icon" type="image/png" href="imgs/icona-32.png" sizes="32x32">
+<link rel="icon" type="image/png" href="imgs/icona-192.png" sizes="192x192">
+<link rel="icon" type="image/png" href="imgs/icona-512.png" sizes="512x512">
+<link rel="apple-touch-icon-precomposed" href="imgs/icona-180.png">
+<link rel="stylesheet" type="text/css" href="theme.css">
+</head>
+<body>
+<div id="fullscreen">
+<div id="middlerow">
+<p>'.$msg.'</p>
+</div>
+</div>
+</body>
+</html>
+');
+	exit(1);
+}
+
+?>

+ 9 - 0
web/admin/include/myconn.php

@@ -0,0 +1,9 @@
+<?php
+
+$iniarr=parse_ini_file('sec/mastostartadmin.ini')
+	or muoribene('Impossibile aprire il file di configurazione.<br>'.$btl,false);
+$link=mysqli_connect($iniarr['db_host'],$iniarr['db_admin_name'],$iniarr['db_admin_password'],$iniarr['db_name'],$iniarr['db_port'],$iniarr['db_socket'])
+	or muoribene('Impossibile connettersi al database: '.mysqli_connect_error().' ['.mysqli_connect_errno().']',false);
+mysqli_set_charset($link,'utf8');
+
+?>

+ 12 - 0
web/admin/include/sessionstart.php

@@ -0,0 +1,12 @@
+<?php
+
+session_name('mastostartadmin');
+session_start();
+if (!array_key_exists('id',$_SESSION)) {
+	$_SESSION=array();
+	session_destroy();
+// qui è ok usare muoribene perché per iframe viene caricato da muoribenepar.php
+	muoribene('Sessione scaduta.<br>Torna alla <a href="index.php">pagina di accesso</a>.',false);
+}
+
+?>

+ 59 - 0
web/admin/index.php

@@ -0,0 +1,59 @@
+<?php
+
+require('include/glob.php');
+
+?>
+<!DOCTYPE HTML>
+<html lang="it">
+<head>
+<title>Mastodon Startpage Admin Login</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<meta name="description" content="Mastodon Startpage Admin Login">
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+<link rel="icon" type="image/png" href="imgs/icona-32.png" sizes="32x32">
+<link rel="icon" type="image/png" href="imgs/icona-192.png" sizes="192x192">
+<link rel="icon" type="image/png" href="imgs/icona-512.png" sizes="512x512">
+<link rel="apple-touch-icon-precomposed" href="imgs/icona-180.png">
+<link rel="stylesheet" type="text/css" href="theme.css?v=<?php echo($cjrand); ?>">
+<script language="JavaScript" src="js/alerta.js?v=<?php echo($cjrand); ?>"></script>
+<script language="JavaScript">
+<!--
+function ckfl() {
+	var emsgs='';
+	if (document.l.username.value=='') emsgs+='<li>Non hai inserito il nome</li>';
+	if (document.l.password.value=='') emsgs+='<li>Non hai inserito la password</li>';
+	if (emsgs!='')
+		alerta('<ul>'+emsgs+'</ul>');
+	else
+		document.l.submit();
+}
+//-->
+</script>
+</head>
+<body>
+<div id="fullscreen">
+<div id="middlerow">
+<div id="centertit">
+Mastodon Startpage Admin Login
+</div>
+<div id="centerbox">
+<form action="login.php" name="l" method="post">
+<table id="logintable">
+<tr><td>Nome:</td><td class="rtd"><input type="text" name="username" maxlength="64" autofocus></td></tr>
+<tr><td>Password:</td><td class="rtd"><input type="password" name="password" maxlength="64"></td></tr>
+<tr><td></td><td class="rtd"><input type="button" value="Entra" class="button" onClick="ckfl();"></td></tr>
+<tr><td colspan="2" class="tiptd"><a href="passreset.php">Password dimenticata?</a></td></tr>
+</table>
+</form>
+</div>
+</div>
+</div>
+<div id="popup">
+<div id="inpopup">
+<div id="popupcont">
+...
+</div>
+</div>
+</div>
+</body>
+</html>

+ 90 - 0
web/admin/instances.php

@@ -0,0 +1,90 @@
+<?php
+
+require('include/glob.php');
+require('include/muoribene.php');
+require('include/sessionstart.php');
+require('include/menu.php');
+buildmenu($menu);
+
+require('include/myconn.php');
+$res=mysqli_query($link,'SELECT * FROM Instances ORDER BY URI ASC')
+	or muoribene(mysqli_error($link).'<br>'.$btl,false);
+mysqli_close($link);
+if (mysqli_num_rows($res)<1) {
+	$out='<p>Nessuna istanza da mostrare.</p>'.N;
+} else {
+	$out='<table>'.N;
+	while ($row=mysqli_fetch_assoc($res)) {
+		$out.='<tr><td>'.$row['URI'].'</td></tr>'.N;
+	}
+	$out.='</table>'.N;
+}
+
+
+?>
+<!DOCTYPE HTML>
+<html lang="it">
+<head>
+<title>Mastodon Startpage Admin - Main Menu</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<meta name="description" content="Admin pages for Mastodon Startpage">
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+<link rel="icon" type="image/png" href="imgs/icona-32.png" sizes="32x32">
+<link rel="icon" type="image/png" href="imgs/icona-192.png" sizes="192x192">
+<link rel="icon" type="image/png" href="imgs/icona-512.png" sizes="512x512">
+<link rel="apple-touch-icon-precomposed" href="imgs/icona-180.png">
+<link rel="stylesheet" type="text/css" href="theme.css?v=<?php echo($cjrand); ?>">
+<script language="JavaScript">
+<!--
+function chulsh(el,sh) {
+	if (sh)
+		el.querySelector('ul').style='display:block';
+	else
+		el.querySelector('ul').style='display:none';
+}
+function ulsh(el,sh) {
+	if (sh)
+		el.style='display:block';
+	else
+		el.style='display:none';
+}
+function golang(lang) {
+	var loc=document.location.href;
+	loc=loc.replace(/#.*$/,'');
+	loc=loc.replace(/\/$/,'');
+	if (document.documentElement.lang=='en') {
+		if (lang!='en')
+			document.location.href=loc+'/'+lang;
+	} else {
+		if (lang!='en')
+			document.location.href=loc.substr(0,loc.length-3)+'/'+lang;
+		else
+			document.location.href=loc.substr(0,loc.length-3);
+	}
+}
+//-->
+</script>
+</head>
+<body>
+
+<nav>
+<div id="hmenu">
+<ul>
+<?php echo($menuout); ?>
+</ul>
+</div>
+</nav>
+
+<div id="fullscreenm">
+<div id="middlerow">
+<?php echo($out); ?>
+</div>
+</div>
+
+<div id="footer">
+<form action="edinst.php" name="addinst" method="post">
+<table><tr><td>Aggiungi un’istanza:</td><td><input type="text" name="URI" maxlength="512"></td><td><input type="button" value="Vai" onClick="ckaif();"></td></tr></table>
+</form>
+</div>
+</body>
+</html>

+ 4 - 0
web/admin/js/alerta.js

@@ -0,0 +1,4 @@
+function alerta(msg) {
+	document.getElementById('popupcont').innerHTML='<h3>Attenzione</h3>'+msg+'<input type="button" class="pupbut" value="Ok" onClick="document.getElementById(\'popup\').style.display=\'none\'">';
+	document.getElementById('popup').style.display='table';
+}

+ 36 - 0
web/admin/login.php

@@ -0,0 +1,36 @@
+<?php
+
+require('include/glob.php');
+require('include/muoribene.php');
+
+$btl='<a href="index.php">Torna al login</a>';
+
+$errs='';
+
+if (!array_key_exists('username',$_POST) || $_POST['username']=='')
+	$errs.='Non hai specificato il nome<br>'.N;
+if (!array_key_exists('password',$_POST) || $_POST['password']=='')
+	$errs.='Non hai specificato la password<br>'.N;
+if ($errs!='') muoribene($errs.$btl,false);
+
+$iniarr=parse_ini_file('sec/mastostartadmin.ini')
+	or muoribene('Impossibile aprire il file di configurazione.<br>'.$btl,false);
+$link=mysqli_connect($iniarr['db_host'],$iniarr['db_admin_name'],$iniarr['db_admin_password'],$iniarr['db_name'],$iniarr['db_port'],$iniarr['db_socket'])
+	or muoribene('Impossibile connettersi al database: '.mysqli_connect_error().' ['.mysqli_connect_errno().']',false);
+mysqli_set_charset($link,'utf8');
+$res=mysqli_query($link,'SELECT * FROM Admins WHERE Username=\''.mysqli_real_escape_string($link,$_POST['username']).'\'')
+	or muoribene(mysqli_error($link).'<br>'.$btl,true);
+mysqli_close($link);
+if (mysqli_num_rows($res)>1)
+	muoribene('Record admin duplicato.<br>'.$btl,false);
+$row=mysqli_fetch_assoc($res);
+if (mysqli_num_rows($res)<1 || !password_verify($_POST['password'],$row['Password']))
+	muoribene('Nome admin e/o password sbagliati.<br>'.$btl,false);
+
+session_name('mastostartadmin');
+session_start();
+$_SESSION['id']=$row['ID'];
+
+header('Location: instances.php');
+
+?>

+ 2 - 0
web/admin/sec/.htaccess

@@ -0,0 +1,2 @@
+Order deny,allow
+Deny from all

+ 6 - 0
web/admin/sec/mastostartadmin.ini

@@ -0,0 +1,6 @@
+db_host=localhost
+db_port=3306
+db_socket=/run/mysqld/mysqld.sock
+db_name=mastostart
+db_admin_name=MastoStartAdmin
+db_admin_password=MastoStartAdmin

+ 213 - 0
web/admin/theme.css

@@ -0,0 +1,213 @@
+* {
+	box-sizing: border-box;
+	margin: 0;
+	padding: 0;
+}
+html {
+	scroll-behavior: smooth;
+	height: 100%
+}
+body {
+  	background-color: white;
+	color: black;
+	font-family: Arial, Helvetica, Sans-Serif, sans;
+	font-size: 14pt;
+	margin: 0;
+	padding: 5px;
+	height: 100%
+}
+h1,h2,h3,h4,h5,h6 {
+	text-align: center;
+}
+a {
+	text-decoration: none;
+	color: red;
+}
+a:hover {
+	text-decoration: underline;
+}
+ul {
+	list-style-type: disc;
+	padding-left: 0;
+	margin-left: 14pt;
+}
+#fullscreen, #fullscreenm {
+	width: 100%;
+	height: 100%;
+	display: table;
+}
+#fullscreenm {
+	padding-top: 40px;
+}
+#middlerow {
+	display: table-cell;
+	vertical-align: middle;
+	text-align: center;
+}
+#centertit, #centerbox {
+	font-size: 12pt;
+	margin-right: auto;
+	margin-left: auto;
+	width: 320px;
+	padding: 5px;
+}
+#centertit {
+	font-size: 13pt;
+	padding: 8px;
+	background-color: #916f6f;
+	color: white;
+	text-shadow: 1px 1px 2px black;
+	font-weight: bold;
+	text-align: center;
+	border-radius: 9px 9px 0 0;
+}
+#centerbox {
+	background-color: lightgrey;
+	border-radius: 0 0 9px 9px;
+}
+#logintable {
+	width: 100%;
+}
+#logintable td {
+	text-align: right;
+	width: 1%;
+}
+#logintable .rtd {
+	text-align: left;
+	width: 99%;
+}
+#logintable .tiptd {
+	font-size: 10pt;
+	text-align: center;
+	width: 100%;
+	padding-top:10px;
+	padding-bottom: 10px;
+}
+input {
+	width: 100%;
+	font-size: 11pt;
+	padding-top: 2px;
+	padding-bottom: 2px;
+}
+.button {
+	font-size: 11pt;
+	height: 40px;
+}
+#popup {
+	z-index: 1;
+	display: none;
+	top: 0px;
+	position: fixed;
+	width: 100%;
+	height: 100%;
+	background-color: rgba(0,0,0,0.75);
+	color: white;
+	padding: 0;
+	font-size: 12pt;
+}
+#inpopup {
+	display: table-cell;
+	vertical-align: middle;
+}
+#popupcont {
+	position: relative;
+	margin-left: auto;
+	margin-right: auto;
+	border: 1px solid gray;
+	width: 200px;
+	background-color: white;
+	border-radius: 9px;
+	color: black;
+	padding: 5px;
+	text-align: left;
+}
+#footer {
+	color: white;
+	position: fixed;
+	height: 40px;
+	width: 100%;
+	bottom: 0;
+	left: 0;
+	background-color: rgba(0,0,0,0.85);
+	line-height: 40px;
+	vertical-align: middle;
+}
+
+
+#hmenu {
+	font-size: 14pt;
+	background-color: rgba(0,0,0,0.85);
+	color: white;
+	position: fixed;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 40px;
+	margin: 0;
+	padding: 0;
+	/*box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.4);*/
+	/*padding-left: 40px;*/
+	z-index: 2;
+	/*display: none;*/
+}
+#hmenu ul {
+	list-style-type: none;
+	margin: 0;
+	padding: 0;
+	overflow: visible;
+}
+#hmenu ul li {
+	width: 80pt;
+	height: 40px;
+	text-align: center;
+	display: table-cell;
+	vertical-align: middle;
+	color: #3088D4;
+}
+#hmenu ul li:hover {
+	background-color: rgba(255,255,255,0.10);
+	cursor: default;
+}
+#hmenu ul li a {
+	width: 80pt;
+	line-height: 40px;
+	display: block;
+	color: #3088D4;
+}
+#hmenu .ula {
+	position: absolute;
+	top: 40px;
+	background-color: rgba(0,0,0,0.85);
+	display: none;
+}
+#hmenu .ulb {
+	background-color: rgba(0,0,0,0.85);
+	left: 80pt;
+	position: relative;
+	top: -40px;
+	display: none;
+}
+#hmenu .ula li, .ulb li {
+	width: 80pt;
+	height: 40px;
+	display: block;
+	float: none;
+	text-align: center;
+	line-height: 40px;
+	vertical-align: middle;
+}
+#hmenu .ula li:hover, .ulb li:hover {
+	text-decoration: underline;
+}
+#hmenu .hil {
+	background-color: rgba(255,255,255,0.10);
+	color: #97C3E9;
+}
+#hmenu .hil:hover {
+	cursor: default;
+	text-decoration: none;
+}
+#hmenu .ula li.hil:hover, .ulb li.hil:hover {
+	text-decoration: none;
+}
+

BIN
web/favicon.ico


BIN
web/imgs/ClosedPadlock.png


BIN
web/imgs/Earth.png


BIN
web/imgs/OpenPadlock.png


BIN
web/imgs/TrendingHashtags.png


BIN
web/imgs/icona-180.png


BIN
web/imgs/icona-192.png


BIN
web/imgs/icona-32.png


BIN
web/imgs/icona-512.png


+ 15 - 5
web/index.php

@@ -1,6 +1,6 @@
 <?php
 
-const N="\n";
+define('N',"\n");
 
 $instpath='/mastostart';
 $path=preg_replace('/^'.preg_quote($instpath,'/').'/','',$_SERVER['REQUEST_URI']);
@@ -10,7 +10,7 @@ for ($i=0; $i<$ndir; $i++)
 	$prepath.='../';
 
 $menu=array(
-	'guide'=>array('liadd'=>null, 'href'=>$instpath, 'title'=>'Guide', 'selected'=>false, 'submenu'=>null),
+	'guide'=>array('liadd'=>null, 'href'=>$instpath.'/', 'title'=>'Guide', 'selected'=>false, 'submenu'=>null),
 	'instances'=>array('liadd'=>null, 'href'=>$instpath.'/instances', 'title'=>'Instances', 'selected'=>false, 'submenu'=>null),
 	'about'=>array('liadd'=>null, 'href'=>$instpath.'/about', 'title'=>'About', 'selected'=>false, 'submenu'=>null),
 	'language'=>array('liadd'=>null, 'href'=>null, 'title'=>'Language', 'selected'=>false, 'submenu'=>array(
@@ -25,7 +25,7 @@ function tradmenu($lang) {
 	switch ($lang) {
 		case 'it':
 		$menu['guide']['title']='Guida';
-		$menu['guide']['href'].='/it';
+		$menu['guide']['href'].='it';
 		$menu['instances']['title']='Istanze';
 		$menu['instances']['href'].='/it';
 		$menu['about']['title']='Info';
@@ -42,6 +42,7 @@ switch($path) {
 		'fp'=>'home_en.php',
 		'lang'=>'en',
 		'atit'=>' - Guide',
+		'desc'=>'A thorough introduction to Mastodon',
 		'js'=>array('shsum','guideanchors','scrolltrack')
 	);
 	$menu['guide']['liadd']='onclick="shsum()" onmouseover="this.style.cursor=\'pointer\'; this.style.textDecoration=\'underline\'" onmouseout="this.style.textDecoration=\'none\'"';
@@ -56,6 +57,7 @@ switch($path) {
 		'fp'=>'home_it.php',
 		'lang'=>'it',
 		'atit'=>' - Guida',
+		'desc'=>'Una approfondita introduzione a Mastodon',
 		'js'=>array('shsum','guideanchors','scrolltrack')
 	);
 	tradmenu('it');
@@ -71,6 +73,7 @@ switch($path) {
 		'fp'=>'instances_en.php',
 		'lang'=>'en',
 		'atit'=>' - Instances',
+		'desc'=>'Recommended Mastodon Instances',
 		'js'=>array()
 	);
 	$menu['instances']['href']=null;
@@ -84,6 +87,7 @@ switch($path) {
 		'fp'=>'instances_it.php',
 		'lang'=>'it',
 		'atit'=>' - Istanze',
+		'desc'=>'Istanze Mastodon consigliate',
 		'js'=>array()
 	);
 	tradmenu('it');
@@ -98,6 +102,7 @@ switch($path) {
 		'fp'=>'about_en.php',
 		'lang'=>'en',
 		'atit'=>' - About us',
+		'desc'=>'Infos about Mastodon Startpage’s authors, contributors, license',
 		'js'=>array()
 	);
 	$menu['about']['href']=null;
@@ -111,6 +116,7 @@ switch($path) {
 		'fp'=>'about_it.php',
 		'lang'=>'it',
 		'atit'=>' - Info',
+		'desc'=>'Informazioni sugli autori, i collaboratori e la licenza di Mastodon Startpage',
 		'js'=>array()
 	);
 	tradmenu('it');
@@ -125,6 +131,7 @@ switch($path) {
 		'fp'=>'404.php',
 		'lang'=>'en',
 		'atit'=>' - 404',
+		'desc'=>'Page not found',
 		'js'=>array()
 	);
 	break;
@@ -178,9 +185,12 @@ $cjrand=rand(0,999999);
 <head>
 <title>Mastodon Startpage<?php echo($cont['atit']); ?></title>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-<meta name="description" content="A thorough introduction to Mastodon">
+<meta name="description" content="<?php echo($cont['desc']); ?>">
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
-<link rel="shortcut icon" href="<?php echo($prepath); ?>favicon.ico">
+<link rel="icon" type="image/png" href="<?php echo($prepath); ?>imgs/icona-32.png" sizes="32x32">
+<link rel="icon" type="image/png" href="<?php echo($prepath); ?>imgs/icona-192.png" sizes="192x192">
+<link rel="icon" type="image/png" href="<?php echo($prepath); ?>imgs/icona-512.png" sizes="512x512">
+<link rel="apple-touch-icon-precomposed" href="<?php echo($prepath); ?>imgs/icona-180.png">
 <link rel="stylesheet" type="text/css" href="<?php echo($prepath); ?>theme.css?v=<?php echo($cjrand); ?>">
 <?php
 foreach ($cont['js'] as $val)

+ 8 - 2
web/theme.css

@@ -333,7 +333,13 @@ a:active {
 	#hmenu {
 		font-size: 12pt;
 	}
-	/*#hmenu li, .ula li, .ulb li {
+	#hmenu ul li,
+	#hmenu ul li a,
+	#hmenu .ula li,
+	#hmenu .ulb li {
 		width: 60pt;
-	}*/
+	}
+	#hmenu .ulb {
+		left: 60pt;
+	}
 }