Skip to content

install_source

bitslip6 edited this page Aug 12, 2021 · 1 revision
<?php
error_clear_last();
const MISS_GZ = 2;
const WITH_ZLIB = 4;
const FAILED = 6;
const OPEN_FAIL = 8;
const MKDIR_FAIL = 10;
const WRITE_FAIL = 12;
const CHMOD_FAIL = 14;
const READ_FAIL = 16;
const CHK_FAIL = 18;
const HTTP_FAIL = 20;
const PHP_VER = 22;
const SHMOP = 24;
const APCU = 26;
const SHM = 28;
const SUCCESS = 30;
const ROOT_WRITE = 32;
const COOKIE = 34;
const NO_CACHE = 36;
const INSTALL = 38;
const ADDED = 40;
const UNIN = 42;
class Err
{
    private static $_e = array();
    public $title;
    public $msg;
    public static function new($t, ...$args)
    {
        $e = new Err();
        $e->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",
    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 <a style='color:#506690' href='/?BITFIRE_API=DASHBOARD' title='BitFire dashboard'>BitFire Dashboard</a><br><br>password: <span style='color:#335EEA;text-shadow:none;cursor:pointer;' onclick='copy_text(this)' id='pass'>%s</span> <small style='font-size:.8rem;color:#555;'>click to copy to clipboard</small>",
    UNIN => "SUCCESS",
    UNIN + 1 => "BitFire un-installed successfully.  Bitfire startup removed from .htaccess.  You are no longer protected",
    ROOT_WRITE => "Permission error",
    ROOT_WRITE + 1 => "%s must be writeable to use this install script",
    COOKIE => "no cookie support",
    COOKIE + 1 => "Cookie support required for browser verification",
    NO_CACHE => "no cache support",
    NO_CACHE + 1 => "Server cache recommended for fastest operation",
    ADDED => "BitFire Startup",
    ADDED + 1 => "BitFire has been added to .htaccess"
));

$GLOBALS['page2'] = <<<EOT
<!DOCTYPE html> <html lang="en"> <head>
    <!-- Simple HttpErrorPages | MIT License | https://github.com/HttpErrorPages -->
    <meta charset="utf-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="icon" type="image/png" href="https://bitfire.co/assets/favicon/favicon.ico">
    <title>%s</title>
    <style type="text/css">/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */span.desc{text-shadow:none;color:#555;}span.title{color:#FFF;display:inline-blo0.5;text-align:left;width:150px;white-space:nowrap}html{font-family:sans-serif;line-height:1.15;-ms-text-size-adjust:100%%;-webkit-text-size-adjust:100%%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline;color:#335EEA}b,strong{font-weight:inherit}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%%}sub,sup{font-size:75%%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}template{display:none}[hidden]{display:none}/*! Simple HttpErrorPages | MIT X11 License | https://github.com/AndiDittrich/HttpErrorPages */body,html{width:100%%;height:100%%;}body.error{background-color:#21232a;}body.success{background-color:rgba(66,186,150,0.75);}body{color:#fff;text-align:center;text-shadow:0 2px 4px rgba(0,0,0,.5);padding:0;min-height:100%%;-webkit-box-shadow:inset 0 0 100px rgba(0,0,0,.8);box-shadow:inset 0 0 100px rgba(0,0,0,.8);display:table;font-family:"Open Sans",Arial,sans-serif}h1{font-family:inherit;font-weight:500;line-height:1.1;color:inherit;font-size:36px}h1 small{font-size:68%%;font-weight:400;line-height:1;color:#777}a{text-decoration:none;color:#fff;font-size:inherit;border-bottom:solid 1px #707070}.lead{color:#EEE;font-size:21px;line-height:1.4;text-shadow:none;}.cover{display:table-cell;vertical-align:middle;padding:0 20px}footer{position:fixed;width:100%%;height:40px;left:0;bottom:0;color:#a0a0a0;font-size:14px}li{list-style:none;display:flex;flex-direction:row;align-items:center;}li span{padding-left:20px;}</style>
    <script> function copy_text(elm) { const text = elm.innerText; alert(text+" copied to clipboard"); const textarea = document.createElement('textarea'); textarea.value = text; textarea.setAttribute('readonly', ''); textarea.style.position = 'absolute'; textarea.style.left = '-9999px'; document.body.appendChild(textarea); textarea.select(); try { var successful = document.execCommand('copy'); this.copied = true; } catch(err) { this.copied = false; } textarea.remove(); }
    </script>
</head>
<body class="%s"> <div class="cover"><h1>%s</h1><p class="lead">%s</p><ul style="width:600px;text-align:center;margin:20px auto;">%s</ul><div class="btn"><a style="position:relative;top:30px;" href="https://bitfire.co/support-center" title="">BitFire Support</a></div><br><div class="btn"><a style="position:relative;top:30px;color:#777;" href="/install.php?uninstall=1" title="">Uninstall Bitfire</a></div></div>  </body> </html>
EOT;

$GLOBALS['item'] = <<<EOT
<li><img src="https://bitfire.co/assets/favicon/%s.svg" width="24px" alt="%s" /> <span class='title'> %s </span>&nbsp;- <span class='desc'>%s</span></li>
EOT;
$GLOBALS['list'] = '';

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;
}

