pull.php 11.1 KB
Newer Older
MatrixCrawler's avatar
MatrixCrawler committed
1
<?php
2
//* Copyright (c) 2011, David Morley. This file is licensed under the Affero General Public License version 3 or later. See the COPYRIGHT file. */
3

noplanman's avatar
noplanman committed
4 5
use RedBeanPHP\R;

David Morley's avatar
David Morley committed
6 7 8
$debug   = isset($_GET['debug']) || (isset($argv) && in_array('debug', $argv, true));
$newline = PHP_SAPI === 'cli' ? "\n" : '<br>';

noplanman's avatar
noplanman committed
9 10 11 12
$_domain = $_GET['domain'] ?? null;

// Must have a domain, except if called from CLI.
$_domain || PHP_SAPI === 'cli' || die('No valid input');
13

David Morley's avatar
David Morley committed
14
require_once __DIR__ . '/../vendor/autoload.php';
noplanman's avatar
noplanman committed
15 16 17
require_once __DIR__ . '/../config.php';

define('PODUPTIME', microtime(true));
dmorley's avatar
dmorley committed
18

noplanman's avatar
noplanman committed
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
// Set up global DB connection.
R::setup("pgsql:host={$pghost};dbname={$pgdb}", $pguser, $pgpass, true);
R::testConnection() || die('Error in DB connection');

try {
  $sql = '
    SELECT domain, score, date_created, adminrating, weight, hidden, podmin_notify, email
    FROM pods
  ';

  $pods = [];
  if ($_domain) {
    $sql .= ' WHERE domain = ?';
    $pods = R::getAll($sql, [$_domain]);
  } elseif (PHP_SAPI === 'cli') {
    $pods = R::getAll($sql);
  }
} catch (\RedBeanPHP\RedException $e) {
  die('Error in SQL query: ' . $e->getMessage());
dmorley's avatar
dmorley committed
38
}
noplanman's avatar
noplanman committed
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58

