sentinelPath(); if ($sentinel && is_file($sentinel)) { return; } $params = $this->buildParams(); $params['sign'] = $this->sign($params, $secret); $body = json_encode($params, JSON_UNESCAPED_SLASHES); if ($body === false) { return; } $url = defined('OPEN_SOURCE_STAT_REPORT_URL') ? trim((string) OPEN_SOURCE_STAT_REPORT_URL) : ''; if ($url === '') { return; } $timeout = defined('OPEN_SOURCE_STAT_HTTP_TIMEOUT') ? (int) OPEN_SOURCE_STAT_HTTP_TIMEOUT : 20; if ($timeout < 1) { $timeout = 20; } $ok = $this->postJson($url, $body, $timeout); if ($ok && $sentinel) { @file_put_contents($sentinel, (string) time(), LOCK_EX); } } /** * @param array $params payload without sign * @param string $secret * @return string uppercase md5 */ public function sign(array $params, $secret) { unset($params['sign']); ksort($params, SORT_STRING); $s = ''; foreach ($params as $k => $v) { if ($v === null) { continue; } if (is_bool($v)) { $v = $v ? 1 : 0; } $s .= $k . (is_array($v) ? json_encode($v, JSON_UNESCAPED_SLASHES) : $v); } return strtoupper(md5($secret . $s . $secret)); } private function buildParams() { $product = defined('OPEN_SOURCE_STAT_PRODUCT') ? (string) OPEN_SOURCE_STAT_PRODUCT : 'oms'; return array( 'product' => $product, 'instance_id' => $this->detectInstanceId(), 'version' => $this->detectVersion(), 'timestamp' => time(), 'is_docker' => $this->detectDocker() ? 1 : 0, 'is_vm' => $this->detectVm() ? 1 : 0, ); } private function sentinelPath() { if (!defined('ROOT_DIR')) { return null; } $dir = defined('DATA_DIR') ? DATA_DIR : (ROOT_DIR . '/data'); if (!is_dir($dir)) { @mkdir($dir, 0775, true); } $name = defined('OPEN_SOURCE_STAT_SENTINEL_NAME') ? trim((string) OPEN_SOURCE_STAT_SENTINEL_NAME) : '.opensource_install_stat_reported'; if ($name === '' || strpos($name, '/') !== false || strpos($name, "\0") !== false) { $name = '.opensource_install_stat_reported'; } return $dir . '/' . $name; } private function detectVersion() { if (!class_exists('base_setup_config')) { return '0.0.0'; } try { $info = base_setup_config::deploy_info(); if (!empty($info['ver'])) { return (string) $info['ver']; } } catch (Exception $e) { // ignore } return '0.0.0'; } private function detectInstanceId() { if (PHP_OS_FAMILY === 'Linux' || stripos(PHP_OS, 'Linux') !== false) { $glob = @glob('/sys/class/net/*/address'); if (is_array($glob)) { foreach ($glob as $path) { if (strpos($path, '/lo/') !== false) { continue; } $raw = @trim((string) @file_get_contents($path)); if ($raw !== '' && strcasecmp($raw, '00:00:00:00:00:00') !== 0) { return strtolower(str_replace('-', ':', $raw)); } } } } $host = function_exists('php_uname') ? php_uname('n') : 'unknown'; $root = defined('ROOT_DIR') ? ROOT_DIR : __DIR__; return md5($host . '|' . $root); } private function detectDocker() { if (@is_file('/.dockerenv')) { return true; } if (@is_readable('/proc/1/cgroup')) { $c = @file_get_contents('/proc/1/cgroup'); if ($c !== false && strpos($c, 'docker') !== false) { return true; } } return false; } private function detectVm() { $paths = array( '/sys/class/dmi/id/product_name', '/sys/class/dmi/id/sys_vendor', ); foreach ($paths as $p) { if (!@is_readable($p)) { continue; } $t = strtolower((string) @file_get_contents($p)); if ($t === '') { continue; } if (preg_match('/vmware|virtualbox|kvm|qemu|xen|parallels|microsoft corporation|bochs|innotek/', $t)) { return true; } } return false; } /** * @param string $url * @param string $body * @param int $timeoutSec * @return bool true if HTTP 2xx */ private function postJson($url, $body, $timeoutSec) { if (function_exists('curl_init')) { $ch = curl_init($url); if ($ch === false) { return false; } curl_setopt_array($ch, array( CURLOPT_POST => true, CURLOPT_HTTPHEADER => array('Content-Type: application/json'), CURLOPT_POSTFIELDS => $body, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => $timeoutSec, CURLOPT_CONNECTTIMEOUT => min(10, $timeoutSec), CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2, )); $resp = curl_exec($ch); $code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return $code >= 200 && $code < 300; } $ctx = stream_context_create(array( 'http' => array( 'method' => 'POST', 'header' => "Content-Type: application/json\r\nConnection: close\r\n", 'content' => $body, 'timeout' => $timeoutSec, 'ignore_errors' => true, ), 'ssl' => array( 'verify_peer' => true, 'verify_peer_name' => true, ), )); $resp = @file_get_contents($url, false, $ctx); if ($resp === false) { return false; } if (isset($http_response_header[0]) && preg_match('#\s(\d{3})\s#', $http_response_header[0], $m)) { $code = (int) $m[1]; return $code >= 200 && $code < 300; } return false; } }