crawler.php 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017
  1. #!/bin/php
  2. <?php
  3. /*
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. require __DIR__ . "/../../vendor/autoload.php";
  16. use LanguageDetection\Language;
  17. define('N',"\n");
  18. $link=false;
  19. $logf=false;
  20. $jsonf=false;
  21. declare(ticks=1);
  22. if(defined("pcntl_signal")) {
  23. pcntl_signal(SIGTERM,'signalHandler');// Termination ('kill' was called)
  24. pcntl_signal(SIGHUP,'signalHandler');// Terminal log-out
  25. pcntl_signal(SIGINT,'signalHandler');// Interrupted (Ctrl-C is pressed)
  26. function signalHandler($signal) {
  27. global $link, $logf, $jsonf;
  28. lecho(N.'Sono stato interrotto.'.N);
  29. if ($link) {
  30. lecho('La connessione MySQL è aperta, la chiudo.'.N);
  31. mysqli_close($link);
  32. }
  33. if ($jsonf) {
  34. lecho('Il file di dump json è aperto, lo chiudo.'.N);
  35. // qui no, altrimenti "riprendi" fa poi casino
  36. // fwrite($jsonf,'"Fine?": true'.N.'}'.N);
  37. fclose($jsonf);
  38. }
  39. if ($logf) {
  40. lecho('Il file di log è aperto, lo chiudo.'.N);
  41. fclose($logf);
  42. }
  43. exit(2);
  44. }
  45. }
  46. $opts=array(
  47. 'timeout'=>3,
  48. 'log'=>true,
  49. 'jsonfp'=>__DIR__.'/instances.json',
  50. 'jsonwrite'=>true,
  51. 'jsonread'=>false
  52. );
  53. use function mysqli_real_escape_string as myesc;
  54. function tosec($str) {
  55. if (preg_match('/^([0-9]+)([smogSMA]?)/',$str,$buf)===1) {
  56. switch ($buf[2]) {
  57. case '':
  58. case 's':
  59. return($buf[1]);
  60. break;
  61. case 'm':
  62. return($buf[1]*60);
  63. break;
  64. case 'o':
  65. return($buf[1]*60*60);
  66. break;
  67. case 'g':
  68. return($buf[1]*60*60*24);
  69. break;
  70. case 'S':
  71. return($buf[1]*60*60*24*7);
  72. break;
  73. case 'M':
  74. return($buf[1]*60*60*24*30);
  75. break;
  76. case 'A':
  77. return($buf[1]*60*60*24*365);
  78. break;
  79. }
  80. } else {
  81. return(false);
  82. }
  83. }
  84. function mexit($msg,$code) {
  85. global $link, $jsonf, $logf;
  86. lecho($msg);
  87. if ($link)
  88. mysqli_close($link);
  89. if ($jsonf)
  90. fclose($jsonf);
  91. if ($logf)
  92. fclose($logf);
  93. exit($code);
  94. }
  95. function lecho($msg,$logonly=false) {
  96. global $opts, $logf;
  97. if (!$logonly)
  98. echo($msg);
  99. if ($opts['log'])
  100. fwrite($logf,$msg);
  101. }
  102. $instsjfp=__DIR__.'/instances.job';
  103. $currinstjfp=__DIR__.'/currinst.job';
  104. if (file_exists($currinstjfp) && file_exists($instsjfp)) {
  105. $riprendi=true;
  106. } else {
  107. $riprendi=false;
  108. }
  109. $logfp=__DIR__.'/crawler.log';
  110. if ($opts['log']) {
  111. if ($riprendi)
  112. $mode=array('a','aggiunta');
  113. else
  114. $mode=array('w','scrittura');
  115. $logf=@fopen($logfp,$mode[0]);
  116. if ($logf===false) {
  117. echo('Non ho potuto aprire in modalità '.$mode[1].' il file di log «'.$logfp.'».'.N);
  118. exit(1);
  119. }
  120. }
  121. $inifp=__DIR__.'/../sec/mastostartadmin.ini';
  122. $iniarr=@parse_ini_file($inifp)
  123. or mexit('Impossibile aprire il file di configurazione «'.$inifp.'»'.N,1);
  124. $link=@mysqli_connect($iniarr['db_host'],$iniarr['db_admin_name'],$iniarr['db_admin_password'],$iniarr['db_name'],$iniarr['db_port'],$iniarr['db_socket'])
  125. or mexit('Impossibile connettersi al server MySQL: '.mysqli_connect_error().N,1);
  126. mysqli_set_charset($link,'utf8mb4')
  127. or mexit(mysqli_error($link).N,1);
  128. $tables=array();
  129. $res=mysqli_query($link,'SHOW TABLES')
  130. or mexit(mysqli_error($link).N,1);
  131. while ($row=mysqli_fetch_row($res)) {
  132. $resb=mysqli_query($link,'SHOW COLUMNS FROM '.$row[0])
  133. or mexit(mysqli_error($link).N,1);
  134. $fields=array();
  135. // lo uso solo per alcuni tipi, quindi non sto a cercare completezza
  136. while ($rowb=mysqli_fetch_assoc($resb)) {
  137. if (preg_match('/(\w+)\((.*)\)( unsigned)?/',$rowb['Type'],$buf)===1) {
  138. switch ($buf[1]) {
  139. case 'char':
  140. case 'varchar':
  141. $fields[$rowb['Field']]=$buf[2];
  142. break;
  143. case 'tinyint':
  144. if (array_key_exists(3,$buf))
  145. $fields[$rowb['Field']]=array('min'=>0,'max'=>255);
  146. else
  147. $fields[$rowb['Field']]=array('min'=>-128,'max'=>127);
  148. break;
  149. case 'smallint':
  150. if (array_key_exists(3,$buf))
  151. $fields[$rowb['Field']]=array('min'=>0,'max'=>65535);
  152. else
  153. $fields[$rowb['Field']]=array('min'=>-32768,'max'=>32767);
  154. break;
  155. case 'mediumint':
  156. if (array_key_exists(3,$buf))
  157. $fields[$rowb['Field']]=array('min'=>0,'max'=>16777215);
  158. else
  159. $fields[$rowb['Field']]=array('min'=>-8388608,'max'=>8388607);
  160. break;
  161. case 'int':
  162. if (array_key_exists(3,$buf))
  163. $fields[$rowb['Field']]=array('min'=>0,'max'=>4294967295);
  164. else
  165. $fields[$rowb['Field']]=array('min'=>-2147483648,'max'=>2147483647);
  166. break;
  167. // bigint non ci sta in php a meno di usare bcmath o gmp che non è detto siano abilitate sul server, in ogni caso poco importa perché valori bigint vengono usati solo internamente al db, non "vengono da fuori"
  168. case 'bigint':
  169. if (array_key_exists(3,$buf))
  170. $fields[$rowb['Field']]=array('min'=>'0','max'=>'18446744073709551615');
  171. else
  172. $fields[$rowb['Field']]=array('min'=>'-9223372036854775808','max'=>'9223372036854775807');
  173. break;
  174. case 'decimal':
  175. // questo è da testare contro un decimale vero
  176. // fatto, il risultato è che in mysql devo usare decimal(14,4)
  177. if (preg_match('/,/',$buf[2])===1) {
  178. $lim=explode(',',$buf[2]);
  179. } else {
  180. $lim[0]=$buf[2];
  181. $lim[1]=0;
  182. }
  183. $int=$lim[0]-$lim[1];
  184. $sint='';
  185. for ($i=0; $i<$int; $i++)
  186. $sint.='9';
  187. $sdec='';
  188. for ($i=0; $i<$lim[1]; $i++)
  189. $sdec.='9';
  190. $max=$sint.'.'.$sdec;
  191. if (array_key_exists(3,$buf))
  192. $fields[$rowb['Field']]=array('min'=>0,'max'=>floatval($max));
  193. else
  194. $fields[$rowb['Field']]=array('min'=>floatval('-'.$max),'max'=>floatval($max));
  195. break;
  196. default:
  197. $fields[$rowb['Field']]=$rowb['Type'];
  198. break;
  199. }
  200. } elseif ($rowb['Type']=='text') {
  201. $fields[$rowb['Field']]=65535;
  202. } else {
  203. $fields[$rowb['Field']]=$rowb['Type'];
  204. }
  205. }
  206. $tables[$row[0]]=$fields;
  207. }
  208. if ($riprendi) {
  209. lecho('Pare che ci sia un lavoro in sospeso, provo a riprenderlo...'.N);
  210. $buf=@file($instsjfp,FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES)
  211. or mexit('Non ho potuto aprire in lettura il file «'.$instsjfp.'».'.N,1);
  212. $insts=array();
  213. foreach ($buf as $line)
  214. $insts[]=$line;
  215. $buf=@file($currinstjfp,FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES)
  216. or mexit('Non ho potuto aprire in lettura il file «'.$currinstjfp.'».'.N,1);
  217. $buf=explode("\t",$buf[0]);
  218. $currinst=array('dom'=>$buf[0], 'i'=>$buf[1], 'qok'=>$buf[2], 'qgood'=>$buf[3]);
  219. $riprendi=true;
  220. }
  221. $tronconi=array();
  222. function flushtronc($id) {
  223. global $tronconi;
  224. foreach ($tronconi as $row) {
  225. if (!is_null($id)) {
  226. if ($row['tab']=='Blacklist')
  227. $eurl='editblinst.php';
  228. elseif ($row['tab']=='Instances')
  229. $eurl='editinst.php';
  230. elseif ($row['tab']=='Languages')
  231. $eurl='editlang.php';
  232. // questo qui sotto non è errore: la tabella InstTrends non ha ID perciò non è editabile, il massimo che si può fare è andare a vedere la tabella Instances e i trends collegati (l'id che viene passato è infatti quello della tabella Instances)
  233. elseif ($row['tab']=='InstTrends')
  234. $eurl='editinst.php';
  235. }
  236. $msg=$row['ctx'].': ho dovuto troncare a '.$row['size'].' caratteri il valore da inserire nella colonna «'.$row['col'].'» della tabella «'.$row['tab'].'» perché troppo lungo ('.$row['len'].' caratteri).';
  237. if (!is_null($id))
  238. $msg.=' Puoi <a href="'.$eurl.'?id='.$id.'">editarlo qui</a>.';
  239. notify($msg,2);
  240. }
  241. $tronconi=array();
  242. }
  243. function truncs($str,$tab,$col,$ctx) {
  244. global $tables, $tronconi;
  245. $size=$tables[strtolower($tab)][$col];
  246. $len=mb_strlen($str,'UTF-8');
  247. if ($len>$size) {
  248. $tronconi[]=array('id'=>null,'tab'=>$tab,'col'=>$col,'ctx'=>$ctx,'len'=>$len,'size'=>$size);
  249. $str=mb_substr($str,0,$size-1,'UTF-8').'…';
  250. }
  251. return($str);
  252. }
  253. function truncn($num,$tab,$col,$ctx) {
  254. global $tables;
  255. if (is_numeric($num)) {
  256. if ($num>$tables[strtolower($tab)][$col]['max']) {
  257. notify($ctx.': ho dovuto troncare «'.$num.'» al valore massimo «'.$tables[strtolower($tab)][$col]['max'].'» che può avere nella colonna «'.$col.'» della tabella «'.$tab.'»).',2);
  258. $num=$tables[strtolower($tab)][$col]['max'];
  259. } elseif ($num<$tables[strtolower($tab)][$col]['min']) {
  260. notify($ctx.': ho dovuto troncare «'.$num.'» al valore minimo «'.$tables[strtolower($tab)][$col]['min'].'» che può avere nella colonna «'.$col.'» della tabella «'.$tab.'»).',2);
  261. $num=$tables[strtolower($tab)][$col]['min'];
  262. }
  263. } else {
  264. notify($ctx.': truncn(): mi aspettavo un numero, invece non lo era; ritorno «0».',3);
  265. $num=0;
  266. }
  267. return($num);
  268. }
  269. $contextopts=array(
  270. 'http'=>array(
  271. 'timeout'=>$opts['timeout']
  272. ),
  273. 'socket'=>array(
  274. 'tcp_nodelay'=>true
  275. )
  276. );
  277. $context=stream_context_create($contextopts);
  278. $blacklist=array();
  279. lecho('Carico la blacklist dal database...'.N);
  280. $res=mysqli_query($link,'SELECT * FROM Blacklist')
  281. or mexit(mysqli_error($link).N,3);
  282. lecho(mysqli_num_rows($res).' istanze nella blacklist.'.N);
  283. while($row=mysqli_fetch_assoc($res)) {
  284. $blacklist[$row['Domain']]=$row;
  285. }
  286. function pgdatetomy($pgdate) {
  287. if (preg_match('/^(\d+)-(\d+)-(\d+)[ T]{1}(\d+):(\d+):(\d+)(\.\d+)?Z?$/',$pgdate,$buf)===1) {
  288. $mtime=mktime($buf[4],$buf[5],$buf[6],$buf[2],$buf[3],$buf[1]);
  289. if (array_key_exists(7,$buf))
  290. $mtime=$mtime+floatval('0'.$buf[7]);
  291. return($mtime);
  292. } else {
  293. notify('pgdatetomy: «'.$pgdate.'» non è un formato di data riconosciuto! Ritorno il magico momento attuale.',3);
  294. return(time());
  295. }
  296. }
  297. function blpgdumplinetomy($line) {
  298. $truefalse=array('f'=>0,'t'=>1);
  299. $row=explode("\t",$line);
  300. $row=array('Domain'=>$row[0],
  301. 'CreatedAt'=>pgdatetomy($row[1]),
  302. 'ModifiedAt'=>pgdatetomy($row[2]),
  303. 'Severity'=>$row[3],
  304. 'RejectMedia'=>$truefalse[$row[4]],
  305. 'RejectReports'=>$truefalse[$row[5]],
  306. 'PublicComment'=>$row[6]);
  307. return($row);
  308. }
  309. if (!$riprendi) {
  310. $blacklistnew=array();
  311. $insts=array();
  312. lecho('Carico le istanze di partenza...'.N);
  313. $res=mysqli_query($link,'SELECT Domain FROM StartNodes')
  314. or mexit(mysqli_error($link).N,3);
  315. lecho(mysqli_num_rows($res).' istanze di partenza.'.N);
  316. while($row=mysqli_fetch_assoc($res)) {
  317. $insts[]=$row['Domain'];
  318. lecho('Recupero la lista delle istanze note a «'.$row['Domain'].'» ... ');
  319. $buf=@file_get_contents('https://'.$row['Domain'].'/api/v1/instance/peers',false,$context);
  320. if ($buf!==false) {
  321. lecho('OK :-)'.N);
  322. $peers=json_decode($buf,true);
  323. foreach ($peers as $pdom) {
  324. if (willtrunc($pdom,'Instances','URI'))
  325. notify('L’istanza «'.$pdom.'» non sarà considerata perché il suo dominio è troppo lungo per il campo «URI» della tabella «Instances» nel DB',1);
  326. if (!in_array($pdom,$insts) && !willtrunc($pdom,'Instances','URI'))
  327. $insts[]=$pdom;
  328. }
  329. } else {
  330. lecho('ERRORE :-('.N);
  331. }
  332. lecho('Recupero la blacklist di «'.$row['Domain'].'» ... ');
  333. $buf=@file_get_contents('https://'.$row['Domain'].'/domain_blocks.txt',false,$context);
  334. if ($buf!==false) {
  335. lecho('OK :-)'.N);
  336. $buf=explode(N,$buf);
  337. foreach ($buf as $line) {
  338. if (preg_match('/(^#.*$)|(^\s*$)/',$line)===0) {
  339. $brow=blpgdumplinetomy($line);
  340. if (!array_key_exists($brow['Domain'],$blacklist)) {
  341. $blacklistnew[$brow['Domain']]=$brow;
  342. }
  343. $blacklist[$brow['Domain']]=$brow;
  344. }
  345. }
  346. } else {
  347. lecho('ERRORE :-('.N);
  348. }
  349. }
  350. foreach ($blacklistnew as $row) {
  351. if (!willtrunc($row['Domain'],'Blacklist','Domain')) {
  352. mysqli_query($link,'INSERT INTO Blacklist (ID, Domain, CreatedAt, ModifiedAt, Severity, RejectMedia, RejectReports, PrivateComment, PublicComment) VALUES (NULL, \''.myesc($link,$row['Domain']).'\', \''.myesc($link,$row['CreatedAt']).'\', \''.myesc($link,$row['ModifiedAt']).'\', \''.myesc($link,$row['Severity']).'\', \''.myesc($link,$row['RejectMedia']).'\', \''.myesc($link,$row['RejectReports']).'\', NULL, \''.myesc($link,$row['Domain']).'\')')
  353. or mexit(mysqli_error($link).N,3);
  354. flushtronc(mysqli_insert_id($link));
  355. } else {
  356. notify('Non ho potuto inserire «'.$row['Domain'].'» nella tabella delle istanze blacklistate perché il dominio è troppo lungo per il campo corrispondente nel DB.',2);
  357. }
  358. }
  359. //lecho('Carico le istanze note dal DB e aggiungo alla lista di quelle da controllare quelle che non ci sono già.'.N);
  360. $res=mysqli_query($link,'SELECT URI FROM Instances')
  361. or mexit(mysqli_error($link).N,3);
  362. while($row=mysqli_fetch_assoc($res)) {
  363. if (!in_array($row['URI'],$insts))
  364. $insts[]=$row['URI'];
  365. }
  366. sort($insts);
  367. ksort($blacklist);
  368. ksort($blacklistnew);
  369. lecho('Istanze recuperate: '.count($insts).N);
  370. lecho('Istanze blacklistate: '.count($blacklist).', di cui '.count($blacklistnew).' nuove aggiunte al DB.'.N);
  371. $instsf=@fopen($instsjfp,'w')
  372. or mexit('Non ho potuto aprire in scrittura il file «'.$instsjfp.'».'.N,1);
  373. foreach ($insts as $dom)
  374. fwrite($instsf,$dom.N);
  375. fclose($instsf);
  376. }
  377. function willtrunc($str,$tab,$col) {
  378. global $tables;
  379. if (mb_strlen($str,'UTF-8')>$tables[strtolower($tab)][$col])
  380. return(true);
  381. else
  382. return(false);
  383. }
  384. function b2i($bool,$pre) {
  385. if (is_bool($bool)) {
  386. if ($bool)
  387. return(1);
  388. else
  389. return(0);
  390. } else {
  391. notify($pre.'il valore «'.$bool.'» non è booleano, lo assumo come falso e ritorno «0».',2);
  392. return(0);
  393. }
  394. }
  395. //is array, array key exists and value is not null
  396. function akeavinn($key,&$arr) {
  397. if (is_array($arr) && array_key_exists($key,$arr) && !is_null($arr[$key]))
  398. return(true);
  399. else
  400. return(false);
  401. }
  402. function nempty($str) {
  403. if (preg_match('/^\s*$/',$str)===1)
  404. return(null);
  405. else
  406. return($str);
  407. }
  408. function subarimp($glue,$key,&$arr) {
  409. $str='';
  410. $i=1;
  411. $carr=count($arr);
  412. foreach ($arr as $inarr) {
  413. $str.=$inarr[$key];
  414. if ($i<$carr)
  415. $str.=$glue;
  416. $i++;
  417. }
  418. return($str);
  419. }
  420. function notify($msg,$sev) {
  421. global $link, $tables;
  422. lecho('NOTIFICAZIÒ: '.strip_tags($msg).N);
  423. mysqli_query($link,'INSERT INTO Notifications (ID, Notification, Severity, Microtime, Seen) VALUES (NULL, \''.myesc($link,mb_substr($msg,0,$tables['notifications']['Notification'],'UTF-8')).'\', '.$sev.', \''.microtime(true).'\', 0)')
  424. or mexit(mysqli_error($link).N,3);
  425. }
  426. /** <LANGUAGE MANAGEMENT> */
  427. /**
  428. * Effettua una chiamata alla API di Mastodon.
  429. *
  430. * @param string $host L'host da chiamare (e.g.: "mastodon.bida.im")
  431. * @param string $path Il path della API (e.g.: "/api/v1/timelines/public?local=true")
  432. * @return mixed L'oggetto ritornato dalla chiamata, già parsato da json_decode, o NULL se la chiamata fallisce
  433. */
  434. function get_api($host, $path) {
  435. global $context;
  436. try {
  437. $buf = @file_get_contents('https://' . $host . $path, false, $context);
  438. } catch(Exception $e) {
  439. echo "error:";
  440. echo $e;
  441. return NULL;
  442. }
  443. if ($buf!==false) {
  444. $data = json_decode($buf, true);
  445. return $data;
  446. } else {
  447. return NULL;
  448. }
  449. }
  450. /**
  451. * Torna un elenco di linguaggi riconosciuti nel toot fornito con relativa probabilità.
  452. *
  453. * @param mixed $toot Il toot da analizzare, come ritornato dalle API
  454. * @return array Mappa tra codice lingua e probabilità che il toot sia in quella lingua.
  455. */
  456. function get_toot_languages($toot) {
  457. $l = $toot['language'];
  458. $res = [];
  459. if($l !== NULL) {
  460. // la lingua è specificata già nel toot: usa quella
  461. $langs[$l] = 1;
  462. } else {
  463. // la lingua non è specificata: deducila
  464. $text = strip_tags($toot['content']);
  465. $ld = new Language;
  466. $langs = $ld->detect($text)->bestResults()->close();
  467. }
  468. // raggruppa le lingue derivate, e.g.: "zh" e "zh-CN"
  469. $grouped_langs = array();
  470. foreach($langs as $key => $value) {
  471. $l = explode("-", $key)[0];
  472. if(array_key_exists($l, $grouped_langs)) {
  473. $grouped_langs[$l] = max($grouped_langs[$l], $value);
  474. } else {
  475. $grouped_langs[$l] = $value;
  476. }
  477. }
  478. return $grouped_langs;
  479. }
  480. /**
  481. * Date le probabilità di lingua per ogni toot, calcola la media.
  482. *
  483. * @param array $detected_langs Array di mappe tra lingua e probabilità
  484. * @return array Mappa tra lingua e probabilità
  485. */
  486. function summary($detected_langs) {
  487. $res = Array();
  488. foreach($detected_langs as $langs) {
  489. foreach($langs as $l => $weight) {
  490. if(!array_key_exists($l, $res)) {
  491. $res[$l] = 0;
  492. }
  493. $res[$l] += $weight;
  494. }
  495. }
  496. foreach($res as $l => $sumweight) {
  497. $res[$l] = $sumweight / count($detected_langs);
  498. }
  499. return $res;
  500. }
  501. /**
  502. * Helper function per usort: compara due array usando il primo elemento.
  503. *
  504. * @param array $entry1 Primo array da comparare
  505. * @param array $entry2 Secondo array da comparare
  506. * @return number -1, 0 o 1 a seconda che $entry1[0] sia minore, uguale o superiore a $entry2[0]
  507. */
  508. function sort_weights($entry1, $entry2) {
  509. $w1 = $entry1[0];
  510. $w2 = $entry2[0];
  511. return $w1 < $w2 ? 1 : $w1 == $w2 ? 0 : -1;
  512. }
  513. /**
  514. * Data una mappa di lingue, ritorna una lista di linguaggi considerati probabili.
  515. *
  516. * @param array $summary Mappa tra lingue e probabilità
  517. * @return string[] Elenco di lingue considerate probabili
  518. */
  519. function get_languages($summary) {
  520. $lst = [];
  521. foreach($summary as $code => $weight) {
  522. $lst[] = [$weight, $code];
  523. }
  524. usort($lst, 'sort_weights');
  525. $languages = [];
  526. $lastweight = 0;
  527. foreach($lst as $entry) {
  528. $l = $entry[1];
  529. $weight = $entry[0];
  530. if($weight < $lastweight * 2 / 3) {
  531. break;
  532. }
  533. $languages[] = $l;
  534. $lastweight = $weight;
  535. }
  536. return $languages;
  537. }
  538. /**
  539. * Ritorna una lista di lingue probabili per la data istanza.
  540. *
  541. * @param string $host Hostname dell'istanza (e.g.: "mastodon.bida.im")
  542. * @return string[] Lista di lingue probabili
  543. */
  544. function get_instance_langs($host) {
  545. $data = get_api($host, '/api/v1/timelines/public?local=true');
  546. if($data == NULL) {
  547. return [];
  548. }
  549. $detected_langs = array_map('get_toot_languages', $data);
  550. $summary = summary($detected_langs);
  551. $languages = get_languages($summary);
  552. return $languages;
  553. }
  554. /** </LANGUAGE MANAGEMENT> */
  555. /**
  556. * ucfirst UTF-8 aware function
  557. *
  558. * @param string $string
  559. * @return string
  560. * @see http://ca.php.net/ucfirst
  561. */
  562. function my_ucfirst($string, $e ='utf-8') {
  563. if (function_exists('mb_strtoupper') && function_exists('mb_substr') && !empty($string)) {
  564. $string = mb_strtolower($string, $e);
  565. $upper = mb_strtoupper($string, $e);
  566. preg_match('#(.)#us', $upper, $matches);
  567. $string = $matches[1] . mb_substr($string, 1, mb_strlen($string, $e), $e);
  568. } else {
  569. $string = ucfirst($string);
  570. }
  571. return $string;
  572. }
  573. function langs($instid, $uri) {
  574. global $info, $instrow, $link;
  575. $instlangs=array();
  576. $languages = get_instance_langs($uri);
  577. if(count($languages) == 0 && akeavinn('languages',$info)) {
  578. $languages = $info['languages'];
  579. }
  580. echo "Lingue trovate: " . implode(", ", $languages).N;
  581. $pos=0;
  582. foreach($languages as $lang) {
  583. $res=mysqli_query($link,'SELECT * FROM Languages WHERE Code=\''.myesc($link,$lang).'\'')
  584. or mexit(mysqli_error($link).N,3);
  585. if (mysqli_num_rows($res)<1) {
  586. $NameIt=myesc($link,truncs(my_ucfirst(locale_get_display_name($lang,'it')),'Languages','NameIT','«'.$instrow['URI'].'»'));
  587. $NameEn=myesc($link,truncs(my_ucfirst(locale_get_display_name($lang,'en')),'Languages','NameEN','«'.$instrow['URI'].'»'));
  588. $NameFr=myesc($link,truncs(my_ucfirst(locale_get_display_name($lang,'fr')),'Languages','NameFR','«'.$instrow['URI'].'»'));
  589. $NameEs=myesc($link,truncs(my_ucfirst(locale_get_display_name($lang,'es')),'Languages','NameES','«'.$instrow['URI'].'»'));
  590. $NameOrig=myesc($link,truncs(my_ucfirst(locale_get_display_name($lang,$lang)),'Languages','NameES','«'.$instrow['URI'].'»'));
  591. $q = 'INSERT INTO Languages (ID, Code, NameIT, NameEN, NameFR, NameES, NameOrig) VALUES (NULL, \''.myesc($link,truncs($lang,'Languages','Code','«'.$instrow['URI'].'»')).'\', \''.$NameIt.'\', \''.$NameEn.'\', \''.$NameFr.'\', \''.$NameEs.'\', \''.$NameOrig.'\')';
  592. mysqli_query($link, $q)
  593. or mexit(mysqli_error($link).N,3);
  594. $langid=mysqli_insert_id($link);
  595. flushtronc($langid);
  596. } else {
  597. $row=mysqli_fetch_assoc($res);
  598. $langid=$row['ID'];
  599. }
  600. $pos++;
  601. $instlangs[]=array('InstID'=>$instid,'LangID'=>$langid,'Pos'=>$pos,'Code'=>$lang);
  602. }
  603. // }
  604. return($instlangs);
  605. }
  606. function varbdump($var) {
  607. ob_start();
  608. var_dump($var);
  609. $content=ob_get_contents();
  610. ob_end_clean();
  611. return($content);
  612. }
  613. function mdasortbykey(&$arr,$key,$rev=false) {
  614. $karr=array();
  615. foreach ($arr as $akey=>$subarr)
  616. $karr[$subarr[$key]]=array($akey,$subarr);
  617. if (!$rev)
  618. ksort($karr);
  619. else
  620. krsort($karr);
  621. $arr=array();
  622. foreach ($karr as $akey=>$subarr)
  623. $arr[$subarr[0]]=$subarr[1];
  624. }
  625. /*
  626. * Nodeinfo ('https://'.$dom.'/nodeinfo/2.0') è stato aggiunto nella 3.0.0
  627. * Trends ('https://'.$dom.'/api/v1/trends') è stato aggiunto nella 3.0.0
  628. * Activity ('https://'.$dom.'/api/v1/instance/activity') è stato aggiunto nella 2.1.2
  629. */
  630. if ($opts['jsonwrite']) {
  631. if ($riprendi)
  632. $mode=array('a','aggiunta');
  633. else
  634. $mode=array('w','scrittura');
  635. $jsonf=@fopen($opts['jsonfp'],$mode[0])
  636. or mexit('Non ho potuto aprire in modalità '.$mode[1].' il file di dump delle info json «'.$opts['jsonfp'].'».',1);
  637. if ($mode[0]=='w')
  638. fwrite($jsonf,'{'.N);
  639. }
  640. $cinsts=count($insts);
  641. $i=0;
  642. $qok=0;
  643. $qgood=0;
  644. if ($riprendi) {
  645. $i=$currinst['i'];
  646. $qok=$currinst['qok'];
  647. $qgood=$currinst['qgood'];
  648. }
  649. while ($i<$cinsts) {
  650. $dom=$insts[$i];
  651. @file_put_contents($currinstjfp,$dom."\t".$i."\t".$qok."\t".$qgood.N)
  652. or mexit('Non ho potuto aprire in scrittura il file «'.$currinstjfp.'».',1);
  653. $i++;
  654. $ok=true;
  655. $info=null;
  656. lecho('~~~~~~~~~~~~~~~'.N);
  657. lecho('Provo a recuperare info su «'.$dom.'» ['.$i.'/'.$cinsts.' ('.$qok.' OK; '.$qgood.' BUONE) - '.round(100/$cinsts*$i).'%]'.N);
  658. lecho('Provo a recuperare le informazioni API sull’istanza ... ');
  659. $buf=@file_get_contents('https://'.$dom.'/api/v1/instance',false,$context);
  660. if ($buf!==false) {
  661. $info=json_decode($buf,true);
  662. if (is_array($info)) {
  663. lecho('OK :-)'.N);
  664. lecho('Provo a recuperare le informazioni Nodeinfo sull’istanza ... ');
  665. $buf=@file_get_contents('https://'.$dom.'/nodeinfo/2.0',false,$context);
  666. if ($buf!==false) {
  667. lecho('OK :-)'.N);
  668. $info['x-nodeinfo']=json_decode($buf,true);
  669. // per ora teniamo solo quelle che, se si identificano, si identificano come mastodon o corgidon (derivato di mastodon)
  670. // teniamo d'occhio le notifiche di cui sotto per includere eventualmente altri derivati di mastodon?
  671. // visti fin qui, verificare cosa sono: epicyon
  672. if (is_array($info['x-nodeinfo']) && array_key_exists('software',$info['x-nodeinfo']) && array_key_exists('name',$info['x-nodeinfo']['software']) &&!is_null($info['x-nodeinfo']['software']['name'])) {
  673. if (preg_match('/^mastodon|corgidon/',$info['x-nodeinfo']['software']['name'])===0)
  674. $ok=false;
  675. $res=mysqli_query($link,'SELECT Name FROM Platforms WHERE Name=\''.myesc($link,$info['x-nodeinfo']['software']['name']).'\'')
  676. or mexit(mysqli_error($link).N,3);
  677. if (mysqli_num_rows($res)<1) {
  678. $res=mysqli_query($link,'INSERT INTO Platforms (Name) VALUES (\''.myesc($link,truncs($info['x-nodeinfo']['software']['name'],'Platforms','Name','«'.$info['uri'].'»')).'\')')
  679. or mexit(mysqli_error($link).N,3);
  680. notify('«'.$info['uri'].'» utilizza come software «'.$info['x-nodeinfo']['software']['name'].'»; lo aggiungo alla tabella delle piattaforme incontrate. Se non si tratta di mastodon o corgidon, che già vengono accettati, sarebbe buona cosa verificare se è una variante di mastodon e quanto è compatibile, per valutare se accettare le istanze che lo utilizzano.',1);
  681. flushtronc(null);
  682. }
  683. }
  684. } else {
  685. lecho('ERRORE :-('.N);
  686. }
  687. if ($ok && array_key_exists('version',$info)) {
  688. if ($info['version']>='2.1.2') {
  689. lecho('Provo a recuperare le informazioni API sull’attività dell’istanza ... ');
  690. $buf=@file_get_contents('https://'.$dom.'/api/v1/instance/activity',false,$context);
  691. if ($buf!==false) {
  692. lecho('OK :-)'.N);
  693. $info['x-activity']=json_decode($buf,true);
  694. } else {
  695. lecho('ERRORE :-('.N);
  696. }
  697. }
  698. if ($info['version']>='3.0.0') {
  699. lecho('Provo a recuperare le informazioni API sui trends dell’istanza ... ');
  700. $buf=@file_get_contents('https://'.$dom.'/api/v1/trends',false,$context);
  701. if ($buf!==false) {
  702. lecho('OK :-)'.N);
  703. $info['x-trends']=json_decode($buf,true);
  704. } else {
  705. lecho('ERRORE :-('.N);
  706. }
  707. }
  708. }
  709. } else {
  710. $ok=false;
  711. lecho('ERRORE :-('.N);
  712. }
  713. } else {
  714. $ok=false;
  715. lecho('ERRORE :-('.N);
  716. // questo è anche il limbo delle istanze che non rispondono, perciò controlliamo se già esistono nel db e, nel caso, aggiorniamo InstChecks
  717. $res=mysqli_query($link,'SELECT * FROM Instances WHERE URI=\''.myesc($link,mb_substr($dom,0,$tables['instances']['URI'],'UTF-8')).'\'')
  718. or mexit(mysqli_error($link).N,3);
  719. if (mysqli_num_rows($res)>0) {
  720. lecho('«'.$dom.'» non risponde, ma è presente nel database; aggiorno InstChecks.'.N);
  721. $row=mysqli_fetch_assoc($res);
  722. mysqli_query($link,'INSERT INTO InstChecks (InstID, Time, Status) VALUES ('.$row['ID'].', '.time().', 0)')
  723. or mexit(mysqli_error($link).N,3);
  724. }
  725. }
  726. if (is_array($info) && count($info)>0) {
  727. lecho('Dumpone json di tutte le info recuperate:'.N.json_encode($info,JSON_PRETTY_PRINT).N,true);
  728. if ($opts['jsonwrite'])
  729. fwrite($jsonf,'"'.$dom.'": '.json_encode($info,JSON_PRETTY_PRINT).','.N);
  730. }
  731. if ($ok && !is_null($info) && akeavinn('uri',$info) && !is_null(nempty($info['uri'])) && !willtrunc($info['uri'],'Instances','URI') && akeavinn('version',$info) && preg_match('/pleroma|pixelfed/i',$info['version'])===0) {
  732. $qok++;
  733. $instrow=array('ID'=>null, 'New'=>0, 'Good'=>0, 'Chosen'=>0, 'Visible'=>0, 'Blacklisted'=>0, 'URI'=>null, 'Title'=>null, 'ShortDesc'=>null, 'LongDesc'=>null, 'OurDesc'=>null, 'LocalityID'=>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);
  734. if (array_key_exists($info['uri'],$blacklist))
  735. $instrow['Blacklisted']=1;
  736. $instrow['URI']=$info['uri'];
  737. if (akeavinn('title',$info))
  738. $instrow['Title']=nempty(truncs($info['title'],'Instances','Title','«'.$instrow['URI'].'»'));
  739. if (akeavinn('short_description',$info))
  740. $instrow['ShortDesc']=nempty(truncs($info['short_description'],'Instances','ShortDesc','«'.$instrow['URI'].'»'));
  741. if (akeavinn('description',$info))
  742. $instrow['LongDesc']=nempty(truncs($info['description'],'Instances','LongDesc','«'.$instrow['URI'].'»'));
  743. if (akeavinn('email',$info))
  744. $instrow['Email']=nempty(truncs($info['email'],'Instances','Email','«'.$instrow['URI'].'»'));
  745. if (akeavinn('version',$info))
  746. $instrow['Version']=nempty(truncs($info['version'],'Instances','Version','«'.$instrow['URI'].'»'));
  747. if (akeavinn('stats',$info)) {
  748. if (akeavinn('user_count',$info['stats']))
  749. $instrow['UserCount']=truncn($info['stats']['user_count'],'Instances','UserCount','«'.$instrow['URI'].'»');
  750. if (akeavinn('status_count',$info['stats']))
  751. $instrow['StatusCount']=truncn($info['stats']['status_count'],'Instances','StatusCount','«'.$instrow['URI'].'»');
  752. if (akeavinn('domain_count',$info['stats']))
  753. $instrow['DomainCount']=truncn($info['stats']['domain_count'],'Instances','DomainCount','«'.$instrow['URI'].'»');
  754. }
  755. if (akeavinn('thumbnail',$info))
  756. $instrow['Thumb']=nempty(truncs($info['thumbnail'],'Instances','Thumb','«'.$instrow['URI'].'»'));
  757. if (akeavinn('max_toot_chars',$info))
  758. $instrow['MaxTootChars']=truncn($info['max_toot_chars'],'Instances','MaxTootChars','«'.$instrow['URI'].'»');
  759. if (akeavinn('registrations',$info))
  760. $instrow['RegOpen']=b2i($info['registrations'],'Istanza «'.$instrow['URI'].'»: ');
  761. if (akeavinn('approval_required',$info))
  762. $instrow['RegReqApproval']=b2i($info['approval_required'],'Istanza «'.$instrow['URI'].'»: ');
  763. if (akeavinn('contact_account',$info)) {
  764. if (akeavinn('acct',$info['contact_account']))
  765. $instrow['AdmAccount']=nempty(truncs($info['contact_account']['acct'],'Instances','AdmAccount','«'.$instrow['URI'].'»'));
  766. if (akeavinn('display_name',$info['contact_account']))
  767. $instrow['AdmDisplayName']=nempty(truncs($info['contact_account']['display_name'],'Instances','AdmDisplayName','«'.$instrow['URI'].'»'));
  768. if (akeavinn('created_at',$info['contact_account']))
  769. $instrow['AdmCreatedAt']=pgdatetomy($info['contact_account']['created_at']);
  770. if (akeavinn('note',$info['contact_account']))
  771. $instrow['AdmNote']=nempty(truncs(strip_tags($info['contact_account']['note'],'<a>'),'Instances','AdmNote','«'.$instrow['URI'].'»'));
  772. if (akeavinn('url',$info['contact_account']))
  773. $instrow['AdmURL']=nempty(truncs($info['contact_account']['url'],'Instances','AdmURL','«'.$instrow['URI'].'»'));
  774. if (akeavinn('avatar',$info['contact_account']))
  775. $instrow['AdmAvatar']=nempty(truncs($info['contact_account']['avatar'],'Instances','AdmAvatar','«'.$instrow['URI'].'»'));
  776. if (akeavinn('header',$info['contact_account']))
  777. $instrow['AdmHeader']=nempty(truncs($info['contact_account']['header'],'Instances','AdmHeader','«'.$instrow['URI'].'»'));
  778. }
  779. if (akeavinn('x-nodeinfo',$info)) {
  780. if (akeavinn('software',$info['x-nodeinfo']) && akeavinn('name',$info['x-nodeinfo']['software']))
  781. $instrow['Software']=nempty(truncs($info['x-nodeinfo']['software']['name'],'Instances','Software','«'.$instrow['URI'].'»'));
  782. if (akeavinn('usage',$info['x-nodeinfo']) && akeavinn('users',$info['x-nodeinfo']['usage'])) {
  783. if (akeavinn('activeMonth',$info['x-nodeinfo']['usage']['users']))
  784. $instrow['ActiveUsersMonth']=truncn($info['x-nodeinfo']['usage']['users']['activeMonth'],'Instances','ActiveUsersMonth','«'.$instrow['URI'].'»');
  785. if (akeavinn('activeHalfyear',$info['x-nodeinfo']['usage']['users']))
  786. $instrow['ActiveUsersHalfYear']=truncn($info['x-nodeinfo']['usage']['users']['activeHalfyear'],'Instances','ActiveUsersHalfYear','«'.$instrow['URI'].'»');
  787. }
  788. }
  789. $whynot=array();
  790. if ($instrow['Blacklisted']==1)
  791. $whynot[]='è nella blacklist';
  792. if (is_null($instrow['RegOpen'])) {
  793. $whynot[]='non se ne conosce lo stato delle registrazioni (aperte/chiuse)';
  794. } elseif ($instrow['RegOpen']==0) {
  795. $whynot[]='ha le registrazioni chiuse';
  796. }
  797. if (is_null($instrow['UserCount'])) {
  798. $whynot[]='non se ne conosce il numero di utenti';
  799. } elseif ($instrow['UserCount']<10 || $instrow['UserCount']>30000) {
  800. $whynot[]='il numero di utenti non è compreso tra 10 e 30.000';
  801. }
  802. if (is_null($instrow['DomainCount'])) {
  803. $whynot[]='non se ne conosce il numero di istanze note';
  804. } elseif ($instrow['DomainCount']<500) {
  805. $whynot[]='il numero di istanze note è minore di 500';
  806. }
  807. if (!is_null($instrow['ActiveUsersMonth'])) {
  808. if ($instrow['ActiveUsersMonth']<10)
  809. $whynot[]='il numero di utenti attivi nell’ultimo mese è minore di 10';
  810. } elseif (!is_null($instrow['StatusCount']) && $instrow['StatusCount']/$instrow['UserCount']<10) {
  811. $whynot[]='il numero medio di toots per utente è minore di 10';
  812. }
  813. if (count($whynot)==0) {
  814. $instrow['Good']=1;
  815. lecho('Siamo in presenza di un’istanza BUONA! :-)'.N);
  816. $qgood++;
  817. } else {
  818. lecho('Siamo in presenza di un’istanza CATTIVA: '.implode('; ',$whynot).' :-('.N);
  819. }
  820. $res=mysqli_query($link,'SELECT * FROM Instances WHERE URI=\''.myesc($link,$instrow['URI']).'\'')
  821. or mexit(mysqli_error($link).N,3);
  822. if (mysqli_num_rows($res)>0) {
  823. lecho('«'.$instrow['URI'].'» è già presente nel DB, la aggiorno...'.N);
  824. $oldinstrow=mysqli_fetch_assoc($res);
  825. flushtronc($oldinstrow['ID']);
  826. $instid=$oldinstrow['ID'];
  827. $instrow['ID']=$oldinstrow['ID'];
  828. $instrow['New']=$oldinstrow['New'];
  829. if ($instrow['Good']==1 && $oldinstrow['Good']==0) {
  830. notify('L’istanza «<a href="editinst.php?id='.$instrow['ID'].'">'.$instrow['URI'].'</a>» non era papabile, ma lo è diventata!',1);
  831. } elseif ($instrow['Good']==0 && $oldinstrow['Good']==1) {
  832. notify('L’istanza «<a href="editinst.php?id='.$instrow['ID'].'">'.$instrow['URI'].'</a>» era papabile, ma non lo è più per i seguenti motivi: '.implode('; ',$whynot),3);
  833. }
  834. $instrow['Chosen']=$oldinstrow['Chosen'];
  835. $instrow['Visible']=$oldinstrow['Visible'];
  836. if ($instrow['ShortDesc']!=$oldinstrow['ShortDesc'])
  837. notify('<p>La «Descrizione breve» dell’istanza «<a href="editinst.php?id='.$instrow['ID'].'">'.$instrow['URI'].'</a>» è cambiata. La vecchia era...</p><div class="valdesc">'.$oldinstrow['ShortDesc'].'</div><p>La nuova è...</p><div class="valdesc">'.$instrow['ShortDesc'].'</div>',1);
  838. if ($instrow['LongDesc']!=$oldinstrow['LongDesc'])
  839. notify('<p>La «Descrizione lunga» dell’istanza «<a href="editinst.php?id='.$instrow['ID'].'">'.$instrow['URI'].'</a>» è cambiata. La vecchia era...</p><div class="valdesc">'.$oldinstrow['LongDesc'].'</div><p>La nuove è...</p><div class="valdesc">'.$instrow['LongDesc'].'</div>',1);
  840. $instrow['OurDesc']=$oldinstrow['OurDesc'];
  841. $instrow['LocalityID']=$oldinstrow['LocalityID'];
  842. $query='UPDATE Instances SET ';
  843. foreach ($instrow as $field=>$value) {
  844. if (!is_null($value))
  845. $query.=$field.'=\''.myesc($link,$value).'\', ';
  846. else
  847. $query.=$field.'=NULL, ';
  848. }
  849. $query=substr($query,0,-2).' WHERE Instances.ID='.$instrow['ID'];
  850. lecho('QUERONA DI UPDATE: «'.$query.'».'.N);
  851. mysqli_query($link,$query)
  852. or mexit(mysqli_error($link).N,3);
  853. $res=mysqli_query($link,'SELECT InstID, LangID, Pos, Code FROM InstLangs LEFT JOIN Languages ON Languages.ID=LangID WHERE InstID='.$instrow['ID'].' ORDER BY Pos ASC')
  854. or mexit(mysqli_error($link).N,3);
  855. $oldinstlangs=array();
  856. while ($row=mysqli_fetch_assoc($res))
  857. $oldinstlangs[]=$row;
  858. $instlangs=langs($instrow['ID'], $instrow['URI']);
  859. if ($instlangs!=$oldinstlangs) {
  860. notify('La lista delle lingue utilizzate dichiarate dall’istanza «<a href="editinst.php?id='.$instrow['ID'].'">'.$instrow['URI'].'</a>» è cambiata da «'.subarimp(', ','Code',$oldinstlangs).'» a «'.subarimp(', ','Code',$instlangs).'».',1);
  861. mysqli_query($link,'DELETE FROM InstLangs WHERE InstID='.$instrow['ID'])
  862. or mexit(mysqli_error($link).N,3);
  863. foreach ($instlangs as $row) {
  864. mysqli_query($link,'INSERT INTO InstLangs (InstID, LangID, Pos) VALUES ('.$row['InstID'].', '.$row['LangID'].', '.$row['Pos'].')')
  865. or mexit(mysqli_error($link).N,3);
  866. }
  867. }
  868. } else {
  869. lecho('«'.$info['uri'].'» non è già presente nel DB, la aggiungo...'.N);
  870. $instrow['New']=1;
  871. $fields=array();
  872. $values='';
  873. foreach ($instrow as $field=>$value) {
  874. $fields[]=$field;
  875. if (!is_null($value))
  876. $values.='\''.myesc($link,$value).'\', ';
  877. else
  878. $values.='NULL, ';
  879. }
  880. $values=substr($values,0,-2);
  881. $query='INSERT INTO Instances ('.implode(', ',$fields).') VALUES ('.$values.')';
  882. lecho('QUERONA DI INSERT: «'.$query.'»'.N);
  883. mysqli_query($link,$query)
  884. or mexit(mysqli_error($link).N,3);
  885. $instid=mysqli_insert_id($link);
  886. flushtronc($instid);
  887. $instlangs=langs($instid, $instrow['URI']);
  888. foreach ($instlangs as $row) {
  889. mysqli_query($link,'INSERT INTO InstLangs (InstID, LangID, Pos) VALUES ('.$row['InstID'].', '.$row['LangID'].', '.$row['Pos'].')')
  890. or mexit(mysqli_error($link).N,3);
  891. }
  892. if ($instrow['Good']==1)
  893. notify('La nuova istanza «<a href="editinst.php?id='.$instid.'">'.$instrow['URI'].'</a>» è papabile!',1);
  894. }
  895. if (array_key_exists('x-activity',$info) && is_array($info['x-activity'])) {
  896. mysqli_query($link,'DELETE FROM InstActivity WHERE InstID='.$instid);
  897. $pos=0;
  898. foreach ($info['x-activity'] as $buf) {
  899. if (akeavinn('week',$buf) && akeavinn('statuses',$buf) && akeavinn('logins',$buf) && akeavinn('registrations',$buf)) {
  900. $pos++;
  901. $query='INSERT INTO InstActivity (InstID, Week, Statuses, Logins, Registrations, Pos) VALUES (\''.$instid.'\', \''.myesc($link,$buf['week']).'\', \''.myesc($link,$buf['statuses']).'\', \''.myesc($link,$buf['logins']).'\', \''.myesc($link,$buf['registrations']).'\', '.$pos.')';
  902. mysqli_query($link,$query)
  903. or mexit(mysqli_error($link).N,3);
  904. }
  905. }
  906. }
  907. if (array_key_exists('x-trends',$info) && is_array($info['x-trends'])) {
  908. $trends=array();
  909. foreach ($info['x-trends'] as $buf) {
  910. if (akeavinn('name',$buf) && akeavinn('url',$buf) && akeavinn('history',$buf) && is_array($buf['history'])) {
  911. $trend=0;
  912. foreach ($buf['history'] as $row) {
  913. if ($row['uses']>0)
  914. $trend+=($row['accounts']/$row['uses']);
  915. }
  916. $trends[]=array(
  917. 'InstID'=>$instid,
  918. 'LastDay'=>$buf['history'][0]['day'],
  919. 'Name'=>$buf['name'],
  920. 'URL'=>$buf['url'],
  921. 'Pos'=>null,
  922. 'trend'=>$trend
  923. );
  924. }
  925. }
  926. mdasortbykey($trends,'trend',true);
  927. // print_r($trends);
  928. mysqli_query($link,'DELETE FROM InstTrends WHERE InstID='.$instid);
  929. $pos=0;
  930. foreach ($trends as $trend) {
  931. $pos++;
  932. $query='INSERT INTO InstTrends (InstID, LastDay, Name, URL, Pos) VALUES ('.$trend['InstID'].', \''.$trend['LastDay'].'\', \''.myesc($link,truncs($trend['Name'],'InstTrends','Name','«'.$instrow['URI'].'»')).'\', \''.myesc($link,truncs($trend['URL'],'InstTrends','URL','«'.$instrow['URI'].'»')).'\', '.$pos.')';
  933. mysqli_query($link,$query)
  934. or mexit(mysqli_error($link).N,3);
  935. // questo qui sotto non è errore, vedi il commento relativo nella funzione
  936. flushtronc($instid);
  937. }
  938. }
  939. mysqli_query($link,'INSERT INTO InstChecks (InstID, Time, Status) VALUES ('.$instid.', '.time().', 1)')
  940. or mexit(mysqli_error($link).N,3);
  941. }
  942. }
  943. mysqli_close($link);
  944. if ($opts['jsonwrite']) {
  945. fwrite($jsonf,'"Fine?": true'.N.'}'.N);
  946. fclose($jsonf);
  947. }
  948. unlink($instsjfp);
  949. unlink($currinstjfp);
  950. exit(0);
  951. ?>