123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 |
- <?php namespace HashOver;
- // Copyright (C) 2010-2017 Jacob Barkdull
- // This file is part of HashOver.
- //
- // HashOver is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Affero General Public License as
- // published by the Free Software Foundation, either version 3 of the
- // License, or (at your option) any later version.
- //
- // HashOver is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Affero General Public License for more details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with HashOver. If not, see <http://www.gnu.org/licenses/>.
- // Display source code
- if (basename ($_SERVER['PHP_SELF']) === basename (__FILE__)) {
- if (isset ($_GET['source'])) {
- header ('Content-type: text/plain; charset=UTF-8');
- exit (file_get_contents (basename (__FILE__)));
- } else {
- exit ('<b>HashOver</b>: This is a class file.');
- }
- }
- class Setup extends Settings
- {
- public $usage;
- public $encryption;
- public $remoteAccess = false;
- public $pageURL;
- public $pageTitle;
- public $filePath;
- public $threadDirectory;
- public $dir;
- public $URLQueryList = array ();
- public $URLQueries;
- public $executingScript = false;
- // Required extensions to check for
- public $extensions = array (
- 'date',
- 'dom',
- 'mbstring',
- 'mcrypt',
- 'pcre',
- 'PDO',
- 'SimpleXML'
- );
- // Characters that aren't allowed in directory names
- public $reservedCharacters = array (
- '<',
- '>',
- ':',
- '"',
- '/',
- '\\',
- '|',
- '?',
- '&',
- '!',
- '*',
- '.',
- '=',
- '_',
- '+',
- ' '
- );
- // HashOver-specific URL queries to be ignored
- public $ignoredQueries = array (
- 'hashover-reply',
- 'hashover-edit'
- );
- // Default metadata
- public $metadata = array (
- 'title' => '',
- 'url' => '',
- 'status' => 'open',
- 'latest' => array ()
- );
- public function __construct (array $usage)
- {
- parent::__construct ();
- $this->usage = $usage;
- $this->misc = new Misc ($usage['mode']);
- // Check if PHP version is the minimum required
- if (version_compare (PHP_VERSION, '5.3.3') < 0) {
- $version_parts = explode ('-', PHP_VERSION);
- $version = current ($version_parts);
- throw new \Exception ('PHP ' . $version . ' is too old. Must be at least version 5.3.3.');
- }
- // Check for required extensions
- $this->extensionsLoaded ($this->extensions);
- // JSON settings file path
- $json_settings = $this->getAbsolutePath ('settings.json');
- // Check for JSON settings file; parse it if it exists
- if (file_exists ($json_settings)) {
- $this->JSONSettings ($json_settings);
- }
- // Throw exception if for Blowfish hashing support isn't detected
- if ((defined ('CRYPT_BLOWFISH') and CRYPT_BLOWFISH) === false) {
- throw new \Exception ('Failed to find CRYPT_BLOWFISH. Blowfish hashing support is required.');
- }
- // Throw exception if notification email is set to the default
- if ($this->notificationEmail === 'example@example.com') {
- throw new \Exception ('You must use a UNIQUE notification e-mail in ' . __FILE__);
- }
- // Throw exception if encryption key is set to the default
- if ($this->encryptionKey === '8CharKey') {
- throw new \Exception ('You must use a UNIQUE encryption key in ' . __FILE__);
- }
- // Throw exception if administrative password is set to the default
- if ($this->adminPassword === 'password') {
- throw new \Exception ('You must use a UNIQUE admin password in ' . __FILE__);
- }
- // Throw exception if the script wasn't requested by this server
- if ($this->usage['mode'] === 'javascript' and $this->refererCheck () === false) {
- throw new \Exception ('External use not allowed.');
- }
- // Check if we are placing HashOver at a specific script's position
- if (!empty ($_GET['hashover-script'])) {
- // If so, make the script query XSS safe
- $hashover_script = $this->misc->makeXSSsafe ($_GET['hashover-script']);
- // Check if the script query contains a numeric value
- if (is_numeric ($hashover_script)) {
- // If so, set it as the executing script
- $this->executingScript = (int)($hashover_script);
- } else {
- // If not, throw an exception
- throw new \Exception ('Script query must have a numeric value.');
- }
- }
- // Instantiate encryption class
- $this->encryption = new Encryption ($this->encryptionKey);
- // Check if visitor is on mobile device
- if (!empty ($_SERVER['HTTP_USER_AGENT'])) {
- if (preg_match ('/(android|blackberry|phone|mobile|tablet)/i', $_SERVER['HTTP_USER_AGENT'])) {
- // Adjust settings to accommodate
- $this->isMobile = true;
- $this->imageFormat = 'svg';
- }
- }
- }
- public function extensionsLoaded (array $extensions)
- {
- // Throw exceptions if an extension isn't loaded
- foreach ($extensions as $extension) {
- if (extension_loaded ($extension) === false) {
- throw new \Exception ('Failed to detect required extension: ' . $extension . '.');
- }
- }
- }
- public function getAbsolutePath ($file)
- {
- return $this->rootDirectory . '/' . trim ($file, '/');
- }
- protected function JSONSettings ($path)
- {
- // Get JSON data
- $data = @file_get_contents ($path);
- // Load and decode JSON settings file
- $json_settings = @json_decode ($data, true);
- // Return void on failure
- if ($json_settings === null) {
- return;
- }
- // Loop through each setting
- foreach ($json_settings as $key => $value) {
- // Convert setting name to camelCase
- $title_case_key = ucwords (str_replace ('-', ' ', strtolower ($key)));
- $setting = lcfirst (str_replace (' ', '', $title_case_key));
- // Check if the JSON setting property exists in the defaults
- if (property_exists ($this, $setting)) {
- // Check if the JSON value is the same type as the default
- if (gettype ($value) === gettype ($this->{$setting})) {
- // Override default setting
- $this->{$setting} = $value;
- }
- }
- }
- // Synchronize settings
- $this->syncSettings ();
- }
- protected function getDomainWithPort ($url = '')
- {
- // Parse URL
- $url = parse_url ($url);
- if ($url === false or empty ($url['host'])) {
- throw new \Exception ('Failed to obtain domain name.');
- return false;
- }
- // If URL has a port, return domain with port
- if (!empty ($url['port'])) {
- return $url['host'] . ':' . $url['port'];
- }
- // Otherwise return domain without port
- return $url['host'];
- }
- protected function refererCheck ()
- {
- // No referer set
- if (empty ($_SERVER['HTTP_REFERER'])) {
- return false;
- }
- // Get HTTP referer domain with port
- $domain = $this->getDomainWithPort ($_SERVER['HTTP_REFERER']);
- // Check if the script was requested by this server
- if ($domain === $this->domain) {
- return true;
- }
- // Check if the script was requested from an allowed domain
- foreach ($this->allowedDomains as $allowed_domain) {
- $sub_regex = '/^' . preg_quote ('\*\.') . '/';
- $safe_domain = preg_quote ($allowed_domain);
- $domain_regex = preg_replace ($sub_regex, '(?:.*?\.)*', $safe_domain);
- $domain_regex = '/^' . $domain_regex . '$/i';
- if (preg_match ($domain_regex, $domain)) {
- // Setup remote access
- $this->remoteAccess = true;
- $this->httpRoot = $this->absolutePath . $this->httpRoot;
- $this->allowsLikes = false;
- $this->allowsDislikes = false;
- $this->usesAJAX = false;
- $this->syncSettings ();
- return true;
- }
- }
- return false;
- }
- protected function getRequest ($key)
- {
- if (empty ($_GET[$key]) and empty ($_POST[$key])) {
- return false;
- }
- // Attempt to obtain GET data
- if (!empty ($_GET[$key])) {
- $request = $_GET[$key];
- }
- // Attempt to obtain POST data
- if (!empty ($_POST[$key])) {
- $request = $_POST[$key];
- }
- // Strip escape slashes from POST or GET
- if (get_magic_quotes_gpc ()) {
- $request = stripslashes ($request);
- }
- return $request;
- }
- protected function getPageURL ()
- {
- // Attempt to obtain URL via GET or POST
- $request = $this->getRequest ('url');
- // Return on success
- if ($request !== false) {
- return $request;
- }
- // Attempt to obtain URL via HTTP referer
- if (!empty ($_SERVER['HTTP_REFERER'])) {
- return $_SERVER['HTTP_REFERER'];
- }
- // Error on failure
- throw new \Exception ('Failed to obtain page URL.');
- }
- public function setThreadDirectory ($directory_name = '')
- {
- // Replace reserved characters with dashes
- $directory_name = str_replace ($this->reservedCharacters, '-', $directory_name);
- // Remove multiple dashes
- if (mb_strpos ($directory_name, '--') !== false) {
- $directory_name = preg_replace ('/-{2,}/', '-', $directory_name);
- }
- // Remove leading and trailing dashes
- $directory_name = trim ($directory_name, '-');
- // Final comment directory name
- $this->threadDirectory = $directory_name;
- $this->dir = $this->getAbsolutePath ('pages/' . $directory_name);
- }
- protected function getIgnoredQueries ()
- {
- // Ignored URL queries list file
- $ignored_queries = $this->getAbsolutePath ('ignored-queries.json');
- // Queries to be ignored
- $queries = $this->ignoredQueries;
- // Check if ignored URL queries list file exists
- if (file_exists ($ignored_queries)) {
- // If so, get ignored URL queries list
- $data = @file_get_contents ($ignored_queries);
- // Parse ignored URL queries list JSON
- $json = @json_decode ($data, true);
- // Check if file parsed successfully
- if ($json !== null) {
- // If so, merge ignored URL queries file with defaults
- $queries = array_merge ($json, $queries);
- }
- }
- return $queries;
- }
- public function setPageURL ($url = '')
- {
- // Set page URL
- $this->pageURL = $url;
- try {
- // Request page URL by default
- if (empty ($url) or $url === 'request') {
- $this->pageURL = $this->getPageURL ();
- }
- // Strip HTML tags from page URL
- $this->pageURL = strip_tags (html_entity_decode ($this->pageURL, false, 'UTF-8'));
- // Turn page URL into array
- $url_parts = parse_url ($this->pageURL);
- // Set initial path
- if (empty ($url_parts['path']) or $url_parts['path'] === '/') {
- $this->threadDirectory = 'index';
- $this->filePath = '/';
- } else {
- // Remove starting slash
- $this->threadDirectory = mb_substr ($url_parts['path'], 1);
- // Set file path
- $this->filePath = $url_parts['path'];
- }
- // Remove unwanted URL queries
- if (!empty ($url_parts['query'])) {
- $url_queries = explode ('&', $url_parts['query']);
- $ignored_queries = $this->getIgnoredQueries ();
- for ($q = 0, $ql = count ($url_queries); $q < $ql; $q++) {
- if (!in_array ($url_queries[$q], $ignored_queries, true)) {
- $equals = explode ('=', $url_queries[$q]);
- if (!in_array ($equals[0], $ignored_queries, true)) {
- $this->URLQueryList[] = $url_queries[$q];
- }
- }
- }
- $this->URLQueries = implode ('&', $this->URLQueryList);
- $this->threadDirectory .= '-' . $this->URLQueries;
- }
- // Encode HTML characters in page URL
- $this->pageURL = htmlspecialchars ($this->pageURL, false, 'UTF-8', false);
- // Final URL
- if (!empty ($url_parts['scheme']) and !empty ($url_parts['host'])) {
- $this->pageURL = $url_parts['scheme'] . '://';
- $this->pageURL .= $url_parts['host'];
- } else {
- throw new \Exception ('URL needs a hostname and scheme.');
- return;
- }
- // Add optional port to URL
- if (!empty ($url_parts['port'])) {
- $this->pageURL .= ':' . $url_parts['port'];
- }
- // Add file path
- $this->pageURL .= $this->filePath;
- // Add option queries
- if (!empty ($this->URLQueries)) {
- $this->pageURL .= '?' . $this->URLQueries;
- }
- // Set thread directory name to page URL
- $this->setThreadDirectory ($this->threadDirectory);
- } catch (\Exception $error) {
- throw new \Exception ($error->getMessage ());
- }
- }
- protected function getPageTitle ()
- {
- // Attempt to obtain title via GET or POST
- $request = $this->getRequest ('title');
- // Return on success
- if ($request !== false) {
- return $request;
- }
- // Return empty string by default
- return '';
- }
- public function setPageTitle ($title = '')
- {
- // Set page title
- $this->pageTitle = $title;
- // Request page title by default
- if ($title === 'request') {
- $this->pageTitle = $this->getPageTitle ();
- }
- // Strip HTML tags from page title
- $this->pageTitle = strip_tags (html_entity_decode ($this->pageTitle, false, 'UTF-8'));
- // Encode HTML characters in page title
- $this->pageTitle = htmlspecialchars ($this->pageTitle, false, 'UTF-8', false);
- }
- // Check if a give API format is enabled
- public function APIStatus ($api)
- {
- // Check if all available APIs are enabled
- if ($this->enablesAPI === true) {
- return 'enabled';
- }
- // Check if the given API is enabled
- if (is_array ($this->enablesAPI)) {
- if (in_array ($api, $this->enablesAPI)) {
- return 'enabled';
- }
- }
- // Assume API is disabled by default
- return 'disabled';
- }
- // Check if user name and password again admin name and password
- public function verifyAdmin ($name, $password)
- {
- // Check if user is logged in as admin
- if ($name === $this->adminName) {
- if ($this->encryption->verifyHash ($this->adminPassword, $password) === true) {
- return true;
- }
- }
- return false;
- }
- }
|