diff --git a/backend/getIP.php b/backend/getIP.php index 0eb2f3a..a2ed9a0 100644 --- a/backend/getIP.php +++ b/backend/getIP.php @@ -80,7 +80,10 @@ function getLocalOrPrivateIpInfo($ip) */ function getIpInfoTokenString() { - if (!file_exists(API_KEY_FILE)) { + if ( + !file_exists(API_KEY_FILE) + || !is_readable(API_KEY_FILE) + ) { return ''; } @@ -139,7 +142,10 @@ function getIsp($rawIspInfo) function getServerLocation() { $serverLoc = null; - if (file_exists(SERVER_LOCATION_CACHE_FILE)) { + if ( + file_exists(SERVER_LOCATION_CACHE_FILE) + && is_readable(SERVER_LOCATION_CACHE_FILE) + ) { require SERVER_LOCATION_CACHE_FILE; } if (is_string($serverLoc) && !empty($serverLoc)) { diff --git a/doc.md b/doc.md index 856edfb..74592b5 100644 --- a/doc.md +++ b/doc.md @@ -44,7 +44,7 @@ Server side, you'll need: * PHP 5.4 or newer, a 64-bit version is strongly recommended * OpenSSL and its PHP module (this is usually installed automatically by most distros) * If you want to store test results (telemetry), one of the following: - - MySQL/MariaDB and the mysqli PHP module + - MySQL/MariaDB and its PHP PDO module - PostgreSQL and its PHP PDO module - SQLite 3 and its PHP PDO module * If you want to enable results sharing: @@ -137,7 +137,7 @@ Requirements: * Apache 2 (nginx and IIS also supported). A fast connection is not mandatory, but is still recommended * PHP 5.4 or newer * If you want to store test results (telemetry), one of the following: - - MySQL/MariaDB and the mysqli PHP module + - MySQL/MariaDB and its PHP PDO module - PostgreSQL and its PHP PDO module - SQLite 3 and its PHP PDO module * If you want to enable results sharing: diff --git a/results/idObfuscation.php b/results/idObfuscation.php index 3b4482a..f7dd8a5 100644 --- a/results/idObfuscation.php +++ b/results/idObfuscation.php @@ -1,43 +1,72 @@ >1)|($id&0x55555555)<<1; - $id=(($id&0x0000FFFF)<<16)|(($id&0xFFFF0000)>>16); - return $id; - }else{ - $id=(($id&0x0000FFFF)<<16)|(($id&0xFFFF0000)>>16); - $id=(($id&0xAAAAAAAA)>>1)|($id&0x55555555)<<1; - return $id^$salt; - } -} -function obfuscateId($id){ - return str_pad(base_convert(obfdeobf($id+1,false),10,36),7,0,STR_PAD_LEFT); -} -function deobfuscateId($id){ - return obfdeobf(base_convert($id,36,10),true)-1; + +define('ID_OBFUSCATION_SALT_FILE', __DIR__.'/idObfuscation_salt.php'); + +/** + * @return string|int + */ +function getObfuscationSalt() +{ + if (!file_exists(ID_OBFUSCATION_SALT_FILE)) { + $bytes = openssl_random_pseudo_bytes(4); + + $saltData = " \ No newline at end of file +/** + * This is a simple reversible hash function I made for encoding and decoding test IDs. + * It is not cryptographically secure, don't use it to hash passwords or something! + * + * @param int|string $id + * @param bool $dec + * + * @return int|string + */ +function obfdeobf($id, $dec) +{ + $salt = getObfuscationSalt() & 0xFFFFFFFF; + $id &= 0xFFFFFFFF; + if ($dec) { + $id ^= $salt; + $id = (($id & 0xAAAAAAAA) >> 1) | ($id & 0x55555555) << 1; + $id = (($id & 0x0000FFFF) << 16) | (($id & 0xFFFF0000) >> 16); + + return $id; + } + + $id = (($id & 0x0000FFFF) << 16) | (($id & 0xFFFF0000) >> 16); + $id = (($id & 0xAAAAAAAA) >> 1) | ($id & 0x55555555) << 1; + + return $id ^ $salt; +} + +/** + * @param int $id + * + * @return string + */ +function obfuscateId($id) +{ + return str_pad(base_convert(obfdeobf($id + 1, false), 10, 36), 7, 0, STR_PAD_LEFT); +} + +/** + * @param string $id + * + * @return int + */ +function deobfuscateId($id) +{ + return obfdeobf(base_convert($id, 36, 10), true) - 1; +} diff --git a/results/index.php b/results/index.php index a9dac38..52c7316 100644 --- a/results/index.php +++ b/results/index.php @@ -1,164 +1,213 @@ prepare("select ispinfo,dl,ul,ping,jitter from speedtest_users where id=?"); - $q->bind_param("i",$id); - $q->execute(); - $q->bind_result($ispinfo,$dl,$ul,$ping,$jit); - $q->fetch(); -}else if($db_type=="sqlite"){ - $conn = new PDO("sqlite:$Sqlite_db_file") or die(); - $q=$conn->prepare("select ispinfo,dl,ul,ping,jitter from speedtest_users where id=?") or die(); - $q->execute(array($id)) or die(); - $row=$q->fetch() or die(); - $ispinfo=$row["ispinfo"]; - $dl=$row["dl"]; - $ul=$row["ul"]; - $ping=$row["ping"]; - $jit=$row["jitter"]; - $conn=null; -}else if($db_type=="postgresql"){ - $conn_host = "host=$PostgreSql_hostname"; - $conn_db = "dbname=$PostgreSql_databasename"; - $conn_user = "user=$PostgreSql_username"; - $conn_password = "password=$PostgreSql_password"; - $conn = new PDO("pgsql:$conn_host;$conn_db;$conn_user;$conn_password") or die(); - $q=$conn->prepare("select ispinfo,dl,ul,ping,jitter from speedtest_users where id=?") or die(); - $q->execute(array($id)) or die(); - $row=$q->fetch() or die(); - $ispinfo=$row["ispinfo"]; - $dl=$row["dl"]; - $ul=$row["ul"]; - $ping=$row["ping"]; - $jit=$row["jitter"]; - $conn=null; -}else die(); + return number_format($d, 0, '.', ''); +} -$dl=format($dl); -$ul=format($ul); -$ping=format($ping); -$jit=format($jit); +/** + * @param array $speedtest + * + * @return array + */ +function formatSpeedtestDataForImage($speedtest) +{ + // format values for the image + $speedtest['dl'] = format($speedtest['dl']); + $speedtest['ul'] = format($speedtest['ul']); + $speedtest['ping'] = format($speedtest['ping']); + $speedtest['jit'] = format($speedtest['jitter']); -$ispinfo=json_decode($ispinfo,true)["processedString"]; -$dash=strpos($ispinfo,"-"); -if(!($dash===FALSE)){ - $ispinfo=substr($ispinfo,$dash+2); - $par=strrpos($ispinfo,"("); - if(!($par===FALSE)) $ispinfo=substr($ispinfo,0,$par); -}else $ispinfo=""; + $ispinfo = json_decode($speedtest['ispinfo'], true)['processedString']; + $dash = strpos($ispinfo, '-'); + if ($dash !== false) { + $ispinfo = substr($ispinfo, $dash + 2); + $par = strrpos($ispinfo, '('); + if ($par !== false) { + $ispinfo = substr($ispinfo, 0, $par); + } + } else { + $ispinfo = ''; + } -$dlBbox=imageftbbox($FONT_LABEL_SIZE_BIG,0,$FONT_LABEL,$DL_TEXT); -$ulBbox=imageftbbox($FONT_LABEL_SIZE_BIG,0,$FONT_LABEL,$UL_TEXT); -$pingBbox=imageftbbox($FONT_LABEL_SIZE,0,$FONT_LABEL,$PING_TEXT); -$jitBbox=imageftbbox($FONT_LABEL_SIZE,0,$FONT_LABEL,$JIT_TEXT); -$dlMeterBbox=imageftbbox($FONT_METER_SIZE_BIG,0,$FONT_METER,$dl); -$ulMeterBbox=imageftbbox($FONT_METER_SIZE_BIG,0,$FONT_METER,$ul); -$pingMeterBbox=imageftbbox($FONT_METER_SIZE,0,$FONT_METER,$ping); -$jitMeterBbox=imageftbbox($FONT_METER_SIZE,0,$FONT_METER,$jit); -$mbpsBbox=imageftbbox($FONT_MEASURE_SIZE_BIG,0,$FONT_MEASURE,$MBPS_TEXT); -$msBbox=imageftbbox($FONT_MEASURE_SIZE,0,$FONT_MEASURE,$MS_TEXT); -$watermarkBbox=imageftbbox($FONT_WATERMARK_SIZE,0,$FONT_WATERMARK,$WATERMARK_TEXT); -$POSITION_X_WATERMARK=$WIDTH-$watermarkBbox[4]-4*$SCALE; + $speedtest['ispinfo'] = $ispinfo; -imagefilledrectangle($im, 0, 0, $WIDTH, $HEIGHT, $BACKGROUND_COLOR); -imagefttext($im,$FONT_LABEL_SIZE_BIG,0,$POSITION_X_DL-$dlBbox[4]/2,$POSITION_Y_DL_LABEL,$TEXT_COLOR_LABEL,$FONT_LABEL,$DL_TEXT); -imagefttext($im,$FONT_LABEL_SIZE_BIG,0,$POSITION_X_UL-$ulBbox[4]/2,$POSITION_Y_UL_LABEL,$TEXT_COLOR_LABEL,$FONT_LABEL,$UL_TEXT); -imagefttext($im,$FONT_LABEL_SIZE,0,$POSITION_X_PING-$pingBbox[4]/2,$POSITION_Y_PING_LABEL,$TEXT_COLOR_LABEL,$FONT_LABEL,$PING_TEXT); -imagefttext($im,$FONT_LABEL_SIZE,0,$POSITION_X_JIT-$jitBbox[4]/2,$POSITION_Y_JIT_LABEL,$TEXT_COLOR_LABEL,$FONT_LABEL,$JIT_TEXT); -imagefttext($im,$FONT_METER_SIZE_BIG,0,$POSITION_X_DL-$dlMeterBbox[4]/2,$POSITION_Y_DL_METER,$TEXT_COLOR_DL_METER,$FONT_METER,$dl); -imagefttext($im,$FONT_METER_SIZE_BIG,0,$POSITION_X_UL-$ulMeterBbox[4]/2,$POSITION_Y_UL_METER,$TEXT_COLOR_UL_METER,$FONT_METER,$ul); -imagefttext($im,$FONT_METER_SIZE,0,$POSITION_X_PING-$pingMeterBbox[4]/2-$msBbox[4]/2-$SMALL_SEP/2,$POSITION_Y_PING_METER,$TEXT_COLOR_PING_METER,$FONT_METER,$ping); -imagefttext($im,$FONT_METER_SIZE,0,$POSITION_X_JIT-$jitMeterBbox[4]/2-$msBbox[4]/2-$SMALL_SEP/2,$POSITION_Y_JIT_METER,$TEXT_COLOR_JIT_METER,$FONT_METER,$jit); -imagefttext($im,$FONT_MEASURE_SIZE_BIG,0,$POSITION_X_DL-$mbpsBbox[4]/2,$POSITION_Y_DL_MEASURE,$TEXT_COLOR_MEASURE,$FONT_MEASURE,$MBPS_TEXT); -imagefttext($im,$FONT_MEASURE_SIZE_BIG,0,$POSITION_X_UL-$mbpsBbox[4]/2,$POSITION_Y_UL_MEASURE,$TEXT_COLOR_MEASURE,$FONT_MEASURE,$MBPS_TEXT); -imagefttext($im,$FONT_MEASURE_SIZE,0,$POSITION_X_PING+$pingMeterBbox[4]/2+$SMALL_SEP/2-$msBbox[4]/2,$POSITION_Y_PING_MEASURE,$TEXT_COLOR_MEASURE,$FONT_MEASURE,$MS_TEXT); -imagefttext($im,$FONT_MEASURE_SIZE,0,$POSITION_X_JIT+$jitMeterBbox[4]/2+$SMALL_SEP/2-$msBbox[4]/2,$POSITION_Y_JIT_MEASURE,$TEXT_COLOR_MEASURE,$FONT_MEASURE,$MS_TEXT); -imagefttext($im,$FONT_ISP_SIZE,0,$POSITION_X_ISP,$POSITION_Y_ISP,$TEXT_COLOR_ISP,$FONT_ISP,$ispinfo); -imagefttext($im,$FONT_WATERMARK_SIZE,0,$POSITION_X_WATERMARK,$POSITION_Y_WATERMARK,$TEXT_COLOR_WATERMARK,$FONT_WATERMARK,$WATERMARK_TEXT); -imagefilledrectangle($im, 0, $SEPARATOR_Y, $WIDTH, $SEPARATOR_Y, $SEPARATOR_COLOR); -header('Content-Type: image/png'); -imagepng($im); -imagedestroy($im); + return $speedtest; +} -?> +/** + * @param array $speedtest + * + * @return void + */ +function drawImage($speedtest) +{ + // format values for the image + $data = formatSpeedtestDataForImage($speedtest); + $dl = $data['dl']; + $ul = $data['ul']; + $ping = $data['ping']; + $jit = $data['jitter']; + $ispinfo = $data['ispinfo']; + + // initialize the image + $SCALE = 1.25; + $SMALL_SEP = 8 * $SCALE; + $WIDTH = 400 * $SCALE; + $HEIGHT = 229 * $SCALE; + $im = imagecreatetruecolor($WIDTH, $HEIGHT); + $BACKGROUND_COLOR = imagecolorallocate($im, 255, 255, 255); + + // configure fonts + $FONT_LABEL = tryFont('OpenSans-Semibold'); + $FONT_LABEL_SIZE = 14 * $SCALE; + $FONT_LABEL_SIZE_BIG = 16 * $SCALE; + + $FONT_METER = tryFont('OpenSans-Light'); + $FONT_METER_SIZE = 20 * $SCALE; + $FONT_METER_SIZE_BIG = 22 * $SCALE; + + $FONT_MEASURE = tryFont('OpenSans-Semibold'); + $FONT_MEASURE_SIZE = 12 * $SCALE; + $FONT_MEASURE_SIZE_BIG = 12 * $SCALE; + + $FONT_ISP = tryFont('OpenSans-Semibold'); + $FONT_ISP_SIZE = 9 * $SCALE; + + $FONT_WATERMARK = tryFont('OpenSans-Light'); + $FONT_WATERMARK_SIZE = 8 * $SCALE; + + // configure text colors + $TEXT_COLOR_LABEL = imagecolorallocate($im, 40, 40, 40); + $TEXT_COLOR_PING_METER = imagecolorallocate($im, 170, 96, 96); + $TEXT_COLOR_JIT_METER = imagecolorallocate($im, 170, 96, 96); + $TEXT_COLOR_DL_METER = imagecolorallocate($im, 96, 96, 170); + $TEXT_COLOR_UL_METER = imagecolorallocate($im, 96, 96, 96); + $TEXT_COLOR_MEASURE = imagecolorallocate($im, 40, 40, 40); + $TEXT_COLOR_ISP = imagecolorallocate($im, 40, 40, 40); + $SEPARATOR_COLOR = imagecolorallocate($im, 192, 192, 192); + $TEXT_COLOR_WATERMARK = imagecolorallocate($im, 160, 160, 160); + + // configure positioning or the different parts on the image + $POSITION_X_PING = 125 * $SCALE; + $POSITION_Y_PING_LABEL = 24 * $SCALE; + $POSITION_Y_PING_METER = 60 * $SCALE; + $POSITION_Y_PING_MEASURE = 60 * $SCALE; + + $POSITION_X_JIT = 275 * $SCALE; + $POSITION_Y_JIT_LABEL = 24 * $SCALE; + $POSITION_Y_JIT_METER = 60 * $SCALE; + $POSITION_Y_JIT_MEASURE = 60 * $SCALE; + + $POSITION_X_DL = 120 * $SCALE; + $POSITION_Y_DL_LABEL = 105 * $SCALE; + $POSITION_Y_DL_METER = 143 * $SCALE; + $POSITION_Y_DL_MEASURE = 169 * $SCALE; + + $POSITION_X_UL = 280 * $SCALE; + $POSITION_Y_UL_LABEL = 105 * $SCALE; + $POSITION_Y_UL_METER = 143 * $SCALE; + $POSITION_Y_UL_MEASURE = 169 * $SCALE; + + $POSITION_X_ISP = 4 * $SCALE; + $POSITION_Y_ISP = 205 * $SCALE; + + $SEPARATOR_Y = 211 * $SCALE; + + $POSITION_Y_WATERMARK = 223 * $SCALE; + + // configure labels + $MBPS_TEXT = 'Mbps'; + $MS_TEXT = 'ms'; + $PING_TEXT = 'Ping'; + $JIT_TEXT = 'Jitter'; + $DL_TEXT = 'Download'; + $UL_TEXT = 'Upload'; + $WATERMARK_TEXT = 'LibreSpeed'; + + // create text boxes for each part of the image + $mbpsBbox = imageftbbox($FONT_MEASURE_SIZE_BIG, 0, $FONT_MEASURE, $MBPS_TEXT); + $msBbox = imageftbbox($FONT_MEASURE_SIZE, 0, $FONT_MEASURE, $MS_TEXT); + $pingBbox = imageftbbox($FONT_LABEL_SIZE, 0, $FONT_LABEL, $PING_TEXT); + $pingMeterBbox = imageftbbox($FONT_METER_SIZE, 0, $FONT_METER, $ping); + $jitBbox = imageftbbox($FONT_LABEL_SIZE, 0, $FONT_LABEL, $JIT_TEXT); + $jitMeterBbox = imageftbbox($FONT_METER_SIZE, 0, $FONT_METER, $jit); + $dlBbox = imageftbbox($FONT_LABEL_SIZE_BIG, 0, $FONT_LABEL, $DL_TEXT); + $dlMeterBbox = imageftbbox($FONT_METER_SIZE_BIG, 0, $FONT_METER, $dl); + $ulBbox = imageftbbox($FONT_LABEL_SIZE_BIG, 0, $FONT_LABEL, $UL_TEXT); + $ulMeterBbox = imageftbbox($FONT_METER_SIZE_BIG, 0, $FONT_METER, $ul); + $watermarkBbox = imageftbbox($FONT_WATERMARK_SIZE, 0, $FONT_WATERMARK, $WATERMARK_TEXT); + $POSITION_X_WATERMARK = $WIDTH - $watermarkBbox[4] - 4 * $SCALE; + + // put the parts together to draw the image + imagefilledrectangle($im, 0, 0, $WIDTH, $HEIGHT, $BACKGROUND_COLOR); + // ping + imagefttext($im, $FONT_LABEL_SIZE, 0, $POSITION_X_PING - $pingBbox[4] / 2, $POSITION_Y_PING_LABEL, $TEXT_COLOR_LABEL, $FONT_LABEL, $PING_TEXT); + imagefttext($im, $FONT_METER_SIZE, 0, $POSITION_X_PING - $pingMeterBbox[4] / 2 - $msBbox[4] / 2 - $SMALL_SEP / 2, $POSITION_Y_PING_METER, $TEXT_COLOR_PING_METER, $FONT_METER, $ping); + imagefttext($im, $FONT_MEASURE_SIZE, 0, $POSITION_X_PING + $pingMeterBbox[4] / 2 + $SMALL_SEP / 2 - $msBbox[4] / 2, $POSITION_Y_PING_MEASURE, $TEXT_COLOR_MEASURE, $FONT_MEASURE, $MS_TEXT); + // jitter + imagefttext($im, $FONT_LABEL_SIZE, 0, $POSITION_X_JIT - $jitBbox[4] / 2, $POSITION_Y_JIT_LABEL, $TEXT_COLOR_LABEL, $FONT_LABEL, $JIT_TEXT); + imagefttext($im, $FONT_METER_SIZE, 0, $POSITION_X_JIT - $jitMeterBbox[4] / 2 - $msBbox[4] / 2 - $SMALL_SEP / 2, $POSITION_Y_JIT_METER, $TEXT_COLOR_JIT_METER, $FONT_METER, $jit); + imagefttext($im, $FONT_MEASURE_SIZE, 0, $POSITION_X_JIT + $jitMeterBbox[4] / 2 + $SMALL_SEP / 2 - $msBbox[4] / 2, $POSITION_Y_JIT_MEASURE, $TEXT_COLOR_MEASURE, $FONT_MEASURE, $MS_TEXT); + // dl + imagefttext($im, $FONT_LABEL_SIZE_BIG, 0, $POSITION_X_DL - $dlBbox[4] / 2, $POSITION_Y_DL_LABEL, $TEXT_COLOR_LABEL, $FONT_LABEL, $DL_TEXT); + imagefttext($im, $FONT_METER_SIZE_BIG, 0, $POSITION_X_DL - $dlMeterBbox[4] / 2, $POSITION_Y_DL_METER, $TEXT_COLOR_DL_METER, $FONT_METER, $dl); + imagefttext($im, $FONT_MEASURE_SIZE_BIG, 0, $POSITION_X_DL - $mbpsBbox[4] / 2, $POSITION_Y_DL_MEASURE, $TEXT_COLOR_MEASURE, $FONT_MEASURE, $MBPS_TEXT); + // ul + imagefttext($im, $FONT_LABEL_SIZE_BIG, 0, $POSITION_X_UL - $ulBbox[4] / 2, $POSITION_Y_UL_LABEL, $TEXT_COLOR_LABEL, $FONT_LABEL, $UL_TEXT); + imagefttext($im, $FONT_METER_SIZE_BIG, 0, $POSITION_X_UL - $ulMeterBbox[4] / 2, $POSITION_Y_UL_METER, $TEXT_COLOR_UL_METER, $FONT_METER, $ul); + imagefttext($im, $FONT_MEASURE_SIZE_BIG, 0, $POSITION_X_UL - $mbpsBbox[4] / 2, $POSITION_Y_UL_MEASURE, $TEXT_COLOR_MEASURE, $FONT_MEASURE, $MBPS_TEXT); + // isp + imagefttext($im, $FONT_ISP_SIZE, 0, $POSITION_X_ISP, $POSITION_Y_ISP, $TEXT_COLOR_ISP, $FONT_ISP, $ispinfo); + // separator + imagefilledrectangle($im, 0, $SEPARATOR_Y, $WIDTH, $SEPARATOR_Y, $SEPARATOR_COLOR); + // watermark + imagefttext($im, $FONT_WATERMARK_SIZE, 0, $POSITION_X_WATERMARK, $POSITION_Y_WATERMARK, $TEXT_COLOR_WATERMARK, $FONT_WATERMARK, $WATERMARK_TEXT); + + // send the image to the browser + header('Content-Type: image/png'); + imagepng($im); +} + +$speedtest = getSpeedtestUserById($_GET['id']); +if (!is_array($speedtest)) { + exit(1); +} + +drawImage($speedtest); diff --git a/results/stats.php b/results/stats.php index 1e6ff1c..be35a6e 100644 --- a/results/stats.php +++ b/results/stats.php @@ -1,6 +1,10 @@ -
-Test ID | =htmlspecialchars(($enable_id_obfuscation?(obfuscateId($id)." (deobfuscated: ".$id.")"):$id), ENT_HTML5, 'UTF-8') ?> |
---|---|
Date and time | =htmlspecialchars($timestamp, ENT_HTML5, 'UTF-8') ?> |
IP and ISP Info | =$ip ?> =htmlspecialchars($ispinfo, ENT_HTML5, 'UTF-8') ?> |
User agent and locale | =$ua ?> =htmlspecialchars($lang, ENT_HTML5, 'UTF-8') ?> |
Download speed | =htmlspecialchars($dl, ENT_HTML5, 'UTF-8') ?> |
Upload speed | =htmlspecialchars($ul, ENT_HTML5, 'UTF-8') ?> |
Ping | =htmlspecialchars($ping, ENT_HTML5, 'UTF-8') ?> |
Jitter | =htmlspecialchars($jitter, ENT_HTML5, 'UTF-8') ?> |
Log | =htmlspecialchars($log, ENT_HTML5, 'UTF-8') ?> |
Extra info | =htmlspecialchars($extra, ENT_HTML5, 'UTF-8') ?> |
Test ID | += htmlspecialchars($speedtest['id_formatted'], ENT_HTML5, 'UTF-8') ?> | +
---|---|
Date and time | += htmlspecialchars($speedtest['timestamp'], ENT_HTML5, 'UTF-8') ?> | +
IP and ISP Info | +
+ = htmlspecialchars($speedtest['ip'], ENT_HTML5, 'UTF-8') ?> + = htmlspecialchars($speedtest['ispinfo'], ENT_HTML5, 'UTF-8') ?> + |
+
User agent and locale | += htmlspecialchars($speedtest['ua'], ENT_HTML5, 'UTF-8') ?> + = htmlspecialchars($speedtest['lang'], ENT_HTML5, 'UTF-8') ?> + |
+
Download speed | += htmlspecialchars($speedtest['dl'], ENT_HTML5, 'UTF-8') ?> | +
Upload speed | += htmlspecialchars($speedtest['ul'], ENT_HTML5, 'UTF-8') ?> | +
Ping | += htmlspecialchars($speedtest['ping'], ENT_HTML5, 'UTF-8') ?> | +
Jitter | += htmlspecialchars($speedtest['jitter'], ENT_HTML5, 'UTF-8') ?> | +
Log | += htmlspecialchars($speedtest['log'], ENT_HTML5, 'UTF-8') ?> | +
Extra info | += htmlspecialchars($speedtest['extra'], ENT_HTML5, 'UTF-8') ?> | +