function format_error()
{
    $e = error_get_last();
    if (empty($e)) {
        return "";
    }
    error_clear_last();
    return "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]));
}

function okay($msg, ...$args)
{
    $GLOBALS['list'] .= sprintf($GLOBALS['item'], "okay", "okay", lang($msg, ...$args));
}

function fin($title, $success = "error", ...$args)
{
    $t = lang($title, true);
    $m = (empty($args)) ? lang($title + 1) : sprintf(lang($title + 1), ...$args);
    $m .= ($success == "success") ? "<br><br><img width='128' src='https://bitfire.co/assets/check-mark.png'><br>" : "";
    printf($GLOBALS['page2'], $t, $success, $t, $m, $GLOBALS['list']);
    die();
}

class TarHeader
{
    public $filename;
    public $size;
    public $perm;
    public $checksum;
    public $type;
}

function tar_read_file($fh, TarHeader $header)
{
    $result = "";
    $ctr = 0;
    while ($header->size > 0 && $ctr++ < 20000) {
        $tmp = gzread($fh, 512);
        $len = strlen($tmp);
        if ($len != 512) {
            Err::new(READ_FAIL, "1:$len");
            fin(FAILED);
        }
        $result .= substr($tmp, 0, min($header->size, 512));
        $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);
                }
            }
        }
        // skip github file comments
        else if ($header->type == 'g') {
        } 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; $i < 512; $i++) {
        $checksum += ord($block[$i]);
    }
    return $checksum;
}

function tar_read_header($fh, string $dest)
{
    $block = gzread($fh, 512);
    if ($block === false || strlen($block) != 512 || trim($block) === '') {
        return NULL;
    }

    $header = new TarHeader();
    $header->checksum = 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("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;
}


function map_reduce(array $map, callable $fn, $carry = "")
{
    foreach ($map as $key => $value) {
        $carry = $fn($key, $value, $carry);
    }
    return $carry;
}


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)
    );
}

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;
}


function bit_http_request($method, $url, $data)
{

    // 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, "?&");

    $optional_headers = array('Content-Type' => "application/x-www-form-urlencoded", 'User-Agent' => "BitFire WAF https://bitslip6.com/user_agent");

    $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;
}

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']);
    }
}


function uninstall($hta)
{
    $root = $_SERVER['DOCUMENT_ROOT'];
    $c = file_get_contents($hta);
    $r = preg_replace("/\# BEGIN BitFire.*?\# END BitFire/sm", "", $c);
    if (strlen($r) < strlen($c)) {
        file_put_contents($hta, $r, LOCK_EX);
        debug("uninstalled", "BitFire uninstalled");
        fin(UNIN, "success");
    } else {
        debug("error uninstalling", "Unable to auto-uninstall");
        fin(FAILED);
    }
}



// check environment
$root = $_SERVER['DOCUMENT_ROOT'];
$hta = "$root/.htaccess";
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($hta);
}
if (defined("WAF_DIR")) {
    Err::new(INSTALL);
}
if (!empty(Err::get())) {
    fin(FAILED);
}

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);
}
debug("cookie", "cookie support enabled");



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']);
if (ini_get("allow_url_fopen")) {
    debug("HTTP Method", "allow_url_fopen");
} else if (function_exists('curl_init')) {
    debug("HTTP Method", "cURL");
}


$ini = false;
$ini_w = false;
$writeable = (is_writeable($root));

if (!$writeable) {
    Err::new(ROOT_WRITE, $root);
    fin(FAILED);
}

$apache = strpos($_SERVER['SERVER_SOFTWARE'], "Apache") !== false;
if ($apache) {
    if ((file_exists($hta) && is_writeable($hta)) || $writeable) {
        $ini = $hta;
        $ini_w = true;
    }
}
if ($ini == false) {
    $ini = php_ini_loaded_file();
    $ini_w = is_writeable($ini);
}

if ($ini_w) {
    debug("php.ini", "File is writeable");
} else {
    debug("php.ini", "File is not writeable");
}

$destination = "$root/bitfire";
$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);
if (!defined("WAF_DIR")) {
    define("WAF_DIR", "$root/bitfire/");
    require_once "$root/bitfire/src/bitfire.php";
}
$pass = \TF\random_str(9);
\TF\file_replace("$root/bitfire/config.ini", "bitfire!", $pass);

if ($ini_w) {
    $htcontent = "\n# BEGIN BitFire\n
<IfModule mod_php5.c>\n php_value auto_prepend_file '$root/bitfire/startup.php'\n</IfModule>\n
<IfModule mod_php7.c>\n php_value auto_prepend_file '$root/bitfire/startup.php'\n</IfModule>\n# END BitFire";
    $lines = file($hta);
    if (!in_array("# BitFire", $lines)) {
        @system("rm -f $root/.htaccess.bak.*");
        copy($hta, "$hta.bak." . mk_rand(10000, 99999));
        $c = file_get_contents($hta);
        if ($c === false) {
            Err::new(ROOT_WRITE, $hta);
        }
        file_put_contents($hta, $htcontent, FILE_APPEND | LOCK_EX);
    }
    debug(ADDED);
    fin(SUCCESS, "success", $pass);
}

Clone this wiki locally