title = lang($t); $e->msg = lang($t+1); error($e, ...$args); self::$_e[] = $e; } public static function get(){return self::$_e; } } $GLOBALS['lang'] = array("en" => array( MISS_GZ => "missing gzopen", MISS_GZ+1 => "The PHP environment does not support gzopen. Unable to extract BitFire release. Please recompile with --with-zlib", FAILED => "BitFire Install Failed", FAILED+1 => "A problem was found with your PHP environment, please check the error and visit the support center.", INSTALL => "BitFire Installed", INSTALL+1 => "BitFire is already installed", OPEN_FAIL => "Unable to open file", OPEN_FAIL+1 => "Error ocurred opening file [%s]", MKDIR_FAIL => "Unable to mkdir", MKDIR_FAIL+1 => "Unable to create the directory [%s]", WRITE_FAIL => "Unable to write file", WRITE_FAIL+1 => "Error ocurred writing to file [%s]", CHMOD_FAIL => "Permission set error", CHMOD_FAIL+1 => "Unable to set the permissions on file [%s]", READ_FAIL => "Unable to read archive", READ_FAIL+1 => "Unable to read archive, read %d of 512 bytes", CHK_FAIL => "Checksum fail", CHK_FAIL+1 => "Archive Checksum error. expected %d, but found %d.", HTTP_FAIL => "No HTTP Support", HTTP_FAIL+1 => "BitFire requires allow_url_fopen or cURL support", PHP_VER => "PHP 7.0+", PHP_VER+1 => "BitFire requires PHP 7.0 or greater", COOKIE => "\$_COOKIE", COOKIE+1 => "cookie support found. Browser verify available.", SHMOP => "SHMOP", SHMOP+1 => "SHMOP support found. Server cache available.", APCU => "APCU", APCU+1 => "APCU support found. Server cache available.", SHM => "SHM", SHM+1 => "SHM support found. Server cache available.", SUCCESS => "SUCCESS", SUCCESS+1 => "BitFire installed successfully. Please visit BitFire Dashboard

password: %s (click to copy to clipboard)", UNIN => "SUCCESS", UNIN+1 => "BitFire un-installed successfully. Bitfire startup removed from .htaccess, user.ini and index.php. /bitfire directory removed.", ROOT_WRITE => "Permission error", ROOT_WRITE+1 => "%s must be writeable to use this install script", COOKIE_FAIL => "no cookie support", COOKIE_FAIL+1 => "Cookie support required for browser verification", NO_CACHE => "no cache support", NO_CACHE+1 => "Server cache recommended for fastest operation", ADDED => "Startup", ADDED+1 => "BitFire has been added to .htaccess", ADDEDINI => "Startup", ADDEDINI+1 => "BitFire has been added to user.ini", ADDEDINDEX => "Startup", ADDEDINDEX+1 => "BitFire has been added to the top of /index.php", ADDED_FAIL => "Startup", ADDED_FAIL+1 => "Unable to add startup to .htaccess, .user.ini or index.php", UNIN_FAIL => "ERROR", UNIN_FAIL+1 => "BitFire un-installed successfully. Bitfire startup removed from .htaccess, user.ini and index.php. You are no longer protected", REMOVE_SUCCESS => "BitFire startup has been removed from %d", REMOVE_FAIL => "BitFire startup unable to removed from %d" )); $GLOBALS['page2'] = << %s

%s

%s


'; $GLOBALS['item'] = <<%s %s  - %s EOT; $GLOBALS['list'] = ''; // setup function function init() { error_clear_last(); $root = $_SERVER['DOCUMENT_ROOT']; if (PHP_VERSION_ID < 70000) { Err::new(PHP_VER); } if (!function_exists('gzopen')) { Err::new(MISS_GZ); } if (!ini_get("allow_url_fopen") && !function_exists('curl_init')) { Err::new(HTTP_FAIL); } if (isset($_GET['uninstall']) || isset($_GET['?uninstall'])) { uninstall(); } if (defined("WAF_DIR")) { Err::new(INSTALL); } if (!is_writeable($root)) { Err::new(ROOT_WRITE, $root); } if (!empty(Err::get())) { fin(FAILED); } } // get translation function lang($msg, $uc = false) { $l = "en"; if (isset($GLOBALS['lang'][$l]) && (isset($GLOBALS['lang'][$l][$msg]))) { $m = $GLOBALS['lang'][$l][$msg]; return ($uc) ? ucwords($m) : $m; } return $msg; } // error functions function format_error() { $e = error_get_last(); error_clear_last(); return (empty($e)) ? "" : "errno: {$e['type']} line: {$e['line']} , {$e['message']}"; } function error(Err $e, ...$args) { $err = format_error(); if (!empty($err)) { $GLOBALS['list'] .= sprintf($GLOBALS['item'], "gear", "gear", 'php error', $err); } if (!empty($args) && strpos($e->msg, '%') !== false) { $GLOBALS['list'] .= sprintf($GLOBALS['item'], "error", "error", $e->title, sprintf($e->msg, ...$args)); } else { $GLOBALS['list'] .= sprintf($GLOBALS['item'], "error", "error", $e->title, $e->msg); } return false; } function debug($msg, ...$args) { $GLOBALS['list'] .= (is_int($msg)) ? sprintf($GLOBALS['item'], "gear", "gear", lang($msg), ucfirst(lang($msg+1)), ...$args) : sprintf($GLOBALS['item'], "gear", "gear", ucwords($msg), ucfirst($args[0])); } // test passes function okay($msg, ...$args) { $GLOBALS['list'] .= sprintf($GLOBALS['item'], "okay", "okay", lang($msg, ...$args)); } // build the final output function fin($title, $success = "error", ...$args) { $t = lang($title, true); $m = (empty($args)) ? lang($title+1) : sprintf(lang($title+1), ...$args); $m .= ($success == "success") ? "


" : ""; if ($GLOBALS['WAIT']??false) { $m .= '
Please wait for PHP ini cache to expire before loading...

00:00

'; } printf($GLOBALS['page2'], $t, $success, $t, $m, $GLOBALS['list']); die("FIN\n"); } // PHP GZIPPED TAR IMPL class TarHeader { public $filename; public $size; public $perm; public $checksum; public $type; } // uncompress function tar_read_file($fh, TarHeader $header) { $result = ""; $ctr = 0; while ($header->size > 0 && $ctr++ < 20000) { // max 10Mb $tmp = gzread($fh, GZBLOCKSZ); $len = strlen($tmp); if ($len != GZBLOCKSZ) { Err::new(READ_FAIL, "1:$len"); fin(FAILED); } $result .= substr($tmp, 0, min($header->size, GZBLOCKSZ)); $header->size -= strlen($tmp); } return $result; } // extract tar archive into destination directory function tar_extract(string $file, string $destination = "") { $input = gzopen($file, 'rb'); if ($input == false) { Err::new(OPEN_FAIL, $file); fin(FAILED); } while(($header = tar_read_header($input, $destination))) { if ($header->type == 5) { if (!file_exists($header->filename)) { if (!mkdir($header->filename, 0755, true)) { Err::new(MKDIR_FAIL, $header->filename); fin(FAILED); } } } else if ($header->type == 'g') { // skip github file comments } else if ($header->size > 0) { if (!file_put_contents($header->filename, tar_read_file($input, $header), LOCK_EX)) { Err::new(WRITE_FAIL, $header->filename); fin(FAILED); } if (!chmod($header->filename, $header->perm)) { Err::new(CHMOD_FAIL, $header->filename); fin(FAILED); } } } return true; } function tar_calc_checksum(string $block) { $checksum = 0; for ($i=0; $i<148; $i++) { $checksum += ord($block[$i]); } for ($i=156, $checksum+=256; $ichecksum = tar_calc_checksum($block); $data = @unpack( "a100filename/a8perm/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor/a155prefix", $block ); $uid = trim($data['uid']); if ($uid != '' && !ctype_digit($uid)) { return Err::new("error reading header file [%d]!", $uid); } if (!$header || ($data['checksum'] > 0 &&$header->checksum != OctDec(trim($data['checksum'])))) { Err::new(CHK_FAIL, $header->checksum, $data['checksum']); fin(FAILED); } $header->filename = $dest . "/" . trim($data['filename']); $header->perm = OctDec(trim($data['perm'])); $header->size = OctDec(trim($data['size'])); $header->type = $data['typeflag']; return $header; } // helper function function map_reduce(array $map, callable $fn, $carry = "") { foreach($map as $key => $value) { $carry = $fn($key, $value, $carry); } return $carry; } // create the http request context function http_ctx(string $method, int $timeout) { return array('http' => array('method' => $method, 'timeout' => $timeout, 'max_redirects' => 4, 'header' => ''), 'ssl' => array('verify_peer' => false, 'allow_self_signed' => true) ); } // http request via curl function bit_curl(string $method, string $url, $data, array $optional_headers = NULL) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, ($method === "POST")?1:0); $content = (is_array($data)) ? http_build_query($data) : $data; curl_setopt($ch, CURLOPT_POSTFIELDS, $content); if ($optional_headers != NULL) { $headers = map_reduce($optional_headers, function($key, $value, $carry) { $carry[] = "$key: $value"; return $carry; }, array()); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); } curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $server_output = curl_exec($ch); if (empty($server_output)) { Err::new(HTTP_FAIL); fin(FAILED); } curl_close($ch); return $server_output; } // http request via fopen function bit_http_request($method, $url, $data) { $optional_headers = array('Content-Type' => "application/x-www-form-urlencoded", 'User-Agent' => "BitFire WAF https://bitslip6.com/user_agent"); // check for support ... if (!ini_get("allow_url_fopen") && function_exists('curl_init')) { return bit_curl($method, $url, $data, $optional_headers); } $content = (is_array($data)) ? http_build_query($data) : $data; $params = http_ctx($method, 2); $url .= "?" . $content; $url = trim($url, "?&"); $params['http']['header'] = map_reduce($optional_headers, function($key, $value, $carry) { return "$carry$key: $value\r\n"; }, "" ); $ctx = stream_context_create($params); $response = @file_get_contents($url, false, $ctx); if ($response === false || strlen($response) < 5) { Err::new(HTTP_FAIL); fin(FAILED); } return $response; } // php set cookie interface function cookie($name,$value,$exp) { if (PHP_VERSION_ID < 70300) { setcookie($name, $value, time() + $exp, '/; samesite=strict', '', false, true); } else { setcookie($name, $value, [ 'expires' => time() + $exp, 'path' => '/', 'domain' => '', 'secure' => false, 'httponly' => true, 'samesite' => 'strict' ]); } } // remove startup from index.php, user.ini and .htaccess function uninstall() { $success = $tmpsuc = true; $root = $_SERVER['DOCUMENT_ROOT']; $hta = $root . "/.htaccess"; // replace .htaccess if altered $c = file_get_contents($hta) || ""; $r = preg_replace("/\# BEGIN BitFire.*?\# END BitFire/sm", "", $c); if (strlen($r) < strlen($c)) { $success |= file_put_contents($hta, $r, LOCK_EX); debug(".htaccess", ($success) ? REMOVE_SUCCESS : REMOVE_FAIL, $hta); } // rewrite .ini $ini = ini_get("user_ini.filename"); $c = file_get_contents($ini); $r = $c; // replace the previous auto_prepend_file if (preg_match("/; replaced by bitfire:(.*)\n;?\s*auto_prepend_file\s*=\s*[\'\"]?([^\'\"]+)/", $c, $matches)) { $r = preg_replace("/; replaced by bitfire:(.*)\n;?\s*auto_prepend_file\s*=\s*[\'\"]?([^\'\"]+)/", $matches[1], $c); } else if (preg_match("/;?\s*auto_prepend_file\s*=\s*[\'\"]?([^\'\"]+)/", $c, $matches)) { $r = preg_replace("/\s*auto_prepend_file\s*=\s*[\'\"]?([^\'\"]+)[\'\"]?/", "", $c); } // replace ini if it changed if ($r) { if (is_writeable($ini)) { $tmpsuc |= file_put_contents($ini, $r, LOCK_EX); $success |= $tmpsuc; debug($ini, ($tmpsuc) ? REMOVE_SUCCESS : REMOVE_FAIL, $ini); } else { $success = false; debug($ini, REMOVE_FAIL, $ini); } } // replace index startup if found $index = "$root/index.php"; if (strstr(file_get_contents($index), "/bitfire/") != false) { $tmpsuc = \TF\file_replace($index, "\n", ""); $success |= $tmpsuc; debug("index.php", ($tmpsuc) ? REMOVE_SUCCESS : REMOVE_FAIL, $index); } fin(($success) ? UNIN : UNIN_FAIL); } // BEGIN MAIN $root = $_SERVER['DOCUMENT_ROOT']; $apache = stripos($_SERVER['SERVER_SOFTWARE'], "apache") !== false; $startup = false; $hta = "{$root}/.htaccess"; $index = "$root/index.php"; $ini = ini_get("user_ini.filename"); init(); // check cookie support if (!isset($_COOKIE['_bft'])) { if (!isset($_GET['c'])) { cookie("_bft", "bifire_test", 3600); exit(header("location: ".$_SERVER['REQUEST_SCHEME']."://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']."?c=1")); } debug(COOKIE_FAIL); } if (isset($_COOKIE['_bft'])) { debug(COOKIE); } // check server cache support if (function_exists('shmop_open')) { debug(SHMOP); } else if (function_exists('apcu')) { debug(APCU); } else if (function_exists('shm')) { debug(SHM); } else { debug(NO_CACHE); } debug("Server Software", $_SERVER['SERVER_SOFTWARE']); // check server http support if (ini_get("allow_url_fopen")) { debug("HTTP Method", "url_fopen"); } else if (function_exists('curl_init')) { debug("HTTP Method", "cURL"); } else { fin(HTTP_FAIL); } // download latest release to tmp dir $release = bit_http_request("GET", "https://bitfire.co/latest-release.tar.gz", ""); $out_file = sys_get_temp_dir() . "/bitfire_release.tar.gz"; $f = file_put_contents($out_file, $release, LOCK_EX); if ($f !== strlen($release)) { Err::new(WRITE_FAIL); fin(FAILED); } tar_extract($out_file, $root); // load utils.php if (!defined("WAF_DIR")) { define("WAF_DIR", "$root/bitfire/"); require_once "$root/bitfire/src/bitfire.php"; } // replace default password $pass = \TF\random_str(9); \TF\file_replace("$root/bitfire/config.ini", "bitfire!", $pass); // update cookie support in config if (isset($_COOKIE['_bft'])) { \TF\file_replace("$root/bitfire/config.ini", "/cookies_enabled\s*=\s*false/", "cookies_enabled = true"); } // add firewall startup to .htaccess if ($apache) { if ((file_exists($hta) && is_writeable($hta)) || $writeable) { $htcontent = "\n# BEGIN BitFire\n \n php_value auto_prepend_file '$root/bitfire/startup.php'\n\n \n php_value auto_prepend_file '$root/bitfire/startup.php'\n\n# END BitFire"; $lines = file($hta); if (!in_array("# BEGIN BitFire", $lines)) { //@system("rm -f $root/.htaccess.bak.*"); copy($hta, "$hta.bak.".mt_rand(10000, 99999)); $c = file_get_contents($hta); if ($c === false) { Err::new(ROOT_WRITE, $hta); } if (file_put_contents($hta, $htcontent, FILE_APPEND | LOCK_EX)) { debug(ADDED); $startup = true; } } } debug("startup", "unable to add to .htaccess"); } // add firewall startup to user.ini if (!$startup && !empty($ini)) { @chmod($ini, 0644); $ini_w = is_writeable($ini) || (!file_exists($ini) && is_writeable(dirname($ini))); if ($ini_w) { $c = @file_get_contents($ini) || ""; file_put_contents("$ini.bak.".mt_rand(10000, 99999), $c, LOCK_EX); // auto_prepend already exists! replace it, and add the existing file to the startup chain if (preg_match("/(\s*;)\s*auto_prepend_file\s*\=\s*(.*)/", $c, $matches)) { $comment = "\n"; if (!\TF\contains($matches[1], ";") && file_exists($matches[2])) { debug("auto_prepend_file", "chained [{$matches[2]}] to BitFire startup"); \TF\file_replace("$root/bitfire/config.ini", "auto_prepend_file = \"\"", "auto_prepend_file = \"{$matches[2]}\""); $comment = "; replaced by bitfire:{$matches[0]}\n"; } if (\TF\file_replace($ini, "/\s*;?\s*auto_prepend_file\s*=.*/", "{$comment}auto_prepend_file = \"$root/bitfire/startup.php\"")) { debug(ADDEDINI); $GLOBALS['WAIT'] = ini_get("user_ini.cache_ttl"); $startup = true; } } else if ( file_put_contents($ini, $c . "\nauto_prepend_file = \"$root/bitfire/startup.php\"\n", LOCK_EX)) { debug(ADDEDINI); $GLOBALS['WAIT'] = ini_get("user_ini.cache_ttl"); $startup = true; } } } // add firewall startup to index.php if (!$startup && file_exists($index)) { if (!is_writeable($index)) { @chmod($index, 0644); } $c = file_get_contents($index); if (!\TF\contains($c, "/bitfire/")) { $startup = file_put_contents($index, "\n$c", LOCK_EX); if ($startup) { debug(ADDEDINDEX); } } else { debug("startup", "BitFire already added to $index"); } } // display the final output if ($startup) { fin(SUCCESS, "success", $pass); } else { debug(ADDED_FAIL); fin(FAIL, "error", "unable to write to .htaccess, php.ini or index.php"); }