foreach ($pods as $pod) {
  $domain    = $pod['domain'];
  $score     = (int) $pod['score'];
  $dateadded = $pod['date_created'];
  $admindb   = (int) $pod['adminrating'];
  $weight    = $pod['weight'];
  $hiddennow = $pod['hidden'];
  $email     = $pod['email'];
  $notify    = $pod['podmin_notify'];

  try {
    $ratings = R::getAll('
      SELECT admin, rating
      FROM rating_comments
      WHERE domain = ?
    ', [$domain]);
  } catch (\RedBeanPHP\RedException $e) {
    die('Error in SQL query: ' . $e->getMessage());
  }
59

David Morley's avatar
David Morley committed
60
  _debug('Domain', $domain);
noplanman's avatar
noplanman committed
61

David Morley's avatar
David Morley committed
62 63
  $user_ratings  = [];
  $admin_ratings = [];
noplanman's avatar
noplanman committed
64
  foreach ($ratings as $rating) {
David Morley's avatar
David Morley committed
65 66 67 68
    if ($rating['admin'] == 0) {
      $user_ratings[] = $rating['rating'];
    } elseif ($rating['admin'] == 1) {
      $admin_ratings[] = $rating['rating'];
69
    }
David Morley's avatar
David Morley committed
70 71 72
  }
  $user_rating  = empty($user_ratings) ? 0 : max(10, round(array_sum($user_ratings) / count($user_ratings), 2));
  $admin_rating = empty($admin_ratings) ? 0 : max(10, round(array_sum($admin_ratings) / count($admin_ratings), 2));
noplanman's avatar
noplanman committed
73

David Morley's avatar
David Morley committed
74 75 76
  if ($admindb == -1) {
    $admin_rating = -1;
  }
77

David Morley's avatar
David Morley committed
78 79
  $chss = curl_init();
  curl_setopt($chss, CURLOPT_URL, 'https://' . $domain . '/nodeinfo/1.0');
80 81
  curl_setopt($chss, CURLOPT_CONNECTTIMEOUT, 10);
  curl_setopt($chss, CURLOPT_TIMEOUT, 30);
David Morley's avatar
David Morley committed
82 83
  curl_setopt($chss, CURLOPT_RETURNTRANSFER, 1);
  curl_setopt($chss, CURLOPT_CERTINFO, 1);
dmorley's avatar
dmorley committed
84
  curl_setopt($chss, CURLOPT_CAINFO, $cafullpath);
David Morley's avatar
David Morley committed
85 86 87
  $outputssl      = curl_exec($chss);
  $outputsslerror = curl_error($chss);
  $info           = curl_getinfo($chss, CURLINFO_CERTINFO);
88 89 90
  $conntime       = curl_getinfo($chss, CURLINFO_CONNECT_TIME);
  $nstime         = curl_getinfo($chss, CURLINFO_NAMELOOKUP_TIME);
  $latency        = $conntime - $nstime;
dmorley's avatar
dmorley committed
91
  $sslexpire      = $info[0]['Expire date'] ?? null;
David Morley's avatar
David Morley committed
92
  curl_close($chss);
93

David Morley's avatar
David Morley committed
94 95
  _debug('Nodeinfo output', $outputssl, true);
  _debug('Nodeinfo output error', $outputsslerror, true);
96
  _debug('Cert expire date', $sslexpire);
97 98
  _debug('Conntime', $conntime);
  _debug('NStime', $nstime);
dmorley's avatar
dmorley committed
99
  _debug('Latency', $latency);
noplanman's avatar
noplanman committed
100

David Morley's avatar
David Morley committed
101
  $jsonssl = json_decode($outputssl);
noplanman's avatar
noplanman committed
102

103 104 105 106 107
  $xdver                 = $jsonssl->software->version ?? 0;
  $dverr                 = explode('-', trim($xdver));
  $shortversion          = $dverr[0];
  $signup                = ($jsonssl->openRegistrations ?? false) === true;
  $softwarename          = $jsonssl->software->name ?? 'unknown';
David Morley's avatar
David Morley committed
108
  $name                  = $jsonssl->metadata->nodeName ?? $softwarename;
109 110 111 112 113 114
  $total_users           = $jsonssl->usage->users->total ?? 0;
  $active_users_halfyear = $jsonssl->usage->users->activeHalfyear ?? 0;
  $active_users_monthly  = $jsonssl->usage->users->activeMonth ?? 0;
  $local_posts           = $jsonssl->usage->localPosts ?? 0;
  $comment_counts        = $jsonssl->usage->localComments ?? 0;
  $service_xmpp          = ($jsonssl->metadata->xmppChat ?? false) === true;
dmorley's avatar
dmorley committed
115 116 117 118
  $service_facebook      = false;
  $service_twitter       = false;
  $service_tumblr        = false;
  $service_wordpress     = false;
119
  if (json_last_error() === 0) {
dmorley's avatar
dmorley committed
120
    (!$jsonssl->software->version) || $score += 1;
noplanman's avatar
noplanman committed
121 122 123 124
    $service_facebook  = in_array('facebook', $jsonssl->services->outbound, true);
    $service_twitter   = in_array('twitter', $jsonssl->services->outbound, true);
    $service_tumblr    = in_array('tumblr', $jsonssl->services->outbound, true);
    $service_wordpress = in_array('wordpress', $jsonssl->services->outbound, true);
125
  }
noplanman's avatar
noplanman committed
126

127
  if ($jsonssl !== null) {
noplanman's avatar
noplanman committed
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
    $status = 'Up';

    try {
      $c                   = R::dispense('checks');
      $c['domain']         = $domain;
      $c['online']         = true;
      $c['latency']        = $latency;
      $c['total_users']    = $total_users;
      $c['local_posts']    = $local_posts;
      $c['comment_counts'] = $comment_counts;
      $c['shortversion']   = $shortversion;
      R::store($c);
    } catch (\RedBeanPHP\RedException $e) {
      die('Error in SQL query: ' . $e->getMessage());
    }
David Morley's avatar
David Morley committed
143
  }
noplanman's avatar
noplanman committed
144 145

  if (!$jsonssl) {
146
    _debug('Connection', 'Can not connect to pod');
noplanman's avatar
noplanman committed
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163

    try {
      $c            = R::dispense('checks');
      $c['domain']  = $domain;
      $c['online']  = false;
      $c['error']   = $outputsslerror;
      $c['latency'] = $latency;
      R::store($c);
    } catch (\RedBeanPHP\RedException $e) {
      die('Error in SQL query: ' . $e->getMessage());
    }

    $score        -= 1;
    $shortversion = '0.error';
    $status       = 'Down';
  }

164
  _debug('Version code', $shortversion);
David Morley's avatar
David Morley committed
165
  _debug('Signup Open', $signup);
David Morley's avatar
David Morley committed
166

David Morley's avatar
David Morley committed
167 168 169
  $delv = new NPM\Xec\Command("delv @{$dnsserver} {$domain}");
  $delv->throwExceptionOnError(false);

170
  $ip         = '';
David Morley's avatar
David Morley committed
171 172 173
  $iplookupv4 = explode(PHP_EOL, trim($delv->execute([], null, 15)->stdout));
  $dnssec     = in_array('; fully validated', $iplookupv4, true) ?? false;
  $getaonly   = array_values(preg_grep('/\s+IN\s+A\s+.*/', $iplookupv4));
174
  if ($getaonly) {
175
    preg_match('/A\s(.*)/', $getaonly[0], $aversion);
176
    $ip = trim($aversion[1]) ?? '';
177
  }
David Morley's avatar
David Morley committed
178 179 180

  $ipv6        = false;
  $iplookupv6  = explode(PHP_EOL, trim($delv->execute(['AAAA'], null, 15)->stdout));
David Morley's avatar
David Morley committed
181 182 183
  $getaaaaonly = array_values(preg_grep('/\s+IN\s+AAAA\s+.*/', $iplookupv6));
  if ($getaaaaonly) {
    preg_match('/AAAA\s(.*)/', $getaaaaonly[0], $aaaaversion);
David Morley's avatar
David Morley committed
184
    $ipv6 = trim($aaaaversion[1]) ?? '';
dmorley's avatar
dmorley committed
185
  }
dmorley's avatar
dmorley committed
186
  $ip || $score -= 2;
David Morley's avatar
David Morley committed
187 188

  _debug('IPv4', $ip);
189
  _debug('Iplookupv4', $iplookupv4, true);
David Morley's avatar
David Morley committed
190
  _debug('IPv6', $ipv6);
191
  _debug('Iplookupv6', $iplookupv6, true);
David Morley's avatar
David Morley committed
192 193 194

  $location = geoip_record_by_name($ip);
  _debug('Location', $location, true);
195 196 197 198 199
  $country  = !empty($location['country_code']) ? iconv('UTF-8', 'UTF-8//IGNORE', $location['country_code']) : null;
  $city     = !empty($location['city']) ? iconv('UTF-8', 'UTF-8//IGNORE', $location['city']) : null;
  $state    = !empty($location['region']) ? iconv('UTF-8', 'UTF-8//IGNORE', $location['region']) : null;
  $lat      = !empty($location['latitude']) ? $location['latitude'] : 0;
  $long     = !empty($location['longitude']) ? $location['longitude'] : 0;
noplanman's avatar
noplanman committed
200

David Morley's avatar
David Morley committed
201 202
  echo $newline;
  $statslastdate = date('Y-m-d H:i:s');
David Morley's avatar
David Morley committed
203

noplanman's avatar
noplanman committed
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
  $diff   = (new DateTime())->diff(new DateTime($dateadded));
  $months = $diff->m + ($diff->y * 12);

  try {
    $checks = R::getRow('
      SELECT
        round(avg(latency) * 1000) AS latency,
        round(avg(online::INT) * 100, 2) AS online
      FROM checks
      WHERE domain = ?
    ', [$_domain]);

    $avglatency = $checks['latency'] ?? 0;
    $uptime     = $checks['online'] ?? 0;
  } catch (\RedBeanPHP\RedException $e) {
    die('Error in SQL query: ' . $e->getMessage());
  }

222
  _debug('Uptime', $uptime);
David Morley's avatar
David Morley committed
223

noplanman's avatar
noplanman committed
224 225 226 227 228
  try {
    $masterversion = R::getCell('SELECT version FROM masterversions WHERE software = ?', [$softwarename]);
  } catch (\RedBeanPHP\RedException $e) {
    die('Error in SQL query: ' . $e->getMessage());
  }
David Morley's avatar
David Morley committed
229 230

  _debug('Masterversion', $masterversion);
noplanman's avatar
noplanman committed
231

David Morley's avatar
David Morley committed
232 233
  $hidden = $score <= 70;
  _debug('Hidden', $hidden ? 'yes' : 'no');
234

noplanman's avatar
noplanman committed
235
  if (!$hiddennow && $hidden && $notify) {
236 237 238 239 240
    $to      = $email;
    $headers = ['From: ' . $adminemail, 'Bcc: ' . $adminemail];
    $subject = 'Monitoring notice from poduptime';
    $message = 'Notice for ' . $domain . '. Your score fell to ' . $score . ' and your pod is now marked as hidden.';
    @mail($to, $subject, $message, implode("\r\n", $headers));
noplanman's avatar
noplanman committed
241
    _debug('Mail Notice', 'sent to ' . $email);
242
  }
David Morley's avatar
David Morley committed
243 244 245 246 247
  if ($score > 100) {
    $score = 100;
  } elseif ($score < 0) {
    $score = 0;
  }
David Morley's avatar
David Morley committed
248
  _debug('Score', $score);
249 250
  $weightedscore = ($uptime + $score - (10 - $weight)) / 2;
  _debug('Weighted Score', $weightedscore);
noplanman's avatar
noplanman committed
251

noplanman's avatar
noplanman committed
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
  try {
    $p                          = R::findOne('pods', 'domain = ?', [$domain]);
    $p['secure']                = true;
    $p['hidden']                = $hidden;
    $p['ip']                    = $ip;
    $p['ipv6']                  = ($ipv6 !== null);
    $p['monthsmonitored']       = $months;
    $p['uptime_alltime']        = $uptime;
    $p['status']                = $status;
    $p['date_laststats']        = $statslastdate;
    $p['date_updated']          = date('Y-m-d H:i:s');
    $p['latency']               = $avglatency;
    $p['score']                 = $score;
    $p['adminrating']           = $admin_rating;
    $p['country']               = $country;
    $p['city']                  = $city;
    $p['state']                 = $state;
    $p['lat']                   = $lat;
    $p['long']                  = $long;
    $p['userrating']            = $user_rating;
    $p['shortversion']          = $shortversion;
    $p['masterversion']         = $masterversion;
    $p['signup']                = $signup;
    $p['total_users']           = $total_users;
    $p['active_users_halfyear'] = $active_users_halfyear;
    $p['active_users_monthly']  = $active_users_monthly;
    $p['local_posts']           = $local_posts;
    $p['name']                  = $name;
    $p['comment_counts']        = $comment_counts;
    $p['service_facebook']      = $service_facebook;
    $p['service_tumblr']        = $service_tumblr;
    $p['service_twitter']       = $service_twitter;
    $p['service_wordpress']     = $service_wordpress;
    $p['service_xmpp']          = $service_xmpp;
    $p['weightedscore']         = $weightedscore;
    $p['softwarename']          = $softwarename;
    $p['sslvalid']              = $outputsslerror;
    $p['dnssec']                = $dnssec;
    $p['sslexpire']             = $sslexpire;
291 292 293 294 295
    
    // @todo Temporary fix! https://github.com/gabordemooij/redbean/issues/547
    foreach ($p->getProperties() as $key => $value) {
      $pod[$key] = $value;
    }
noplanman's avatar
noplanman committed
296 297 298 299 300

    R::store($p);
  } catch (\RedBeanPHP\RedException $e) {
    die('Error in SQL query: ' . $e->getMessage());
  }
David Morley's avatar
David Morley committed
301

noplanman's avatar
noplanman committed
302 303
  echo 'Success ' . $domain;

David Morley's avatar
David Morley committed
304 305 306
  echo $newline;
  echo $newline;
}
307

David Morley's avatar
David Morley committed
308 309
/**
 * Output a debug message and variable value
noplanman's avatar
noplanman committed
310
 *
David Morley's avatar
David Morley committed
311 312 313 314
 * @param string $label
 * @param mixed  $var
 * @param bool   $dump
 */
noplanman's avatar
noplanman committed
315 316
function _debug($label, $var = null, $dump = false)
{
David Morley's avatar
David Morley committed
317
  global $debug, $newline;
318

David Morley's avatar
David Morley committed
319 320
  if (!$debug) {
    return;
dmorley's avatar
dmorley committed
321
  }
noplanman's avatar
noplanman committed
322

David Morley's avatar
David Morley committed
323
  if ($dump || is_array($var)) {
noplanman's avatar
noplanman committed
324
    $output = print_r($var, true);
David Morley's avatar
David Morley committed
325 326 327 328 329
  } elseif (is_bool($var)) {
    $output = $var ? 'true' : 'false';
  } else {
    $output = (string) $var;
  }
noplanman's avatar
noplanman committed
330

David Morley's avatar
David Morley committed
331
  printf('%s: %s%s', $label, $output, $newline);
332
}